0
votes

Comment obtenir le seul rectangle de zone minimale sur une image à contours multiples avec cv2.minAreaRect (cnt)?

Je souhaite utiliser un seul rectangle pour couvrir le cercle de cette image:

Et obtenez ce résultat avec cv2.minAreaRect ( cnt) :

 Après le traitement

Cette image semble être divisée en plusieurs parties. C'est peut-être parce qu'il y a un point d'arrêt sur le bord de cette image. pouvez-vous me dire comment utiliser un seul rectangle pour couvrir ce cercle de mon image? Merci beaucoup!

Voici mon code:

def draw_min_rect_circle(img, cnts):  # conts = contours
    img = np.copy(img)

    for cnt in cnts:
        x, y, w, h = cv2.boundingRect(cnt)
        cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)  # blue

        min_rect = cv2.minAreaRect(cnt)  # min_area_rectangle
        min_rect = np.int0(cv2.boxPoints(min_rect))
        cv2.drawContours(img, [min_rect], 0, (0, 255, 0), 2)  # green

        (x, y), radius = cv2.minEnclosingCircle(cnt)
        center, radius = (int(x), int(y)), int(radius)  # center and radius of minimum enclosing circle
        img = cv2.circle(img, center, radius, (0, 0, 255), 2)  # red
return img


1 commentaires

On pourrait trouver une solution sur l'image binaire fournie, bien sûr, mais je pense que ce serait mieux si vous fournissiez votre image d'entrée d'origine, car il pourrait y avoir une meilleure solution globale pour votre problème.


3 Réponses :


0
votes

Ce que vous devez faire est que vous ne devez obtenir qu'un seul contour de l'image en fusionnant les contours, c'est un peu difficile à faire, donc si vous voulez seulement un rect englobant autour de tous les contours, vous pouvez faire quelque chose en tant que tel

def draw_min_rect_circle(img, cnts):  # conts = contours
    img = np.copy(img)
    x1,y1 = np.inf
    x2,y2 = 0
    for cnt in cnts:
        x, y, w, h = cv2.boundingRect(cnt)
        if x > x1:
           x1=x
        if y > y1:
           y1=y
        if x2 < x+w
           x2 = x+w
        if y2 < y+h
           y2 = y+h
     w = x2 - x1
     h = y2 - y1
     r = math.sqrt((w*w) + (h*h)) / 2

     cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
     cv2.circle(img, (x1+w/2,y1+h/2), r, (0, 0, 255), 2)


0 commentaires

5
votes

Vous avez probablement recherché des contours avec cv2.findContours () et les avez parcourus pour dessiner le rectangle sur l'image. Le problème est que votre image n'a pas le cercle constitué d'une ligne connectée mais de plusieurs lignes brisées.

Les contours sont des courbes joignant tous les points continus (le long de la frontière), ayant la même couleur ou intensité (documentation OpenCV).

Donc, pour obtenir un meilleur résultat, vous devez d'abord préparer votre image avant de rechercher contours. Vous pouvez utiliser divers outils pour prétraiter l'image (vous pouvez rechercher la documentation OpenCV). Dans ce cas, j'essaierais d'exécuter la procédure appelée "fermeture" avec un petit noyau. La fermeture est une dilatation suivie d'une érosion des pixels. Cela peut aider à relier vos petits contours à un seul grand contour (cercle). Ensuite, vous pouvez sélectionner le plus grand et dessiner un rectangle de délimitation.

Exemple:

Image d'entrée:

 entrez la description de l'image ici

import cv2
import numpy as np

img = cv2.imread('test.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
kernel = np.ones((3,3), dtype=np.uint8)
closing = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
_, contours, hierarchy = cv2.findContours(closing, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x,y), (x+w, y+h), (255,255,0), 1)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Résultat:

entrez la description de l'image ici

Image après avoir effectué l'opération de fermeture:

 entrez la description de l'image ici

J'espère que cela vous aidera. Bravo!


3 commentaires

C'est une excellente solution. La méthode d'élimination du bruit en fermant puis en parcourant les contours pour trouver le maximum est simple mais très efficace.


Des solutions simples, s'ils fonctionnent, sont dans mon expérience les meilleures solutions. Heureux d'avoir aidé.


@kavko Merci pour votre aide! Votre solution fonctionne. C'est vraiment une excellente solution!



0
votes

Il est possible de joindre tous les contours en un seul contour, car ce ne sont que de nombreux tableaux de coordonnées de points décrivant le contour. Vous pouvez utiliser np.concatenate (contours) et il semble que la fonction cv2.minAreaRect ne se soucie pas que les points du nouveau tableau ne soient pas continus. Dans mon cas, cela fonctionnait mieux que d'utiliser la fonction de fermeture, car j'ai des objets plus complexes. Si vous voulez, vous pouvez l'essayer, c'est simple. Voici à quoi devrait ressembler votre fonction:

def draw_min_rect_circle(img, cnts):  # conts = contours
    img = np.copy(img)

    join_cnts = np.concatenate(cnts)

    x, y, w, h = cv2.boundingRect(join_cnts)
    cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)  # blue

    min_rect = cv2.minAreaRect(join_cnts)  # min_area_rectangle
    min_rect = np.int0(cv2.boxPoints(min_rect))
    cv2.drawContours(img, [min_rect], 0, (0, 255, 0), 2)  # green

    (x, y), radius = cv2.minEnclosingCircle(join_cnts)
    center, radius = (int(x), int(y)), int(radius)  # center and radius of minimum enclosing circle
    img = cv2.circle(img, center, radius, (0, 0, 255), 2)  # red
    
return img


0 commentaires