实现一个基于python+opencv的简单个人图片检测标注工具

项目背景

最近有一个简单的图片检测任务,需要对目标图片进行简单的多点标注。网上一些开源的项目,例如labelme什么的由于各式各样的原因,都没能配置成功。加上由于项目本省不是很复杂,故萌发了自己基于python+opencv搭建一个简单标注工具的想法。最终效果如下:
在这里插入图片描述

前期准备

  • python 3.0 up
  • opencv 4.0 (低版本的opencv也可以适用,具体参见相关opencv文档)

任务整理

基本任务:

  1. 完成对目标图像的点标注
  2. 记录标注点的坐标

基本流程:

  1. 使用opencv读取图片,并且显示
  2. 在显示的窗口上通过鼠标点击方式标注关键点,并且记录
  3. 标注完成后读取下一张图片,重复标注过程
  4. 所有标注完成后将图片关键点记录输出为文件

补充功能

  1. [-] 通过右键点击方式取消标注点,并且在图片上显示(未完全实现)

实现细节:

1. 实现通过鼠标点击控制opencv窗口

opencv 里面有特定函数 cv2.setMouseCallback() 来完成鼠标控制。具体使用方式可以参考其官方文档,不再细数(我也没看细节)。重点关注一下其实现方式。

具体应用时,首先需要定义一个响应函数,名字可以自取,如 on_EVENT_MOUSE, 其输入参数是固定的:event,x,y,flags,param。其中event是鼠标的操作方式,x,y是鼠标当前位置,flags在本应用中没有使用到,就不细述了,param可以用来传递参数。本函数没有返回值。

而在主函数中,通过上文说到 cv2.setMouseCallback() 来调用这个相应函数进行对鼠标点击的相应,其具体代码如下:

def on_EVENT_MOUSE(event,x,y,flags,param):
    外部需要读取的内容 = param
    if event == cv2.EVENT_LBUTTONDOWN:
        点击鼠标左键后做什么
    if event == cv2.EVENT_LBUTTONDOWN:
        点击鼠标右键后做什么
... 

def main(**kargs):
    ...
    cv2.setMouseCallback("image",on_EVENT_MOUSE,(*待传入的内容))
    # "image" 是当前窗口的名称,on_EVENT_MOUSE 是响应函数的名称,后面跟待传入的内容
    ...

2. 具体实现点击内容

  1. 左键单击后,需要完成的工作有两个:

    • 记录现有位置
    • 在窗口上有所表示

    我通过全局变量完成第一个目标。即最开始定义了一个全局变量points,并且在响应函数和main函数中开头定义 global points,即可在函数中操作此变量,具体实现如下:

points = []

def on_EVENT_MOUSE(event,x,y,flags,param):
global points
if event == cv2.EVENT_LBUTTONDOWN:
    points.append([x,y]) # 将鼠标当前位置存到points里面

...

def main(**kargs):
    global points
    use_points(points)

第二个目标实现比较简单,可以通过调用cv2的画图函数完成。注意一点,需要重新显示一下图片,并且使用同样的窗口名。

  1. 右键单击后,希望实现功能:
  • 删除标注点
  • 窗口上删除标注

第一点是先比较容易,通过 pop方式即可将points最后标注的点删除。第二点暂时没有发现好的实现方式。本文通过将原本标记为黑色的部分标记为白色实现,胜在相应迅速,但效果不是很好。之后可以尝试其他开发方法。

3. 一些其他注意事项

  1. 窗口大小调整:

    使用默认cv2.imshow的话,图片会按照实际尺寸打开。一般而言,手机照相机出来的图片都会比较大。不进行修改的话,屏幕是很难放下的。所以建议对窗口尺寸进行设置

  2. 存储形式

    现在为了方便起见,且由于个人使用原因,固定每张图输入八点坐标,且在整体txt上面按照 x1,y1,x2,y2 … x8,y8的顺序存储。后续待优化此处。

完整代码:

git地址, 顺手点个star也是极好的

import cv2
import numpy as np
import os 

points = []

def on_EVENT_MOUSE(event, x, y, flags, param):
    global points
    img = param
    if event == cv2.EVENT_LBUTTONDOWN:
        xy = "%d,%d" % (x, y)
        points.append([x,y])
        cv2.circle(img, (x, y), 10, (255, 0, 0), thickness = -1)
        cv2.putText(img, xy, (x, y), cv2.FONT_HERSHEY_PLAIN,
                    10.0, (0,0,0), thickness = 10)
        cv2.imshow("image", img)
    if event == cv2.EVENT_RBUTTONDOWN:
        x1,y1 = points.pop()
        xy = "%d,%d" % (x1, y1)
        cv2.circle(img, (x1, y1), 10, (255, 255, 255), thickness = -1)
        cv2.putText(img, xy, (x1, y1), cv2.FONT_HERSHEY_PLAIN,
                    10.0, (255,255,255), thickness = 10)
        cv2.imshow("image", img)



def main(path):
    global points
    for name in os.listdir(images_path):
        image_name = os.path.join(images_path, name)
        image = cv2.imread(image_name)
        h,w,_ = image.shape
        cv2.namedWindow("image",cv2.WINDOW_NORMAL)
        if h>w:
            cv2.resizeWindow('image',720,960)
        else:
            cv2.resizeWindow("image",960,720)
        cv2.setMouseCallback("image",on_EVENT_MOUSE,image)
        while(1):
            cv2.imshow("image", image)
            if cv2.waitKey(0)&0xFF == 27:
                break
        cv2.destroyAllWindows()
        if len(points) != 8:
            break
        print(points)
        with open("label.txt",'a',encoding='utf8') as record:
            str1 = '{}{}'.format(name[:-3],'\t')
            for x,y in points:
                str1 += '{} {} '.format(x,y)
            str1 += '\n'
            record.write(str1)
        points = []

if __name__ == '__main__':
    images_path  = './images'
    main(images_path)
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值