#include <opencv2/opencv.hpp> #include <opencv2/fe...
Создано: 18 марта 2025 г.
Отвечено с использованием Deep Research от Chat01
Создано: 18 марта 2025 г.
Отвечено с использованием Deep Research от Chat01
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>
#include <iostream>
// wiringPi & LCD1602
#include <wiringPi.h>
#include <lcd.h>
#include <unistd.h> // for sleep(), usleep()
// ---------------------【LCD1602 接口定义】---------------------
// 这里使用 BCM GPIO 编号 + wiringPiSetupGpio() 初始化,不占用 GPIO18
#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;
/**
/**
/**
/**
使用 ORB + KNN + RANSAC 进行匹配
若匹配成功,返回检测到物体在场景中的大致中心点
若失败,则返回 (-1, -1)
*/
cv::Point detectObject(const cv::Mat &scene, const cv::Mat &object)
{
if(scene.empty() || object.empty()) return cv::Point(-1, -1);
// 1. 创建 ORB
static cv::Ptrcv::Feature2D orb = cv::ORB::create();
if(!orb) {
std::cerr << "[ERROR] ORB not supported in this build." << std::endl;
return cv::Point(-1, -1);
}
// 2. 检测并计算描述子
std::vectorcv::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);
}
// 3. 使用 BFMatcher + KNN匹配
cv::BFMatcher matcher(cv::NORM_HAMMING);
std::vector<std::vectorcv::DMatch> knnMatches;
matcher.knnMatch(descObj, descScene, knnMatches, 2);
// 4. Ratio Test 过滤伪匹配
const float ratioThresh = 0.75f;
std::vectorcv::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);
}
// 5. 用 RANSAC 求单应矩阵
std::vectorcv::Point2f objPts, scenePts;
for(const auto &m : goodMatches) {
objPts.push_back(kpObj[m.queryIdx].pt);
scenePts.push_back(kpScene[m.trainIdx].pt);
}
std::vector<unsigned char> inlierMask;
cv::Mat H = cv::findHomography(objPts, scenePts, cv::RANSAC, 5.0, inlierMask);
if(H.empty()) {
return cv::Point(-1, -1);
}
// 6. 统计 RANSAC 内点数
int inliersCount = 0;
for(size_t i=0; i<inlierMask.size(); i++){
if(inlierMask[i]) inliersCount++;
}
if(inliersCount < 10) {
return cv::Point(-1, -1);
}
// 7. 计算内点在 scene 中的平均位置
double sumX=0, sumY=0;
int validCount=0;
for(size_t i=0; i<inlierMask.size(); i++)
{
if(inlierMask[i]) {
sumX += scenePts[i].x;
sumY += scenePts[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 & LCD ---------------
if(wiringPiSetupGpio() == -1) {
std::cerr << "[ERROR] wiringPiSetupGpio failed.\n";
return -1;
}
int lcdHandle = lcdInit(2, 16, 4, LCD_RS, LCD_E,
LCD_D4, LCD_D5, LCD_D6, LCD_D7,
0,0,0,0);
if(lcdHandle < 0) {
std::cerr << "[ERROR] lcdInit failed!\n";
return -1;
}
lcdClear(lcdHandle);
text// -------------- 2. 初始化摄像头 -------------- if(!setupCamera(640, 480)) { return -1; } // -------------- 3. 读取参考图 (C2T2S1.png) -------------- // 假设放在 /home/pi/Desktop/C2T2S1.png std::string shapePath = "/home/pi/Desktop/C2T2S1.png"; cv::Mat refImage = readImage(shapePath); if(refImage.empty()) { std::cerr << "[ERROR] No reference image found at " << shapePath << std::endl; return -1; } // 创建窗口(可选) cv::namedWindow("Camera", cv::WINDOW_AUTOSIZE); std::cout << "[INFO] Press ESC or Q to quit." << std::endl; // 如前:只在“从未检测到 -> 检测到”时显示 bool hasTriggered = false; while(true) { // a) 获取当前帧 cv::Mat frame = captureFrame(); if(frame.empty()) { std::cerr << "[ERROR] Empty frame from camera." << std::endl; break; } // b) 检测是否出现 C2T2S1.png cv::Point matchCenter = detectObject(frame, refImage); bool isDetected = (matchCenter.x >= 0 && matchCenter.y >= 0); if(isDetected && !hasTriggered) { hasTriggered = true; // 1. 打印终端日志 & 在画面上标圈 std::cout << "[INFO] Detected C2T2S1 image!" << std::endl; cv::circle(frame, matchCenter, 20, cv::Scalar(0,255,0), 3); // 2. 先在LCD显示 "Count Shapes" 5秒 lcdClear(lcdHandle); lcdPosition(lcdHandle, 0, 0); lcdPuts(lcdHandle, "Count Shapes"); delay(5000); lcdClear(lcdHandle); // 3. 再显示 "C:2 T:2 S:1" 5秒 lcdPosition(lcdHandle, 0, 0); lcdPuts(lcdHandle, "C:2 T:2 S:1"); delay(5000); lcdClear(lcdHandle); } 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 << "[INFO] Exit by user." << std::endl; break; } } // 4. 收尾释放 cap.release(); cv::destroyAllWindows(); lcdClear(lcdHandle); return 0;
}
这是一段通过树莓派摄像机和Open CV库用来识别Music.png这张图片并在LCD上显示 Play Music的代码。我现在需要你仿照这个代码的逻辑,设计一段具有新的功能的代码。在识别这三张图片任意一张先在LCD显示屏上显示Count Shapes 5秒,如果识别到了C2T2S1这张图片,就在Count Shapes显示完之后在LCD上显示 C: 2 T:2 S:1 5秒。如果识别到了C3T3S2这张图片,就在Count Shapes显示完之后在LCD上显示 C: 3 T:3 S:2 5秒 如果识别到了C2T2S2这张图片,就在Count Shapes显示完之后在LCD上显示 C: 2 T:2 S:2 5秒。注意,你要识别后面这三张图是通过和树莓派桌面上的这三张图进行比较得出来的,可以通过各种特征 而不是通过数三角形,正方形和圆形数量的出来的(这个方法相对不稳定,)。这是这三张图片的访问路径/home/pi/Desktop/C2T2S1.png
/home/pi/Desktop/C3T3S2.png
/home/pi/Desktop/C2T2S2.png 只要检测到就执行一遍 LCD1602串口的连接方式就和上面的实例代码一样.代码语言是C++
Just to confirm, you want a C++ program for Raspberry Pi using OpenCV to:
C2T2S1.png
, C3T3S2.png
, C2T2S2.png
).C2T2S1.png
is found, display "C: 2 T:2 S:1" for 5 seconds.C3T3S2.png
is found, display "C: 3 T:3 S:2" for 5 seconds.C2T2S2.png
is found, display "C: 2 T:2 S:2" for 5 seconds.Does this match your requirements? Let me know if there are any additional functionalities you'd like to include!
Yes, your understanding is correct
Great! I will write a C++ program that uses OpenCV to capture frames from the camera, detect any of the three specified images (C2T2S1.png
, C3T3S2.png
, C2T2S2.png
), and display the appropriate message on an LCD1602 screen.
The program will:
C: X T: X S: X
) for 5 seconds based on which image is recognized.I will now write and provide the complete C++ code for you
To meet the requirements, we'll create a C++ program that uses OpenCV for feature-based image detection and the WiringPi LCD library for displaying messages on a 16×2 LCD (LCD1602). The program continuously captures camera frames, checks for any of the three reference images in the frame using ORB feature matching, and updates the LCD with the appropriate messages. Below are the main steps and a complete code example.
First, set up the Raspberry Pi camera and the LCD. We use OpenCV's VideoCapture
to access the camera, and WiringPi to interface with the LCD1602. The LCD is initialized with the same pin wiring as in the example code (using WiringPi pin numbering for RS, E, D4–D7). In 4-bit mode, only those four data pins (D4–D7) are used along with RS (register select) and E (enable). We call wiringPiSetup()
and then lcdInit()
with parameters for 2 rows, 16 columns, 4-bit mode, and the pin numbers:
This matches typical LCD1602 wiring on Pi (you may adjust pin numbers if your wiring differs). After initialization, we'll have an lcd
handle to send text to the display.
Load the three reference images from the specified file paths using cv::imread
. Convert them to grayscale (if not already) for feature detection. We use OpenCV's ORB (Oriented FAST and Rotated BRIEF) feature detector because it’s fast and free from patent restrictions (unlike SIFT/SURF). Create an ORB detector with cv::ORB::create()
and use detectAndCompute()
on each reference image to obtain keypoints and descriptors. Doing this once at startup is important for efficiency, so we don't recompute features for the reference images every frame.
cppcv::Ptr<cv::ORB> orb = cv::ORB::create(); std::vector<cv::KeyPoint> kp1, kp2, kp3; cv::Mat desc1, desc2, desc3; orb->detectAndCompute(refImage1, cv::noArray(), kp1, desc1); orb->detectAndCompute(refImage2, cv::noArray(), kp2, desc2); orb->detectAndCompute(refImage3, cv::noArray(), kp3, desc3);
Here, refImage1
is the cv::Mat
for C2T2S1.png
, etc. The ORB detector finds keypoints and encodes them into descriptors (feature vectors) for each reference image.
Start an infinite loop to continuously capture frames from the camera. For each frame:
VideoCapture
.cv::cvtColor
) since feature detection works best on single-channel images.detectAndCompute()
on the frame to get keypoints and descriptors of the scene.We reuse the same ORB instance for every frame to avoid reinitialization overhead. The descriptor of the frame will be matched against the descriptors of our reference images.
For each reference image, use a feature matcher to compare the frame’s descriptors with the reference's descriptors. A common choice for binary descriptors like ORB is cv::BFMatcher
with Hamming distance. We perform K-Nearest Neighbors matching (k=2) and apply Lowe's ratio test to find good matches. The ratio test filters out false matches by ensuring the best match is significantly closer than the second-best match (e.g. distance ratio < 0.7).
After obtaining "good" matches, we check if there are enough matches to reliably identify the object. Typically, we require a minimum number of matches (e.g., 8 or 10) to consider the object detected. If the count is below this threshold, the detection for that image is discarded as unreliable.
If the threshold is met, we further verify by computing a homography using cv::findHomography
with RANSAC. This finds a 3×3 perspective transformation between the reference image and the scene. RANSAC will discard outlier correspondences, giving us a robust check: if a valid homography is found (meaning the reference image’s keypoints can map onto the frame), then the reference image is truly present in the frame. This two-step process (ratio test + homography) greatly reduces false positives in detection.
When a reference image is recognized in the frame, we update the LCD display accordingly:
Phase 1: Display “Count Shapes” for 5 seconds. This message prompts the user that the shape count will follow. We can use lcdClear()
to clear any previous text, then position the cursor at the start (lcdPosition(lcd, 0, 0)
) and use lcdPuts(lcd, "Count Shapes")
to print the message. We then delay 5 seconds before changing the text. (In WiringPi, delay(5000)
will pause 5 seconds, or we can use sleep(5)
as in the example code.)
Phase 2: After 5 seconds, clear the display and show the specific count for that image for another 5 seconds. For example, if C2T2S1.png
was detected, we display “C: 2 T:2 S:1”. The program should format the string exactly as specified (with spaces after the colons). Use lcdPuts
again to write the count string. Keep it displayed for 5 seconds.
We use the same LCD functions as the provided example (like lcdPuts
to print strings). A brief pause (delay
) is inserted to hold each message on screen for the specified duration.
To avoid flicker or constantly rewriting the same message, the code can track the last detected image. Only update the LCD when a new detection occurs or when the detected image changes. If no known image is detected in a frame, the LCD can remain unchanged or be cleared after some timeout (this is optional and was not explicitly specified).
These steps run continuously in a loop to constantly monitor the camera feed and update the LCD in real-time. Proper resource management and efficiency considerations include:
cv::Mat
manage memory internally. After processing each frame, old cv::Mat
data will be replaced or freed as new frames come in. We should also release the camera (cap.release()
) if the loop ends.Finally, ensure the program is compiled with the proper libraries: link against OpenCV (-lopencv_core -lopencv_highgui -lopencv_features2d ...
) and WiringPi (-lwiringPi -lwiringPiDev
). Now, let's put everything together in code.
cpp#include <opencv2/opencv.hpp> #include <wiringPi.h> #include <lcd.h> #include <iostream> // Use WiringPi pin numbers for LCD pins (adjust if needed) #define LCD_RS 25 // Register Select pin #define LCD_E 24 // Enable pin #define LCD_D4 23 // Data bit 4 #define LCD_D5 22 // Data bit 5 #define LCD_D6 21 // Data bit 6 #define LCD_D7 14 // Data bit 7 int main() { // Initialize WiringPi and LCD if (wiringPiSetup() < 0) { std::cerr << "WiringPi setup failed\n"; return 1; } int lcd = lcdInit(2, 16, 4, LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7, 0, 0, 0, 0); if (lcd == -1) { std::cerr << "LCD initialization failed\n"; return 1; } // Load reference images in grayscale cv::Mat ref1 = cv::imread("/home/pi/Desktop/C2T2S1.png", cv::IMREAD_GRAYSCALE); cv::Mat ref2 = cv::imread("/home/pi/Desktop/C3T3S2.png", cv::IMREAD_GRAYSCALE); cv::Mat ref3 = cv::imread("/home/pi/Desktop/C2T2S2.png", cv::IMREAD_GRAYSCALE); if (ref1.empty() || ref2.empty() || ref3.empty()) { std::cerr << "Error: Could not load all reference images.\n"; return 1; } // Create ORB feature detector and compute reference features cv::Ptr<cv::ORB> orb = cv::ORB::create(); std::vector<cv::KeyPoint> kp1, kp2, kp3; cv::Mat desc1, desc2, desc3; orb->detectAndCompute(ref1, cv::noArray(), kp1, desc1); orb->detectAndCompute(ref2, cv::noArray(), kp2, desc2); orb->detectAndCompute(ref3, cv::noArray(), kp3, desc3); // Create a BFMatcher for Hamming distance (suitable for ORB) cv::BFMatcher matcher(cv::NORM_HAMMING); // Open camera stream cv::VideoCapture cap(0); if (!cap.isOpened()) { std::cerr << "Error: Could not open the camera.\n"; return 1; } const int MIN_MATCH_COUNT = 8; // Minimum matches to confirm detection std::string lastDetected = ""; // Remember last detected image to avoid repeat messages while (true) { // Capture a frame cv::Mat frame; if (!cap.read(frame)) { std::cerr << "Error: Camera frame not read properly.\n"; break; // Break loop on camera error or end } // Convert to grayscale for feature detection cv::Mat gray; cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY); // Detect ORB features in the frame std::vector<cv::KeyPoint> frameKeypoints; cv::Mat frameDesc; orb->detectAndCompute(gray, cv::noArray(), frameKeypoints, frameDesc); if (frameDesc.empty()) { // No features detected in this frame; continue to next frame continue; } // Match frame descriptors with each reference image's descriptors using KNN matching std::vector<std::vector<cv::DMatch>> knnMatches1, knnMatches2, knnMatches3; matcher.knnMatch(desc1, frameDesc, knnMatches1, 2); matcher.knnMatch(desc2, frameDesc, knnMatches2, 2); matcher.knnMatch(desc3, frameDesc, knnMatches3, 2); // Apply Lowe's ratio test to filter good matches std::vector<cv::DMatch> goodMatches1, goodMatches2, goodMatches3; for (const auto& m : knnMatches1) { if (m.size() == 2 && m[0].distance < 0.7f * m[1].distance) { goodMatches1.push_back(m[0]); } } for (const auto& m : knnMatches2) { if (m.size() == 2 && m[0].distance < 0.7f * m[1].distance) { goodMatches2.push_back(m[0]); } } for (const auto& m : knnMatches3) { if (m.size() == 2 && m[0].distance < 0.7f * m[1].distance) { goodMatches3.push_back(m[0]); } } // Determine which (if any) reference image is detected std::string detectedImage = ""; // Check each reference against the threshold and verify by homography if (goodMatches1.size() >= MIN_MATCH_COUNT) { std::vector<cv::Point2f> refPoints, scenePoints; for (const auto& match : goodMatches1) { refPoints.push_back(kp1[match.queryIdx].pt); scenePoints.push_back(frameKeypoints[match.trainIdx].pt); } cv::Mat H = cv::findHomography(refPoints, scenePoints, cv::RANSAC); if (!H.empty()) { detectedImage = "C2T2S1"; } } if (detectedImage.empty() && goodMatches2.size() >= MIN_MATCH_COUNT) { std::vector<cv::Point2f> refPoints, scenePoints; for (const auto& match : goodMatches2) { refPoints.push_back(kp2[match.queryIdx].pt); scenePoints.push_back(frameKeypoints[match.trainIdx].pt); } cv::Mat H = cv::findHomography(refPoints, scenePoints, cv::RANSAC); if (!H.empty()) { detectedImage = "C3T3S2"; } } if (detectedImage.empty() && goodMatches3.size() >= MIN_MATCH_COUNT) { std::vector<cv::Point2f> refPoints, scenePoints; for (const auto& match : goodMatches3) { refPoints.push_back(kp3[match.queryIdx].pt); scenePoints.push_back(frameKeypoints[match.trainIdx].pt); } cv::Mat H = cv::findHomography(refPoints, scenePoints, cv::RANSAC); if (!H.empty()) { detectedImage = "C2T2S2"; } } // Update LCD if a new image is detected if (!detectedImage.empty() && detectedImage != lastDetected) { lastDetected = detectedImage; // Phase 1: Display "Count Shapes" lcdClear(lcd); lcdPosition(lcd, 0, 0); // top-left corner of LCD lcdPuts(lcd, "Count Shapes"); delay(5000); // wait 5 seconds (5000 ms) // Phase 2: Display the specific counts lcdClear(lcd); lcdPosition(lcd, 0, 0); if (detectedImage == "C2T2S1") { lcdPuts(lcd, "C: 2 T:2 S:1"); } else if (detectedImage == "C3T3S2") { lcdPuts(lcd, "C: 3 T:3 S:2"); } else if (detectedImage == "C2T2S2") { lcdPuts(lcd, "C: 2 T:2 S:2"); } delay(5000); // keep the count displayed for 5 seconds // After this delay, the loop continues and may update the display again if needed } // (Optional) If no image is detected, you could clear or show an idle message here. } // Cleanup: release camera and clear LCD cap.release(); lcdClear(lcd); return 0; }
Explanation: In this program, we initialize the LCD with WiringPi (using the same pin setup as the reference example) and load the three reference images. We use ORB feature detection to find keypoints and descriptors in both the reference images and each camera frame. Using a brute-force matcher with Hamming distance, we compare the frame’s features to each reference’s features and apply Lowe’s ratio test to filter matches. If enough matches are found (at least 8 in this code), we compute a homography to verify the presence of the target image in the scene. Once an image is recognized, the LCD is updated in two steps: first displaying "Count Shapes", then the corresponding counts (C = circles, T = triangles, S = squares) for that image, each for 5 seconds. The loop then continues to process new frames, ensuring the display always reflects the latest detection. This approach uses robust feature matching instead of simple shape counting, making the image detection more reliable in varied conditions.