palabras escritas delante
Recientemente, la escuela está cerrada y el dormitorio está cerrado. La red del dormitorio no es buena y el experimento no se puede ejecutar de forma remota. Escribiré una interpretación del código fuente de CenterNet. El trabajo que quedó desde el momento en que escribí la tesis. , ¡respeto!
Este artículo es principalmente para analizar parte del código que genera el núcleo gaussiano en CenterNet. El principio específico no se explicará en detalle, pero este artículo agrega un código Puede ejecutarlo usted mismo y puede entenderlo cuando depura el significado del autor, espero que sea útil para los lectores.
Enlace de descarga de código visual: https://download.csdn.net/download/weixin_42899627/87157112
Ubicación del código fuente de Centernet
El código central de este artículo se puede encontrar en CenterNet/src/lib/utils/image.py
La fórmula para la función gaussiana bidimensional
La función gaussiana bidimensional en el código fuente de CenterNet se implementa de la siguiente manera:
consejo: falta algo en la fórmula de comparación, pero no afecta las características de la función gaussiana. La clave aquí es observar el cálculo del radio del núcleo gaussiano
def gaussian2D(shape, sigma=1):
m, n = [(ss - 1.) / 2. for ss in shape]
y, x = np.ogrid[-m:m + 1, -n:n + 1]#np.orgin 生成二维网格坐标
h = np.exp(-(x * x + y * y) / (2 * sigma * sigma))
h[h < np.finfo(h.dtype).eps * h.max()] = 0 #np.finfo()常用于生成一定格式,数值较小的偏置项eps,以避免分母或对数变量为零
return h
Cálculo del radio del núcleo gaussiano
Desde el punto de vista del código, es la fórmula para encontrar la raíz de la ecuación cuadrática en una variable
Cabe señalar aquí que el cálculo del radio gaussiano en el código se basa en los puntos de las esquinas del marco, mientras que en Centernet, lo que se necesita calcular es el radio gaussiano del punto central del marco. la razón es la misma. El desplazamiento de los puntos de las esquinas del marco de la red central se puede aproximar para el desplazamiento del punto central de la caja
Caso 1: Ambas esquinas están dentro del cuadro de verdad
Caso 2: Ambas esquinas están fuera del cuadro de verdad
Caso 3: Una esquina está dentro del cuadro de verdad y la otra esquina está fuera del cuadro de verdad
Artículo de referencia:
radio guassiano de CornerNet Determinación del radio gaussiano: fórmula matemática
punto de explicación detallada Código de Cornernet/Centernet dentro del mapa de calor GT cómo aplicar el kernel de dispersión gaussiana
def gaussian_radius(det_size, min_overlap=0.7):
height, width = det_size
a1 = 1
b1 = (height + width)
c1 = width * height * (1 - min_overlap) / (1 + min_overlap)
sq1 = np.sqrt(b1 ** 2 - 4 * a1 * c1)
r1 = (b1 + sq1) / 2
a2 = 4
b2 = 2 * (height + width)
c2 = (1 - min_overlap) * width * height
sq2 = np.sqrt(b2 ** 2 - 4 * a2 * c2)
r2 = (b2 + sq2) / 2
a3 = 4 * min_overlap
b3 = -2 * min_overlap * (height + width)
c3 = (min_overlap - 1) * width * height
sq3 = np.sqrt(b3 ** 2 - 4 * a3 * c3)
r3 = (b3 + sq3) / 2
return min(r1, r2, r3)
La función draw_umich_gaussian en el código fuente de CenterNet se implementa de la siguiente manera:
Consejo: No hay una operación especial, principalmente para colocar un kernel gaussiano bidimensional generado (tamaño de fotograma de destino) en la posición correspondiente de la imagen original (tamaño de imagen)
def draw_umich_gaussian(heatmap, center, radius, k=1):
diameter = 2 * radius + 1
gaussian = gaussian2D((diameter, diameter), sigma=diameter / 6)
x, y = int(center[0]), int(center[1])
height, width = heatmap.shape[0:2]
left, right = min(x, radius), min(width - x, radius + 1)
top, bottom = min(y, radius), min(height - y, radius + 1)
masked_heatmap = heatmap[y - top:y + bottom, x - left:x + right]
masked_gaussian = gaussian[radius - top:radius + bottom, radius - left:radius + right]
if min(masked_gaussian.shape) > 0 and min(masked_heatmap.shape) > 0: # TODO debug
np.maximum(masked_heatmap, masked_gaussian * k, out=masked_heatmap)#逐个元素比较大小,保留大的值
return heatmap
import numpy as np
import math
import xml.etree.ElementTree as ET
import glob
from image import draw_dense_reg, draw_msra_gaussian, draw_umich_gaussian
from image import get_affine_transform, affine_transform, gaussian_radius
data_dir = r"*.jpg"
a_file = glob.glob(data_dir)[0]
print(a_file, a_file.replace(".jpg", ".xml"))
tree = ET.parse(a_file.replace(".jpg", ".xml"))
root = tree.getroot()
size = root.find('size')
width = int(size.find('width').text)
height = int(size.find('height').text)
print(f"原图宽:{
width} 高:{
height}")
num_classes = 3
output_h = height
output_w = width
hm = np.zeros((num_classes, output_h, output_w), dtype=np.float32)
anns = []
for obj in root.iter('object'):
bbox = obj.find('bndbox')
cate = obj.find('name').text
# print(cate, bbox.find("xmin").text, bbox.find("xmax").text,
# bbox.find("ymin").text, bbox.find("ymax").text)
xyxy = [int(bbox.find("xmin").text), int(bbox.find("ymin").text),
int(bbox.find("xmax").text),int(bbox.find("ymax").text)]
anns.append({
"bbox" : xyxy,'category_id':int(cate)})
num_objs = len(anns)
flipped = False #是否经过全图翻转
import matplotlib.pyplot as plt
plt.figure(figsize=(19, 6))
plt.ion()
plt.subplot(131)
img = plt.imread(a_file)
plt.title('Origin_img')
plt.imshow(img)
for k in range(num_objs):
ann = anns[k]
bbox = ann['bbox']
cls_id = ann['category_id']
if flipped:
bbox[[0, 2]] = width - bbox[[2, 0]] - 1
# bbox[:2] = affine_transform(bbox[:2], trans_output)# 仿射变换
# bbox[2:] = affine_transform(bbox[2:], trans_output)
# bbox[[0, 2]] = np.clip(bbox[[0, 2]], 0, output_w - 1)#裁剪
# bbox[[1, 3]] = np.clip(bbox[[1, 3]], 0, output_h - 1)
h, w = bbox[3] - bbox[1], bbox[2] - bbox[0]
if h > 0 and w > 0:
radius = gaussian_radius((math.ceil(h), math.ceil(w)))
radius = max(0, int(radius))
# radius = self.opt.hm_gauss if self.opt.mse_loss else radius
ct = np.array(
[(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2], dtype=np.float32)
ct_int = ct.astype(np.int32)
plt.subplot(133)
hm_out, gaussian = draw_umich_gaussian(hm[cls_id], ct_int, radius)
plt.title('Umich Heatmap')
# hm_out = draw_msra_gaussian(hm[cls_id], ct_int, radius)
# print(hm_out.shape)
# plt.title("Mara Heatmap")
plt.text(ct[0], ct[1], f"(class:{
cls_id})", c='white')
plt.plot([bbox[0], bbox[2], bbox[2], bbox[0], bbox[0]], [bbox[1], bbox[1], bbox[3], bbox[3], bbox[1]])
plt.imshow(hm_out)
plt.subplot(132)
plt.title(f'Gaussian: bbox_h={
h},bbox_w={
w}, radius={
radius}')
plt.imshow(gaussian)
plt.pause(2)
artículo de referencia
1. Uso de np.ogrid y np.mgrid
2. Funciones gaussianas unidimensionales y bidimensionales y sus primeras y segundas derivadas