最近读了一些代码,不同的开源框架采用画图方式不一样,就学习了一下。

目标检测普遍的正矩形(hbb)其实就是四边形的一个特例,以下的代码都是以绘制四边形的检测结果为例。

opencv

def draw_box_cv(img, boxes, labels, scores):
    img = img + np.array([103.939, 116.779, 123.68])
    boxes = boxes.astype(np.int64)
    labels = labels.astype(np.int32)
    img = np.array(img, np.float32)
    img = np.array(img*255/np.max(img), np.uint8)

    num_of_object = 0
    for i, box in enumerate(boxes):
        x_c, y_c, w, h, theta = box[0], box[1], box[2], box[3], box[4]

        label = labels[i]
        if label != 0:
            num_of_object += 1
            color = (np.random.randint(255), np.random.randint(255), np.random.randint(255))
            rect = ((x_c, y_c), (w, h), theta)
            #根据四边形的中心x_c, y_c,w,h以及偏移角度theta,恢复出四个点点的坐标
            rect = cv2.boxPoints(rect)
            rect = np.int0(rect)  #转为int
            cv2.drawContours(img, [rect], -1, color, 3)  #在图中根据rect绘制

            category = LABEl_NAME_MAP[label]  #类别

            if scores is not None:
                cv2.rectangle(img,
                              pt1=(x_c, y_c),
                              pt2=(x_c + 120, y_c + 15),
                              color=color,
                              thickness=-1)
                cv2.putText(img,
                            text=category+": "+str(scores[i]),
                            org=(x_c, y_c+10),
                            fontFace=1,
                            fontScale=1,
                            thickness=2,
                            color=(color[1], color[2], color[0]))
            else:
                cv2.rectangle(img,
                              pt1=(x_c, y_c),
                              pt2=(x_c + 40, y_c + 15),
                              color=color,
                              thickness=-1)
                cv2.putText(img,
                            text=category,
                            org=(x_c, y_c + 10),
                            fontFace=1,
                            fontScale=1,
                            thickness=2,
                            color=(color[1], color[2], color[0]))
    cv2.putText(img,
                text=str(num_of_object),
                org=((img.shape[1]) // 2, (img.shape[0]) // 2),
                fontFace=3,
                fontScale=1,
                color=(255, 0, 0))
return img

cv2实际上用起来是最方便的了,但是唯一的缺点就是我要写检测结果的标注text的时候,不能写中文,如果写出来的画都变成问号了。因此就尝试了用PIL

PIL

def draw_box_cv(img, imgname, boxes, labels, scores):
    img = img + np.array([103.939, 116.779, 123.68])
    boxes = boxes.astype(np.int64)
    labels = labels.astype(np.int32)
    img = np.array(img, np.float32)
    img = np.array(img*255/np.max(img), np.uint8)

    num_of_object = 0

    img_PIL = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))#由于读取图片用到的函数是cv2,所以读入的图片是BGR的格式,因此要转变成RGB
    draw = ImageDraw.Draw(img_PIL)  #创建pil的画笔
    font = ImageFont.truetype("/usr/share/fonts/truetype/sim/simhei.ttf", 20)  # 参数1:字体文件路径,参数2:字体大小
    for i, box in enumerate(boxes):
        x_c, y_c, w, h, theta = box[0], box[1], box[2], box[3], box[4]

        label = labels[i]
        if label != 0:
            num_of_object += 1
            color = (np.random.randint(255), np.random.randint(255), np.random.randint(255))
            rect = ((x_c, y_c), (w, h), theta)
            rect = cv2.boxPoints(rect)
            rect = np.int0(rect)
            draw.polygon([(rect[0,0],rect[0,1]),#制检测的方框
                          (rect[1,0],rect[1,1]),
                          (rect[2,0],rect[2,1]),
                          (rect[3,0],rect[3,1])], outline=color)
            category = LABEl_NAME_MAP[label]             #类别


            if scores is not None:
                outlines = '{:s} {:.3f}'.format(category, scores[i])
                outlines = outlines.decode('utf-8') #一定要有这句话!
                draw.text((x_c, y_c+10), outlines ,color=(color[1], color[2], color[0]),font = font)
            else:
                outlines = category.decode('utf-8') #一定要有这句话!
                draw.text((x_c, y_c+10), outlines,color=(color[1], color[2], color[0]),font = font)

    ext = os.path.splitext(imgname)[-1]
    img_name = os.path.splitext(imgname)[0]
    outname = img_name + '_fpn' + ext
    img_PIL.save(cfgs.INFERENCE_SAVE_PATH + '/'+ outname)

PIL的优点就是可以写中文的标注了,但是缺点就是画四边形的时候不能改变线宽,以至于画出来非常细,很影响观看。(如果是要绘制矩形的检测结果,是可以改变线宽的)

matplotlib

def draw_box_cv(img, imgname, gt, savepath, boxes, labels, scores):
    img = img + np.array([103.939, 116.779, 123.68])
    boxes = boxes.astype(np.int64)
    labels = labels.astype(np.int32)
    img = np.array(img, np.float32)
    img = np.array(img*255/np.max(img), np.uint8)

    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)#由于读取图片用到的函数是cv2,所以读入的图片是BGR的格式,因此要转变成RGB
    num_of_object = 0
    dpi=100  #一般300dpi就比较清晰了
    fig = plt.figure(frameon=False)
    fig.set_size_inches(img.shape[1]/dpi, img.shape[0]/dpi)
    ax = plt.Axes(fig, [0., 0., 1., 1.])
    ax.axis('off')
    fig.add_axes(ax)
    ax.imshow(img)
    for i, box in enumerate(boxes):
        x_c, y_c, w, h, theta = box[0], box[1], box[2], box[3], box[4]

        label = labels[i]
        if label != 0:
            num_of_object += 1
            color = (rand(), rand(), rand())
            rect = ((x_c, y_c), (w, h), theta)
            rect = cv2.boxPoints(rect) #根据上面的((x_c, y_c), (w, h), theta)变换成四个点的坐标
            rect = np.int0(rect)
            rect = mpathes.Polygon(rect,fill=False,
                                   edgecolor=color, linewidth=2.0) #画多边行,不填充,线的宽度为2.5
            ax.add_patch(rect)

            category = LABEl_NAME_MAP[label]

            if scores is not None:
                ax.text(x_c, y_c+10,
                        '{:s} {:.3f}'.format(category, scores[i]),
                        bbox=dict(facecolor='blue', alpha=0.3),#在写标注的位置会绘制一个据带填充的矩形框,其透明度为alpha
                        fontsize=14, color='red')
            else:
                ax.text(x_c, y_c+10,
                        '{:s}'.format(category),
                        bbox=dict(facecolor='blue', alpha=0.3),
                        fontsize=14, color='red')
    ext = os.path.splitext(imgname)[-1]
    img_name = os.path.splitext(imgname)[0]
    if gt == True:
        outname = img_name + '_gt' + ext
    else:
        outname = img_name + '_fpn' + ext
    fig.savefig(savepath + '/'+ outname)
    plt.close('all')

matplotlib的优点就是可以写中文的标注,但是需要配置一下:windows及ubuntu下解决matplotlib显示中文文本为方框的问题

缺点就是保存的图片可能会有白色的边框

03-07 11:54