从打开cv的相机我可以得到一个红叉(见下图),我不知道计算交叉中心坐标(x,y)的最佳方法?我们可以假设激光是红色的。
可能我将不得不使用某种物体识别。但我需要计算它的中心,性能很重要。
任何人都可以提供帮助?
我已经创建了如何通过搜索图片中的大多数红色像素来找到激光指针(红点坐标),但在这种情况下,中心并不总是最红的(整条线是红色的,有时cv计算它比中心更红) )。
答案 0 :(得分:5)
以下是我使用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;
}
这导致以下输出:
您还可以使用cornerSubPix来提高答案的准确性。
编辑:我对实施vasile的答案很好奇,所以我坐下来试了一下。这看起来效果很好!这是我对他描述的内容的实现。对于分段,我决定使用Otsu方法进行自动阈值选择。只要您在激光十字和背景之间有很高的间隔,这就可以正常工作,否则您可能需要切换到像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;
}
希望有所帮助!
答案 1 :(得分:3)
扫描图像的某一行,例如向下1/4,寻找红色像素的中心。然后在底部附近重复一行 - 例如向下3/4。这会在垂直条上给出两个点
现在重复图像边缘附近的两列 - 例如1/4和3/4跨 - 这会在水平部分给出两个点。
一个简单的联立方程可以得到crossing point。
如果这是一个视频序列并且您的时间非常紧张,您可以使用在前一帧中找到的点并在该点周围搜索一个小窗口 - 假设十字架没有移动太多。
PS。如果线条不直,或移动到帧之间的随机角度,或者您需要一小部分像素精度,则有更好的技术。
答案 2 :(得分:2)
Hough Lines应该可以帮到你,在更具挑战性的情况下它也足够好。
所以,你可以
霍夫线(古典)。 CV :: HoughLines();它将返回由rho和theta描述的许多行。 (如果你使用细分,可能会有它们的hundreads)
对于不属于同一红线的每一对(abs(theta1-theta2)&gt; minTheta),calculate the intersection。这里需要一些几何
以下是您可以开始使用的示例。请务必将预处理器#if 0
更改为#if 1
,以便使用经典转换。