web-dev-qa-db-fra.com

Gérer les contours et le rectangle englobant dans OpenCV 2.4 - python 2.7

Je travaille avec openCv et python et je traite avec l'analyse structurelle et les descripteurs de forme. J'ai trouvé ce blog: http://opencvpython.blogspot.it/2012/06 /contours-2-brotherhood.html c'est très utile et j'ai essayé avec une image en noir et blanc de dessiner un rectangle englobant et cela fonctionne. Mais maintenant, à partir d'une image, j'extrait, par exemple, la couleur jaune et sur celle je voudrais dessiner un rectangle de délimitation. Le problème est que l'image en noir et blanc n'est pas uniforme elle a du bruit et comme ça le code ne reconnaît pas la forme entière.

origianl image

black and white image

final image

Et voici le code:

import numpy as np
import cv2

im = cv2.imread('shot.bmp')
hsv_img = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
COLOR_MIN = np.array([20, 80, 80],np.uint8)
COLOR_MAX = np.array([40, 255, 255],np.uint8)
frame_threshed = cv2.inRange(hsv_img, COLOR_MIN, COLOR_MAX)
imgray = frame_threshed
ret,thresh = cv2.threshold(frame_threshed,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt=contours[0]
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("Show",im)
cv2.waitKey()
cv2.destroyAllWindows()
19
Gianfra

Étant donné que votre image d'origine est assez bruyante, une solution simple consiste à supprimer une partie du bruit à l'aide de cv2.medianBlur() Cela supprimera les petites zones de bruit dans votre image d'origine et ne vous laissera qu'un seul contour. Les premières lignes de votre code ressembleraient à ceci:

im = cv2.imread('shot.bmp')
im = cv2.medianBlur(im,5)    # 5 is a fairly small kernel size
hsv_img = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)

Cependant, cette méthode n'est pas la plus robuste car vous devez spécifier manuellement une taille de noyau, et la ligne cnt=contours[0] Dans votre code suppose que le contour d'intérêt est le premier dans la liste des contours, ce qui n'est vrai que si il n'y a qu'un seul contour. Une méthode plus robuste consiste à supposer que vous êtes intéressé par le contour - le plus grand, ce qui vous permettra de compenser un bruit même modéré.

Pour ce faire, ajoutez les lignes:

# Find the index of the largest contour
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]

après la ligne:

contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

Résultat dans ce code:

import numpy as np
import cv2

im = cv2.imread('shot.bmp')
hsv_img = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
COLOR_MIN = np.array([20, 80, 80],np.uint8)
COLOR_MAX = np.array([40, 255, 255],np.uint8)
frame_threshed = cv2.inRange(hsv_img, COLOR_MIN, COLOR_MAX)
imgray = frame_threshed
ret,thresh = cv2.threshold(frame_threshed,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

# Find the index of the largest contour
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]

x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("Show",im)
cv2.waitKey()
cv2.destroyAllWindows()

Ces deux méthodes donnent un résultat avec une boîte englobante correcte:

Bounding Box Result

N.B.
A partir de OpenCV 3.x la méthode findContours() renvoie 3 résultats (comme on peut le voir ici ), donc la valeur de retour supplémentaire doit être pris comme:

_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPL‌​E)
38
Aurelius