#include <opencv2/opencv.hpp> #include <opencv2/fe...

創建於:2025年3月17日

使用 O1 Pro 回答 Chat01

問題

#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>
#include <iostream>

// wiringPi & LCD
#include <wiringPi.h>
#include <lcd.h>
#include <unistd.h> // for sleep(), usleep()

// ---------------------【LCD1602 接口定义】---------------------
// 这里使用 BCM GPIO 编号 + wiringPiSetupGpio() 初始化
#define LCD_RS 6 // RS 引脚
#define LCD_E 13 // E 引脚
#define LCD_D4 19
#define LCD_D5 26
#define LCD_D6 12
#define LCD_D7 16

static cv::VideoCapture cap; // 全局摄像头

// -------------------- 初始化摄像头 ---------------------
bool setupCamera(int width, int height)
{
cap.open(0);
if(!cap.isOpened()) {
std::cerr << "Unable to open camera!" << std::endl;
return false;
}
cap.set(cv::CAP_PROP_FRAME_WIDTH, width);
cap.set(cv::CAP_PROP_FRAME_HEIGHT, height);
return true;
}

// -------------------- 捕获一帧图像 ---------------------
cv::Mat captureFrame()
{
cv::Mat frame;
cap >> frame;
return frame;
}

// -------------------- 读取参考图 -----------------------
cv::Mat readImage(const std::string &path)
{
cv::Mat img = cv::imread(path, cv::IMREAD_COLOR);
if (img.empty()) {
std::cerr << "Cannot load image: " << path << std::endl;
}
return img;
}

// -------------------- ORB + KNN + RANSAC 检测 ----------------
cv::Point detectObject(const cv::Mat &scene, const cv::Mat &object)
{
if (scene.empty() || object.empty()) return cv::Point(-1, -1);

text
static cv::Ptr<cv::Feature2D> orb = cv::ORB::create(); if(!orb) { std::cerr << "ORB not supported in this OpenCV build." << std::endl; return cv::Point(-1, -1); } // 提取特征 std::vector<cv::KeyPoint> kpObj, kpScene; cv::Mat descObj, descScene; orb->detectAndCompute(object, cv::noArray(), kpObj, descObj); orb->detectAndCompute(scene, cv::noArray(), kpScene, descScene); if(descObj.empty() || descScene.empty()) { return cv::Point(-1, -1); } // BFMatcher + KNN 匹配 cv::BFMatcher matcher(cv::NORM_HAMMING); std::vector<std::vector<cv::DMatch>> knnMatches; matcher.knnMatch(descObj, descScene, knnMatches, 2); // 比例测试 const float ratioThresh = 0.75f; std::vector<cv::DMatch> goodMatches; for(size_t i = 0; i < knnMatches.size(); i++) { if(knnMatches[i].size() == 2) { const cv::DMatch &best = knnMatches[i][0]; const cv::DMatch &second = knnMatches[i][1]; if(best.distance < ratioThresh * second.distance) { goodMatches.push_back(best); } } } if(goodMatches.size() < 10) { return cv::Point(-1, -1); } // RANSAC Homography std::vector<cv::Point2f> objPoints, scenePoints; for(const auto &m : goodMatches) { objPoints.push_back(kpObj[m.queryIdx].pt); scenePoints.push_back(kpScene[m.trainIdx].pt); } std::vector<unsigned char> inlierMask; cv::Mat H = cv::findHomography(objPoints, scenePoints, cv::RANSAC, 5.0, inlierMask); if(H.empty()) { return cv::Point(-1, -1); } // 统计内点 int inliersCount = 0; for(size_t i=0; i<inlierMask.size(); i++) { if(inlierMask[i]) inliersCount++; } if(inliersCount < 10) { return cv::Point(-1, -1); } // 计算所有内点的平均坐标 double sumX = 0, sumY = 0; int validCount = 0; for(size_t i=0; i<inlierMask.size(); i++) { if(inlierMask[i]) { sumX += scenePoints[i].x; sumY += scenePoints[i].y; validCount++; } } if(validCount==0) return cv::Point(-1, -1); cv::Point center((int)(sumX/validCount), (int)(sumY/validCount)); return center;

}

// -------------------- 主函数 ---------------------------
int main()
{
// 1. 初始化 wiringPi (BCM模式)
if(wiringPiSetupGpio() == -1) {
std::cerr << "wiringPiSetupGpio failed.\n";
return -1;
}

text
// 2. 初始化 LCD1602 (2 行, 16 列, 4 位接口) int lcdHandle = lcdInit(2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0,0,0,0); // 剩余参数用不到时填0 if(lcdHandle < 0) { std::cerr << "lcdInit failed!\n"; return -1; } lcdClear(lcdHandle); // 3. 初始化摄像头 if(!setupCamera(640, 480)) { return -1; } // 4. 读取参考图片 (Music.png),匹配后播放 Sun.mp3 std::string musicPath = "/home/pi/Desktop/Music.png"; cv::Mat refImage = readImage(musicPath); if(refImage.empty()) { std::cerr << "No reference image found at " << musicPath << std::endl; return -1; } cv::namedWindow("Camera", cv::WINDOW_AUTOSIZE); std::cout << "Press ESC or Q to quit.\n"; // 额外添加:只在“从未检测到->检测到”时才触发 bool hasTriggered = false; // 记录当前是否处于“已检测”状态 while(true) { // a) 采集当前帧 cv::Mat frame = captureFrame(); if(frame.empty()) { std::cerr << "Empty frame.\n"; break; } // b) 检测是否出现 Music.png cv::Point matchCenter = detectObject(frame, refImage); bool isDetected = (matchCenter.x >= 0 && matchCenter.y >= 0); if(isDetected && !hasTriggered) { // “从未检测”到“检测”的瞬间 hasTriggered = true; // 1. 终端输出 + 画面上画圈 std::cout << "Play Music" << std::endl; cv::circle(frame, matchCenter, 20, cv::Scalar(0,255,0), 3); // 2. LCD1602 显示 "Play Music" 5 秒 lcdClear(lcdHandle); lcdPosition(lcdHandle, 0, 0); lcdPuts(lcdHandle, "Play Music"); // 3. 后台播放 mp3 system("omxplayer /home/pi/Desktop/Sun.mp3 &"); // 先等待 5 秒,让 LCD 显示 delay(5000); lcdClear(lcdHandle); // 如果你要播放19秒: // 其实上面已经播放了5秒 + 14秒 =19秒 delay(14000); // 手动结束 omxplayer system("pkill omxplayer"); } else if(!isDetected) { // 当检测不到图像时,重置触发标记 hasTriggered = false; } // c) 显示结果 cv::imshow("Camera", frame); // d) 按键退出 char c = (char) cv::waitKey(30); if(c == 27 || c == 'q' || c == 'Q') { std::cout << "Exit.\n"; break; } } cap.release(); cv::destroyAllWindows(); lcdClear(lcdHandle); return 0;

}
现在这个程序有个问题就是每次我在codeblock(内置Open CV库上运行了一遍之后,这个程序就没要办法第二次执行,必须在树莓派终端上输入sudo fuser /dev/video0
sudo fuser -k /dev/video0
ps aux | grep -E 'v4l|video|cam|opencv'
sudo rmmod bcm2835_v4l2
sudo modprobe bcm2835_v4l2这一串命令行才能再运行一次。 你有没有什么可以一劳永逸的方法让这个程序每次运行之后不用在终端输入这些了

思考

Examining camera access

The user is analyzing issues when accessing the camera with OpenCV on Raspberry Pi, particularly seeing the camera stays locked and inaccessible after the first use.

Identifying the issue

The camera device remains locked due to a process or driver bug after the code execution. The user suggests ensuring full capture release and running cv::destroyAllWindows() as potential solutions.

Examining root access

Running code as root might resolve permission issues. Subtle alternatives include changing group membership or dynamically running kill commands, though these aren't generally advisable.

Evaluating driver alternatives

Considering alternatives to bcm2835_v4l2, such as dtparam=legacy=on, to resolve issues. Also, ensuring uniform environment execution between codeblocks and terminal, leveraging system calls, and addressing known raspicam driver bugs.

Evaluating configuration tweaks

I'm thinking through adding settings like "start_x=1" to /boot/config.txt or ensuring uniform OpenCV initialization to prevent the camera from remaining locked.

Investigating potential fixes

I'm exploring various strategies to ensure the camera device isn't held after exit, such as using LD_PRELOAD, reconfiguring CodeBlocks, running from the command line, or addressing known bugs with older Raspbian versions.

Releasing camera resources

Ensuring the camera handle is released with cap.release() is crucial. Additionally, providing a custom code to forcibly kill camera usage can help address lingering issues.

Enhancing camera access

I’m exploring methods to ensure consistent camera access, suggesting running CodeBlocks with root access or adding users to the video group to avoid needing fuser commands.

Enhancing access

I’m discussing direct access via sudo codeblocks, adding users to the video group, and incorporating commands into boot scripts. I'm also considering potential issues with omxplayer and the Pi camera library.

Enhancing camera driver functionality

I’m advising on adding "bcm2835_v4l2" to /etc/modules for startup loading, stopping debugging sessions, and tweaking /boot/config.txt settings to streamline driver management.

Creating solutions

I’m suggesting running the program with sudo, ensuring proper closure, and possibly crafting a script to automatically handle device cleanup. Additionally, checking for any unreferenced code bugs is advised.

Addressing environment quirks

I’m noting that Code::Blocks might be causing these issues due to improper termination of debugging, leading to a locked camera device. Checking debug logs or using 'Run Without Debugging' might help.

Implementing a solution

I’m mentioning a post-build step in CodeBlocks to terminate camera processes, introducing the new picamera2 library for RPi OS, and proposing a final system() call or script for cleanup.

Reasoned for 2m 31s

回答

Shannon, this is kind of a classic gotcha when running camera code repeatedly on a Pi. The short story: something (usually the V4L2 driver) is still hanging on to the camera even after your program ends. Then Code::Blocks tries to run it again, and boom, “Device or resource busy.”

Below are a few ways to handle this so you’re not stuck typing those fuser and modprobe commands every time:

1. Make sure Code::Blocks really stops the program.

  • Sometimes, if you run code in the debugger and don’t fully stop or terminate the debug session, the camera process can still be hogging /dev/video0.
  • Double-check that Code::Blocks is actually killing off the process when you stop debugging or close the program window.

2. Add a post-exec (or post-build) script in Code::Blocks.

  • Many IDEs let you run custom commands after your program finishes. You could automate those shell commands:
    bash
    sudo fuser -k /dev/video0 sudo rmmod bcm2835_v4l2 sudo modprobe bcm2835_v4l2
  • This way, each time your program finishes, Code::Blocks will clean up the camera driver for you.

3. Wrap those commands in a single shell script.

  • If the IDE doesn’t have an easy “post-exec” hook, just put this into a script:
    bash
    #!/bin/bash sudo fuser -k /dev/video0 sudo rmmod bcm2835_v4l2 sudo modprobe bcm2835_v4l2
  • Call it something like reset_cam.sh, make it executable (chmod +x reset_cam.sh), and run it whenever you want a clean slate. You could also add it as a custom menu option in Code::Blocks or run it just before launching the program again.

4. Double-check you’re releasing everything properly in code.

  • Technically your code does cap.release() and cv::destroyAllWindows() at the end, so that’s good. Just make sure there aren’t any other references to the camera that might be alive (for instance, a separate thread or something else that never got shut down).

5. Consider the new Raspberry Pi “libcamera” approach (if you’re using the Pi cam module).

  • The legacy bcm2835_v4l2 method can be a bit finicky, especially if you’re not specifically using the old Pi Camera stack.
  • If you’re on a newer Pi OS (Bullseye or later), you might want to look into libcamera and OpenCV’s GStreamer-based pipeline, though that can be a bigger change in your code.

Bottom line: The simplest route is probably a script or a Code::Blocks “post-exec” command that forcibly resets the camera driver. Then you can just run your program over and over without manually typing out the same kill/rmmod/modprobe sequence. Good luck!

分享這個問答