DshanPI-A1第三篇opencv调试与cpu直接推理识别手势
经验总结对比日志是发现问题的关键 :通过对比正常和异常情况的日志,快速定位到 no first iq setting 这个关键线索系统性诊断 :不要盲目尝试,先检查各个组件(IQ文件、3A服务器、设备节点)的状态前台运行看详细日志 :很多后台服务的问题需要前台运行才能看到详细输出理解组件间的协作关系 :RK平台的摄像头涉及ISP驱动、3A服务器、IQ参数文件三者的协作,任何一个环节出问题都会导致异
C41349295DshanPI-A1第三篇opencv调试与cpu直接推理识别手势2026年1月4日 · 阅读需 10 分钟前面我们已经调试好了摄像头和屏幕,终于可以开始我们的手势识别啦!这次我会在RK3576 Buildroot系统上实现一个基于OpenCV的实时手势识别系统。系统能够识别五种手势(拳头/一指、二指、三指、四指、五指),并在屏幕上实时显示处理结果。由于嵌入式系统的特殊性,我们将重点讲解如何在无X11、无OpenGL的Wayland环境下实现图像显示。手势识别算法原理因为我们张开的手指之间会形成凹陷,通过计算凹陷点的角度和深度,可以准确识别手指数量。1. 肤色检测使用HSV色彩空间提取肤色区域:def detect_hand ( self , frame ) :hsv = cv2 . cvtColor ( frame , cv2 . COLOR_BGR2HSV )mask = cv2 . inRange ( hsv , [ 0 , 30 , 60 ] , [ 25 , 255 , 255 ] ) # 肤色范围# 形态学处理去噪kernel = np . ones ( ( 7 , 7 ) , np . uint8 )mask = cv2 . morphologyEx ( mask , cv2 . MORPH_CLOSE , kernel , iterations = 3 )mask = cv2 . morphologyEx ( mask , cv2 . MORPH_OPEN , kernel , iterations = 2 )# 查找最大轮廓contours , _ = cv2 . findContours ( mask , cv2 . RETR_EXTERNAL , cv2 . CHAIN_APPROX_SIMPLE )if contours :c = max ( contours , key = cv2 . contourArea )if cv2 . contourArea ( c ) > 3000 : # 面积阈值过滤噪声return c , maskreturn None , mask2. 手指计数(凸包缺陷法)通过检测手掌轮廓的凹陷点来识别手指:def recognize ( self , contour ) :hull = cv2 . convexHull ( contour , returnPoints = False )defects = cv2 . convexityDefects ( contour , hull )finger_count = 0for i in range ( defects . shape [ 0 ] ) 😒 , e , f , d = defects [ i , 0 ]start = tuple ( contour [ s ] [ 0 ] ) # 凸点1end = tuple ( contour [ e ] [ 0 ] ) # 凸点2far = tuple ( contour [ f ] [ 0 ] ) # 凹点(指间)# 计算角度判断是否为有效指尖a = np . linalg . norm ( np . array ( start ) - np . array ( end ) )b = np . linalg . norm ( np . array ( start ) - np . array ( far ) )c = np . linalg . norm ( np . array ( end ) - np . array ( far ) )angle = np . arccos ( ( b ** 2 + c ** 2 - a ** 2 ) / ( 2 * b * c ) )if angle <= np . pi / 2.2 and d > 8000 : # 角度和深度阈值finger_count += 1gestures = [ “Fist/One” , “Two” , “Three” , “Four” , “Five” ]return gestures [ finger_count ]如何显示OpenCV处理的图像有了前面的理论知识还是不够的,我们还需要实践才可以啊!如何显示OpenCV处理的图像这是本教程的重点。通常我们用 cv2.imshow() 显示图像,但在无X11/OpenGL的嵌入式系统上,这个方法不可用。我们需要使用 GStreamer+Wayland 方案(这个也是我们上一篇文章的方案)。方案探索过程方案A: stdin管道传输最直观的想法是通过stdin管道传输图像数据:proc = subprocess . Popen ( [ ‘gst-launch-1.0’ , ‘fdsrc’ , ‘!’ , . . . ] , stdin = subprocess . PIPE )proc . stdin . write ( frame_data )结果 :频繁出现Broken pipe错误,数据传输不稳定,这个我怀疑是管道超时自动关闭了又或者是我的格式不对。所以我直接尝试用fifo来传输原始的数据方案B: 命名管道(FIFO)传输原始数据尝试用FIFO传输原始RGB数据:mkfifo /tmp/video_fifo
很遗憾啊,这个太容易动不动就内核奔溃了。到这个时候我面临几个问题:如果我单纯的用命令行看识别结果,我就不知道我的摄像头能不能正常运行;但是如果OpenCV和GStreamer同时读取摄像头是不可以的(摄像头只能被一个进程读取)。后面又觉得GStreamer的显示不需要实时读取摄像头的画面,我只要显示OpenCV处理过的就好了。说干就干,于是我尝试直接在OpenCV里面把处理后的图像通过GStreamer传到屏幕上面,但技术不达标无果,管道不是报错就是关闭。停下来慢慢思考,最后有了方案C(搞到这步已经花费三天了)。方案C: 多文件序列改变思路,将处理后的图像保存为JPEG文件序列:# Python端cv2 . imwrite ( f’/dev/shm/gesture_frames/frame_ { frame_index : 03d } .jpg’ , processed )# GStreamer端gst-launch-1.0 multifilesrc location=frame_%03d.jpg loop=true ! jpegdec ! …结果 :屏幕终于可以有变动了,开心坏了!但是效果很差,重影严重,而且因为是循环读出文件夹的图片导致一直在循环播放,影响美观和体验度。于是我打算利用FIFO,基于前面的思路升级为现在的方案:OpenCV处理完一帧 → 立即编码JPEG → 直接写入FIFO → GStreamer立即解码显示方案D: FIFO+JPEG流(最终方案!)# Python端: 直接往FIFO写JPEG数据fifo = open ( ‘/tmp/gesture_fifo’ , ‘wb’ )_ , jpeg = cv2 . imencode ( ‘.jpg’ , processed , [ cv2 . IMWRITE_JPEG_QUALITY , 85 ] )fifo . write ( jpeg . tobytes ( ) )fifo . flush ( )# GStreamer端: 用jpegparse自动分割JPEG帧gst-launch-1.0 filesrc location=/tmp/gesture_fifo ! jpegparse ! jpegdec ! …补充:为什么JPEG流可行?JPEG格式自带开始(0xFFD8)和结束(0xFFD9)标记 GStreamer的 jpegparse 插件能自动识别边界,分割独立的JPEG帧 避免了原始数据流的粘包问题结果 :总算是可以流畅地观察到画面了(流程不卡顿,甚至比直接点屏幕上的摄像头图标看摄像头画面都流畅)。演示视频和代码我会放在附件里面。
番外篇—摄像头第二次启动色彩偏绿偏暗问题描述我在RK3576 Buildroot系统上使用IMX415摄像头,通过GStreamer+Wayland显示画面。遇到了一个诡异的问题: 第一次启动摄像头色彩正常,但第二次启动后画面就变得偏暗偏绿 。
初步分析:对比启动日志我首先对比了两次启动的kernel日志,发现了关键差异:第一次启动(色彩正常) :[20.528641] rkisp_hw 27c00000.isp: set isp clk = 594000000Hz[20.529097] rkcif-mipi-lvds 3: stream[0] start streaming[20.529317] rockchip-csi2-dphy 3: dphy3, data_rate_mbps 892[20.529356] imx415 3-0037: s_stream: 1.3864x2192, hdr: 0, bpp: 10第二次启动(色彩异常) :[79.209321] rkisp_hw 27c00000.isp: set isp clk = 594000000Hz[79.209967] rkisp rkisp-vir3: first params buf queue[79.210051] rkisp rkisp-vir3: id: 0 no first iq setting cfg_upd: c000dfecc7fe473b en_upd: 0 en s: 5ffcc7fe473b[79.210351] rkcif-mipi-lvds 3: stream[0] start streaming关键发现: 第二次启动多了一条警告 no first iq setting 。这说明ISP的图像质量参数没有正确加载,导致使用了错误的默认参数,造成色彩偏暗偏绿。问题解决过程第一阶段:尝试硬件层面解决一开始我以为是ISP驱动状态没有正确复位,尝试了几种方法:尝试unbind/bind ISP驱动 :echo “27c00000.isp” > /sys/bus/platform/drivers/rkisp_hw/unbindecho “27c00000.isp” > /sys/bus/platform/drivers/rkisp_hw/bind结果: 摄像头直接打不开了 ,操作太激进导致驱动状态完全错乱。尝试使用v4l2-ctl重置、media-ctl reset 等方法,都没有解决问题。第二阶段:深入诊断系统配置我开始系统性地诊断整个摄像头子系统:# 查找IQ参数文件find / -name “imx415.xml” -o -name “imx415.json” 2>/dev/null# 结果: 找到了 /etc/iqfiles/imx415_CMK-OT2022-PX1_IR0147-50IRC-8M-F20.json# 检查3A服务器ps aux | grep rkaiq_3A_server# 结果: 服务器正在运行# 查看设备拓扑v4l2-ctl --list-devices# 确认 /dev/video-camera0 -> video11关键发现 :IQ参数文件存在 3A服务器(rkaiq_3A_server)正在运行 但为什么IQ参数没有加载?第三阶段:抓取3A服务器日志我决定前台运行3A服务器,查看详细输出:killall rkaiq_3A_server/usr/bin/rkaiq_3A_server 2>&1 &启动日志显示:DBG: get rkisp-isp-subdev devname: /dev/v4l-subdev3DBG: get rkisp-input-params devname: /dev/video18DBG: get rkisp-statistics devname: /dev/video17XCORE: K: cid[1] rk_aiq_uapi2_sysctl_init success. iq: /etc/iqfiles//imx415_CMK-OT2022-PX1_IR0147-50IRC-8M-F20.jsonXCORE: K: cid[1] rk_aiq_uapi2_sysctl_prepare success. mode: 0DBG: /dev/media1: wait stream start event…重大发现 :3A服务器实际上工作正常!IQ文件已经成功加载了!这时我进行了第二次摄像头启动测试,观察到:[625.216117] rkisp-vir3: waiting on params stream one event timeout真相大白 :第二次启动时,3A服务器超时无响应!第四阶段:找到根本原因通过多次测试和日志分析,我终于理解了问题的本质:第一次启动流程(正常) :系统启动时,3A服务器自动启动 3A服务器加载IQ参数文件到内存 3A服务器预先准备好IQ参数缓冲区 GStreamer启动摄像头 ISP请求IQ参数 3A服务器立即响应并推送IQ参数 色彩正常第二次启动流程(异常) :停止第一次的GStreamer进程 3A服务器还在运行,但进入了某种等待状态 IQ参数缓冲区已经被消费 立即重启GStreamer ISP请求IQ参数 3A服务器来不及响应或状态异常 ISP使用默认参数处理第一帧 出现 no first iq setting 警告 色彩偏暗偏绿解决方案问题的根源是: 3A服务器在摄像头第一次运行后进入异常状态,无法正确响应第二次启动的IQ参数请求 。最终的解决方法很简单: 每次启动摄像头前,重启3A服务器 。我编写了一个封装脚本:#!/bin/shecho “=== Starting Camera with 3A Server Reset ===”# 1. 停止所有摄像头进程pkill -9 gst-launch 2>/dev/null# 2. 重启3A服务器killall rkaiq_3A_server 2>/dev/nullsleep 2rm -f /tmp/.rkaiq_3A*# 3. 启动3A服务器/etc/init.d/S40rkaiq_3A startecho "Waiting for 3A server to initialize…"sleep 5# 4. 确认3A服务器运行正常if ! pgrep rkaiq_3A_server > /dev/null; thenecho "ERROR: 3A server failed to start!"exit 1fiecho “3A server ready, starting camera…”# 5. 启动摄像头gst-launch-1.0 v4l2src device=/dev/video11 ! \video/x-raw,format=NV12,width=640,height=480,framerate=30/1 ! \waylandsinkecho "Camera stopped"exit 0
验证结果
使用新脚本后,连续多次启动摄像头,色彩始终正常,日志中不再出现 no first iq setting 或超时错误。经验总结对比日志是发现问题的关键 :通过对比正常和异常情况的日志,快速定位到 no first iq setting 这个关键线索系统性诊断 :不要盲目尝试,先检查各个组件(IQ文件、3A服务器、设备节点)的状态前台运行看详细日志 :很多后台服务的问题需要前台运行才能看到详细输出理解组件间的协作关系 :RK平台的摄像头涉及ISP驱动、3A服务器、IQ参数文件三者的协作,任何一个环节出问题都会导致异常状态管理很重要 :嵌入式系统的服务重启问题往往是状态机管理不当导致的,彻底重置是最可靠的方案。
更多推荐
所有评论(0)