OpenCV – ¿Cómo encontrar el contorno de un rectángulo con una esquina redonda?

Estoy tratando de encontrar el contorno de un objeto rectangular con una esquina redonda en una imagen. HoughLinesP y findContours , pero no findContours el resultado deseado.

resultados

Quiero encontrar el rectángulo de esta manera: resultado deseado

Código:

 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"]) 

util:

 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 

Estoy usando Python 2.7.13 y OpenCV 2.4.13.3 .

He estado pensando en extender estas líneas y obtener puntos de intersección de líneas. Finalmente, obtendré cuatro coordenadas de rectángulo. Pero si la imagen es más compleja, no sé cómo tratar.

Necesitas encontrar el rectángulo delimitador de los contornos encontrados.

 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) 

resultado

Puedes encontrar el rectángulo delimitador de puntos distintos de cero.

 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) 

He estado pensando en extender estas líneas y obtener puntos de intersección de líneas. Finalmente, obtendré cuatro coordenadas de rectángulo.

Más o menos, este es un buen enfoque si los contornos no funcionan para usted. En efecto, como usted dice,

Pero si la imagen es más compleja, no sé cómo tratar.

Hay algunas complicaciones para tratar. El principal problema es que la detección de línea típica no le brinda segmentos de línea perfectos en todo momento. Puede tener varios segmentos de línea a lo largo de la misma línea, ya sea astackdos a lo largo o superpuestos múltiples. Además, deberá segmentar las líneas automáticamente de alguna manera para que no esté tratando de encontrar la intersección de líneas paralelas.

Sin embargo, estos dos problemas no son demasiado difíciles de tratar. Hace un tiempo respondí una pregunta en este sitio sobre cómo encontrar puntos de intersección de HoughLinesP que usan muchas de las sugerencias a continuación, aunque no es tan robusta (segmentar las líneas en dos grupos, por ejemplo, se hizo mucho más ingenuamente), pero debería darle Un buen lugar para empezar.

Después de detectar las líneas, debe segmentar las líneas en grupos o segmentos paralelos. Si su rectángulo tiene una orientación definida, entonces puede simplemente filtrar las líneas basándose en eso, lo que sería un caso fácil. Pero si el rectángulo puede estar en cualquier orientación, necesitarás alguna otra forma de segmentarlos. Puede usar k- significa agrupar con k = 2 para encontrar los dos angularjs principales, y poner las líneas correspondientes a un ángulo en un bin y las líneas correspondientes al otro ángulo en otra bin y encontrar las intersecciones de las líneas en una bin con las líneas en la otra papelera. Lo bueno de este enfoque es que funcionaría para cualquier paralelogramo. También puede rechazar las líneas de cada bandeja si no están dentro de algún umbral (como 10 grados o algo) por estar en un ángulo recto con el ángulo medio de la otra bandeja, o algo así, si desea atenerse a los rectangularjs.

Una vez que tenga todas las líneas agrupadas en consecuencia, puede calcular sus puntos de intersección. En realidad, hay una buena fórmula que utiliza determinantes para calcular los puntos de intersección entre dos líneas dados dos puntos en la línea , que ya tiene de los puntos finales. Entonces, eso es práctico! Cada línea en la bandeja 0 tendrá un punto de intersección con la línea de la bandeja 1, y eso es todo lo que necesita hacer.

Así que al final aquí tendrías 4 grupos de puntos de intersección. Una opción es simplemente agruparlos, de nuevo con k- significa con k = 4 , y tendrá los centroides de esos cuatro grupos de puntos, que representan las esquinas de su rectángulo. Por supuesto, ya que has usado muchos pasos de aproximación en el camino, tus puntos no definirán exactamente un rectángulo, por lo que tendrás que ajustar el rectángulo más cercano posible a esos puntos. O, en lugar de los medios k , otro método sería tratar de encontrar un subconjunto de sus muchos puntos de intersección que representen con mayor precisión un rectángulo, y luego ajustar el rectángulo más cercano. Probablemente alguna forma de usar regresión lineal, mínimos cuadrados, RANSAC, etc. para este problema. O si lo desea, puede encontrar el rectángulo delimitador de los cuatro puntos con boundingRect() .