通过open cv相机可以获取到一个红色的十字标记(如下图所示),我不知道计算十字中心坐标(x,y)的最佳方法是什么?我们可以假设激光是红色的。
可能我需要使用某种物体识别技术。但我需要计算它的中心,并且性能很重要。
有人可以帮忙吗?
我已经找到了如何通过在图片中搜索最红的像素来找到激光指针(红点坐标),但在这种情况下,中心并不总是最红的(整条线都是红色的,有时cv会认为整条线比中心更红)。
使用goodFeaturesToTrack
函数,以下是我是如何实现的:
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
int main(int argc, char* argv[])
{
Mat laserCross = imread("laser_cross.png");
vector<Mat> laserChannels;
split(laserCross, laserChannels);
vector<Point2f> corners;
// only using the red channel since it contains the interesting bits...
goodFeaturesToTrack(laserChannels[2], corners, 1, 0.01, 10, Mat(), 3, false, 0.04);
circle(laserCross, corners[0], 3, Scalar(0, 255, 0), -1, 8, 0);
imshow("laser red", laserChannels[2]);
imshow("corner", laserCross);
waitKey();
return 0;
}
Canny
这样的边缘检测器。我确实必须处理一些垂直线(即0和180度)的角度歧义,但代码似乎运行正常(可能有更好的处理角度歧义的方法)。#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;
Point2f computeIntersect(Vec2f line1, Vec2f line2);
vector<Point2f> lineToPointPair(Vec2f line);
bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta);
int main(int argc, char* argv[])
{
Mat laserCross = imread("laser_cross.png");
vector<Mat> laserChannels;
split(laserCross, laserChannels);
namedWindow("otsu", CV_WINDOW_NORMAL);
namedWindow("intersect", CV_WINDOW_NORMAL);
Mat otsu;
threshold(laserChannels[2], otsu, 0.0, 255.0, THRESH_OTSU);
imshow("otsu", otsu);
vector<Vec2f> lines;
HoughLines( otsu, lines, 1, CV_PI/180, 70, 0, 0 );
// compute the intersection from the lines detected...
int lineCount = 0;
Point2f intersect(0, 0);
for( size_t i = 0; i < lines.size(); i++ )
{
for(size_t j = 0; j < lines.size(); j++)
{
Vec2f line1 = lines[i];
Vec2f line2 = lines[j];
if(acceptLinePair(line1, line2, CV_PI / 4))
{
intersect += computeIntersect(line1, line2);
lineCount++;
}
}
}
if(lineCount > 0)
{
intersect.x /= (float)lineCount; intersect.y /= (float)lineCount;
Mat laserIntersect = laserCross.clone();
circle(laserIntersect, intersect, 1, Scalar(0, 255, 0), 3);
imshow("intersect", laserIntersect);
}
waitKey();
return 0;
}
bool acceptLinePair(Vec2f line1, Vec2f line2, float minTheta)
{
float theta1 = line1[1], theta2 = line2[1];
if(theta1 < minTheta)
{
theta1 += CV_PI; // dealing with 0 and 180 ambiguities...
}
if(theta2 < minTheta)
{
theta2 += CV_PI; // dealing with 0 and 180 ambiguities...
}
return abs(theta1 - theta2) > minTheta;
}
// the long nasty wikipedia line-intersection equation...bleh...
Point2f computeIntersect(Vec2f line1, Vec2f line2)
{
vector<Point2f> p1 = lineToPointPair(line1);
vector<Point2f> p2 = lineToPointPair(line2);
float denom = (p1[0].x - p1[1].x)*(p2[0].y - p2[1].y) - (p1[0].y - p1[1].y)*(p2[0].x - p2[1].x);
Point2f intersect(((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].x - p2[1].x) -
(p1[0].x - p1[1].x)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom,
((p1[0].x*p1[1].y - p1[0].y*p1[1].x)*(p2[0].y - p2[1].y) -
(p1[0].y - p1[1].y)*(p2[0].x*p2[1].y - p2[0].y*p2[1].x)) / denom);
return intersect;
}
vector<Point2f> lineToPointPair(Vec2f line)
{
vector<Point2f> points;
float r = line[0], t = line[1];
double cos_t = cos(t), sin_t = sin(t);
double x0 = r*cos_t, y0 = r*sin_t;
double alpha = 1000;
points.push_back(Point2f(x0 + alpha*(-sin_t), y0 + alpha*cos_t));
points.push_back(Point2f(x0 - alpha*(-sin_t), y0 - alpha*cos_t));
return points;
}
霍夫变换可以帮助您解决这个问题,即使在更具挑战性的情况下也足够好。
因此,您可以:
使用经典的霍夫变换算法进行直线检测。cv::HoughLines(); 它将返回由rho和theta描述的许多线条。(如果使用分割算法,则可能有数百条线条)
对于那些不属于同一红线的每对线条(abs(theta1-theta2)>minTheta),计算交点。这里需要一些几何知识。
以下是您可以开始使用的示例。确保将预处理器#if 0
更改为#if 1
,以便使用经典变换。