香港云主机最佳企业级服务商!

ADSL拨号VPS包含了中国大陆(联通,移动,电信,)

中国香港,国外拨号VPS。

当前位置:云主机 > python >

电信ADSL拨号VPS
联通ADSL拨号VPS
移动ADSL拨号VPS

python实现简单图片物体标注工具


时间:2022-04-02 10:23 作者:admin


本文实例为大家分享了python/' target='_blank'>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 osimport 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 = 2490368KEY_DOWN = 2621440KEY_LEFT = 2424832KEY_RIGHT = 2555904KEY_DELETE = 3014656# 空键用于默认循环KEY_EMPTY = 0get_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()  print label_colors  return label_colors  # 读取图像文件和对应标注框信息(如果有的话) @staticmethod def load_sample(filepath):  img = cv2.imread(filepath)  bbox_filepath = get_bbox_name(filepath)  bboxes = []  if os.path.exists(bbox_filepath):   bboxes = SimpleBBoxLabeling.load_bbox(bbox_filepath)  return img, bboxes  # 导出当前标注框信息并清空 def _export_n_clean_bbox(self):  bbox_filepath = os.sep.join([self._data_dir, get_bbox_name(self._filelist[self._index])])  self.export_bbox(bbox_filepath, self._bboxes)  self._clean_bbox()  # 删除当前样本和对应的标注框信息 def _delete_current_sample(self):  filename = self._filelist[self._index]  filepath = os.sep.join([self._data_dir, filename])  if os.path.exists(filepath):    os.remove(filepath)  filepath = get_bbox_name(filepath)  if os.path.exists(filepath):    os.remove(filepath)  self._filelist.pop(self._index)  print('{} is deleted!'.format(filename))  # 开始OpenCV窗口循环的方法,程序的主逻辑 def start(self):  # 之前标注的文件名,用于程序判断是否需要执行一次图像读取  last_filename = ''   # 标注物体在列表中的下标  label_index = 0   # 所有标注物体名称的列表  labels = self.label_colors.keys()   # 带标注物体的种类数  n_labels = len(labels)   # 定义窗口和鼠标回调  cv2.namedWindow(self.window_name)  cv2.setMouseCallback(self.window_name, self._mouse_ops)  key = KEY_EMPTY   # 定义每次循环的持续时间  delay = int(1000 / FPS)   # 只要没有按下Delete键,就持续循环  while key != KEY_DELETE:   # 上下方向键选择当前标注物体   if key == KEY_UP:    if label_index == 0:     pass    else:     label_index -= 1   elif key == KEY_DOWN:    if label_index == n_labels - 1:     pass    else:     label_index += 1   # 左右方向键选择标注图片   elif key == KEY_LEFT:    # 已经到了第一张图片的话就不需要清空上一张    if self._index > 0:     self._export_n_clean_bbox()    self._index -= 1    if self._index < 0:     self._index = 0   elif key == KEY_RIGHT:    # 已经到了最后一张图片的就不需要清空上一张    if self._index < len(self._filelist) - 1:     self._export_n_clean_bbox()    self._index += 1    if self._index > len(self._filelist) - 1:     self._index = len(self._filelist) - 1   # 删除当前图片和对应标注的信息   elif key == KEY_DELETE:    if askyesno('Delete Sample', 'Are you sure?'):     self._delete_current_sample()     key = KEY_EMPTY     continue   # 如果键盘操作执行了换图片, 则重新读取, 更新图片   filename = self._filelist[self._index]   if filename != last_filename:    filepath = os.sep.join([self._data_dir, filename])    img, self._bboxes = self.load_sample(filepath)   # 更新当前标注物体名称   self._cur_label = labels[label_index]   # 把标注和相关信息画在图片上并显示指定的时间   canvas = self._draw_bbox(img)   cv2.imshow(self.window_name, canvas)   key = cv2.waitKey(delay)   # 当前文件名就是下次循环的老文件名   last_filename = filename  print 'Finished!'  cv2.destroyAllWindows()  #如果退出程序,需要对当前文件进行保存  self.export_bbox(os.sep.join([self._data_dir, get_bbox_name(filename)]), self._bboxes)  print 'Labels updated!'

以上实现了工具类,当然需要一个入口函数,将工具类保存为SimpleBBoxLabeling.py,新建Run_Detect.py,写以下内容:

# coding:utf-8 # tkinter是Python内置的简单GUI库,实现打开文件夹、确认删除等操作十分方便from tkFileDialog import askdirectory# 导入创建的工具类from SimpleBBoxLabeling import SimpleBBoxLabeling if __name__ == '__main__': dir_with_images = askdirectory(title='Where is the images?') labeling_task = SimpleBBoxLabeling(dir_with_images) labeling_task.start()

以下是实现后的效果:

需要的文件


.labels文件内容格式


选择文件夹


进行标注


生成相应标签内容


标注结果
标注后的文件格式为:物体,左上角(起点)和右下角(终点)的坐标。

参考资料: 《深度学习与计算机视觉——算法原理、框架应用与代码实现》 叶韵(编著)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

(责任编辑:admin)






帮助中心
会员注册
找回密码
新闻中心
快捷通道
域名登录面板
虚机登录面板
云主机登录面板
关于我们
关于我们
联系我们
联系方式

售前咨询:17830004266(重庆移动)

企业QQ:383546523

《中华人民共和国工业和信息化部》 编号:ICP备00012341号

Copyright © 2002 -2018 香港云主机 版权所有
声明:香港云主机品牌标志、品牌吉祥物均已注册商标,版权所有,窃用必究

云官方微信

在线客服

  • 企业QQ: 点击这里给我发消息
  • 技术支持:383546523

  • 公司总台电话:17830004266(重庆移动)
  • 售前咨询热线:17830004266(重庆移动)