PYQT5+openCV项目实战:微循环仪图片、视频记录和人工对比软件(附源码)
文章目录
这是一个使用PYQT5编写的基于openCV的图片视频采集软件。
源代码:
链接:https://pan.baidu.com/s/1BtLGvmnfWBqo3tPlRmBQNQ?pwd=74s6
提取码:74s6
1.主要功能
播放微循环显微仪的实时视频,可以保存图片及短视频到本地硬盘;保存的图片以编号+日期时间命名;回查记录时,以类似资源管理器的方式查看,可以随意比较2张图片。
1.1主界面说明
按键功能说明:
①打开视频: 视频没有打开时,按下“打开视频”,打开实时图像,按钮文字变为“关闭视频”;这时再按键本按钮,关闭实时图像。
②拍照:截取实时画面到保存图片的位置。
③保存图片:将拍照的图片以编号+时间命名后,保存到硬盘。
④查看记录:打开“回查界面”。
⑤新增编号:增加列表中没有的名字,如果已经在列表中,可以在下拉列表中选择。
1.2回查记录界面说明
回查界面的左侧是一个2级的文件管理列表,以上图为例,de01、de02等等是编号,编号的子列表是图片,图片以 编号+日期时间命名,单击图片名称时,图片出现在浏览图片;双击图片名称,图片出现在比对图片。
2.主界面功能详解
2.1 摄像头操作
实时显示摄像头影像的原理就是一秒钟内读取超过24帧的图片并显示出来。具体操作就是设定一个40ms(一秒25帧,或者更小时间)的定时器,每40ms去读一次摄像头的图片,在固定区域显示,这样就可以看到流畅的视频。
从摄像头读取一帧图像并显示的代码
def show_pic(self):
# 1. 读取一帧图像
success, frame=self.cap.read()
if success:
# 2.获取当前时间,选择字体
datet = (datetime.datetime.now()).strftime("%Y-%m-%d_%H:%M:%S")
font = cv.FONT_HERSHEY_SIMPLEX
# 3.微循环仪的分辨率是600 x 480,把图片放大到 720 x 540
Image = cv.resize(frame,(720,540),interpolation=cv.INTER_CUBIC)
# 4.在图片中加上时间戳
Image= cv.putText(Image, datet, (10, 540-10), font, 0.5, (0, 0, 255), 1, cv.LINE_AA)
show = cv.cvtColor(Image, cv.COLOR_BGR2RGB)
# 5.在PYQT5显示,需要转化为QPixmap格式
self.showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
self.vediolabel.setPixmap(QPixmap.fromImage(self.showImage))
# 6.是否记录视频
if self.IsVedioRecord:
# 6.1 写入帧到视频文件中
self.aviout.write(frame)
# 6.2 只能记录几秒钟的时长
if time.time() > self.recordStartTime:
self.IsVedioRecord = False
self.label_tip.setText("录像结束! "+datet)
# 6.3务必释放摄像头
self.aviout.release()
2.2拍照功能
按下“拍照”按键,就把当前的图像临时保存下来(还没有写入硬盘),名字是《编号+日期+时间.png》。
“拍照”按键对应的代码:
def show_picA(self):
# 1.变量self.showImage 中存放最近的图像,对此刻的图片进行临时保存,在拍照图片区域显示
self.label.setPixmap(QPixmap.fromImage(self.showImage))
self.recordImage = self.showImage
datet = (datetime.datetime.now()).strftime("%Y-%m-%d_%H:%M:%S")
self.label_tip.setText("拍照成功! "+datet)
datet = (datetime.datetime.now()).strftime("%Y-%m-%d_%H-%M-%S")
# 2.生成图片名称
self.saveName = self.nameBox.currentText() +'_'+ datet + '.png'
2.3保存图片
拍照后,觉得这个图片值得保存,按下“保存图片”按键,将图片保存到硬盘。
“保存图片”按键对应的代码:
def save_pic_to_userfolder(self):
if len(self.saveName) == 0:
return
datet = (datetime.datetime.now()).strftime("%Y-%m-%d_%H:%M:%S")
self.label_tip.setText("图片保存成功! "+datet)
self.savePath = self.Code51_dir+'/ymtdata/'+self.nameBox.currentText()+'/'+self.saveName
self.recordImage.save(self.savePath, 'PNG')
2.4录制短视频
录制视频的代码在2.1摄像头操作,“拍视频”按键的功能是:创建视频流对象,设置录制时长,打开录制开关。
“拍视频”按键对应的代码:
def start_vedio_record(self):
# 1.设置视频编码格式
FRAME_WIDTH = 640
FRAME_HEIGHT = 480
FPS = 10
if not self.IsVedioRecord:
# 2.生成需要保存的视频文件名称
datet = (datetime.datetime.now()).strftime("%Y-%m-%d_%H-%M-%S")
self.vedioName = self.Code51_dir+'/ymtdata/'+self.nameBox.currentText()+'/' \
+self.nameBox.currentText()+'_'+datet+'.avi'
# 3.创建视频流对象
fourcc = cv.VideoWriter_fourcc(*'XVID')
self.aviout = cv.VideoWriter(self.vedioName, fourcc, FPS, (int(FRAME_WIDTH), int(FRAME_HEIGHT)))
# 4.启动录制开关
self.IsVedioRecord = True
# 5.设定录制时长为5秒
self.recordStartTime = time.time()+5.0
self.label_tip.setText("录像开始! "+datet)
pass
录制的视频实例:
hwd_2021-10-19_09-19-46
de01_2021-10-19_09-34-49
2.5查看记录
软件所在目录有个《ymtdata》,下面存放了各个编号的子文件夹,子文件夹里面存放图片或视频。“查看记录”按键是打开新的窗口(子界面),子界面的代码在下一章讲述。
“查看记录”按键对应的代码:
def open_ViewDialog(self):
# 1.打开查看图片的子窗口,需要关闭摄像头,退出后需要手动打开
if self.IsTimerStart:
self.timer_camera.stop()
self.OpenCamera.setText("打开摄像头")
self.IsTimerStart = False
Dlg = ViewDlg(self)
# 2. 显示窗口
Dlg.exec_()
2.6新建名称
新建一个编号,这个编号会出现在下拉条里面,还会创建一个子文件夹。
“新建名称”按键对应的代码:
def input_name(self):
#1. 创建文件夹
text, ok = QInputDialog.getText(self, '输入名称', '输入名称:')
if ok:
datet = (datetime.datetime.now()).strftime("%Y-%m-%d_%H:%M:%S")
self.label_tip.setText(str(text)+datet)
self.savePath = self.Code51_dir+'/ymtdata'+'/'+str(text)
#2. 判断文件夹是否已经存在,如果是,不创建
if not os.path.exists(self.savePath):
os.mkdir(self.savePath) #创建 空文件夹
#3. 添加到nameBox
self.nameBox.addItem(text)
#4.将 新建项 作为 选择项
self.nameBox.setCurrentIndex(self.nameBox.count()-1)
3.子界面功能详解
打开子界面,会在界面的左侧生成一个2级的文件管理列表,图片显示区有左右两个区域,左边是浏览图片,单击时,图片或视频静态显示在本区域;双击文件,如果是图片文件,则在右边的对比图片区域显示,如果是视频,则在左边播放视频。
3.1 创建文件管理列表
遍历《ymtdata》下面的子文件夹和文件,结构比较单一。
def __init__(self, parent=None):
super(ViewDlg, self).__init__(parent)
self.setupUi(self)
......
#2.1 根名称
root1=QTreeWidgetItem(self.tree)
root1.setText(0,'图片浏览')
if os.path.exists(self.rootDir):
self.CreateTree( os.listdir(self.rootDir),root1 ,self.rootDir )
#只展开第一级
self.tree.expandToDepth(0)
.......
pass
def CreateTree(self, dirs, root, path):
for i in dirs:
path_new = path + '/' + i
if os.path.isdir(path_new):
child = QTreeWidgetItem(root)
child.setText(0,i)
child.setIcon(0,QIcon(":/{0}.png".format("Folder")))
dirs_new = os.listdir(path_new)
self.CreateTree(dirs_new, child, path_new)
else:
if '.png' in i or '.avi' in i:
child = QTreeWidgetItem(root)
child.setText(0,i)
if '.png' in i:
child.setIcon(0,QIcon(":/{0}.png".format("jpg32")))
elif '.avi' in i:
child.setIcon(0,QIcon(":/{0}.png".format("vedio32")))
3.2单击文件
def onClicked(self, item):
item=self.tree.currentItem()
if '.png' in item.text(0) : #1.如果是图片,在左侧显示
self.fileName =self.rootDir +'/'+item.parent().text(0)+'/'+ item.text(0)
self.label_view.setPixmap(QPixmap(self.fileName))
elif '.avi' in item.text(0) : #2. 如果是视频,只读取第一帧 显示
self.fileName =self.rootDir +'/'+item.parent().text(0)+'/'+ item.text(0)
self.cap = cv.VideoCapture(self.fileName)
success, frame = self.cap.read()
if success:
Image = cv.resize(frame,(720,540),interpolation=cv.INTER_CUBIC)
show = cv.cvtColor(Image, cv.COLOR_BGR2RGB)
showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
self.label_view.setPixmap(QPixmap.fromImage(showImage))
self.cap.release()
3.3双击文件
def onTreeDoubleClicked(self,qmodeLindex): #对工程列表中的文件双击
item=self.tree.currentItem()
if '.png' in item.text(0) : #1.如果是图片,在右侧显示
self.fileName =self.rootDir +'/'+item.parent().text(0)+'/'+ item.text(0)
self.label_cmp.setPixmap(QPixmap(self.fileName))
elif '.avi' in item.text(0) : #2. 如果是视频,左侧播放视频
self.fileName =self.rootDir +'/'+item.parent().text(0)+'/'+ item.text(0)
self.cap = cv.VideoCapture(self.fileName)
#打开视频,打开定时器
self.timer_camera = QTimer(self)
self.timer_camera.timeout.connect(self.show_pic)
self.timer_camera.start(30)
self.isTimerOn=True