webentwicklung-frage-antwort-db.com.de

OpenCV - Wie finde ich die Rechteckkontur eines Rechtecks ​​mit runder Ecke?

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.

 results

Ich möchte das Rechteck so finden:  desired result

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.

4
tomfriwel

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)

 result

11
zindarod

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)
1
Kamil Szelag

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.

0