Точная настройка параметров функции Hough Line OpenCV

Я пытался получить 4 линии вокруг квадрата, чтобы я мог получить вершины квадрата. Я придерживаюсь этого подхода, а не нахожу углы напрямую, используя метод Харриса или контуров из-за точности. Используя грубые линии во встроенной функции в opencv, я не могу получить линии полной длины, чтобы получить точки пересечения, и я также получаю слишком много нерелевантных линий. Я хотел бы знать, могут ли параметры быть точно настроены для получения моих требований? Если да, то как мне это сделать? Мой вопрос точно такой же, как этот здесь. Однако я не получаю эти строки сам, даже после изменения этих параметров. Я приложил исходное изображение вместе с кодом и выводом:

Исходное изображение:

Исходное изображение

Код:

#include <Windows.h>
#include "opencv2\highgui.hpp"
#include "opencv2\imgproc.hpp"
#include "opencv2/imgcodecs/imgcodecs.hpp"
#include "opencv2/videoio/videoio.hpp"

using namespace cv;
using namespace std;

int main(int argc, const char** argv)
{

    Mat image,src;
    image = imread("c:/pics/output2_1.bmp");
    src = image.clone();
    cvtColor(image, image, CV_BGR2GRAY);
    threshold(image, image, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);

    namedWindow("thresh", WINDOW_NORMAL);
    resizeWindow("thresh", 600, 400);

    imshow("thresh", image);

    cv::Mat edges;

    cv::Canny(image, edges, 0, 255);

    vector<Vec2f> lines;
    HoughLines(edges, lines, 1, CV_PI / 180, 100, 0, 0);
    for (size_t i = 0; i < lines.size(); i++)
    {
        float rho = lines[i][0], theta = lines[i][1];
        Point pt1, pt2;
        double a = cos(theta), b = sin(theta);
        double x0 = a*rho, y0 = b*rho;
        pt1.x = cvRound(x0 + 1000 * (-b));
        pt1.y = cvRound(y0 + 1000 * (a));
        pt2.x = cvRound(x0 - 1000 * (-b));
        pt2.y = cvRound(y0 - 1000 * (a));
        line(src, pt1, pt2, Scalar(0, 0, 255), 3, CV_AA);
    }

namedWindow("Edges Structure", WINDOW_NORMAL);
resizeWindow("Edges Structure", 600, 400);

imshow("Edges Structure", src);
waitKey(0);

return(0);
}

Выходное изображение:

HoughLinesOutput

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

1 ответ

Решение

Есть много способов сделать это, я приведу пример только одного. Тем не менее, я быстрее всех python, так что мой пример кода будет на этом языке. Хотя это не должно быть сложно перевести (пожалуйста, не стесняйтесь редактировать свое сообщение с помощью своего решения C++ после того, как вы закончили его для других).

Для предварительной обработки я настоятельно рекомендую dilate() Инг ваш край изображения. Это сделает линии более толстыми, что поможет лучше соответствовать линиям Хафа. Функция абстрактных линий Hough в основном состоит в создании сетки линий, проходящих через тонну углов и расстояний, и если линии проходят через любые белые пиксели от Canny, то она дает этой линии оценку за каждую пройденную точку, Тем не менее, линии от Canny не будут идеально прямыми, поэтому вы получите несколько разных выигрышных линий. Толщина этих линий Canny будет означать, что каждая линия, которая действительно близка к хорошей подгонке, будет иметь больше шансов набрать больше очков.

Дилатационная банка

Если вы собираетесь использовать HoughLinesP, тогда ваши выходные данные будут отрезками, где у вас есть только две точки на линии.

Поскольку линии в основном вертикальные и горизонтальные, вы можете легко разделить линии в зависимости от их положения. Если две y- координаты одной линии находятся рядом друг с другом, то линия в основном горизонтальная. Если две x- координаты находятся рядом друг с другом, то линия в основном вертикальная. Таким образом, вы можете разделить ваши линии на вертикальные и горизонтальные линии таким образом.

def segment_lines(lines, delta):
    h_lines = []
    v_lines = []
    for line in lines:
        for x1, y1, x2, y2 in line:
            if abs(x2-x1) < delta: # x-values are near; line is vertical
                v_lines.append(line)
            elif abs(y2-y1) < delta: # y-values are near; line is horizontal
                h_lines.append(line)
    return h_lines, v_lines

Сегментированные линии Хафа

Затем вы можете получить точки пересечения двух отрезков от их конечных точек, используя детерминанты.

def find_intersection(line1, line2):
    # extract points
    x1, y1, x2, y2 = line1[0]
    x3, y3, x4, y4 = line2[0]
    # compute determinant
    Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    return Px, Py

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

Пересечения

Тем не менее, все они в одном векторе, поэтому вам нужно не только усреднить точки в каждом углу, но и сгруппировать их. Этого можно добиться с помощью кластеризации k- средних, которая реализована в OpenCV как kmeans(),

def cluster_points(points, nclusters):
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    _, _, centers = cv2.kmeans(points, nclusters, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
    return centers

Наконец, мы можем просто нанести эти центры (удостоверившись, что мы округлились первыми - поскольку пока что все является плавающим) на изображение с circle() чтобы убедиться, что мы все сделали правильно.

k-означает кластерные угловые точки

И у нас это есть; четыре точки, по углам коробки.

Вот мой полный код на python, включая код для генерации рисунков выше:

import cv2
import numpy as np 

def find_intersection(line1, line2):
    # extract points
    x1, y1, x2, y2 = line1[0]
    x3, y3, x4, y4 = line2[0]
    # compute determinant
    Px = ((x1*y2 - y1*x2)*(x3-x4) - (x1-x2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    Py = ((x1*y2 - y1*x2)*(y3-y4) - (y1-y2)*(x3*y4 - y3*x4))/  \
        ((x1-x2)*(y3-y4) - (y1-y2)*(x3-x4))
    return Px, Py

def segment_lines(lines, delta):
    h_lines = []
    v_lines = []
    for line in lines:
        for x1, y1, x2, y2 in line:
            if abs(x2-x1) < delta: # x-values are near; line is vertical
                v_lines.append(line)
            elif abs(y2-y1) < delta: # y-values are near; line is horizontal
                h_lines.append(line)
    return h_lines, v_lines

def cluster_points(points, nclusters):
    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
    _, _, centers = cv2.kmeans(points, nclusters, None, criteria, 10, cv2.KMEANS_PP_CENTERS)
    return centers

img = cv2.imread('image.png')

# preprocessing
img = cv2.resize(img, None, fx=.5, fy=.5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 150)
dilated = cv2.dilate(edges, np.ones((3,3), dtype=np.uint8))

cv2.imshow("Dilated", dilated)
cv2.waitKey(0)
cv2.imwrite('dilated.png', dilated)

# run the Hough transform
lines = cv2.HoughLinesP(dilated, rho=1, theta=np.pi/180, threshold=100, maxLineGap=20, minLineLength=50)

# segment the lines
delta = 10
h_lines, v_lines = segment_lines(lines, delta)

# draw the segmented lines
houghimg = img.copy()
for line in h_lines:
    for x1, y1, x2, y2 in line:
        color = [0,0,255] # color hoz lines red
        cv2.line(houghimg, (x1, y1), (x2, y2), color=color, thickness=1)
for line in v_lines:
    for x1, y1, x2, y2 in line:
        color = [255,0,0] # color vert lines blue
        cv2.line(houghimg, (x1, y1), (x2, y2), color=color, thickness=1)

cv2.imshow("Segmented Hough Lines", houghimg)
cv2.waitKey(0)
cv2.imwrite('hough.png', houghimg)

# find the line intersection points
Px = []
Py = []
for h_line in h_lines:
    for v_line in v_lines:
        px, py = find_intersection(h_line, v_line)
        Px.append(px)
        Py.append(py)

# draw the intersection points
intersectsimg = img.copy()
for cx, cy in zip(Px, Py):
    cx = np.round(cx).astype(int)
    cy = np.round(cy).astype(int)
    color = np.random.randint(0,255,3).tolist() # random colors
    cv2.circle(intersectsimg, (cx, cy), radius=2, color=color, thickness=-1) # -1: filled circle

cv2.imshow("Intersections", intersectsimg)
cv2.waitKey(0)
cv2.imwrite('intersections.png', intersectsimg)

# use clustering to find the centers of the data clusters
P = np.float32(np.column_stack((Px, Py)))
nclusters = 4
centers = cluster_points(P, nclusters)
print(centers)

# draw the center of the clusters
for cx, cy in centers:
    cx = np.round(cx).astype(int)
    cy = np.round(cy).astype(int)
    cv2.circle(img, (cx, cy), radius=4, color=[0,0,255], thickness=-1) # -1: filled circle

cv2.imshow("Center of intersection clusters", img)
cv2.waitKey(0)
cv2.imwrite('corners.png', img)

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

Детектор углов Harris

Это было сделано с помощью следующего кода:

import cv2
import numpy as np

img = cv2.imread('image.png')

# preprocessing
img = cv2.resize(img, None, fx=.5, fy=.5)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
r, gray = cv2.threshold(gray, 120, 255, type=cv2.THRESH_BINARY)
gray = cv2.GaussianBlur(gray, (3,3), 3)

# run harris
gray = np.float32(gray)
dst = cv2.cornerHarris(gray,2,3,0.04)

# dilate the corner points for marking
dst = cv2.dilate(dst,None)
dst = cv2.dilate(dst,None)

# threshold
img[dst>0.01*dst.max()]=[0,0,255]

cv2.imshow('dst',img)
cv2.waitKey(0)
cv2.imwrite('harris.png', img)

Я думаю, что с некоторыми незначительными корректировками детектор углов Харриса, вероятно, может быть гораздо более точным, чем экстраполяция пересечений линий Хафа.

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