Python编写一个验证码图片数据标注GUI程序附源码


Posted in Python onDecember 09, 2019

做验证码图片的识别,不论是使用传统的ORC技术,还是使用统计机器学习或者是使用深度学习神经网络,都少不了从网络上采集大量相关的验证码图片做数据集样本来进行训练。

采集验证码图片,可以直接使用Python进行批量下载,下载完之后,就需要对下载下来的验证码图片进行标注。一般情况下,一个验证码图片的文件名就是图片中验证码的实际字符串。

在不借助工具的情况下,我们对验证码图片进行上述标注的流程是:

1、打开图片所在的文件夹;
2、选择一个图片;
3、鼠标右键重命名;
4、输入正确的字符串;
5、保存

州的先生亲身体验,一个验证码完成数据的标注,大概需要10到20秒。大量的时间浪费在了重复地进行鼠标右键重命名操作了。于是,使用Qt的Python封装包——PyQt5,编写了一个小工具,方便进行验证码图片的数据标注,节省时间,珍惜生命。

程序的运行如下动图所示:

Python编写一个验证码图片数据标注GUI程序附源码

下面我们来了解一下如何编写这个验证码图片数据标注程序。

首先,我们来构建一个图形界面。这个图形界面里面包含了一个图像展示控件、一个文本输入控件、四个按钮控件。基于此,我们选择三个布局来排列图形界面的布局。图形界面窗口中的核心控件是一个QWidget(),其布局层设置为网格布局QGridLayout()。在其中放置三个控件:图像展示控件QWidget()、文本输入控件QLineText()、四个按钮组QWidget()。

同时,图像展示控件QWidget()用水平布局层QHBoxLayout()包含一个QLabel()标签来占位;按钮组控件QWidget()用一个垂直布局层QVBoxLayout()将4个按钮控件QPushButton()添加进去。最后,代码如下所示:

class ImgTag(QtWidgets.QMainWindow):
 def __init__(self):
 super().__init__()
 self.setWindowTitle("验证码图片标注 州的先生 zmister.com")
 # 主控件和主控件布局
 self.main_widget = QtWidgets.QWidget()
 self.main_layout = QtWidgets.QGridLayout()
 self.main_widget.setLayout(self.main_layout)

 # 图像展示控件
 self.img_widget = QtWidgets.QWidget()
 self.img_layout = QtWidgets.QHBoxLayout()
 self.img_widget.setLayout(self.img_layout)
 # 标签占位
 self.img_view = QtWidgets.QLabel("请选择一个文件夹!")
 self.img_view.setAlignment(QtCore.Qt.AlignCenter)
 self.img_layout.addWidget(self.img_view)

 # 图像标注控件
 self.img_input = QtWidgets.QLineEdit()

 # 控制按钮控件
 self.opera_widget = QtWidgets.QWidget()
 self.opera_layout = QtWidgets.QVBoxLayout()
 self.opera_widget.setLayout(self.opera_layout)
 # 各个按钮
 self.select_img_btn = QtWidgets.QPushButton("选择目录")
 self.previous_img_btn = QtWidgets.QPushButton("上一张")
 self.previous_img_btn.setEnabled(False)
 self.next_img_btn = QtWidgets.QPushButton("下一张")
 self.next_img_btn.setEnabled(False)
 self.save_img_btn = QtWidgets.QPushButton("保存")
 self.save_img_btn.setEnabled(False)
 # 添加按钮到布局
 self.opera_layout.addWidget(self.select_img_btn)
 self.opera_layout.addWidget(self.previous_img_btn)
 self.opera_layout.addWidget(self.next_img_btn)
 self.opera_layout.addWidget(self.save_img_btn)

 # 将控件添加到主控件布局层
 self.main_layout.addWidget(self.img_widget,0,0,4,4)
 self.main_layout.addWidget(self.opera_widget,0,4,5,1)
 self.main_layout.addWidget(self.img_input,4,0,1,4)

 # 状态栏
 self.img_total_current_label = QtWidgets.QLabel()
 self.img_total_label = QtWidgets.QLabel()
 self.statusBar().addPermanentWidget(self.img_total_current_label)
 self.statusBar().addPermanentWidget(self.img_total_label, stretch=0) # 在状态栏添加永久控件

 # 设置UI界面核心控件
 self.setCentralWidget(self.main_widget)

运行上述代码,我们可以得到以下如下图所示的图形界面:

Python编写一个验证码图片数据标注GUI程序附源码

下面,我们为这个静态的图形界面添加事件响应。

二、选择目录读取文件

首先,我们来实现“选择目录”按钮的功能。这个按钮点击之后,需要打开文件夹选择框,然后在选择一个文件夹之后,自动读取文件夹内的图片文件,并将第一张图片显示到图形展示控件上。

在这里,我们通过QFileDialog.getExistingDirectory()来实现调用文件夹对话框,其会返回所选择文件夹路径的字符串。然后通过os模块的listdir()方法,获取文件夹下所有的文件,对其进行遍历,提取出图片文件,将这些图片文件添加到一个新的列表中。代码如下所示:

# 选择目录按钮
def select_img_click(self):
 self.dir_path = QtWidgets.QFileDialog.getExistingDirectory(self,'选择文件夹')
 # print(self.dir_path)
 dir_list = os.listdir(self.dir_path)
 img_list = []
 for dir in dir_list:
 suffix_list = ['jpg','png','jpeg','bmp',]
 if dir.split('.')[-1].lower() in suffix_list:
  img_list.append(dir)

接着,我们继续遍历这个列表,生成一个图片的索引字典,用于记录每个图片的顺序信息,方便进行上一张、下一张按钮的切换操作。

# 图像文件索引字典
self.img_index_dict = dict()
for i,d in enumerate(img_list):
 self.img_index_dict[i] = d
self.current_index = 0 # 当前的图像索引
# 当前图片文件路径
self.current_filename = os.path.join(
 self.dir_path,self.img_index_dict[self.current_index]
)

然后,借助QImage()类实例化一个Qt的图像,在图像占位标签中通过setPixmap设置显示图像。

# 实例化一个图像
image = QtGui.QImage(self.current_filename)
self.img_width = image.width() # 图片宽度
self.img_height = image.height() # 图片高度
self.img_scale = 1
self.image = image.scaled(self.img_width*self.img_scale,self.img_height*self.img_scale)

# 在img_view控件中显示图像
self.img_view.setPixmap(QtGui.QPixmap.fromImage(self.image))

接着再设置文本输入框的内容、获取文本输入框的焦点并全选文本输入框的内容:

# 设置img_input控件文本内容
self.img_input.setText(self.current_text)
self.img_input.setFocus() # 获取输入框焦点
self.img_input.selectAll() # 全选文本

最后在状态栏设置图片数量的信息,包括当前图片和图片总数:

# 设置状态栏 图片数量信息
self.img_total_current_label.setText("{}".format(self.current_index+1))
self.img_total_label.setText("/{total}".format(total=len(img_list)))

以上这些代码都是写在select_img_click()方法操作。在完成select_img_click()这个方法的编写后,我们将其绑定到“选择目录”的点击信号上:

self.select_img_btn.clicked.connect(self.select_img_click)

这样,就实现了选择目录,并显示目录中的第一张图片的功能。效果如下动图所示:

Python编写一个验证码图片数据标注GUI程序附源码

下面,我们再来实现下一张图片的按钮功能

三、切换下一张图片

要切换下一张图片,我们首先需要将当前显示的图片重命名为文本输入框中的内容:

# 下一张图片
def next_img_click(self):
 # 修改当前图像文件名
 new_tag = self.img_input.text() # 获取当前输入框内容
 current_img = self.img_index_dict[self.current_index] # 获取当前图片名称
 try:
 os.rename(
  os.path.join(self.dir_path,current_img),
  os.path.join(self.dir_path,new_tag+'.'+current_img.split('.')[-1])
 ) # 修改文件名
 self.img_index_dict[self.current_index] = new_tag+'.'+current_img.split('.')[-1]
 except FileExistsError as e: # 同名文件异常
 print(repr(e))
 QtWidgets.QMessageBox.information(
  self, '提示', '已存在同名文件!',
  QtWidgets.QMessageBox.Ok
 )

接下来,将图片当前索引变量值加1,通过这个索引值获取到下一张图片的文件名,再按照之前的方式将其读取为图像并显示在标签占位控件上,同时更新状态栏的信息:

# 当前图像索引加1
self.current_index += 1
if self.current_index in self.img_index_dict.keys():
 # 当前图片文件路径
 self.current_filename = os.path.join(
 self.dir_path, self.img_index_dict[self.current_index]
 )
 # 实例化一个图像
 image = QtGui.QImage(self.current_filename)
 self.img_width = image.width() # 图片宽度
 self.img_height = image.height() # 图片高度
 self.img_scale = 1
 self.image = image.scaled(self.img_width * self.img_scale, self.img_height * self.img_scale)

 # 在img_view控件中显示图像
 self.img_view.setPixmap(QtGui.QPixmap.fromImage(self.image))
 # 当前文件名
 self.current_text = self.img_index_dict[self.current_index].split('.')[0]
 # 设置img_input控件文本内容
 self.img_input.setText(self.current_text)
 self.img_input.setFocus() # 获取输入框焦点
 self.img_input.selectAll() # 全选文本

 # 设置状态栏
 self.img_total_current_label.setText(str(self.current_index+1))
else:
 self.current_index -=1
 QtWidgets.QMessageBox.information(
 self,'提示','所有图片已标注完!',
 QtWidgets.QMessageBox.Ok
 )

这样,调用next_img_click()方法,我们就可以切换下一张图片。我们将其绑定在“下一张”按钮、“保存”按钮和文本输入框的回车信号上,就可以实现点击“下一张”按钮、“保存”按钮或是在标注完一个数据后直接回车就能切换到下一张图片:

self.next_img_btn.clicked.connect(self.next_img_click)
self.save_img_btn.clicked.connect(self.next_img_click)
self.img_input.returnPressed.connect(self.next_img_click) # 回车事件绑定

这样,切换下一张图片的功能也实现了,其效果如下动图所示:

Python编写一个验证码图片数据标注GUI程序附源码

四、切换上一张图片

有时候我们需要返回前面标注的图片,这时候切换上一张图片的功能也是很有必要的。切换上一张图片的逻辑与切换下一张图片的逻辑基本一致,只是需要将图像的索引值减1:

# 上一张图片
def previous_img_click(self):
 # 修改当前图像文件名
 new_tag = self.img_input.text() # 获取当前输入框内容
 current_img = self.img_index_dict[self.current_index] # 获取当前图片名称
 try:
 os.rename(
  os.path.join(self.dir_path, current_img),
  os.path.join(self.dir_path, new_tag + '.' + current_img.split('.')[-1])
 ) # 修改文件名
 self.img_index_dict[self.current_index] = new_tag + '.' + current_img.split('.')[-1]
 except FileExistsError as e: # 同名文件异常
 print(repr(e))
 QtWidgets.QMessageBox.information(
  self, '提示', '已存在同名文件!',
  QtWidgets.QMessageBox.Ok
 )

 # 当前图像索引加1
 self.current_index -= 1
 if self.current_index in self.img_index_dict.keys():
 # 当前图片文件路径
 self.current_filename = os.path.join(
  self.dir_path, self.img_index_dict[self.current_index]
 )
 # 实例化一个图像
 image = QtGui.QImage(self.current_filename)
 self.img_width = image.width() # 图片宽度
 self.img_height = image.height() # 图片高度
 self.img_scale = 1
 self.image = image.scaled(self.img_width * self.img_scale, self.img_height * self.img_scale)

 # 在img_view控件中显示图像
 self.img_view.setPixmap(QtGui.QPixmap.fromImage(self.image))
 # 当前文件名
 self.current_text = self.img_index_dict[self.current_index].split('.')[0]
 # 设置img_input控件文本内容
 self.img_input.setText(self.current_text)
 self.img_input.setFocus() # 获取输入框焦点
 self.img_input.selectAll() # 全选文本

 # 设置状态栏
 self.img_total_current_label.setText(str(self.current_index + 1))
 else:
 self.current_index += 1
 QtWidgets.QMessageBox.information(
  self, '提示', '图片列表到顶了!',
  QtWidgets.QMessageBox.Ok
 )

可以看到,这和切换下一张图片的代码几乎是一致的,因为其核心逻辑本来就是一样的,我们将“上一张”按钮的点击信号绑定在这个方法上,就可以实现切换上一张图片的功能了:

self.previous_img_btn.clicked.connect(self.previous_img_click)

其效果如下动图所示:

Python编写一个验证码图片数据标注GUI程序附源码

五、图片缩放

到这里,我们的验证码图片数据标注程序基本上已经完成了,但是突然发现,有些验证码图片很变态,它的干扰线和干扰点简直让人无法看清它到底是什么字符,这样的情况下可能需要把图片放大或缩小一点,方便我们确认验证码图片上的信息,所以,我们的程序还需要一个图片缩放功能。最终,我们实现的效果是,按住Ctrl+鼠标滚轮,滚轮向上,图片放大,滚轮向下,图片缩小。这是通过重写鼠标滚轮事件来实现的:

# 重写鼠标滚轮事件
def wheelEvent(self, event):
 # 如果按住了Ctrl
 if event.modifiers() == QtCore.Qt.ControlModifier:
 try:
  delta = event.angleDelta().y()
  if delta > 0:
  self.img_scale += 0.25
  self.image_scaled = self.image.scaled(self.img_width * self.img_scale, self.img_height * self.img_scale)
  self.img_view.setPixmap(QtGui.QPixmap.fromImage(self.image_scaled))
  self.statusBar().showMessage("当前图片缩放比例为:{}%".format(self.img_scale * 100))
  elif delta < 0:
  if self.img_scale > 0.25:
   self.img_scale -= 0.25
   self.image_scaled = self.image.scaled(self.img_width * self.img_scale, self.img_height * self.img_scale)
   self.img_view.setPixmap(QtGui.QPixmap.fromImage(self.image_scaled))
   self.statusBar().showMessage("当前图片缩放比例为:{}%".format(self.img_scale * 100))
 except Exception as e:
  print(traceback.print_exc())
  print(repr(e))

最后,这样图片缩放的功能也实现了,其效果如下所示:

Python编写一个验证码图片数据标注GUI程序附源码

六、程序完整代码

以上,我们的图片验证码数据标注程序就完全编写好了,基于此,我们可以进一步使用Pyinstaller等打包工具,将其打包为二进制的可执行文件,方便传播使用。

源码下载地址:链接: https://pan.baidu.com/s/1FadzPC2FoIJNPMCmpYBKRg 提取码: e4w4

总结

以上所述是小编给大家介绍的Python编写一个验证码图片数据标注GUI程序附源码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
详解python中xlrd包的安装与处理Excel表格
Dec 16 Python
python实现12306火车票查询器
Apr 20 Python
python爬虫_微信公众号推送信息爬取的实例
Oct 23 Python
Django rest framework基本介绍与代码示例
Jan 26 Python
python将pandas datarame保存为txt文件的实例
Feb 12 Python
基于opencv的selenium滑动验证码的实现
Jul 24 Python
Pycharm无法打开双击没反应的问题及解决方案
Aug 17 Python
python简单实现9宫格图片实例
Sep 03 Python
Pytest单元测试框架如何实现参数化
Sep 05 Python
解决Python import .pyd 可能遇到路径的问题
Mar 04 Python
Python语言规范之Pylint的详细用法
Jun 24 Python
梳理总结Python开发中需要摒弃的18个坏习惯
Jan 22 Python
Python内置方法实现字符串的秘钥加解密(推荐)
Dec 09 #Python
opencv-python 读取图像并转换颜色空间实例
Dec 09 #Python
opencv-python 提取sift特征并匹配的实例
Dec 09 #Python
python 多维高斯分布数据生成方式
Dec 09 #Python
使用python模拟高斯分布例子
Dec 09 #Python
使用python+whoosh实现全文检索
Dec 09 #Python
Python 实现顺序高斯消元法示例
Dec 09 #Python
You might like
第八节--访问方式
2006/11/16 PHP
PHP中strtr字符串替换用法详解
2014/11/26 PHP
php socket通信(tcp/udp)实例分析
2016/02/14 PHP
解决Laravel5.x的php artisan migrate数据库迁移创建操作报错SQLSTATE[42000]
2020/04/06 PHP
Javascript - HTML的request类
2007/01/09 Javascript
JavaScript中的对象化编程
2008/01/16 Javascript
JavaScript 封装Ajax传递的数据代码
2009/06/05 Javascript
关于jquery性能最佳实践的讨论,与求教
2012/03/30 Javascript
JQuery之focus函数使用介绍
2013/08/20 Javascript
简单的JavaScript互斥锁分享
2014/02/02 Javascript
node.js中的fs.write方法使用说明
2014/12/15 Javascript
JavaScript实现下拉列表框数据增加、删除、上下排序的方法
2015/08/11 Javascript
RequireJS入门一之实现第一个例子
2015/09/30 Javascript
JavaScript中字符串与Unicode编码互相转换的实现方法
2015/12/18 Javascript
picLazyLoad 实现图片延时加载(包含背景图片)
2016/07/21 Javascript
vue2.0组件之间传值、通信的多种方式(干货)
2018/02/10 Javascript
Angular5中调用第三方js插件的方法
2018/02/26 Javascript
JSON的parse()方法介绍
2019/01/31 Javascript
vue 获取视频时长的实例代码
2019/08/20 Javascript
jquery将信息遍历到界面上实例代码
2020/01/21 jQuery
[01:55]《走出家门看比赛》——DOTA2 2015国际邀请赛同城线下观战
2015/07/18 DOTA
[01:38]DOTA2 2015国际邀请赛中国区预选赛 Showopen
2015/06/01 DOTA
PYTHON正则表达式 re模块使用说明
2011/05/19 Python
简单实现python数独游戏
2018/03/30 Python
python 密码学示例——凯撒密码的实现
2020/09/21 Python
浅谈Selenium+Webdriver 常用的元素定位方式
2021/01/13 Python
HTML5实现经典坦克大战坦克乱走还能发出一个子弹
2013/09/02 HTML / CSS
求职信的要素有哪些呢
2013/12/26 职场文书
小区门卫值班制度
2014/01/24 职场文书
抄作业检讨书
2014/02/17 职场文书
资助贫困学生倡议书
2014/05/16 职场文书
运动会入场口号
2014/06/07 职场文书
宣传活动总结范文
2014/07/01 职场文书
2015年餐厅服务员工作总结
2015/04/23 职场文书
redis哨兵常用命令和监控示例详解
2021/05/27 Redis
python使用pycharm安装pyqt5以及相关配置
2022/04/22 Python