Как найти лазерный крестовый центр с открытым резюме

Из камеры с открытым cv я могу получить красный крест (см. Рисунок ниже), я не знаю лучший метод для вычисления координат центра креста (x,y)? Можно предположить, что лазер красного цвета.

Вероятно, мне придется использовать какое-то распознавание объектов. Но мне нужно рассчитать его центр и производительность важна.

Кто-нибудь может помочь?

Я выяснил, как найти лазерный указатель (координаты красной точки) путем поиска большинства красных пикселей на изображении, но в этом случае центр не всегда является самым красным (вся линия красная, а иногда cv вычисляет, что он больше красного, чем центр).

3 ответа

Вот как я это сделал используя 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 для повышения точности ответа.

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

Теперь повторите для двух столбцов у края изображения - например, 1/4 и 3/4 по горизонтали - это даст вам две точки на горизонтальной части.

Простое уравнение одновременности дает вам точку пересечения.

Если это видеопоследовательность и у вас очень мало времени, вы можете использовать точки, которые вы нашли в предыдущем кадре, и искать в небольшом окне вокруг этой точки - при условии, что крест не сильно сместился.

пс. Если линии не являются прямыми или перемещаются под случайными углами между кадрами, или вам нужна доля пиксельной точности, есть лучшие методы.

Hough Lines должен помочь вам в этом, и он также достаточно хорош в более сложных ситуациях.

Так что вы можете

  • Фильтр Гаусса / Медиана (опционально)
  • Canny или сегментация. Я рекомендую вам сегментацию. Это даст вам гораздо больше строк, и следующие шаги потребуют больше, но точность будет субпиксельной
  • Линии Hough (классические). резюме::HoughLines(); Он вернет количество строк, описанных rho и theta. (может быть сотни из них, если вы используете сегментацию)

  • для каждой пары из них, которые не принадлежат одной и той же красной линии (abs(theta1-theta2)>minTheta), вычислите пересечение. Здесь нужна некоторая геометрия

  • усреднить эти центры по х и у. Или используйте другую статистику, чтобы получить среднюю центральную точку.

Вот пример использования, с которого вы можете начать. Убедитесь, что изменили препроцессор #if 0 в #if 1 так что вы будете использовать классическое преобразование.

Другие вопросы по тегам