webentwicklung-frage-antwort-db.com.de

Wie erkennt man Linien in opencv?

Ich versuche, Parklinien wie unten gezeigt zu erkennen

Empty parking lot

Was ich hoffe zu bekommen, ist die klare Linie und (x, y) Position in der gekreuzten Linie, aber das Ergebnis ist nicht sehr vielversprechend

Parking lot with Hough Lines drawn

Ich denke, das hat zwei Hauptgründe

  1. einige Linien sind sehr gebrochen oder es fehlen sogar menschliche Augen, um sie eindeutig zu identifizieren. (Sogar HoughLine kann helfen, einige fehlende Leitungen zu verbinden, da HoughLine manchmal unnötige Leitungen miteinander verbindet, also würde ich es lieber manuell machen.)

  2. es gibt einige wiederholte Zeilen

Die allgemeine Pipeline für die Arbeit ist wie folgt dargestellt

1. Wählen Sie einige bestimmte Farben (weiß oder gelb)

import cv2
import numpy as np
import matplotlib
from matplotlib.pyplot import imshow
from matplotlib import pyplot as plt

# white color mask
img = cv2.imread(filein)
#converted = convert_hls(img)
image = cv2.cvtColor(img,cv2.COLOR_BGR2HLS)
lower = np.uint8([0, 200, 0])
upper = np.uint8([255, 255, 255])
white_mask = cv2.inRange(image, lower, upper)
# yellow color mask
lower = np.uint8([10, 0,   100])
upper = np.uint8([40, 255, 255])
yellow_mask = cv2.inRange(image, lower, upper)
# combine the mask
mask = cv2.bitwise_or(white_mask, yellow_mask)
result = img.copy()
cv2.imshow("mask",mask) 

Binary image

2. Wiederholen Sie die Dilatation und Erosion, bis das Bild nicht mehr geändert werden kann ( Referenz )

height,width = mask.shape
skel = np.zeros([height,width],dtype=np.uint8)      #[height,width,3]
kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3))
temp_nonzero = np.count_nonzero(mask)
while(np.count_nonzero(mask) != 0 ):
    eroded = cv2.erode(mask,kernel)
    cv2.imshow("eroded",eroded)   
    temp = cv2.dilate(eroded,kernel)
    cv2.imshow("dilate",temp)
    temp = cv2.subtract(mask,temp)
    skel = cv2.bitwise_or(skel,temp)
    mask = eroded.copy()

cv2.imshow("skel",skel)
#cv2.waitKey(0)

 After the erosion and dialation

3. Wenden Sie den Canny an, um die Linien zu filtern, und verwenden Sie HoughLinesP, um die Linien zu erhalten

edges = cv2.Canny(skel, 50, 150)
cv2.imshow("edges",edges)
lines = cv2.HoughLinesP(edges,1,np.pi/180,40,minLineLength=30,maxLineGap=30)
i = 0
for x1,y1,x2,y2 in lines[0]:
    i+=1
    cv2.line(result,(x1,y1),(x2,y2),(255,0,0),1)
print i

cv2.imshow("res",result)
cv2.waitKey(0)

After Canny

Ich frage mich, ob nach dem ersten Schritt der Auswahl einer bestimmten Farbe die Linien gebrochen und verrauscht sind. Ich denke, wir sollten in diesem Schritt etwas tun, um die gebrochene Linie zu einer vollständigen, weniger verrauschten Linie zu machen, und dann versuchen, etwas anzuwenden, um das zu tun Canny und Hough Linien, irgendwelche Ideen?

25
user824624

Hier ist meine Pipeline, vielleicht kann sie Ihnen helfen.

Holen Sie sich zuerst das graue Bild und verarbeiten Sie GaussianBlur.

img = cv2.imread('src.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

Zweitens wird für die Kantenerkennung Canny verwendet.

low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

Verwenden Sie dann HoughLinesP, um die Linien abzurufen. Sie können die Parameter anpassen, um eine bessere Leistung zu erzielen.

rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 15  # minimum number of votes (intersections in Hough grid cell)
min_line_length = 50  # minimum number of pixels making up a line
max_line_gap = 20  # maximum gap in pixels between connectable line segments
line_image = np.copy(img) * 0  # creating a blank to draw lines on

# Run Hough on Edge detected image
# Output "lines" is an array containing endpoints of detected line segments
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)

for line in lines:
    for x1,y1,x2,y2 in line:
    cv2.line(line_image,(x1,y1),(x2,y2),(255,0,0),5)

Zeichnen Sie abschließend die Linien auf Ihrem srcImage.

# Draw the lines on the  image
lines_edges = cv2.addWeighted(img, 0.8, line_image, 1, 0)

Hier ist meine letzte Vorstellung.

Endgültiges Bild:

enter image description here

23
veraposeidon

Ich bin mir nicht sicher, was genau Sie fragen, da Ihr Beitrag keine Frage enthält.

Eine nette und robuste Technik zur Erkennung von Liniensegmenten ist LSD (Line Segment Detector), verfügbar in openCV seit openCV 3.

Hier ist ein einfacher grundlegender C++ - Code, der wahrscheinlich leicht in python konvertiert werden kann:

int main(int argc, char* argv[])
{
    cv::Mat input = cv::imread("C:/StackOverflow/Input/parking.png");
    cv::Mat gray;
    cv::cvtColor(input, gray, CV_BGR2GRAY);


    cv::Ptr<cv::LineSegmentDetector> det;
    det = cv::createLineSegmentDetector();



    cv::Mat lines;
    det->detect(gray, lines);

    det->drawSegments(input, lines);

    cv::imshow("input", input);
    cv::waitKey(0);
    return 0;
}

Geben Sie dieses Ergebnis:

enter image description here

Was für die Weiterverarbeitung besser aussieht als Ihr Bild (keine Linienduplikate etc.)

14
Micka

Hier gibt es einige gute Antworten auf den ersten Teil Ihrer Frage, aber für den zweiten Teil (Finden der Linienkreuzungen) sehe ich nicht viel.

Ich schlage vor, Sie werfen einen Blick auf den Algorithmus Bentley-Ottmann .

Es gibt einige python Implementierungen des Algorithmus hier und hier .

Bearbeiten: Unter Verwendung der Houghlines-Implementierung von VeraPoseidon und der hier verlinkten zweiten Bibliothek ist es mir gelungen, das folgende Ergebnis für die Kreuzungserkennung zu erzielen. Wir danken Vera und der Bibliotheksautorin für ihre gute Arbeit. Die grünen Quadrate repräsentieren eine erkannte Kreuzung. Es gibt ein paar Fehler, aber das scheint mir ein wirklich guter Ausgangspunkt zu sein. Es scheint, als hätten die meisten Orte, an denen Sie eine Kreuzung erkennen möchten, mehrere Kreuzungen erkannt. Daher könnten Sie wahrscheinlich ein Fenster mit einer angemessenen Größe über das Bild ziehen, das nach mehreren Kreuzungen suchte, und als echte Kreuzung betrachten, an der dieses Fenster aktiviert wurde.

Bentley-Ottmann applied to Houghlines

Hier ist der Code, mit dem ich dieses Ergebnis erzielt habe:

import cv2
import numpy as np
import isect_segments_bentley_ottmann.poly_point_isect as bot


img = cv2.imread('parking.png')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

low_threshold = 50
high_threshold = 150
edges = cv2.Canny(blur_gray, low_threshold, high_threshold)

rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 15  # minimum number of votes (intersections in Hough grid cell)
min_line_length = 50  # minimum number of pixels making up a line
max_line_gap = 20  # maximum gap in pixels between connectable line segments
line_image = np.copy(img) * 0  # creating a blank to draw lines on

# Run Hough on Edge detected image
# Output "lines" is an array containing endpoints of detected line segments
lines = cv2.HoughLinesP(edges, rho, theta, threshold, np.array([]),
                    min_line_length, max_line_gap)
print(lines)
points = []
for line in lines:
    for x1, y1, x2, y2 in line:
        points.append(((x1 + 0.0, y1 + 0.0), (x2 + 0.0, y2 + 0.0)))
        cv2.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5)

lines_edges = cv2.addWeighted(img, 0.8, line_image, 1, 0)
print(lines_edges.shape)
#cv2.imwrite('line_parking.png', lines_edges)

print points
intersections = bot.isect_segments(points)
print intersections

for inter in intersections:
    a, b = inter
    for i in range(3):
        for j in range(3):
            lines_edges[int(b) + i, int(a) + j] = [0, 255, 0]

cv2.imwrite('line_parking.png', lines_edges)

Sie können so etwas wie diesen Codeblock für eine Strategie verwenden, um mehrere Schnittpunkte in einem kleinen Bereich zu entfernen:

for idx, inter in enumerate(intersections):
    a, b = inter
    match = 0
    for other_inter in intersections[idx:]:
        c, d = other_inter
        if abs(c-a) < 15 and abs(d-b) < 15:
            match = 1
            intersections[idx] = ((c+a)/2, (d+b)/2)
            intersections.remove(other_inter)

    if match == 0:
        intersections.remove(inter)

Ausgabebild: Cleaned Output

Sie müssen jedoch mit der Fensterfunktion spielen.

10
Saedeas

was passiert, wenn Sie maxLineGap oder die Größe Ihres Erosionskerns anpassen? Alternativ können Sie den Abstand zwischen den Linien ermitteln. Sie müssten durch Linienpaare gehen, die ax1, ay1 bis ax2, ay2 sagen, vgl. bx1, by1 bis bx2, by2 finden Sie den Punkt, an dem die Steigung im rechten Winkel (-1 über der Steigung der Linie) zu einer Kreuzungslinie b verläuft. Grundlegende Schulgeometrie und simultane Gleichungen, so etwas wie:

x = (ay1 - by1) / ((by2 - by1) / (bx2 - bx1) + (ax2 - ax1) / (ay2 - ay1))
# then
y = by1 + x * (by2 - by1) / (bx2 - bx1)

und vergleiche x, y mit ax1, ay1

PS: Möglicherweise müssen Sie den Abstand zwischen ax1, ay1 und bx1 nach 1 prüfen, da einige Ihrer Linien als Fortsetzungen anderer Linien erscheinen und diese möglicherweise durch die Nahpunkttechnik beseitigt werden.

1
paddyg