Ответ 1
Вот как я это сделал, используя функцию 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, чтобы повысить точность ответа.
РЕДАКТИРОВАТЬ: Мне было любопытно, как выполнить василирующий ответ, поэтому я сел и попробовал. Это выглядит неплохо! Вот моя реализация того, что он описал. Для сегментации я решил использовать метод 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;
}
Надеюсь, что это поможет!