Ich versuche, die Kontur eines rechteckigen Objekts mit runder Ecke in einem Bild zu finden. Ich habe HoughLinesP
und findContours
ausprobiert, aber nicht das gewünschte Ergebnis erzielt.
Ich möchte das Rechteck so finden:
Code:
import cv2
import matplotlib.pyplot as plt
import util
image = cv2.imread("./img/findrect0.png", 1)
gray = util.grayImage(image)
edges = cv2.Canny(image, 50, 200)
lines = cv2.HoughLinesP(edges, 1, cv2.cv.CV_PI/180, 50, minLineLength=50, maxLineGap=10)[0]
linesImage = image.copy()
util.drawLines(linesImage, lines, thickness=10)
contoursImage = image.copy()
(contours, hierarchy) = cv2.findContours(gray.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
util.drawContours(contoursImage, contours, thickness=10)
util.showOpenCVImagesGrid([image, edges, linesImage, contoursImage], 2, 2, titles=["original image", "canny image", "lines image", "contours image"])
nutzen:
import cv2
import math
import matplotlib.pyplot as plt
def showOpenCVImagesGrid(images, x, y, titles=None, axis="on"):
fig = plt.figure()
i = 1
for image in images:
copy = image.copy()
channel = len(copy.shape)
cmap = None
if channel == 2:
cmap = "gray"
Elif channel == 3:
copy = cv2.cvtColor(copy, cv2.COLOR_BGR2RGB)
Elif channel == 4:
copy = cv2.cvtColor(copy, cv2.COLOR_BGRA2RGBA)
fig.add_subplot(x, y, i)
if titles is not None:
plt.title(titles[i-1])
plt.axis(axis)
plt.imshow(copy, cmap=cmap)
i += 1
plt.show()
def grayImage(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
return gray
def drawLines(image, lines, thickness=1):
for line in lines:
# print("line="+str(line))
cv2.line(image, (line[0], line[1]), (line[2], line[3]),
(0, 0, 255), thickness)
def drawContours(image, contours, thickness=1):
i = 0
for contour in contours:
cv2.drawContours(image, [contours[i]], i, (0, 255, 0), thickness)
area = cv2.contourArea(contour)
i += 1
Ich verwende Python 2.7.13
und OpenCV 2.4.13.3
.
Ich habe überlegt, diese Linien zu verlängern und Schnittpunkte von Linien zu erhalten. Schließlich bekomme ich vier Koordinaten des Rechtecks ... Wenn aber das Bild komplexer ist, kann ich nicht damit umgehen.
Sie müssen das Begrenzungsrechteck der gefundenen Konturen finden.
img = cv2.imread("image.png", -1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
binary = cv2.bitwise_not(gray)
(_,contours,_) = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for contour in contours:
(x,y,w,h) = cv2.boundingRect(contour)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
Sie können ein Begrenzungsrechteck mit Punkten, die nicht Null sind, finden.
image = cv2.imread("./img/findrect0.png", 1)
gray = util.grayImage(image)
gray_inv = cv2.bitwise_not(gray)
points = cv2.findNonZero(gray)
rect = cv2.boundingRect(points)
Ich habe überlegt, diese Linien zu verlängern und Schnittpunkte von Linien zu erhalten. Zum Schluss bekomme ich vier Koordinaten des Rechtecks.
Mehr oder weniger ist dies ein guter Ansatz, wenn Konturen für Sie nicht funktionieren. In der Tat, wie Sie sagen,
Aber wenn das Image komplexer ist, weiß ich nicht, wie ich damit umgehen soll.
es gibt einige Komplikationen, mit denen man fertig werden muss. Das Hauptproblem ist, dass die typische Linienerkennung nicht jedes Mal perfekte Liniensegmente liefert. Sie können mehrere Liniensegmente entlang derselben Linie haben, entweder in Längsrichtung gestapelt oder mehrfach überlappend. Darüber hinaus müssen Sie die Linien automatisch so segmentieren, dass Sie nicht versuchen, den Schnittpunkt paralleler Linien zu finden.
Beide Probleme sind jedoch nicht allzu schwer zu lösen. . /. Ich habe vor einiger Zeit auf dieser Website eine Frage beantwortet über das Finden von Schnittpunkten aus HoughLinesP
, die viele der folgenden Vorschläge verwendet, jedoch nicht so robust ist (das Unterteilen der Linien in zwei Gruppen für zB wurde viel naiver durchgeführt ), aber es sollte Ihnen einen guten Ausgangspunkt geben.
Nachdem Sie die Linien erkannt haben, müssen Sie die Linien in Gruppen oder parallele Segmente segmentieren. Wenn Ihr Rechteck eine definierte Ausrichtung hat, können Sie die Zeilen einfach filtern, was der einfache Fall wäre. Wenn sich das Rechteck jedoch in einer beliebigen Ausrichtung befinden kann, benötigen Sie eine andere Möglichkeit, um es zu segmentieren. Sie können k -Means-Clustering mit k = 2 verwenden, um die zwei Hauptwinkel zu finden, und die Linien, die einem Winkel in einem Bin entsprechen, und die Linien, die dem anderen Winkel entsprechen, in einen anderen Bin setzen und suchen die Schnittpunkte der Linien in einem Fach mit den Linien im anderen Fach. Das Schöne an diesem Ansatz ist, dass er für jedes Parallelogramm funktionieren würde. Sie können Linien aus jedem Fach auch ablehnen, wenn sie sich nicht innerhalb einer bestimmten Schwelle befinden (z. B. 10 Grad oder etwas anderes), als dass sie sich im rechten Winkel zum mittleren Winkel des anderen Fachs befinden, oder etwas, wenn Sie sich an Rechtecken halten möchten.
Wenn Sie alle Linien entsprechend zusammengefügt haben, können Sie deren Schnittpunkte berechnen. Es gibt tatsächlich eine Nice-Formel unter Verwendung von Determinanten zur Berechnung der Schnittpunkte zwischen zwei Linien zwei Punkten auf der Linie, die Sie bereits von den Endpunkten haben. Das ist praktisch! Jede Linie in Fach 0 hat einen Schnittpunkt mit der Linie von Fach 1, und das ist alles, was Sie tun müssen.
Am Ende hätten Sie also 4 Cluster von Schnittpunkten. Eine Möglichkeit besteht darin, diese einfach zusammenzufassen, wiederum mit k - Mitteln mit k = 4, und Sie haben die Schwerpunkte dieser vier Punktcluster, die die Ecken Ihres Rechtecks darstellen. Da Sie auf dem Weg viele Annäherungsschritte verwendet haben, definieren Ihre Punkte natürlich kein Rechteck. Daher müssen Sie das nächstgelegene Rechteck an diese Punkte anpassen. Oder anstelle von k - bedeutet dies eine andere Methode, zu versuchen, eine Untermenge Ihrer vielen Schnittpunkte zu finden, die ein Rechteck am genauesten darstellen, und dann passt in das nächstgelegene Rechteck. Wahrscheinlich eine Möglichkeit, lineare Regression, kleinste Quadrate, RANSAC usw. für dieses Problem zu verwenden. Oder, wenn Sie möchten, können Sie mit boundingRect()
einfach das Begrenzungsrechteck der vier Punkte finden.