python图像处理笔记(六):⼿动获取坐标标注图像
引⾔
之前的两篇⽂章有提到图⽚标注,但⼀个是⽤yolo算法给识别到的图像加框,⼀个是根据霍夫变换做直线与圆的检测,本篇想总结⼀下根据⿏标点击来对图像做⼀些填充处理的⽅式。
opencv⿏标事件
介绍的内容参考⾃,关于opencv⿏标点击有多少种事件,我们可以通过dir过滤筛选出带"EVENT"的字样,为:
import cv2
events=[i for i in dir(cv2)if'EVENT'in i]#将全部事件保存在列表中
print(events)#将列表打印
然后就能知道相关事件:
EVENT_FLAG_LBUTTON 1#左鍵拖曳
EVENT_FLAG_RBUTTON 2#右鍵拖曳
EVENT_FLAG_MBUTTON 4#中鍵拖曳
EVENT_FLAG_CTRLKEY 8#(8~15)按Ctrl不放事件
EVENT_FLAG_SHIFTKEY 16#(16~31)按Shift不放事件
EVENT_FLAG_ALTKEY 32#(32~39)按Alt不放事件
EVENT_MOUSEMOVE 0#滑动
EVENT_LBUTTONDOWN 1#左键点击
EVENT_RBUTTONDOWN 2#右键点击
EVENT_MBUTTONDOWN 3#中键点击
EVENT_LBUTTONUP 4#左键放开
EVENT_RBUTTONUP 5#右键放开
EVENT_MBUTTONUP 6#中键放开
EVENT_LBUTTONDBLCLK 7#左键双击
EVENT_RBUTTONDBLCLK 8#右键双击
EVENT_MBUTTONDBLCLK 9#中键双击
那么根据相关事件类别,就能绑定相应事件完成,绑定⿏标左键与fillPoly完成任意形状的填充:
# coding: utf-8
import cv2
弯的fourimport numpy as np
img = cv2.imread(r"C:\Urs\admin\Pictures\Saved Pictures\333.jpg") # print img.shape
a =[]
def on_EVENT_LBUTTONDOWN(event, x, y, flags, param):
global a
if event == cv2.EVENT_LBUTTONDOWN:
xy ="%d,%d"%(x, y)
a.append([x,y])
cv2.circle(img,(x, y),1,(255,0,0), thickness=1)
cv2.putText(img, xy,(x, y), cv2.FONT_HERSHEY_PLAIN,
1.0,(255,0,0), thickness=1)
# cv2.(img, int(xy), int(a[-1]), color=(255, 0, 0), thickness=1)
cv2.imshow("image", img)
k = cv2.waitKey(0)
if(k ==ord('s')):
cc = np.array([a], dtype=np.int32)固阴煎
# print(cc)
# qq = np.array([[[0,0],[100,0],[100,100],[0,100]]])
# print(qq)
cv2.fillPoly(img, np.array(cc),(255,255,0))
cv2.imshow("image", img)
a.clear()
cv2.namedWindow("image")
cv2.tMouCallback("image", on_EVENT_LBUTTONDOWN)
cv2.imshow("image", img)
while(True):
try:
cv2.waitKey(100)
except Exception:
cv2.destroyWindow("image")
break
cv2.waitKey(0)
cv2.destroyAllWindow()
对图像区域进⾏填充的业务场景⼀般是需要针对图像的某处区域做着重算法分析,或者说对⽐测试才会使⽤,因为在项⽬⾥会有奇效。然后我当时是直接根据前端传值并通过查询数据库⾃动匹配区域达到⽬的,然后根据watchdog与其它程序出的图做对⽐,这也只是⼀种。上述这种⼿动的,还能加⼊撤销以及⾃动做成标注,可以参考:
因为上述参考链接中的代码基于python2,所以我改成了python3的并添加了⼀些注释,如果想要撤销
回上⼀次的操作,可以跟我上述那样,⽤⼀个全局列表对其进⾏append和pop,也可以写成类来不断调⽤:
import os
import cv2
# tkinter是Python内置的简单GUI库,实现⼀些⽐如打开⽂件夹,确认删除等操作⼗分⽅便
from tkinter.filedialog import askdirectory
ssagebox import askyesno
# 定义标注窗⼝的默认名称
WINDOW_NAME ='Simple Bounding Box Labeling Tool'
# 定义画⾯刷新的⼤概帧率(是否能达到取决于电脑性能)
FPS =24
# 定义⽀持的图像格式
SUPPOTED_FORMATS =['jpg','jpeg','png']
# 定义默认物体框的名字为Object,颜⾊蓝⾊,当没有⽤户⾃定义物体时⽤默认物体
DEFAULT_COLOR ={'Object':(255,0,0)}
# 定义灰⾊,⽤于信息显⽰的背景和未定义物体框的显⽰
COLOR_GRAY =(192,192,192)
# 在图像下⽅多出BAR_HEIGHT这么多像素的区域⽤于显⽰⽂件名和当前标注物体等信息
BAR_HEIGHT =16
# 上下左右,ESC及删除键对应的cv.waitKey()的返回值
# 注意这个值根据操作系统不同有不同,可以通过6.4.2中的代码获取
KEY_UP =65362
KEY_UP =65362
KEY_DOWN =65364
KEY_LEFT =65361
KEY_RIGHT =65363
KEY_ESC =27
KEY_DELETE =65535
# 空键⽤于默认循环
KEY_EMPTY =0
get_bbox_name ='{}.bbox'.format
# 定义物体框标注⼯具类
class SimpleBBoxLabeling:
def__init__(lf, data_dir, fps=FPS, window_name=None):
lf._data_dir = data_dir
lf.fps = fps
lf.window_name = window_name if window_name el WINDOW_NAME
# pt0是正在画的左上⾓坐标,pt1是⿏标所在坐标
lf._pt0 =None
lf._pt1 =None
# 表明当前是否正在画框的状态标记
lf._drawing =Fal
打扫厕所
# 当前标注物体的名称
lf._cur_label =None
# 当前图像对应的所有已标注框
微信如何添加表情包
lf._bboxes =[]
# 如果有⽤户⾃定义的标注信息则读取,否则⽤默认的物体和颜⾊
label_path ='{}.labels'.format(lf._data_dir)
lf.label_colors = DEFAULT_COLOR if not ists(label_path)el lf.load_labels(label_path) # 获取已经标注的⽂件列表和还未标注的⽂件列表
imagefiles =[x for x in os.listdir(lf._data_dir)if x[x.rfind('.')+1:].lower()in SUPPOTED_FORMATS] labeled =[x for x in imagefiles if ists(get_bbox_name(x))]
to_be_labeled =[x for x in imagefiles if x not in labeled]
# 每次打开⼀个⽂件夹,都⾃动从还未标注的第⼀张开始
lf._filelist = labeled + to_be_labeled
lf._index =len(labeled)
if lf._index >len(lf._filelist)-1:
lf._index =len(lf._filelist)-1
# ⿏标回调函数消防联动
def_mou_ops(lf, event, x, y, flags, param):
# 按下左键时,坐标为左上⾓,同时表明开始画框,改变drawing标记为True
if event == cv2.EVENT_LBUTTONDOWN:
lf._drawing =True
lf._pt0 =(x, y)
# 左键抬起,表明当前框画完了,坐标记为右下⾓,并保存,同时改变drawing标记为Fal
elif event == cv2.EVENT_LBUTTONUP:
lf._drawing =Fal
lf._pt1 =(x, y)
白甲症lf._bboxes.append((lf._cur_label,(lf._pt0, lf._pt1)))
# 实时更新右下⾓坐标⽅便画框
elif event == cv2.EVENT_MOUSEMOVE:
lf._pt1 =(x, y)
lf._pt1 =(x, y)
# ⿏标右键删除最近画好的框
elif event == cv2.EVENT_RBUTTONUP:
if lf._bboxes:
lf._bboxes.pop()
# 清除所有标注框和当前状态
def_clean_bbox(lf):
lf._pt0 =None
lf._pt1 =None
lf._drawing =Fal
lf._bboxes =[]
# 画标注框和当前信息的函数
def_draw_bbox(lf, img):
# 在图像下⽅多出BAR_HEIGHT这么多像素的区域⽤于显⽰⽂件名和当前标注物体等信息
h, w = img.shape[:2]
canvas = pyMakeBorder(img,0, BAR_HEIGHT,0,0, cv2.BORDER_CONSTANT, value=COLOR_GRAY) # 正在标注的物体信息,如果⿏标左键已经按下,则显⽰两个点坐标,否则显⽰当前待标注物体的名称
label_msg ='{}: {}, {}'.format(lf._cur_label, lf._pt0, lf._pt1) \
if lf._drawing \
el'Current label: {}'.format(lf._cur_label)
# 显⽰当前⽂件名,⽂件个数信息
msg ='{}/{}: {} | {}'.format(lf._index +1,len(lf._filelist), lf._filelist[lf._index], label_msg)
cv2.putText(canvas, msg,(1, h +12),
cv2.FONT_HERSHEY_SIMPLEX,
0.5,(0,0,0),1)
# 画出已经标好的框和对应名字
for label,(bpt0, bpt1)in lf._bboxes:
print(lf._bboxes,"..........11111")
label_color = lf.label_colors[label]if label in lf.label_colors el COLOR_GRAY
cv2.putText(canvas, label,(bpt0[0]+3, bpt0[1]+15),
cv2.FONT_HERSHEY_SIMPLEX,
0.5, label_color,2)
# 画正在标注的框和对应名字
if lf._drawing:
label_color = lf.label_colors[lf._cur_label]if lf._cur_label in lf.label_colors el COLOR_GRAY if lf._pt1[0]>= lf._pt0[0]and lf._pt1[1]>= lf._pt0[1]:
cv2.putText(canvas, lf._cur_label,(lf._pt0[0]+3, lf._pt0[1]+15),
cv2.FONT_HERSHEY_SIMPLEX,
企业管理理念
0.5, label_color,2)
return canvas
# 利⽤repr()导出标注框数据到⽂件
新年对老师的祝福语
@staticmethod
def export_bbox(filepath, bboxes):
if bboxes:
with open(filepath,'w+')as f:
for bbox in bboxes:
line =repr(bbox)+'\n'
f.write(line)
elif ists(filepath):
# 利⽤eval()读取标注框字符串到数据
@staticmethod
def load_bbox(filepath):
bboxes =[]