本文实例为大家分享了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()

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文件内容格式

选择文件夹

进行标注

生成相应标签内容

标注结果

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

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

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

python数据标注工具_python实现简单图片物体标注工具相关推荐

  1. python数据可视化代码_Python实现简单的数据可视化

    现在python这门解释型语言被越来越多的人们喜欢,强大的库支持,使得编程过程变得简单. 我是一个传统的C语言支持者,往后也打算慢慢的了解Python的强大. 今天我就学习一下使用python实现数据 ...

  2. 使用python和PyQt5编写爬取百度图片的界面工具

    使用python和PyQt5编写爬取百度图片的界面工具 本篇文章的主要内容是展示我个人编写的,以界面小工具的方式爬取百度上面的图片,功能很单一,根据关键词爬取图片,代码很简单,新手上路请多指教. 代码 ...

  3. Python 爬虫,推荐一款简单的抓包工具(续)

    点击上方"AirPython",选择"加为星标" 第一时间关注 Python 原创干货! 1. 前言 上篇文章发布之后,有小伙伴后台私信我,说文章只涉及到手机端 ...

  4. python标记_Python实现一个简单的图片物体标注工具

    # coding: utf-8 """ 物体检测标注小工具 基本思路: 对要标注的图像建立一个窗口循环,然后每次循环的时候对图像进行一次复制, 鼠标在画面上画框的操作.画 ...

  5. python爬虫贴吧_Python爬虫简单实现,贴吧图片一键下

    在平常上网阅读网页的时刻,经常会看到一些好看的图片,就盼望把这些图片保留下载,或许用户用来做桌面壁纸,或许用来做计划的素材. 最惯例的做法便是经由过程鼠标右键,抉择另存为.但有些图片鼠标右键的时刻并无 ...

  6. python写图片爬取软件_python制作微博图片爬取工具

    有小半个月没有发博客了,因为一直在研究python的GUI,买了一本书学习了一些基础,用我所学做了我的第一款GUI--微博图片爬取工具.本软件源代码已经放在了博客中,另外软件已经打包好上传到网盘中以供 ...

  7. python 数据框缺失值_Python:处理数据框中的缺失值

    python 数据框缺失值 介绍 (Introduction) In the last article we went through on how to find the missing value ...

  8. python数据科学库_Python数据科学库

    python数据科学库 什么是数据科学? (What is Data Science?) We live in an information age, where the challenge is t ...

  9. python数据可视化库_python和r中用于数据可视化的前9个库

    python数据可视化库 In the rapidly growing world of today, when technology is expanding at a rate like neve ...

最新文章

  1. MegEngine基本概念
  2. java网络编程(六)
  3. 全球最优秀的14位程序员
  4. 《最强蜗牛》运营分析:这个奇葩放置游戏的乐趣在哪里?
  5. 使用腾讯云提供的针对Nuget包管理器的缓存加速服务
  6. 【英语学习】【WOTD】encroach 释义/词源/示例
  7. 【Elasticsearch】Lucene 8 新特性
  8. 都是arm 为什么用java,为什么内存指令在ARM汇编中需要4个周期?
  9. 浅谈UG学习的方法(适合初学者)
  10. MATLAB命令大全
  11. vue-cli脚手架build目录中的webpack.base.conf.js配置文件
  12. 如何创作属于自己的NFT?
  13. DataHunter完成千万级A轮融资 打造数据协作平台
  14. 互联网金融涌动下的冲动与借债
  15. Go channel 通道
  16. 见丰知夏|国漫鼻祖丰子恺,数字藏品独家发售
  17. 日记20050930
  18. python飞行棋项目
  19. 【论文精度】Subdivision-Based Mesh Convolution Networks
  20. 使用SwitchHosts和GitHub520自动更新 github.com的hosts

热门文章

  1. CACTER邮件安全网关发布V6.7版本:新增邮件审核功能
  2. 评论情感分析----多种机器学习模型测试总结
  3. grasshopper butterfly插件报错
  4. SQL SERVER Alwayson 原理及故障排除
  5. windows server2016开启telnet功能
  6. 桌面html快捷方式总消失,桌面软件的快捷方式不见了怎么恢复 快捷方式不见了恢复方法汇总...
  7. 西游记中奎木狼与百花羞的爱情
  8. 高耐压30V线性LDO芯片
  9. [细思极恐]\r\n与\n在java编程语言中的区别测试
  10. ABP框架(.Net Core)-分析初始化过程