基于RK3588开发板的Qt+OpenCV MIPI摄像头实时图像处理实践
1. 从零开始认识你的RK3588开发板与MIPI摄像头如果你手头正好有一块RK3588的开发板比如讯为的iTOP-RK3588并且还配了一个小小的MIPI摄像头比如OV5695那你已经拥有了一个非常强大的边缘图像处理实验平台。很多朋友拿到手的第一反应可能是这玩意儿能跑安卓我装个APP不就能拍照了吗没错但那样就太“普通”了。我们今天要玩的是深入到系统底层和编程框架里用Qt来打造一个带界面的应用程序并用OpenCV这个计算机视觉的“瑞士军刀”来实时处理摄像头拍到的每一帧画面。想象一下你可以实时给人脸打上马赛克、检测运动物体、或者识别简单的颜色块所有这些都在一块巴掌大的板子上独立运行不依赖网络这就是嵌入式AI视觉的魅力。我刚开始接触RK3588的时候也被它丰富的接口和强大的算力吸引。它内置的NPU神经网络处理单元虽然我们今天的主角OpenCV用不上那是另一套推理框架的事儿但它的CPU多核性能和视频编解码能力对于实时图像处理来说已经绰绰有余。而MIPI摄像头是移动设备和嵌入式领域非常主流的摄像头接口体积小、功耗低、带宽高OV5695是一颗500万像素的传感器对于大多数识别和检测任务来说这个分辨率已经足够清晰了。我们的目标就是让Qt这个漂亮的图形界面框架和OpenCV这个强大的图像处理引擎在RK3588这块板子上完美合作把摄像头的图像实时地“搬”到屏幕上并且还能在中间“加工”一下。在开始动手之前我们需要明确整个流程的脉络。首先开发板出厂通常是安卓系统我们需要把它换成更适合开发的Ubuntu。然后我们要在这个Ubuntu系统上搭建起Qt和OpenCV的开发环境。最后才是编写代码用OpenCV打开摄像头用Qt来显示和处理图像。听起来步骤不少但别担心我会把我踩过的坑和验证过的稳定方法都分享出来你只要跟着一步步来肯定能跑通。整个过程我们都会在开发板的Ubuntu系统终端里操作就像在玩一台小电脑一样。2. 打造基础系统烧录与开发环境搭建拿到开发板第一步不是急着写代码而是给它安装一个合适的“工作台”。原厂的安卓系统虽然功能完整但对于我们这种需要底层访问和编译开发的场景来说一个Linux发行版才是更趁手的工具。讯为官方一般会提供适配好的Ubuntu 20.04镜像我们就用它。2.1 烧录Ubuntu系统镜像烧录系统听起来有点唬人其实现在工具都很方便。你需要准备一台Windows或者Linux的电脑一根USB-Type C的数据线通常用于连接开发板的OTG口进行烧录以及从开发板官网下载的烧录工具比如RKDevTool和Ubuntu镜像文件。具体步骤官方的开发手册会写得很清楚给开发板连接好电源和串口调试线方便看日志进入Loader模式一般是按住某个按键再上电然后在电脑上的烧录工具里加载镜像文件点击执行。等待进度条跑完开发板会自动重启。这时通过串口终端或者接上HDMI显示器你应该能看到Ubuntu系统的登录界面了。这个过程一定要耐心确保电源稳定中途断电可能会导致系统损坏。系统启动后我强烈建议你先做两件事第一修改软件源这能让你后续安装软件的速度飞起第二更新系统获取最新的安全补丁和软件包。通过串口或者SSH登录到开发板用户名和密码通常是topeet/topeet或者rock/rock具体看手册打开终端我们来更换为国内的清华源。编辑源列表文件需要管理员权限sudo vim /etc/apt/sources.list如果你不熟悉vim用nano也可以sudo nano /etc/apt/sources.list。把文件里原来的内容全部注释掉或者替换掉换成如下的清华源地址针对Ubuntu 20.04 Focaldeb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-updates main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-backports main restricted universe multiverse deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/ focal-security main restricted universe multiverse注意这里用的是ubuntu-ports因为RK3588是ARM64架构属于“移植”版本。保存退出后依次执行更新命令sudo apt-get update sudo apt-get upgradeupdate是刷新软件包列表upgrade是升级已安装的软件。升级过程可能会比较长取决于网络速度和更新包的大小喝杯咖啡等待一下就好。2.2 安装Qt开发环境系统基础打好了接下来安装Qt。在桌面Linux上我们可能喜欢去官网下载在线安装器但在资源有限的嵌入式板子上用系统自带的包管理器apt来安装是最省心、依赖关系处理得最好的方式。我们要安装的不仅仅是Qt库还有编译工具和集成开发环境虽然我们最后不一定非要用Qt Creator但装上好调试。首先安装必要的编译工具链sudo apt-get install build-essential然后安装Qt5的核心开发包、构建工具以及Qt Creatorsudo apt-get install qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools sudo apt-get install qtcreator为了省事避免后续缺某个Qt模块比如网络、串口、图表等你也可以一股脑安装所有Qt5相关的库空间足够的话sudo apt-get install qt5*安装完成后你可以在应用程序菜单里找到Qt Creator。不过我们后续的重点是写代码和编译用命令行qmake和make也一样高效。你可以通过输入qmake -v来验证是否安装成功它会显示Qt的版本信息。2.3 编译安装OpenCV 3.4.14OpenCV的安装稍微复杂一点因为我们可能需要特定的版本比如原文用的3.4.14并且从源码编译可以更好地控制编译选项确保所有功能都被正确开启。首先安装一大堆编译依赖库这些库提供了图像解码、视频流处理、图形界面显示等基础功能sudo apt-get install cmake gcc g sudo apt-get install python3-dev python3-numpy sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev sudo apt-get install libv4l-dev libxvidcore-dev libx264-dev libatlas-base-dev gfortran libgtk2.0-dev libjpeg-dev libpng-dev这里有个小坑在Ubuntu 20.04上安装python3-dev和python3-numpy时可能会报错提示找不到包。这是因为软件源索引的问题。一个可靠的解决方法是先更新软件包缓存或者指定更通用的包名。如果还不行可以搜索一下错误信息通常有社区提供的解决方案。安装这些依赖的时候如果遇到询问是否继续一律按Y回车。依赖搞定后我们去下载OpenCV 3.4.14的源码。在开发板上下载可能比较慢你也可以在电脑上下好通过SCP或者U盘拷贝到开发板上。这里演示用wget在板子上直接下载wget https://codeload.github.com/opencv/opencv/zip/refs/tags/3.4.14 -O opencv-3.4.14.zip unzip opencv-3.4.14.zip解压后会得到一个opencv-3.4.14文件夹。我们进入该目录并创建一个独立的build目录用于编译这是一种保持源码目录干净的好习惯cd opencv-3.4.14 mkdir build cd build接下来是最关键的CMake配置步骤。我们使用CMake生成Makefile。对于嵌入式环境我们可能不需要一些高级特性比如CUDA因为RK3588没有NVIDIA GPU但基础的图像处理和V4L2Video for Linux 2Linux视频采集框架支持必须打开cmake -D CMAKE_BUILD_TYPERELEASE -D CMAKE_INSTALL_PREFIX/usr/local -D WITH_V4LON -D WITH_GTKON -D WITH_OPENGLON ..参数解释一下CMAKE_BUILD_TYPERELEASE是发布版本优化更好CMAKE_INSTALL_PREFIX/usr/local指定安装目录WITH_V4LON是开启V4L2支持这对摄像头采集至关重要WITH_GTKON是开启GTK界面支持虽然我们用Qt但OpenCV内部的高斯窗口显示可能需要WITH_OPENGLON开启OpenGL可能对某些图像显示加速有帮助。执行这条命令后CMake会检查依赖并生成配置请留意输出最后有没有报“NOT FOUND”的错误。配置成功就可以开始编译了。这是最耗时的一步RK3588有多个核心我们可以用-j参数指定并行编译任务数来加快速度比如用4个核心make -j4编译过程没有报错的话就可以安装了sudo make install安装完成后OpenCV的头文件会被放到/usr/local/include/opencv2库文件在/usr/local/lib。为了让系统能找到这些新安装的库我们需要更新一下动态链接库的缓存sudo sh -c echo /usr/local/lib /etc/ld.so.conf.d/opencv.conf sudo ldconfig最后验证一下安装是否成功。可以写一个简单的C测试程序或者直接用Python测试如果你安装了Python版的OpenCV。在终端输入python3进入Python交互环境然后输入import cv2再输入print(cv2.__version__)如果显示3.4.14那么恭喜你OpenCV环境搭建成功3. 第一个程序让摄像头画面显示在Qt窗口里环境都齐备了是时候动手写代码了。我们的第一个目标很简单在Qt应用程序里打开MIPI摄像头并把实时画面显示在一个窗口里。别小看这个“Hello World”它涉及了Qt项目创建、OpenCV库链接、摄像头设备号查找、图像采集循环等关键环节。3.1 创建Qt项目并配置OpenCV首先我们打开Qt Creator新建一个“Qt Console Application”或者“Qt Widgets Application”都可以。为了方便我们建一个控制台应用这样没有复杂的界面设计专注于逻辑。项目名字就叫CameraTest吧。创建好后你会得到一个.pro文件项目配置文件和一个main.cpp文件。最关键的一步是配置.pro文件告诉Qt编译器去哪里找OpenCV的头文件和库文件。打开CameraTest.pro在文件末尾加上以下内容INCLUDEPATH /usr/local/include \ /usr/local/include/opencv \ /usr/local/include/opencv2 LIBS -L/usr/local/lib \ -lopencv_core \ -lopencv_highgui \ -lopencv_imgproc \ -lopencv_videoioINCLUDEPATH指定头文件路径LIBS指定库文件路径和要链接的库。-L后面跟库文件目录-l后面跟具体的库名去掉前缀lib和后缀.so。我们这里链接了四个最基础的库core核心、highgui高级GUI负责窗口显示、imgproc图像处理、videoio视频输入输出包含了摄像头采集。这样配置后我们的Qt项目就能调用OpenCV的函数了。3.2 编写图像采集与显示代码接下来我们修改main.cpp。我们的思路是使用OpenCV的VideoCapture类打开摄像头设备在一个循环里不断读取帧Mat对象然后用imshow函数把每一帧显示出来。同时为了能退出程序我们需要检测键盘按键。#include QCoreApplication // 如果是控制台应用 // #include QApplication // 如果是Widgets应用 #include opencv2/opencv.hpp #include iostream int main(int argc, char *argv[]) { // QCoreApplication a(argc, argv); // 控制台应用 // QApplication a(argc, argv); // Widgets应用 // 对于纯OpenCV显示甚至可以不用Qt的Application对象这里我们先不用。 cv::VideoCapture capture; cv::Mat frame; // 关键这里的设备号不是固定的0或1而是MIPI摄像头在V4L2子系统中的节点号。 int camera_index 11; // 这是一个示例你的板子可能不同 // 尝试以V4L2后端打开指定设备 capture.open(camera_index, cv::CAP_V4L2); if (!capture.isOpened()) { std::cerr 错误无法打开摄像头设备 camera_index std::endl; // 尝试一下默认的自动探测 capture.open(0); if (!capture.isOpened()) { std::cerr 错误默认摄像头也无法打开。 std::endl; return -1; } } std::cout 摄像头已成功打开按ESC键退出。 std::endl; // 设置摄像头参数可选例如分辨率 capture.set(cv::CAP_PROP_FRAME_WIDTH, 640); capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480); // 主循环 while (true) { capture frame; // 或者用 capture.read(frame); if (frame.empty()) { std::cerr 错误获取到空帧。 std::endl; break; } // 在这里可以对frame进行图像处理例如灰度化、边缘检测等 // cv::cvtColor(frame, frame, cv::COLOR_BGR2GRAY); cv::imshow(RK3588 MIPI Camera Live, frame); // 等待1毫秒并检查是否有按键按下。ASCII 27是ESC键。 char key (char)cv::waitKey(1); if (key 27 || key q) { break; } } // 释放资源 capture.release(); cv::destroyAllWindows(); // return a.exec(); // 如果使用了QApplication需要这个 return 0; }代码有几个要点需要注意。第一camera_index这个设备号是重中之重。在桌面电脑上USB摄像头通常是0或1。但在嵌入式Linux系统里特别是像RK3588这样有复杂多媒体接口的芯片上MIPI摄像头会被映射到/dev/videoX这样的设备节点而这个X数字可能很大。怎么查呢在开发板的终端里运行命令v4l2-ctl --list-devices你会看到类似下面的输出里面会列出所有视频设备及其对应的设备节点比如/dev/video11。找到你的摄像头型号如rkisp_mainpath或mipi_dphy_rx等描述记下后面的数字把它填到代码里。第二我们使用cv::CAP_V4L2标志明确指定使用V4L2后端来打开设备这对于Linux系统下的摄像头兼容性更好。第三循环里的cv::waitKey(1)是必须的它不光负责延时1毫秒还负责刷新OpenCV的高清窗口和处理GUI事件。没有它窗口会卡死。3.3 编译、运行与排错在Qt Creator里直接点击绿色的运行按钮即可。如果是在命令行进入项目目录执行qmake make ./CameraTest如果一切顺利你应该会看到一个名为“RK3588 MIPI Camera Live”的窗口弹出里面是实时摄像头画面。按ESC或Q键可以退出。但现实往往没那么顺利。常见的错误有打不开摄像头首先确认设备号是否正确。其次检查摄像头是否被其他进程占用可以用fuser /dev/video11命令查看。最后确认你的用户是否有访问视频设备的权限通常需要加入video用户组可以用sudo usermod -aG video $USER命令然后重新登录。画面颜色不对或花屏这可能是像素格式不匹配。OpenCV默认期望BGR格式但摄像头可能输出YUV或RGB。可以在capture.read(frame)后尝试用cv::cvtColor进行转换或者更彻底地在打开摄像头后用v4l2-ctl命令查看和设置摄像头支持的格式。程序运行很卡可能是分辨率太高。OV5695支持最高2592x1944但实时处理可能负担太大。在代码里用set函数将分辨率设为640x480或320x240试试。另外确保编译时开启了优化-O2在.pro文件中可以添加QMAKE_CXXFLAGS -O2。当你看到稳定的画面时第一阶段的目标就达成了。我们已经成功打通了从MIPI传感器到屏幕显示的完整链路。4. 进阶实战在Qt界面中嵌入并实时处理OpenCV图像仅仅在OpenCV的独立窗口里显示还不能发挥Qt的优势。更常见的需求是我们有一个Qt制作的GUI界面上面有按钮、滑块、状态栏而摄像头画面只是界面中的一个部分。同时我们希望在画面显示前先做点实时处理比如变成灰度图、检测边缘、或者画上一些分析结果。这就需要我们把OpenCV的Mat图像转换成Qt能显示的QImage或QPixmap。4.1 创建带图像显示区域的Qt Widgets应用这次我们创建一个“Qt Widgets Application”。在Qt Creator的设计界面拖一个Label控件到主窗口上这个Label将用来显示图像。不妨把它的objectName改为labelCameraDisplay并把它拉大一些。我们可能还需要一个“开始”、“停止”按钮以及一个用于退出程序的按钮。在代码层面我们需要做几件事在头文件里包含必要的OpenCV和Qt头文件并声明一个VideoCapture对象和定时器QTimer。使用定时器来驱动图像采集而不是用while循环。这是因为Qt的主线程需要处理GUI事件死循环会阻塞界面。在定时器的槽函数里完成“采集帧 - 处理帧 - 转换格式 - 在Label上显示”这一系列操作。首先看头文件mainwindow.h的改动#ifndef MAINWINDOW_H #define MAINWINDOW_H #include QMainWindow #include opencv2/opencv.hpp QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent nullptr); ~MainWindow(); private slots: void on_pushButtonStart_clicked(); // “开始”按钮的槽函数 void on_pushButtonStop_clicked(); // “停止”按钮的槽函数 void updateCameraFrame(); // 定时器触发的槽函数用于更新图像 private: Ui::MainWindow *ui; cv::VideoCapture m_capture; QTimer *m_timer; bool m_isCameraRunning; }; #endif // MAINWINDOW_H4.2 实现图像采集、处理与显示循环在mainwindow.cpp中我们需要实现这些槽函数。重点是updateCameraFrame()函数它是整个实时处理的核心。#include mainwindow.h #include ui_mainwindow.h #include QTimer #include QImage #include QPixmap #include QDebug MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , m_isCameraRunning(false) { ui-setupUi(this); m_timer new QTimer(this); // 连接定时器超时信号到我们的更新槽函数 connect(m_timer, QTimer::timeout, this, MainWindow::updateCameraFrame); // 连接按钮信号假设你在设计器里将按钮的clicked()信号自动连接到了on_pushButtonX_clicked() } MainWindow::~MainWindow() { if (m_capture.isOpened()) { m_capture.release(); } delete ui; } void MainWindow::on_pushButtonStart_clicked() { if (!m_isCameraRunning) { // 尝试打开摄像头设备号根据实际情况修改 if (!m_capture.open(11, cv::CAP_V4L2)) { qDebug() 打开摄像头失败; return; } // 设置摄像头分辨率 m_capture.set(cv::CAP_PROP_FRAME_WIDTH, 640); m_capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480); m_isCameraRunning true; m_timer-start(33); // 约30帧每秒 (1000ms / 30 ≈ 33ms) ui-pushButtonStart-setEnabled(false); ui-pushButtonStop-setEnabled(true); } } void MainWindow::on_pushButtonStop_clicked() { if (m_isCameraRunning) { m_timer-stop(); m_capture.release(); m_isCameraRunning false; ui-pushButtonStart-setEnabled(true); ui-pushButtonStop-setEnabled(false); // 清空显示 ui-labelCameraDisplay-clear(); } } void MainWindow::updateCameraFrame() { if (!m_capture.isOpened()) { return; } cv::Mat frame; m_capture frame; if (frame.empty()) { return; } // --- 实时图像处理区域 START --- // 示例1转换为灰度图 cv::Mat processedFrame; cv::cvtColor(frame, processedFrame, cv::COLOR_BGR2GRAY); // 再转换回BGR以便用同样的颜色转换代码显示灰度图是单通道 cv::cvtColor(processedFrame, processedFrame, cv::COLOR_GRAY2BGR); // 示例2Canny边缘检测在灰度图上做效果更好 // cv::Mat gray, edges; // cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); // cv::Canny(gray, edges, 50, 150); // cv::cvtColor(edges, processedFrame, cv::COLOR_GRAY2BGR); // 将边缘图转为BGR显示 // 示例3在原图上画一个矩形和文字 // cv::rectangle(frame, cv::Point(100, 100), cv::Point(300, 300), cv::Scalar(0, 255, 0), 2); // cv::putText(frame, RK3588 Qt OpenCV, cv::Point(150, 50), // cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255, 0, 0), 2); // processedFrame frame; // 使用处理后的帧 // --- 实时图像处理区域 END --- // 将OpenCV的Mat转换为Qt的QImage // OpenCV默认是BGR顺序Qt是RGB需要转换 cv::cvtColor(processedFrame, processedFrame, cv::COLOR_BGR2RGB); QImage qimg(processedFrame.data, processedFrame.cols, processedFrame.rows, processedFrame.step, QImage::Format_RGB888); // 将QImage缩放以适应Label的大小并保持宽高比 QPixmap pixmap QPixmap::fromImage(qimg); pixmap pixmap.scaled(ui-labelCameraDisplay-size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); // 在Label上显示图像 ui-labelCameraDisplay-setPixmap(pixmap); }这段代码实现了完整的流程。QTimer每33毫秒触发一次updateCameraFrame在这个函数里我们读取一帧然后可以在注释标注的“实时图像处理区域”添加任何OpenCV处理代码。处理完成后必须注意颜色空间的转换OpenCV用BGRQt用RGB所以要用cv::COLOR_BGR2RGB转换一下。接着用QImage封装图像数据再转换成QPixmap最后通过setPixmap显示在QLabel上。4.3 性能优化与实用技巧当你在RK3588上运行这个程序时可能会发现处理速度跟不上特别是做了复杂的图像处理如Canny边缘检测后帧率会下降。这里有几个优化思路降低分辨率这是最直接有效的方法。将采集分辨率从640x480降到320x240计算量会减少到原来的1/4。优化处理算法考虑使用更轻量级的算法或者降低算法的复杂度。例如做运动检测时可以隔几帧做一次全图计算中间帧只做简单差分。利用多线程将耗时的图像处理任务放到另一个工作线程QThread中避免阻塞主线程GUI线程。主线程只负责采集和显示工作线程处理完一帧后通过信号槽机制将结果图像发送回主线程显示。这是保持界面流畅的关键。使用硬件加速RK3588有强大的视频编解码器VPU和图像处理单元ISP。对于某些操作如缩放、颜色空间转换可以研究使用RKMPPRockchip Media Process Platform或通过V4L2的mem2mem设备进行硬件加速。但这属于更高级的专题需要深入阅读内核驱动和SDK文档。检查内存拷贝在我们的代码中QImage qimg(processedFrame.data, ...)并没有拷贝数据它只是“包装”了processedFrame.data这块内存。这是高效的。但要确保在显示期间processedFrame这个Mat对象不会被析构或覆盖。一个简单的多线程架构建议是主线程用定时器驱动从VideoCapture取帧然后将原始帧通过Qt的信号一个携带cv::Mat的自定义信号发送给工作线程。工作线程在run()函数里循环处理接收到的帧处理完毕后再通过信号发送回主线程更新UI。这样即使处理很慢UI也不会卡死只是显示会有些延迟。5. 项目完善与深入探索方向当基本的实时显示和处理跑通后一个完整的项目还需要考虑更多细节。比如如何让用户方便地调整摄像头参数曝光、增益、白平衡如何保存处理后的视频或图片如何将处理结果比如识别到的物体坐标通过网络发送出去5.1 动态调整摄像头参数OpenCV的VideoCapture类提供了set和get函数来调整摄像头属性。常见的属性ID有cv::CAP_PROP_BRIGHTNESS亮度cv::CAP_PROP_CONTRAST对比度cv::CAP_PROP_SATURATION饱和度cv::CAP_PROP_EXPOSURE曝光注意有些摄像头这个值表示自动曝光模式而非具体值cv::CAP_PROP_GAIN增益你可以在Qt界面上添加几个QSlider滑块分别对应这些参数。在滑块的valueChanged信号槽里调用m_capture.set(propId, value)。但要注意不是所有摄像头都支持所有参数的调整而且V4L2后端下这些属性的可用性和取值范围需要先用v4l2-ctl -d /dev/video11 --list-ctrls命令在终端里查看确认。5.2 添加实用的图像处理功能模块一个完整的演示程序可以集成多个处理功能并通过下拉菜单或按钮切换。例如灰度视图最基本的转换。边缘检测使用Canny、Sobel等算子。人脸检测加载OpenCV自带的Haar级联分类器haarcascade_frontalface_default.xml虽然精度一般但在RK3588上跑起来速度尚可适合演示。颜色追踪将图像转换到HSV色彩空间通过inRange函数阈值化找出特定颜色的区域并标出其轮廓和中心点。运动检测通过比较连续帧的差异找出移动的物体区域。每个功能可以写成一个单独的函数接受一个cv::Mat作为输入返回处理后的cv::Mat。然后在updateCameraFrame函数里根据当前选中的模式调用对应的处理函数。5.3 项目部署与打包开发调试完成后你可能希望将程序部署到另一块干净的RK3588板子上运行。这就需要解决依赖问题。你的程序依赖Qt5和OpenCV 3.4.14的动态库。你可以使用ldd命令查看你的可执行文件依赖哪些库ldd ./YourAppName然后将这些依赖的库文件除了系统标准库如libc.so,libpthread.so等拷贝到目标板子的某个目录例如/opt/yourapp/lib并在运行程序前通过设置环境变量LD_LIBRARY_PATH来指定库的搜索路径export LD_LIBRARY_PATH/opt/yourapp/lib:$LD_LIBRARY_PATH ./YourAppName更专业的方式是制作一个deb安装包或者使用静态编译将Qt和OpenCV都静态链接到你的程序中但这会显著增加最终可执行文件的大小且对开源许可证有要求。5.4 结合RK3588 NPU进行AI推理这才是RK3588的终极玩法。OpenCV的DNN模块可以加载训练好的神经网络模型如Caffe、TensorFlow、ONNX格式但默认是在CPU上推理。RK3588的NPU性能远超CPU要利用它你需要使用Rockchip官方提供的RKNN Toolkit2工具链。流程大概是在PC上将你的模型如YOLO、MobileNet SSD转换成RKNN格式然后在板端程序里通过RKNN SDK提供的C/C或Python API来加载模型、输入图像、执行推理、获取结果。最后将推理结果如边界框、类别标签用OpenCV的画图函数叠加到原始图像上再通过Qt显示出来。这就构成了一个完整的嵌入式AI视觉应用可以实现人脸识别、物体检测、姿态估计等复杂功能。这部分的实践足以展开成为另一个专题。从点亮一颗MIPI摄像头到在自定义的Qt界面里流畅地显示并实时处理图像再到思考如何引入NPU加速这个过程充满了挑战和乐趣。RK3588这个平台给了我们很大的发挥空间。我建议你先把手头这个基础框架搭稳把图像采集和显示的流水线跑流畅然后再逐一添加更高级的功能。遇到问题多查查V4L2的文档、OpenCV的API和Qt的信号槽机制嵌入式开发的乐趣就在于这种一步步解决问题的成就感。

相关新闻

寻音捉影·侠客行开源大模型部署:基于ModelScope FunASR的可复现方案

寻音捉影·侠客行开源大模型部署:基于ModelScope FunASR的可复现方案

寻音捉影侠客行开源大模型部署:基于ModelScope FunASR的可复现方案 1. 项目概述 寻音捉影侠客行是一款基于阿里巴巴ModelScope FunASR语音识别技术的音频关键词检索工具。它能够在海量音频数据中快速定位特定关键词,就像武侠小说中的高手在茫茫人海中精…

2026/7/5 18:00:45 阅读更多 →
Qwen3-0.6B-FP8创意编程展示:根据描述生成趣味小游戏代码

Qwen3-0.6B-FP8创意编程展示:根据描述生成趣味小游戏代码

Qwen3-0.6B-FP8创意编程展示:根据描述生成趣味小游戏代码 最近在玩一些AI代码生成工具,发现了一个挺有意思的现象。很多大模型在写业务逻辑或者算法题时表现不错,但一涉及到需要创意和完整工程实现的小游戏,就容易“翻车”——要…

2026/7/6 4:46:38 阅读更多 →
SenseVoice-Small模型在网络安全领域的语音威胁检测应用

SenseVoice-Small模型在网络安全领域的语音威胁检测应用

SenseVoice-Small模型在网络安全领域的语音威胁检测应用 想象一下,你是一家大型企业的安全主管。每天,公司内部和对外有成千上万通电话在进行,其中可能混杂着试图窃取商业机密的通话、内部员工违规泄露信息的交谈,甚至是针对关键…

2026/7/5 17:56:21 阅读更多 →

最新新闻

Excel ROUND函数底层原理与财务工程级避坑指南

Excel ROUND函数底层原理与财务工程级避坑指南

1. 项目概述:为什么一个“四舍五入”函数值得写上万字?Excel里敲下ROUND(A1,2),结果立刻出来——看起来简单得不能再简单。但如果你在财务报表里用它算过增值税、在工程预算中处理过材料损耗率、在科研数据里校验过有效数字,你大概…

2026/7/6 10:46:37 阅读更多 →
Plone电商基建加固:GetPaid Recipe Release 1.4.1深度解析

Plone电商基建加固:GetPaid Recipe Release 1.4.1深度解析

1. 项目概述:一个被低估的 Plone 电商基建工具包更新 Plone 是个老派但极其扎实的内容管理系统,尤其在政府、教育、科研类机构里扎根很深。它不像 WordPress 那样靠主题和插件堆出花哨界面,而是靠严格的权限模型、内容生命周期管理和可审计性…

2026/7/6 10:46:37 阅读更多 →
ZODB对象数据库原理与Zope内容管理实战

ZODB对象数据库原理与Zope内容管理实战

1. 项目概述:当应用服务器与数据库在对象层面“无缝焊接”我第一次接触 Zope 是在 1998 年,那会儿连“Web 应用框架”这个词都还没被广泛使用。打开文档,第一反应不是兴奋,而是皱眉——“这玩意儿默认不让我连 MySQL?”…

2026/7/6 10:46:37 阅读更多 →
Linux防火墙端口管理:firewalld、iptables、ufw 3种方案深度对比与选择指南

Linux防火墙端口管理:firewalld、iptables、ufw 3种方案深度对比与选择指南

Linux防火墙端口管理:firewalld、iptables、ufw 3种方案深度对比与选择指南在Linux服务器管理中,防火墙配置是确保系统安全的关键环节。面对不同的发行版和业务需求,系统管理员常常需要在firewalld、iptables和ufw这三种主流防火墙工具之间做…

2026/7/6 10:44:32 阅读更多 →
可视化指挥闭环:城市安防视频孪生时空感知技术详解

可视化指挥闭环:城市安防视频孪生时空感知技术详解

可视化指挥闭环:城市安防视频孪生时空感知技术详解摘要传统城市安防指挥体系存在视频孤岛、时空基准割裂、预警处置脱节、调度无空间量化支撑、事后复盘证据碎片化五大核心痛点,指挥链路停留在“看画面、接警情、人工派单”的线性被动模式,无…

2026/7/6 10:44:32 阅读更多 →
如何快速上手eulerfs-test:从配置到执行的完整教程

如何快速上手eulerfs-test:从配置到执行的完整教程

如何快速上手eulerfs-test:从配置到执行的完整教程 【免费下载链接】eulerfs-test test scripts for eulerfs 项目地址: https://gitcode.com/openeuler/eulerfs-test 前往项目官网免费下载:https://ar.openeuler.org/ar/ eulerfs-test是openEul…

2026/7/6 10:42:30 阅读更多 →

日新闻

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2 与 MySQL 单元测试兼容性:5 个关键 SQL 语句差异与规避方案

H2与MySQL单元测试兼容性:5个关键SQL语句差异与规避方案1. 单元测试中的数据库兼容性挑战在Java开发领域,单元测试是保证代码质量的重要环节。当应用涉及数据库操作时,测试环境的搭建往往成为开发者的痛点。H2数据库因其轻量级、内存模式和快…

2026/7/6 0:01:17 阅读更多 →
Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘

Windows任务栏终极清理指南:用RBTray一键隐藏窗口到系统托盘 【免费下载链接】rbtray A fork of RBTray from http://sourceforge.net/p/rbtray/code/. 项目地址: https://gitcode.com/gh_mirrors/rb/rbtray 你是否厌倦了Windows任务栏上密密麻麻的图标&…

2026/7/6 0:01:17 阅读更多 →
Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C++ 运行时库一键安装终极指南:告别DLL缺失烦恼

Visual C 运行时库一键安装终极指南:告别DLL缺失烦恼 【免费下载链接】vcredist AIO Repack for latest Microsoft Visual C Redistributable Runtimes 项目地址: https://gitcode.com/gh_mirrors/vc/vcredist 你是否曾经遇到过这样的情况:下载了…

2026/7/6 0:05:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻