前言——附软件下载地址
上一篇博客中,基于pyk4a实现了使用单个Azure Kinect相机在python环境下进行图像采集,但是需要进行一定的环境配置,所以本文进行了一些改进,写了一个软件界面,并且进行了封装。有需要的朋友可自取
!!!软件存放路径中不要有中文!!!
!!!软件存放路径中不要有中文!!!
!!!软件存放路径中不要有中文!!!
一、软件功能
1、官方SDK中dll文件的封装
该软件封装了Kinect的官方SDK中的相关dll文件
也封装了opencv、numpy、pyk4a等相关的包
2、软件使用pyqt5制作界面,基本功能有如下:
(1)、能够实时显示彩色视频流、实时显示伪彩色的深度视频流
(2)、能够自定义图像存储路径(路径无中文字符)
(3)、点击“拍照”后能够自动为所采集图像进行编号,方便科研数据整理
(4)、采集图像不满意,允许点击“撤销”按钮进行图像的删除,并且下一组图的编号自动更新
(5)、以Qlabel模仿控制台输出,方便错误检查
二、软件代码
1、主函数cap_img.py的原始代码
# -*- coding: utf-8 -*-
from Ui_jiemian import Ui_MainWindow
from PyQt5 import QtGui, QtWidgets
import sys
import os
from PyQt5 import QtWidgets
from Ui_jiemian import Ui_MainWindow
from PyQt5.QtWidgets import QMainWindow, QMessageBox
import numpy as np
import cv2
import pyk4a
from pyk4a import PyK4A, Config, connected_device_count
from PyQt5.QtGui import QImage, QPixmap
from typing import Optional, Tuple
from PyQt5.QtCore import *
from PyQt5.QtGui import *
def color_depth(
image: np.ndarray,
clipping_range: Tuple[Optional[int], Optional[int]] = (None, None),
colormap: int = cv2.COLORMAP_HSV,
) -> np.ndarray:
if clipping_range[0] or clipping_range[1]:
img = image.clip(clipping_range[0], clipping_range[1])
else:
img = image.copy()
img = cv2.normalize(img, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
img = cv2.applyColorMap(img, colormap)
return img
def display_video(self):
try:
self.cnt = connected_device_count()
print("设备数为:", self.cnt )
self.label_6.setText(f"检测到设备数为: {self.cnt}" )
if self.cnt == 1:
self.k4a = PyK4A(
Config(
color_resolution=pyk4a.ColorResolution.RES_3072P,
depth_mode=pyk4a.DepthMode.WFOV_UNBINNED,
camera_fps =pyk4a.FPS.FPS_15
)
)
self.k4a.start()
while True:
capture = self.k4a.get_capture()
rgb_image_show = cv2.resize(capture.color, ( 406, 297))
rows, cols = rgb_image_show.shape[:2]
rgb_image_show = cv2.cvtColor(rgb_image_show, cv2.COLOR_BGR2RGB)
show_RGB = QtGui.QImage(rgb_image_show.data, cols,rows, cols*3, QImage.Format_RGB888) #说明:QImage 函数中,如果 bytesPerline 参数不设置,则会默认按照 4 字节对齐的方式显示图像(如果不满足四字节对齐要求则不会使用零填充),对于不满足四字节对齐的影像在显示的时候就会出现上述失真问题,若将 bytesPerline 参数设置为 image.cols*image.channels() (如上述代码所示)则可解决图像显示失真问题(如上图显示结果)!!!
self.label_2.setPixmap(QPixmap.fromImage(show_RGB)) # 在 GUI 界面上显示 QImage 对象
depth_image_show = cv2.resize(color_depth(capture.transformed_depth, colormap=cv2.COLORMAP_JET), ( 406, 297))
rows, cols = depth_image_show.shape[:2]
depth_image_show = cv2.cvtColor(depth_image_show, cv2.COLOR_BGR2RGB)
show_depth = QtGui.QImage(depth_image_show.data, cols,rows, cols*3, QImage.Format_RGB888)
self.label_3.setPixmap(QPixmap.fromImage(show_depth)) # 在 GUI 界面上显示 QImage 对象
cv2.waitKey(0)
if not self.k4a.opened :
print("视频流已关闭")
self.label_6.setText("视频流已关闭")
break
else:
# 当设备数量不等于1时,弹出错误消息框
QMessageBox.critical(None, "设备数异常", "请检查相机连接情况")
except Exception as e:
print(f"An error occurred: {e}")
QMessageBox.critical(None, "相机连接异常", f"{e}")
def close_video(self):
try:
self.cnt = connected_device_count()
self.cnt_label_text == self.cnt
if self.cnt_label_text == 1:
self.k4a.stop()
white_back1 = np.ones((self.label_2.width(), self.label_2.height(), 4), dtype=np.uint8) * 255
None_RGB = QtGui.QImage(white_back1.data, self.label_2.width(),self.label_2.height(), self.label_2.height()*3, QImage.Format_RGB888)
self.label_2.setPixmap(QPixmap.fromImage(None_RGB))
white_back2 = np.ones((self.label_3.width(), self.label_3.height(), 4), dtype=np.uint8) * 255
none_depth = QtGui.QImage(white_back2.data, self.label_3.width(),self.label_3.height(), self.label_3.height()*3, QImage.Format_RGB888)
self.label_3.setPixmap(QPixmap.fromImage(none_depth))
print("空白背景填充")
# self.label_6.setText("视频流已关闭")
else:
# 当设备数量不等于2时,弹出错误消息框
QMessageBox.critical(None, "关闭异常", "设备数异常")
except Exception as e:
# 异常处理,可以在这里记录错误或采取其他措施
QMessageBox.critical(None, "关闭方式错误", "相机未正常开启")
class MyMainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):
super(MyMainWindow, self).__init__(parent) # 初始化父类
self.setupUi(self) # 继承 Ui_MainWindow 界面类
self.label_2.setScaledContents(True) # 调整 label 大小以适应图片大小
self.label_3.setScaledContents(True) # 调整 label 大小以适应图片大小
self.checkBox.stateChanged.connect(self.on_state_changed)
self.count = 0 # 该参数用于给所拍摄的照片进行编号
def select_folder(self):
current_dir = self.lineEdit.text()
self.savepath = QtWidgets.QFileDialog.getExistingDirectory(None, "选取文件夹", current_dir) # 起始路径为当前文本框中的路径
print(self.savepath)
self.label_6.setText("存储路径为:" + self.savepath)
self.lineEdit.setText(self.savepath) # 已修改
def closeEvent(self, event):
try:
if self.k4a.opened:
self.k4a.stop()
except AttributeError:
event.accept()
def on_state_changed(self, state):
if state == Qt.Checked: # 选中
display_video(self)
else:
close_video(self)
def take_pictures(self):
try:
if not self.k4a.opened:
print("相机未启动,请检查")
self.label_6.setText("相机未启动,请检查")
else:
# 检查变量A是否定义
try:
self.savepath
except AttributeError:
print("请先确定存储路径")
self.label_6.setText("请先确定存储路径")
QMessageBox.information(None, "拍照失败", "请检查存储路径")
else:
self.count += 1
Kinect_rgb_filename = f"{self.savepath}/rgb-{self.count}.png"
cv2.imwrite(Kinect_rgb_filename , self.k4a.get_capture().color)
Kinect_depth_filename = f"{self.savepath}/depth-{self.count}.png"
cv2.imwrite(Kinect_depth_filename , self.k4a.get_capture().depth)
Kinect_ir_filename = f"{self.savepath}/ir-{self.count}.png"
cv2.imwrite(Kinect_ir_filename , self.k4a.get_capture().ir)
Kinect_morp_filename = f"{self.savepath}/morp-{self.count}.png"
cv2.imwrite(Kinect_morp_filename , self.k4a.get_capture().transformed_depth)
print(f"基于Kinect的\n rgb-{self.count}.png\n depth-{self.count}.png\n ir-{self.count}.png\n morp-{self.count}.png 已保存!")
self.label_6.setText(f"基于Kinect的、rgb-{self.count}.png、depth-{self.count}.png、ir-{self.count}.png、morp-{self.count}.png 已保存!")
except Exception as e:
# 异常处理,可以在这里记录错误或采取其他措施
QMessageBox.critical(None, "错误", "未检测到相机连接")
def delete_pictures(self):
if self.count == 0 :
print("暂无最新拍摄照片,请检查")
self.label_6.setText("暂无最新拍摄照片,请检查")
QMessageBox.information(None, "撤销失败", "暂无最新拍摄照片,请检查")
else:
try:
os.remove(f"{self.savepath}/rgb-{self.count}.png")
os.remove(f"{self.savepath}/depth-{self.count}.png")
os.remove(f"{self.savepath}/ir-{self.count}.png")
os.remove(f"{self.savepath}/morp-{self.count}.png")
print(f"基于Kinect的\n rgb-{self.count}.png\n depth-{self.count}.png\n ir-{self.count}.png\n morp-{self.count}.png 已删除!")
self.count -= 1
print("下一张图片编号为:", format(self.count+1))
self.label_6.setText(f"基于Kinect的rgb-{self.count}.png 、depth-{self.count}.png 、ir-{self.count}.png 、 morp-{self.count}.png 已删除!\n" "下一张图片编号为:"+ format(self.count+1))
return True
except OSError as e:
print("删除错误!")
self.label_6.setText("删除错误!")
QMessageBox.information(None, "删除错误!", "删除错误!")
return False
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv) # 初始化界面
# MainWindow = QMainWindow() # 创建主窗口
myWin = MyMainWindow() # 实例化 MyMainWindow 类,创建主窗口
myWin.show() # 在桌面显示控件 myWin
sys.exit(app.exec_()) # 结束进程,退出程序
2、软件界面代码
软件界面使用QT Designer制作,相机开关使用了一个checkBox类来进行状态的切换
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'd:\Python\Image_camera\1_Kinect\jiemian.ui'
#
# Created by: PyQt5 UI code generator 5.15.6
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(850, 550)
MainWindow.setMinimumSize(QtCore.QSize(850, 550))
MainWindow.setLayoutDirection(QtCore.Qt.LeftToRight)
MainWindow.setTabShape(QtWidgets.QTabWidget.Triangular)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setLayoutDirection(QtCore.Qt.LeftToRight)
self.centralwidget.setAutoFillBackground(False)
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setContentsMargins(7, 0, 0, 0)
self.gridLayout.setSpacing(0)
self.gridLayout.setObjectName("gridLayout")
self.frame = QtWidgets.QFrame(self.centralwidget)
self.frame.setMinimumSize(QtCore.QSize(850, 550))
self.frame.setStyleSheet("#frame {\n"
"\n"
" background-color: rgba(203, 255, 181,50);\n"
"}\n"
"QPushButton{\n"
" \n"
"}")
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.verticalLayout = QtWidgets.QVBoxLayout(self.frame)
self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.frame_2 = QtWidgets.QFrame(self.frame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth())
self.frame_2.setSizePolicy(sizePolicy)
self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_2.setObjectName("frame_2")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame_2)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.label = QtWidgets.QLabel(self.frame_2)
font = QtGui.QFont()
font.setFamily("幼圆")
font.setPointSize(19)
font.setBold(True)
font.setWeight(75)
self.label.setFont(font)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.horizontalLayout_4.addWidget(self.label)
self.verticalLayout.addWidget(self.frame_2)
self.frame_3 = QtWidgets.QFrame(self.frame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(9)
sizePolicy.setHeightForWidth(self.frame_3.sizePolicy().hasHeightForWidth())
self.frame_3.setSizePolicy(sizePolicy)
self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_3.setObjectName("frame_3")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame_3)
self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_2.setSpacing(0)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.frame_4 = QtWidgets.QFrame(self.frame_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(3)
sizePolicy.setHeightForWidth(self.frame_4.sizePolicy().hasHeightForWidth())
self.frame_4.setSizePolicy(sizePolicy)
self.frame_4.setStyleSheet("QPushButton{\n"
" \n"
" background-color: rgb(255, 255, 255);\n"
" \n"
" color: rgb(0, 0, 0);\n"
" \n"
" border-radius:15px;\n"
"\n"
"}\n"
"QPushButton:hover {\n"
" padding-bottom:2px\n"
"}\n"
"QPushButton:pressed {\n"
" padding: 2px 2px 0 0;\n"
"\n"
"}\n"
"\n"
"QPushButton:released {\n"
" padding: 0;\n"
"\n"
"}")
self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_4.setObjectName("frame_4")
self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.frame_4)
self.horizontalLayout_6.setContentsMargins(1, 1, 1, 1)
self.horizontalLayout_6.setSpacing(1)
self.horizontalLayout_6.setObjectName("horizontalLayout_6")
self.frame_13 = QtWidgets.QFrame(self.frame_4)
self.frame_13.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_13.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_13.setObjectName("frame_13")
self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.frame_13)
self.verticalLayout_7.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_7.setSpacing(0)
self.verticalLayout_7.setObjectName("verticalLayout_7")
self.frame_15 = QtWidgets.QFrame(self.frame_13)
self.frame_15.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_15.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_15.setObjectName("frame_15")
self.toolButton = QtWidgets.QToolButton(self.frame_15)
self.toolButton.setGeometry(QtCore.QRect(230, 10, 71, 21))
self.toolButton.setObjectName("toolButton")
self.checkBox = QtWidgets.QCheckBox(self.frame_15)
self.checkBox.setGeometry(QtCore.QRect(30, 10, 90, 30))
self.checkBox.setObjectName("checkBox")
self.verticalLayout_7.addWidget(self.frame_15)
self.frame_16 = QtWidgets.QFrame(self.frame_13)
self.frame_16.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_16.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_16.setObjectName("frame_16")
self.pushButton_3 = QtWidgets.QPushButton(self.frame_16)
self.pushButton_3.setGeometry(QtCore.QRect(290, 10, 81, 31))
self.pushButton_3.setObjectName("pushButton_3")
self.lineEdit = QtWidgets.QLineEdit(self.frame_16)
self.lineEdit.setGeometry(QtCore.QRect(20, 10, 231, 31))
self.lineEdit.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.lineEdit.setObjectName("lineEdit")
self.verticalLayout_7.addWidget(self.frame_16)
self.horizontalLayout_6.addWidget(self.frame_13)
self.frame_14 = QtWidgets.QFrame(self.frame_4)
self.frame_14.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_14.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_14.setObjectName("frame_14")
self.pushButton_2 = QtWidgets.QPushButton(self.frame_14)
self.pushButton_2.setGeometry(QtCore.QRect(160, 60, 61, 31))
self.pushButton_2.setObjectName("pushButton_2")
self.pushButton = QtWidgets.QPushButton(self.frame_14)
self.pushButton.setGeometry(QtCore.QRect(160, 10, 61, 31))
self.pushButton.setObjectName("pushButton")
self.horizontalLayout_6.addWidget(self.frame_14)
self.verticalLayout_2.addWidget(self.frame_4)
self.frame_6 = QtWidgets.QFrame(self.frame_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(10)
sizePolicy.setHeightForWidth(self.frame_6.sizePolicy().hasHeightForWidth())
self.frame_6.setSizePolicy(sizePolicy)
self.frame_6.setStyleSheet("#frame_6{\n"
" \n"
" \n"
" background-color: rgba(230, 255, 244, 100);\n"
"}\n"
"QLabel{\n"
" \n"
" background-color: rgba(168, 170, 166, 50);\n"
"}")
self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_6.setObjectName("frame_6")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame_6)
self.horizontalLayout.setSpacing(6)
self.horizontalLayout.setObjectName("horizontalLayout")
self.frame_7 = QtWidgets.QFrame(self.frame_6)
self.frame_7.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_7.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_7.setObjectName("frame_7")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.frame_7)
self.verticalLayout_3.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_3.setSpacing(0)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.frame_9 = QtWidgets.QFrame(self.frame_7)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.frame_9.sizePolicy().hasHeightForWidth())
self.frame_9.setSizePolicy(sizePolicy)
self.frame_9.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_9.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_9.setObjectName("frame_9")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_9)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setSpacing(0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_4 = QtWidgets.QLabel(self.frame_9)
self.label_4.setAlignment(QtCore.Qt.AlignCenter)
self.label_4.setObjectName("label_4")
self.horizontalLayout_2.addWidget(self.label_4)
self.verticalLayout_3.addWidget(self.frame_9)
self.frame_10 = QtWidgets.QFrame(self.frame_7)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(10)
sizePolicy.setHeightForWidth(self.frame_10.sizePolicy().hasHeightForWidth())
self.frame_10.setSizePolicy(sizePolicy)
self.frame_10.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_10.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_10.setObjectName("frame_10")
self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.frame_10)
self.verticalLayout_6.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_6.setSpacing(0)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.label_2 = QtWidgets.QLabel(self.frame_10)
self.label_2.setText("")
self.label_2.setAlignment(QtCore.Qt.AlignCenter)
self.label_2.setObjectName("label_2")
self.verticalLayout_6.addWidget(self.label_2)
self.verticalLayout_3.addWidget(self.frame_10)
self.horizontalLayout.addWidget(self.frame_7)
self.frame_8 = QtWidgets.QFrame(self.frame_6)
self.frame_8.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_8.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_8.setObjectName("frame_8")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame_8)
self.verticalLayout_4.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_4.setSpacing(0)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.frame_11 = QtWidgets.QFrame(self.frame_8)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.frame_11.sizePolicy().hasHeightForWidth())
self.frame_11.setSizePolicy(sizePolicy)
self.frame_11.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_11.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_11.setObjectName("frame_11")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_11)
self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_3.setSpacing(0)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.label_5 = QtWidgets.QLabel(self.frame_11)
self.label_5.setAlignment(QtCore.Qt.AlignCenter)
self.label_5.setObjectName("label_5")
self.horizontalLayout_3.addWidget(self.label_5)
self.verticalLayout_4.addWidget(self.frame_11)
self.frame_12 = QtWidgets.QFrame(self.frame_8)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(10)
sizePolicy.setHeightForWidth(self.frame_12.sizePolicy().hasHeightForWidth())
self.frame_12.setSizePolicy(sizePolicy)
self.frame_12.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_12.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_12.setObjectName("frame_12")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame_12)
self.verticalLayout_5.setContentsMargins(0, 0, 0, 0)
self.verticalLayout_5.setSpacing(0)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.label_3 = QtWidgets.QLabel(self.frame_12)
self.label_3.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_3.setText("")
self.label_3.setAlignment(QtCore.Qt.AlignCenter)
self.label_3.setObjectName("label_3")
self.verticalLayout_5.addWidget(self.label_3)
self.verticalLayout_4.addWidget(self.frame_12)
self.horizontalLayout.addWidget(self.frame_8)
self.verticalLayout_2.addWidget(self.frame_6)
self.frame_5 = QtWidgets.QFrame(self.frame_3)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.frame_5.sizePolicy().hasHeightForWidth())
self.frame_5.setSizePolicy(sizePolicy)
self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame_5.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame_5.setObjectName("frame_5")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.frame_5)
self.horizontalLayout_5.setContentsMargins(0, 0, 0, 2)
self.horizontalLayout_5.setSpacing(0)
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.label_7 = QtWidgets.QLabel(self.frame_5)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(2)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_7.sizePolicy().hasHeightForWidth())
self.label_7.setSizePolicy(sizePolicy)
self.label_7.setObjectName("label_7")
self.horizontalLayout_5.addWidget(self.label_7)
self.label_6 = QtWidgets.QLabel(self.frame_5)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(23)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth())
self.label_6.setSizePolicy(sizePolicy)
self.label_6.setText("")
self.label_6.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.label_6.setObjectName("label_6")
self.horizontalLayout_5.addWidget(self.label_6)
self.verticalLayout_2.addWidget(self.frame_5)
self.verticalLayout.addWidget(self.frame_3)
self.gridLayout.addWidget(self.frame, 0, 0, 1, 1)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
self.pushButton_3.clicked.connect(MainWindow.select_folder) # type: ignore
self.pushButton.clicked.connect(MainWindow.take_pictures) # type: ignore
self.pushButton_2.clicked.connect(MainWindow.delete_pictures) # type: ignore
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "图像采集软件"))
self.label.setText(_translate("MainWindow", "AzureKinect图像采集"))
self.toolButton.setText(_translate("MainWindow", "相机配置"))
self.checkBox.setText(_translate("MainWindow", "相机开关"))
self.pushButton_3.setText(_translate("MainWindow", "选定路径"))
self.lineEdit.setText(_translate("MainWindow", "Path for Save:"))
self.pushButton_2.setText(_translate("MainWindow", "撤销"))
self.pushButton.setText(_translate("MainWindow", "拍照"))
self.label_4.setText(_translate("MainWindow", "RGB图像"))
self.label_5.setText(_translate("MainWindow", "depth图像(伪彩色处理)"))
self.label_7.setText(_translate("MainWindow", "控制台输出"))
3、常见错误的预防
为了防止界面因常见错误而崩溃或卡住。。。。
软件使用try_except语句,结合QT界面的QMessageBox类进行提示弹窗,对一些常见错误进行了处理
(具体语句可通过主函数的源码查看)
(1)相机未连接即打开CheckBOX按钮
在出现此类错误时界面会进行弹窗报警,此时将按钮关闭后正确连接相机,再点击界面**“相机开关”**即可
(2)相机未正常连接,在按钮的开启状态,点击“相机开关”
此时界面不会直接崩溃,弹出窗口报警。这种情况下界面不会崩溃,关闭弹窗,将相机正常连接后,再点击“相机开关”,即可重新打开视频流,全称不用重新启动界面。
(3)在未连接相机时直接点击“拍照”
(4)在未连接相机时直接点击“撤销”
三、软件使用说明
1、将相机连接至电脑
Azure Kinect相机有两根线,一根供电线、一根数据线
需注意Azure Kinect相机对供电功率有要求,如使用电脑的USB接口供电,则有可能会因为供电不足而出现SDK的报错。我自己测试后发现,一般USB3.0的接口都能够进行供电,USB2.0的接口进行供电则会报错,建议可以进行10W充电器的独立供电。
2、点击软件项目文件夹中的exe文件,运行软件界面
3、点击“相机开关”,checkBox开关处于触发状态,等待几秒钟,显示视频流
4、点击“选定路径”,选择图像存储路径,可新建,也可选择现有
!!!路径中不要出现中文!!!
!!!路径中不要出现中文!!!
!!!路径中不要出现中文!!!
5、点击“拍照”,进行图像采集,按需点击“撤销”进行图像的删除
6、图像采集结果
如下图所示,图像已成功采集,其中Morp图像即为D-RGB图像,其分辨率与彩色图像完全一致,便于处理
总结与展望
本文详细讲解了单Azure Kinect相机图像采集软件的使用方法,并且提供了主函数与主界面的源码,该软件的优点在于不用配置任何环境,也不用安装相机SDK,使用起来较为方便,大家可自行取用
但本软件仍存在一下改进的方向,但因此功能已满足课题数据采集需要,就并未进一步优化
各位大佬如果有时间可以帮忙加以改进,到时候可以@我去给大家点赞哈哈哈
1、软件界面视觉效果优化
本软件为了课题图像采集需要而编写,时间仓促,且本人审美水平一言难尽,故并未优化界面视觉效果
现提供界面UI文件,各位大佬如有闲时可自行拿去进行优化
UI界面链接
2、相机配置——未填的坑
本软件界面中有一个按钮,在预想中是用于进行“相机配置”的设置,Azure Kinect相机为彩色、深度图像的采集提供了不同的模式。下面截取一段pyk4a库的config.py文件的源码,源码中呈现的是pyk4a库所提供的不同相机配置
# k4a_fps_t
class FPS(IntEnum):
FPS_5 = 0
FPS_15 = 1
FPS_30 = 2
# k4a_image_format_t
class ImageFormat(IntEnum):
COLOR_MJPG = 0
COLOR_NV12 = 1
COLOR_YUY2 = 2
COLOR_BGRA32 = 3
DEPTH16 = 4
IR16 = 5
CUSTOM8 = 6
CUSTOM16 = 7
CUSTOM = 8
# k4a_depth_mode_t
class DepthMode(IntEnum):
OFF = 0
NFOV_2X2BINNED = 1
NFOV_UNBINNED = 2
WFOV_2X2BINNED = 3
WFOV_UNBINNED = 4
PASSIVE_IR = 5
# k4a_color_resolution_t
class ColorResolution(IntEnum):
OFF = 0
RES_720P = 1
RES_1080P = 2
RES_1440P = 3
RES_1536P = 4
RES_2160P = 5
RES_3072P = 6
# k4a_wired_sync_mode_t
class WiredSyncMode(IntEnum):
STANDALONE = 0
MASTER = 1
SUBORDINATE = 2
# k4a_color_control_command_t
class ColorControlCommand(IntEnum):
EXPOSURE_TIME_ABSOLUTE = 0
AUTO_EXPOSURE_PRIORITY = 1 # deprecated
BRIGHTNESS = 2
CONTRAST = 3
SATURATION = 4
SHARPNESS = 5
WHITEBALANCE = 6
BACKLIGHT_COMPENSATION = 7
GAIN = 8
POWERLINE_FREQUENCY = 9
# k4a_color_control_mode_t
class ColorControlMode(IntEnum):
AUTO = 0
MANUAL = 1
但是在本人的图像采集软件中,相机配置是一段写死的代码,其设定如下:
self.k4a = PyK4A(
Config(
color_resolution=pyk4a.ColorResolution.RES_3072P,
depth_mode=pyk4a.DepthMode.WFOV_UNBINNED,
camera_fps =pyk4a.FPS.FPS_15
)
)
self.k4a.start()
在最初的设想中“相机配置”这一按钮,点击以后会出现不同的下拉菜单,菜单首先提供一个默认配置,但是也可以满足用户不同的需要进行搭配,大致想法如下:
用户根据相机配置的设置,选择是否保存某类图像,并且在可选范围内,设定不同图像的分辨率
但是以上功能由于本人课题需求并不大,所以只能留待有缘人来完善了
3、软件测试
本软件将一些基本的,较为常见的错误进行了处理,但仍可能存在一些未测试出的问题(写代码是这样的),有需要的朋友可以自行进行优化
把try_except语句给我拉满!!!!!!!!
4、软件完整地封装
本软件封装其实并不完整,没有将其封装为一个单一的exe文件,有需要的朋友可以自行重新封装(并且可以的话,教教我该怎么做),如果有懂这方面的朋友,可以在评论区交流指点一下
5、解决中文路径报错
其实一直没搞懂,为什么有些软件可以安装在中文路径下,有些软件不可以,是不是在软件写作、或者软件封装的过程中,可以通过一定的操作来规避中文路径报错这一问题吗,如果有懂这方面的朋友,也请在评论区交流指点一下
附
关于RGB-D图像与D-RGB图像
彩色图像对齐到深度图像、深度图像对齐到彩色图像,这两种情况在深度相机相关的项目中,都有可能会与遇到的情况
1、彩色对齐到深度(RGB-D)
此种情况主要是将彩色图像与深度图像逐像素对齐,在RGB-D图像中进行图像分割,提取掩膜等操作,该掩膜可直接应用于D图像中。比较适用于需要对深度图像进行分割的场景
2、深度对齐到彩色(D-RGB)
此种情况下,可以将D图像的分辨率,通过插值算法(最邻近、或双线性)强行拉至与RGB图像一致。大大提高深度图像素点的数量
Azure Kinect相机:
RGB图像最高分辨率为(3072,4096)
Depth图像最高分辨率为(1024,1024)
有此基础,如果需要进行点云处理,将D-RGB图像转换为点云以后,也相当于提高了点云分辨率
故比较适用于点云处理场景
以上内容,既有本人做课题的经验,也有本人的课题经验,也参考了官方教程
官方教程——use-image-transformation
“去别”——>“区别”
哈哈,图中有个错别字
后续将会更新一下双视角Kinect图像采集的相关代码与软件,希望能帮助到大家
新人博主,点个赞求求!