奥比中光深度相机(二):PyQt5实现打开深度摄像头功能

news2025/3/10 14:03:14

文章目录

  • 奥比中光深度相机(二):PyQt5实现打开深度摄像头功能
    • 官方给出的调用深度相机源码
      • 环境精炼
    • UI界面设计
    • 逻辑代码构建
      • 槽函数连接
      • 提取视频流
      • 在界面中显示深度视频流
      • 注意
      • 关闭相机
    • 总体代码
    • 效果演示
      • 运行main.py代码
      • 选择相机
      • 打开摄像头
      • 关闭摄像头

奥比中光深度相机(二):PyQt5实现打开深度摄像头功能

PyQt5实现打开可见光相机摄像头的功能,我们之前已经介绍过了,具体可参考:https://blog.csdn.net/WYKB_Mr_Q/article/details/129827685

上篇文章中我们介绍了奥比中光深度相机基于Python的环境配置部分。具体可参考:https://blog.csdn.net/WYKB_Mr_Q/article/details/137040226

这篇文章我们在配置好Python环境的基础上,使用PyQt5写个界面,并且设计逻辑,通过点击一个设计好的按钮来打开深度相机。深度相机的打开方法和可见光相机的打开方法略有不同,可见光相机可以利用OpenCV来直接调用,奥比中光深度相机主要是利用官方给出的SDK源码来调用。本文中,我们借鉴官方源码,将调用深度相机的主要代码提取出来,并和我们自己写的代码结合,实现在PyQt5界面中通过点击按钮打开深度相机。

官方给出的调用深度相机源码

官方给出了打开深度相机的Python SDK源码,链接为:https://github.com/orbbec/pyorbbecsdk
在这里插入图片描述

其中也包含了一些.py示例文件,存储在pyorbbecsdk/examples文件夹中,我们找到 “depth_viewer.py” 文件,该文件的主要作用是为了调用深度相机,显示深度图像的。咱们可以先连接上奥比中光深度相机,运行一下该代码试一试,看看是否能够正常运行,展示出深度视频流。如果一切正常,你就可以看到如下界面了。

在这里插入图片描述

下面部分是 “depth_viewer.py” 文件的源代码:

from pyorbbecsdk import Pipeline
from pyorbbecsdk import Config
from pyorbbecsdk import OBSensorType, OBFormat
from pyorbbecsdk import OBError
import cv2
import numpy as np
import time

ESC_KEY = 27
PRINT_INTERVAL = 1  # seconds
MIN_DEPTH = 20  # 20mm
MAX_DEPTH = 10000  # 10000mm

# 管理时间流,这部分不用管
class TemporalFilter:
    def __init__(self, alpha):
        self.alpha = alpha
        self.previous_frame = None

    def process(self, frame):
        if self.previous_frame is None:
            result = frame
        else:
            result = cv2.addWeighted(frame, self.alpha, self.previous_frame, 1 - self.alpha, 0)
        self.previous_frame = result
        return result


def main():
	# 配置深度相机文件
    config = Config()
    pipeline = Pipeline()
    temporal_filter = TemporalFilter(alpha=0.5)
    try:
    	# 调用深度摄像头
        profile_list = pipeline.get_stream_profile_list(OBSensorType.DEPTH_SENSOR)
        assert profile_list is not None
        try:
        	# 配置深度视频流参数,包括视频帧长宽,视频流格式,以及每秒采集的视频帧数
            depth_profile = profile_list.get_video_stream_profile(640, 0, OBFormat.Y16, 30)
        except OBError as e:
            print("Error: ", e)
            depth_profile = profile_list.get_default_video_stream_profile()
        assert depth_profile is not None
        print("depth profile: ", type(depth_profile))
        config.enable_stream(depth_profile)
    except Exception as e:
        print(e)
        return
    pipeline.start(config)
    last_print_time = time.time()
    while True:
        try:
            frames = pipeline.wait_for_frames(100)
            if frames is None:
                continue
            depth_frame = frames.get_depth_frame()
            if depth_frame is None:
                continue
            width = depth_frame.get_width()
            height = depth_frame.get_height()
            scale = depth_frame.get_depth_scale()

            depth_data = np.frombuffer(depth_frame.get_data(), dtype=np.uint16)
            depth_data = depth_data.reshape((height, width))

            depth_data = depth_data.astype(np.float32) * scale
            depth_data = np.where((depth_data > MIN_DEPTH) & (depth_data < MAX_DEPTH), depth_data, 0)
            depth_data = depth_data.astype(np.uint16)
            # Apply temporal filtering
            depth_data = temporal_filter.process(depth_data)

            center_y = int(height / 2)
            center_x = int(width / 2)
            center_distance = depth_data[center_y, center_x]

            current_time = time.time()
            if current_time - last_print_time >= PRINT_INTERVAL:
                print("center distance: ", center_distance)
                last_print_time = current_time

            depth_image = cv2.normalize(depth_data, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
            depth_image = cv2.applyColorMap(depth_image, cv2.COLORMAP_JET)

            cv2.imshow("Depth Viewer", depth_image)
            key = cv2.waitKey(1)
            if key == ord('q') or key == ESC_KEY:
                break
        except KeyboardInterrupt:
            break
    pipeline.stop()


if __name__ == "__main__":
    main()

我们主要是想将调用奥比中光深度相机的关键部分提取出来,并应用在我们自己的代码中。

环境精炼

由于官方给的SDK源码文件较多,如果全部复制到我们的项目中,显得项目很臃肿。
经过我们不断的尝试和测试,发现如果要正常调用深度相机,正常运行 “depth_viewer.py” 文件,只需要将 “OrbbecSDK.dll” 和 “pyorbbecsdk.pyd” 文件复制出去就可以。

在这里插入图片描述

就是下面这三个文件在一个文件夹里,就可以正常调用深度相机了,其余官方提供的文件都可以不要了。这样看起来是不是简单多了?

在这里插入图片描述

UI界面设计

这里主要参考我之前的博客,https://blog.csdn.net/WYKB_Mr_Q/article/details/129827685,不再做详细介绍。
我们将构建的ui代码放置到ui_code文件夹里,命名为depth_camera.py,我们这里做一个如下简单的界面做测试:

在这里插入图片描述

逻辑代码构建

槽函数连接

通过以下两行代码,实现点击按钮与函数功能的连接。

self.pushButton.clicked.connect(self.open_camera)      # 点击第一个按钮运行打开摄像头的函数
self.pushButton_2.clicked.connect(self.close_camera)   # 点击第二个按钮运行关闭摄像头的函数

提取视频流

通过以下代码可以实现视频流的调用,并将深度视频帧转化为伪彩色图像。

self.config = Config()
self.pipeline = Pipeline()
# 获取深度摄像头的所有流配置,包括流分辨率、帧速率和帧格式
profile_list = self.pipeline.get_stream_profile_list(OBSensorType.DEPTH_SENSOR)
# 选择分辨率打开流
depth_profile = profile_list.get_video_stream_profile(1280, 800, OBFormat.Y16, 10)
self.config.enable_stream(depth_profile)
self.pipeline.start(self.config)
self.condition = True
while self.condition:
    try:
        # 以阻塞方式等待数据帧,数据帧是包含配置中启用的所有流的帧数据的复合帧,并将帧等待超时设置为 100ms
        frames = self.pipeline.wait_for_frames(100)
        if frames is None:
            continue
        depth_frame = frames.get_depth_frame()
        if depth_frame is None:
            continue
        width = depth_frame.get_width()
        height = depth_frame.get_height()
        scale = depth_frame.get_depth_scale()

        depth_data = np.frombuffer(depth_frame.get_data(), dtype=np.uint16)
        depth_data = depth_data.reshape((height, width))

        depth_data = depth_data.astype(np.float32) * scale
        depth_data = np.where((depth_data > 20) & (depth_data < 10000), depth_data, 0)
        depth_data = depth_data.astype(np.uint16)
        # 将数据转化为伪彩色图像
		depth_image = cv2.normalize(depth_data, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
        depth_image = cv2.applyColorMap(depth_image, cv2.COLORMAP_JET)

在界面中显示深度视频流

我们主要通过保存当前视频帧,并读取展示视频帧的方式显示视频流。以下函数可以实现视频帧保存本地,读取并居中显示到设计好的界面上。

# 展示伪彩色图像功能函数
def show_pic_func(self, jet_image):
     cv2.imwrite("./read_image_copy.png", jet_image)
     img = QPixmap("./read_image_copy.png")
     w = img.width()
     h = img.height()
     ratio = max(w / self.label.width(), h / self.label.height())
     img.setDevicePixelRatio(ratio)
     self.label.setAlignment(Qt.AlignCenter)
     self.label.setPixmap(img)

注意

在显示视频帧时,需要调用cv2.waitkey()函数,不然显示出来的视频帧是灰色的,看不到图像。

self.key = cv2.waitKey(1)
if self.key == ord('q') or self.key == 27:
    break

关闭相机

关闭相机主要依靠pipeline.stop()函数实现,由于我们在打开摄像头时利用了 self.condition 的值,并将其设置为True,因此这里我们需要将其设置为False来终止循环语句。

# 关闭相机
def close_camera(self):
    self.condition = False
    self.pipeline.stop()
    # 将相机id设置为None
    self.CAM_NUM = None
    self.pushButton.setEnabled(True)
    self.pushButton_2.setEnabled(False)

总体代码

注意我们调用了ui界面设计生成的.py文件,命名为depth_camera.py,并放置到ui_code文件夹中,所以我们这里的调用代码为from ui_code.depth_camera import Ui_MainWindow,这里根据自己的命名做对应的更改。

import sys
from pyorbbecsdk import Pipeline
from pyorbbecsdk import Config
from pyorbbecsdk import OBSensorType, OBFormat
from pyorbbecsdk import OBError
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import cv2
import numpy as np
import time
from ui_code.depth_camera import Ui_MainWindow


class MainWindow(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        # UI界面
        self.setupUi(self)
        self.CAM_NUM = 0
        self.background()
        self.condition = None

    def background(self):
        self.pushButton.clicked.connect(self.open_camera)
        self.pushButton_2.clicked.connect(self.close_camera)
        self.pushButton.setEnabled(True)
        # 初始状态不能关闭摄像头
        self.pushButton_2.setEnabled(False)

    # 展示伪彩色图像功能函数
    def show_pic_func(self, jet_image):
        cv2.imwrite("./read_image_copy.png", jet_image)
        img = QPixmap("./read_image_copy.png")
        w = img.width()
        h = img.height()
        ratio = max(w / self.label.width(), h / self.label.height())
        img.setDevicePixelRatio(ratio)
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setPixmap(img)

    def temporal_filter(self, frame, alpha, previous_frame=None):
        if previous_frame is None:
            result = frame
        else:
            result = cv2.addWeighted(frame, alpha, previous_frame, 1 - alpha, 0)
        previous_frame = result
        return result

    def open_camera(self):
        self.label.setEnabled(True)
        # 选择相机型号
        self.CAM_NUM = self.comboBox.currentIndex()
        # 支持奥比中光相机
        if self.CAM_NUM == 0:
            # 可以关闭摄像头
            self.pushButton_2.setEnabled(True)
            self.pushButton.setEnabled(False)
            self.config = Config()
            self.pipeline = Pipeline()
            # 获取深度摄像头的所有流配置,包括流分辨率、帧速率和帧格式
            profile_list = self.pipeline.get_stream_profile_list(OBSensorType.DEPTH_SENSOR)
            # 选择分辨率打开流
            depth_profile = profile_list.get_video_stream_profile(1280, 800, OBFormat.Y16, 10)
            self.config.enable_stream(depth_profile)
            self.pipeline.start(self.config)
            self.condition = True
            while self.condition:
                try:
                    # 以阻塞方式等待数据帧,数据帧是包含配置中启用的所有流的帧数据的复合帧,并将帧等待超时设置为 100ms
                    frames = self.pipeline.wait_for_frames(100)
                    if frames is None:
                        continue
                    depth_frame = frames.get_depth_frame()
                    if depth_frame is None:
                        continue
                    width = depth_frame.get_width()
                    height = depth_frame.get_height()
                    scale = depth_frame.get_depth_scale()

                    depth_data = np.frombuffer(depth_frame.get_data(), dtype=np.uint16)
                    depth_data = depth_data.reshape((height, width))

                    depth_data = depth_data.astype(np.float32) * scale
                    depth_data = np.where((depth_data > 20) & (depth_data < 10000), depth_data, 0)
                    depth_data = depth_data.astype(np.uint16)
                    # Apply temporal filtering
                    depth_data = self.temporal_filter(depth_data, alpha=0.5)

                    depth_image = cv2.normalize(depth_data, None, 0, 255, cv2.NORM_MINMAX, dtype=cv2.CV_8U)
                    depth_image = cv2.applyColorMap(depth_image, cv2.COLORMAP_JET)
                    # 展示伪彩色深度图像
                    self.show_pic_func(depth_image)
                    self.key = cv2.waitKey(1)
                    if self.key == ord('q') or self.key == 27:
                        break
                except KeyboardInterrupt:
                    break
        else:
            QMessageBox.information(self, "警告", "我们暂时仅支持奥比中光,请选择奥比中光相机!", QMessageBox.Ok)

    # 关闭相机
    def close_camera(self):
        self.condition = False
        self.pipeline.stop()
        self.CAM_NUM = None
        self.pushButton.setEnabled(True)
        self.pushButton_2.setEnabled(False)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    main = MainWindow()
    main.show()
    sys.exit(app.exec_())

效果演示

运行main.py代码

在这里插入图片描述

选择相机

在这里插入图片描述

打开摄像头

在这里插入图片描述

关闭摄像头

在这里插入图片描述

该专栏博文地址:

界面开发(1) — PyQt5环境配置
界面开发(2)— 使用PyQt5制作用户登陆界面
界面开发(3)— PyQt5用户登录界面连接数据库
界面开发(4)— PyQt5实现打开图像及视频播放功能
界面开发(5)— PyQt5实现打开摄像头采集视频功能
奥比中光深度相机(一) — 环境配置

日常学习记录,一起交流讨论吧!侵权联系~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1555151.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【2】单链表

【2】单链表 1、单链表2、单链表的设计3、接口设计4、SingleLinkedList5、node(int index) 返回索引位置的节点6、clear()7、添加8、删除9、indexOf(E element) 1、单链表 &#x1f4d5;动态数组有个明显的缺点 &#x1f58a; 可能会造成内存空间的大量浪费 &#x1f4d5; 能否…

Elementor Pro最新学习版:强大的WordPress页面构建器插件

产品用途 Elementor Pro的核心功能包括拖放编辑器、前端编辑器、实时预览、允许导入和导出模板、支持35预建模板、多种营销工具和插件支持、多种排版选项、能够放置内联元素、Font Awesome图标支持、允许构建移动响应页面、登陆页面构建器、弹出窗口生成器、对评级系统的架构标…

linux 组建raid5详细操作

raid5最多运行损坏一个盘&#xff0c;最少3个盘&#xff0c;容量为少一块硬盘的容量之和。 如果硬盘数量较多&#xff0c;比如8块以上&#xff0c;建议用raid6&#xff0c;raid6最多允许两块硬盘损坏。 如果需要 一、安装raid软件 deb包 apt-get install mdadm或dnf包 dnf …

总结UDP各类知识点

前言 本篇博客博主将详细地介绍UDP有关知识点&#xff0c;坐好板凳发车啦~ 一.UDP特点 1.无连接 UDP传输的过程类似于发短信&#xff0c;知道对端的IP和端口号就直接进行传输&#xff0c;不需要建立连接&#xff1b; 2.不可靠传输 没有任何的安全机制&#xff0c;发送端发…

eNSP综合实验(PPP认证、VPN配置、RIP协议、NAT)

题目如上 第一步:配置IP地址 ip分配如下图所示 开始配置IP(PC省略&#xff09; R1&#xff1a; [R1]undo [R1]undo in [R1]undo info-centere [R1]undo info-center e [R1]undo info-center enable Info: Information center is disabled. [R1]int g0/0/0 [R1-Gigabit…

Mysql连接报错:1130-host ... is not allowed to connect to this MySql server如何处理

我用navicat连接我的阿里云服务器的mysql服务器的时候,出现了1130的报错。&#xff08;mysql Server version: 5.7.42-0ubuntu0.18.04.1 (Ubuntu)&#xff09; 我来记录一下这个原因&#xff0c;以及修改过程&#xff01; 1.首先进入mysql -u root -p&#xff0c; mysql客户端…

从 Azure 部署生成本地 .NET 密钥

作者&#xff1a;Frank Boucher 排版&#xff1a;Alan Wang 通常&#xff0c;示例项目以一些“魔术字符串”开始&#xff0c;这些变量包含与部署或外部资源相关的 URL 和关键信息&#xff0c;我们必须更改这些信息才能使用示例。例如在 .NET 中&#xff0c;它可能如下所示&…

SAMRTFORMS 转换PDF 发送邮件

最终成果&#xff1a; *&---------------------------------------------------------------------**& Report ZLC_FIND_EXIT*&---------------------------------------------------------------------**&根据T-CODE / 程序名查询出口、BADI增强*&-------…

建立一个简单的网页音乐盒模型效果#css#h5

“音乐盒”可以看做一个大盒子&#xff0c;用<div>标签进行定义。大盒子的上面为文本内容&#xff0c;可以在<div>标签中嵌套<h2>和<p>标签来实现&#xff1b;大盒子下面为图像&#xff0c;通过在<div>标签中嵌套<img/>标签来实现。 样式…

基于SDXL——ComfyUI下使用Layerdiffusion一键生成透明底图像

ComfyUI下使用Layerdiffusion一键生成透明底图像 一. 安装workspace工作流插件二. 安装、部署、运行Layerdiffusion1. 下载和安装layerdiffusion节点2. 下载LayerDiffusion 处理模型3. 导入工作流4. 开始运行LayerDiffusion4.1 直接生成透明底图像4.2 生成背景&生成前景4.3…

Ubuntu上安装Python3.11-源码编译

1、下载依赖 sudo apt install -y wget build-essential libreadline-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libffi-dev zlib1g-dev 2、上传文件 &#xff0c;解压并进入 tar -xf Python-3.11.0.tar.xz 3、编译 cd Pyt…

WordPress分页函数function

1、可以通过下面的代码在编辑器上添加一个分页符按钮。 2、将下面的代码添加到当前主题functions.php即可。 3、代码如下&#xff1a; function mce_page_break($mce_buttons) { $pos array_search(wp_more, $mce_buttons, true); if ($pos ! false) { $buttons …

【C语言】结构体详解 (二) 内存函数、结构体传参

目录 1、 结构体的内存对齐 1.1、对齐规则 1.2、练习1、练习2&#xff08;演示对齐规则1、2、3、4&#xff09; 2、为什么存在内存对齐 2.1、平台原因&#xff08;移植原因&#xff09; 2.2、性能原因 2.3、那么如何即满足对齐&#xff0c;又要节省空间呢&#xff1f; …

PTA L2-038 病毒溯源

病毒容易发生变异。某种病毒可以通过突变产生若干变异的毒株&#xff0c;而这些变异的病毒又可能被诱发突变产生第二代变异&#xff0c;如此继续不断变化。 现给定一些病毒之间的变异关系&#xff0c;要求你找出其中最长的一条变异链。 在此假设给出的变异都是由突变引起的&a…

PS从入门到精通视频各类教程整理全集,包含素材、作业等复发(2)

PS从入门到精通视频各类教程整理全集&#xff0c;包含素材、作业等 最新PS以及插件合集&#xff0c;可在我以往文章中找到 由于阿里云盘有分享次受限制和文件大小限制&#xff0c;今天先分享到这里&#xff0c;后续持续更新 初级教程素材 等文件 https://www.alipan.com/s/fC…

AI计算平台设计方案:901-基于3U VPX的图像数据AI计算平台

一、产品概述 设备基于3U VPX的导冷结构&#xff0c;集成FPGA接口预处理卡&#xff0c;GPU板卡、飞腾ARM处理卡&#xff0c;实现光纤、差分电口或者Camera link的图像接入&#xff0c;FPGA信号预处理&#xff0c;GPU AI计算&#xff0c;飞腾ARM的采集管理存储。 二、系统…

Flutter 使用 AndroidStudio 给(Android 安卓)进行签名方法

一、使用 AndroidStudio 创建签名 使用 AndroidStudio 打开 Flutter项目中的 android 文件夹首次打开 AndroidStudio 会加载一会。菜单栏 &#xff1a; Build -> Generate Signed Bundle APK... 选中 APK -> Next点击Create new....下面按照需求填写即可- 文件夹选择 项…

openPLC_Editor C语言编程 在mp157 arm板上调用io等使用记录

1.编程界面比较简单&#xff0c;具备PLC开发编程的四种编程方式。梯形图语言LD &#xff0c;指令表语言IL&#xff0c;结构化文本语言ST&#xff0c;功能模块图语言FBD。 2.官方使用手册。学习资料实在是太少&#xff0c;目前都是自己比较费劲的研究。 3.2 Creating Your First…

vmWare虚拟机下载安装详细教程,手把手一步一步教学

一.下载 先去官网下载vm 官网地址:https://www.vmware.com/ 官网下载速度太慢了 国内应用商店或者别的下载地址:vmware下载_vmware下载免费中文版客户端[虚拟机]-下载之家 然后再去下载一个镜像 地址是:Index of /centos/7/isos/x86_64/ 我个人下载的是这个 ,看各位的需求…

Reasoning on Graphs: Faithful and Interpretable Large Language Model Reasonin

摘要 大型语言模型(llm)在复杂任务中表现出令人印象深刻的推理能力。然而&#xff0c;他们在推理过程中缺乏最新的知识和经验幻觉&#xff0c;这可能导致不正确的推理过程&#xff0c;降低他们的表现和可信度。知识图谱(Knowledge graphs, KGs)以结构化的形式捕获了大量的事实…