Хитрый сегодня, но определенно выполнимый, и я думаю, что я на правильном пути. Я пытаюсь обнаружить пунктирную линию на изображении. Я делаю это, находя пары контуров наилучшего соответствия, уклоны которых также совпадают с углом, созданным друг другом. Смотрите пример фрагмента кода ниже:
//Get canny mat
Mat frame, framegray, detectededges, dummyimg, cannyedges;
cvtColor(frame, framegray, CV_BGR2GRAY);
blur(framegray, detectededges, Size(3,3));
double otsu_thresh_val = cv::threshold(detectededges, dummyimg,0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
cv::Canny( detectededges, cannyedges, otsu_thresh_val*0.5, otsu_thresh_val );
//
Scalar color(255,255,0,255);
//Find contours
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(cannyedges,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
double contourlength = arcLength(contours[idx], false);
vector<Vec4f> vectorofbestfits;
//Find dotted lines
for (vector<Point> contour : contours){
//Some filtering
if ((contourlength > 70.0) && (contours[idx].size() > 5)){
Vec4f bestfitline;
fitLine(contour, bestfitline,CV_DIST_L2,0,0.01,0.01);
vectorofbestfits.push_back(bestfitline);
}
}
float mtol = 0.05;
for (Vec4f veci : vectorofbestfits){
float mi = veci[1]/veci[0];
for (Vec4f vecj : vectorofbestfits){
float mj = vecj[1]/vecj[0];
double length = cv::norm(Mat(Point(veci[2],veci[3])),Mat(Point(vecj[2],vecj[3])));
if (length < 30){
continue;
}
float mk = (veci[3]-vecj[3])/(veci[2]-vecj[2]);
mi = abs(mi);
mj = abs(mj);
mk = abs(mk);
float mij = abs(mi - mj);
float mjk = abs(mj - mk);
float mki = abs(mk - mi);
if ((mij < mtol) && (mjk < mtol) && (mki < mtol)){
line(frame,Point(veci[2],veci[3]),Point(vecj[2],vecj[3]),color,2);
}
}
}
Методология это:
1) Создайте массив линий наилучшего соответствия контурам
2) Шаг через массив и получить наклон линии
3) Шаг через массив снова и наклон каждой другой линии
4) Рассчитайте наклон линии, созданной двумя центрами линии
5) Сравните все 3 склона друг с другом и отфильтруйте с помощью значения допуска
Код ниже генерирует тонну строк из моего изображения, но ни одна из них не попала в очевидную пунктирную линию. Я думаю, что-то не так с расчетами наклона. Сейчас это настолько неэффективно, что мне нужно создавать некоторые изображения и тестировать вместо того, чтобы работать с реальной графикой. Что-нибудь выпрыгнуть?
Кроме того, приведенный ниже код является довольно сложным в вычислительном отношении, приложение предназначено для интерпретации видео, и частота кадров имеет решающее значение, поэтому любые предложения по улучшению производительности приветствуются.
Глядя на спички вот так:
образ
Редактировать:
Вот немного чище кода для чтения. Я также понял, что брать абсолютное значение уклонов ДО того, как найти разницу, не имело никакого смысла, а просто создать более случайные контуры, которые совпадают. Мне нужно поиграть с этим еще, чтобы увидеть, помогло ли это:
//Get canny mat
Mat frame, framegray, detectededges, dummyimg, cannyedges;
cvtColor(frame, framegray, CV_BGR2GRAY);
blur(framegray, detectededges, Size(3,3));
double otsu_thresh_val = cv::threshold(detectededges, dummyimg,0, 255,
CV_THRESH_BINARY | CV_THRESH_OTSU);
cv::Canny( detectededges, cannyedges, otsu_thresh_val*0.5, otsu_thresh_val );
//Set color for Lines
Scalar color(255,255,0,255);
//Find contours
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(cannyedges,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
double contourlength = arcLength(contours[idx], false);
vector<Vec4f> arrayofbestfits;
//Create an array of best fit Lines
for (vector<Point> contour : contours){
//Filter out contours that are too small
if ((contourlength > 70.0) && (contours[idx].size() > 5)){
Vec4f bestfitLine;
fitLine(contour, bestfitLine,CV_DIST_L2,0,0.01,0.01);
arrayofbestfits.push_back(bestfitLine);
}
}
float SlopeTolerance = 0.05;
for (Vec4f firstIterationLine : arrayofbestfits){
float firstIterationSlope = firstIterationLine[1]/firstIterationLine[0];
for (Vec4f secondIterationLine : arrayofbestfits){
//Filter out Lines too close in proxmity
double length = cv::norm(Mat(Point(firstIterationLine[2],firstIterationLine[3])),
Mat(Point(secondIterationLine[2],secondIterationLine[3])));
if (length < 30){
continue;
}
//Find slope between two points
float commonSlope = (firstIterationLine[3]-secondIterationLine[3])
/(firstIterationLine[2]-secondIterationLine[2]);
//Find absolute value of differences (makes comparison simpler)
float commonSlopediff = abs(firstIterationSlope - secondIterationSlope);
float secondtocommonSlopediff = abs(secondIterationSlope - commonSlope);
float commontofirstSlopediff = abs(commonSlope - firstIterationSlope);
//If within tolerances draw the line bridging the two best fit lines together
if ((commonSlopediff < SlopeTolerance) && (secondtocommonSlopediff < SlopeTolerance) && (commontofirstSlopediff < SlopeTolerance)){
Line(frame,Point(firstIterationLine[2],firstIterationLine[3]),
Point(secondIterationLine[2],secondIterationLine[3]),color,2);
}
}
}
Исходя из вашего примера изображения, похоже, что вы пытаетесь обнаружить отрезки линии со следующими ограничениями:
Эта проблема, кажется, очень хорошо подходит для RANSAC решение.
С помощью простого 2D RANSAC вы находите линию, с которой выравнивается большинство ваших точек данных.
Для получения дополнительной информации см. эти слайды на RANSAC
В вашем случае вы хотите немного изменить проблему, чтобы отразить тот факт, что вы ищете отрезки, а не точки.
Есть много способов сделать это.
Одним из решений является кластеризация ваших сегментов на основе наклона, затем применение простого 2D RANSAC к каждому кластеру и обработка сегментов, как если бы они были точками.
Надеюсь, это поможет.
Других решений пока нет …