python图片模板匹配(python实现简单图片物体标注工具)
类别:脚本大全 浏览量:413
时间:2021-11-08 16:36:04 python图片模板匹配
python实现简单图片物体标注工具本文实例为大家分享了python实现简单图片物体标注工具的具体代码,供大家参考,具体内容如下
|
# coding: utf-8 """ 物体检测标注小工具 基本思路: 对要标注的图像建立一个窗口循环,然后每次循环的时候对图像进行一次复制, 鼠标在画面上画框的操作、画好的框的相关信息在全局变量中保存, 并且在每个循环中根据这些信息,在复制的图像上重新画一遍,然后显示这份复制的图像。 简化的设计过程: 1、输入是一个文件夹的路径,包含了所需标注物体框的图片。 如果图片中标注了物体,则生成一个相同名称加额外后缀_bbox的文件,来保存标注信息。 2、标注的方式:按下鼠标左键选择物体框的左上角,松开鼠标左键选择物体框的右下角, 按下鼠标右键删除上一个标注好的物体框。 所有待标注物体的类别和标注框颜色由用户自定义。 如果没有定义则默认只标注一种物体,定义该物体名称为object。 3、方向键 ← 和 → 键用来遍历图片, ↑ 和 ↓ 键用来选择当前要标注的物体, delete键删除一种脏图片和对应的标注信息。 自定义标注物体和颜色的信息用一个元组表示 第一个元素表示物体名字 第二个元素表示bgr颜色的tuple或者代表标注框坐标的元祖 利用repr()保存和eval()读取 """ """ 一些说明: 1. 标注相关的物体标签文件即 .labels 结尾的文件,需要与所选文件夹添加到同一个根目录下 一定要注意这一点,否则无法更新标注物体的类型标签,致使从始至终都只有一个默认物体出现 我就是这个原因,拖了两三天才整好,当然也顺便仔细的读了这篇代码。同时也学习了@staticmethod以及相应python的decorator的知识。 可以说,在曲折中前进才是棒的。 2. .labels文件为预设物体标签文件,其内容具体格式为: 'object1', (b, g, r) 'object2', (b, g, r) 'object3', (b, g, r)…… 具体见文后图片。 3. 最后生成的标注文件,在文后会有,到时再进行解释。 """ import os import cv2 # tkinter是python内置的简单gui库,实现打开文件夹、确认删除等操作十分方便 from tkmessagebox import askyesno # 定义标注窗口的默认名称 window_name = 'simple bounding box labeling tool' # 定义画面刷新帧率 fps = 24 # 定义支持的图像格式 supported_formats = [ 'jpg' , 'jpeg' , 'png' ] # 定义默认物体框的名字为object,颜色为蓝色,当没有用户自定义物体时,使用该物体 default_color = { 'object' : ( 255 , 0 , 0 )} # 定义灰色,用于信息显示的背景和未定义物体框的显示 color_gray = ( 192 , 192 , 192 ) # 在图像下方多处bar_height的区域,用于显示信息 bar_height = 16 # 上下左右,delete键对应的cv2.waitkey()函数的返回值 key_up = 2490368 key_down = 2621440 key_left = 2424832 key_right = 2555904 key_delete = 3014656 # 空键用于默认循环 key_empty = 0 get_bbox_name = '{}.bbox' . format # 定义物体框标注工具类 class simplebboxlabeling: def __init__( self , data_dir, fps = fps, windown_name = window_name): self ._data_dir = data_dir self .fps = fps self .window_name = windown_name if windown_name else window_name # pt0 是正在画的左上角坐标, pt1 是鼠标所在坐标 self ._pt0 = none self ._pt1 = none # 表明当前是否正在画框的状态标记 self ._drawing = false # 当前标注物体的名称 self ._cur_label = none # 当前图像对应的所有已标注框 self ._bboxes = [] # 如果有用户自己定义的标注信息则读取,否则使用默认的物体和颜色 label_path = '{}.labels' . format ( self ._data_dir) self .label_colors = default_color if not os.path.exists(label_path) else self .load_labels(label_path) # self.label_colors = self.load_labels(label_path) # 获取已经标注的文件列表和未标注的文件列表 imagefiles = [x for x in os.listdir( self ._data_dir) if x[x.rfind( '.' ) + 1 :].lower() in supported_formats] labeled = [x for x in imagefiles if os.path.exists(get_bbox_name(x))] to_be_labeled = [x for x in imagefiles if x not in labeled] # 每次打开一个文件夹,都自动从还未标注的第一张开始 self ._filelist = labeled + to_be_labeled self ._index = len (labeled) if self ._index > len ( self ._filelist) - 1 : self ._index = len ( self ._filelist) - 1 # 鼠标回调函数 def _mouse_ops( self , event, x, y, flags, param): # 按下左键,坐标为左上角,同时表示开始画框,改变drawing,标记为true if event = = cv2.event_lbuttondown: self ._drawing = true self ._pt0 = (x, y) # 松开左键,表明画框结束,坐标为有效较并保存,同时改变drawing,标记为false elif event = = cv2.event_lbuttonup: self ._drawing = false self ._pt1 = (x, y) self ._bboxes.append(( self ._cur_label, ( self ._pt0, self ._pt1))) # 实时更新右下角坐标 elif event = = cv2.event_mousemove: self ._pt1 = (x, y) # 按下鼠标右键删除最近画好的框 elif event = = cv2.event_rbuttonup: if self ._bboxes: self ._bboxes.pop() # 清除所有标注框和当前状态 def _clean_bbox( self ): self ._pt0 = none self ._pt1 = none self ._drawing = false self ._bboxes = [] # 画标注框和当前信息的函数 def _draw_bbox( self , img): # 在图像下方多出bar_height的区域,显示物体信息 h, w = img.shape[: 2 ] canvas = cv2.copymakeborder(img, 0 , bar_height, 0 , 0 , cv2.border_constant, value = color_gray) # 正在标注的物体信息,如果鼠标左键已经按下,则像是两个点坐标,否则显示当前待标注物体的名 label_msg = '{}: {}, {}' . format ( self ._cur_label, self ._pt0, self ._pt1) \ if self ._drawing \ else 'current label: {}' . format ( self ._cur_label) # 显示当前文件名,文件个数信息 msg = '{}/{}: {} | {}' . format ( self ._index + 1 , len ( self ._filelist), self ._filelist[ self ._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 self ._bboxes: label_color = self .label_colors[label] if label in self .label_colors else color_gray cv2.rectangle(canvas, bpt0, bpt1, label_color, thickness = 2 ) cv2.puttext(canvas, label, (bpt0[ 0 ] + 3 , bpt0[ 1 ] + 15 ), cv2.font_hershey_simplex, 0.5 , label_color, 2 ) # 画正在标注的框和对应名字 if self ._drawing: label_color = self .label_colors[ self ._cur_label] if self ._cur_label in self .label_colors else color_gray if ( self ._pt1[ 0 ] > = self ._pt0[ 0 ]) and ( self ._pt1[ 1 ] > = self ._pt1[ 0 ]): cv2.rectangle(canvas, self ._pt0, self ._pt1, label_color, thickness = 2 ) cv2.puttext(canvas, self ._cur_label, ( self ._pt0[ 0 ] + 3 , self ._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 os.path.exists(filepath): os.remove(filepath) # 利用eval()函数读取标注框字符串到数据 @staticmethod def load_bbox(filepath): bboxes = [] with open (filepath, 'r' ) as f: line = f.readline().rstrip() while line: bboxes.append( eval (line)) line = f.readline().rstrip() return bboxes # 利用eval()函数读取物体及对应颜色信息到数据 @staticmethod def load_labels(filepath): label_colors = {} with open (filepath, 'r' ) as f: line = f.readline().rstrip() while line: label, color = eval (line) label_colors[label] = color line = f.readline().rstrip()
|