Python >> Tutoriel Python >  >> Python

Trouver des points extérieurs extrêmes dans l'image avec Python OpenCV

Voici une approche potentielle :

  • Convertir l'image en niveaux de gris et flou gaussien
  • Seuil pour obtenir une image binaire
  • Rechercher des contours
  • Obtenir les coordonnées extérieures

Après conversion en niveaux de gris et floutage de l'image, nous établissons un seuil pour obtenir une image binaire

Maintenant, nous trouvons les contours en utilisant cv2.findContours() . Puisque OpenCV utilise des tableaux Numpy pour coder les images, un contour est simplement un tableau Numpy de (x,y) coordonnées. Nous pouvons découper le tableau Numpy et utiliser argmin() ou argmax() pour déterminer les coordonnées extérieures gauche, droite, haut et bas comme ceci

left = tuple(c[c[:, :, 0].argmin()][0])
right = tuple(c[c[:, :, 0].argmax()][0])
top = tuple(c[c[:, :, 1].argmin()][0])
bottom = tuple(c[c[:, :, 1].argmax()][0])

Voici le résultat

gauche :(162, 527)

à droite :(463, 467)

haut :(250, 8)

en bas :(381, 580)

import cv2
import numpy as np

# Load image, grayscale, Gaussian blur, threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 220, 255, cv2.THRESH_BINARY_INV)[1]

# Find contours
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
c = max(cnts, key=cv2.contourArea)

# Obtain outer coordinates
left = tuple(c[c[:, :, 0].argmin()][0])
right = tuple(c[c[:, :, 0].argmax()][0])
top = tuple(c[c[:, :, 1].argmin()][0])
bottom = tuple(c[c[:, :, 1].argmax()][0])

# Draw dots onto image
cv2.drawContours(image, [c], -1, (36, 255, 12), 2)
cv2.circle(image, left, 8, (0, 50, 255), -1)
cv2.circle(image, right, 8, (0, 255, 255), -1)
cv2.circle(image, top, 8, (255, 50, 0), -1)
cv2.circle(image, bottom, 8, (255, 255, 0), -1)

print('left: {}'.format(left))
print('right: {}'.format(right))
print('top: {}'.format(top))
print('bottom: {}'.format(bottom))
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()

Voici une amélioration possible de la réponse de nathancy, d'où provient la majeure partie du code, ainsi que l'idée principale d'utiliser np.argmax . Alors, s'il vous plaît, jetez un coup d'œil à cette réponse avant !

Puisque nous avons déjà une image binarisée de cv2.threshold , de sorte que le fond (blanc) de l'image d'entrée soit mis à zéro, nous pouvons utiliser la capacité de cv2.boundingRect à "calculer le rectangle de délimitation vers le haut à droite d'un ensemble de points ou de pixels non nuls d'une image en niveaux de gris" . La méthode retourne un tuple (x, y, w, h) avec (x, y) le point supérieur gauche ainsi que la largeur w et hauteur h du rectangle englobant. A partir de là, les points mentionnés left , right , etc. peuvent être obtenus facilement en utilisant np.argmax sur la tranche correspondante du thresh photo.

Voici le code complet :

import cv2
import numpy as np

image = cv2.imread('images/dMXjY.png')

blur = cv2.GaussianBlur(image, (3,3), 0)
gray = cv2.cvtColor(blur, cv2.COLOR_BGR2GRAY)

thresh = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)[1]

x, y, w, h = cv2.boundingRect(thresh)           #  Replaced code
                                                # 
left = (x, np.argmax(thresh[:, x]))             # 
right = (x+w-1, np.argmax(thresh[:, x+w-1]))    # 
top = (np.argmax(thresh[y, :]), y)              # 
bottom = (np.argmax(thresh[y+h-1, :]), y+h-1)   # 

cv2.circle(image, left, 8, (0, 50, 255), -1)
cv2.circle(image, right, 8, (0, 255, 255), -1)
cv2.circle(image, top, 8, (255, 50, 0), -1)
cv2.circle(image, bottom, 8, (255, 255, 0), -1)

print('left: {}'.format(left))
print('right: {}'.format(right))
print('top: {}'.format(top))
print('bottom: {}'.format(bottom))
cv2.imshow('thresh', thresh)
cv2.imshow('image', image)
cv2.waitKey()

Les sorties d'image ressemblent à celles de la réponse de nathancy.

Néanmoins, un des points obtenus diffère un peu :

gauche :(162, 527)

à droite :(463, 461) (au lieu de (463, 467))

haut :(250, 8)

en bas :(381, 580)

Si nous regardons de plus près le thresh image, on verra ça pour le 463 -ème colonne, tous les pixels dans la plage de 461 ... 467 avoir une valeur de 255 . Ainsi, pour le bord droit, il n'y a pas de valeur extrême unique.

Le contour c trouvé dans l'approche de nathancy détient les deux points (463, 467) et (463, 461) dans cet ordre, tel que np.argmax trouvera (463, 467) première. Dans mon approche, le 463 -ème colonne est examinée à partir de 0 à (height of image) , tel que np.argmax trouvera (463, 461) d'abord à la place.

De mon point de vue, les deux (ou même tous les autres points intermédiaires) sont des résultats appropriés, car il n'y a pas de contrainte supplémentaire sur la gestion de plusieurs points extrêmes.

Utilisation de cv2.boundingRect enregistre deux lignes de code et fonctionne également plus rapidement, du moins selon quelques tests courts utilisant timeit .

Divulgation :Encore une fois, la majeure partie du code et l'idée principale proviennent de la réponse de nathancy.


Plutôt que d'inspecter chaque élément (et de bloquer le CPU avec un if déclaration pour chaque pixel), il est probablement plus rapide de résumer tous les éléments dans chaque colonne. Ils devraient arriver à 600*255, ou 153 000 s'ils sont tous blancs. Alors, trouvez où 153 000 moins le total de la colonne est différent de zéro. Le premier et le dernier seront le haut et le bas de la statue.

Ensuite, répétez sur les rangées pour trouver les extrêmes gauche et droit.

Donc, en commençant par l'image en niveaux de gris, parcourez chaque ligne en totalisant les pixels :

import numpy as np

# Total up all the elements in each column
colsums = np.sum(gray, axis=0)

Les sommes de chaque colonne ressemblent maintenant à ceci :

array([153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 152991, 153000, 152976, 152920,
       152931, 152885, 151600, 148818, 147448, 146802, 146568, 146367,
       146179, 145888, 145685, 145366, 145224, 145066, 144745, 144627,
       144511, 144698, 144410, 144329, 144162, 143970, 143742, 143381,
       141860, 139357, 135358, 133171, 131138, 129246, 128410, 127866,
       127563, 127223, 126475, 125614, 125137, 124848, 122906, 121653,
       119278, 115548, 114473, 113800, 113486, 112655, 112505, 112670,
       111845, 111124, 110378, 110315, 109996, 109693, 109649, 109411,
       110626, 110628, 112247, 112348, 111865, 111571, 110601, 108308,
       107213, 106768, 105546, 103971, 103209, 101866, 100215,  98964,
        98559,  97008,  94981,  94513,  92490,  91555,  91491,  90072,
        88642,  87210,  86960,  86834,  85759,  84496,  83237,  81911,
        80249,  78942,  77715,  76918,  75746,  75826,  75443,  75087,
        75156,  75432,  75730,  75699,  77028,  77825,  76813,  76718,
        75958,  75207,  74216,  73042,  72527,  72043,  71819,  71384,
        70693,  69922,  69537,  69685,  69688,  69876,  69552,  68937,
        68496,  67942,  67820,  67626,  67627,  68113,  68426,  67894,
        67868,  67365,  66191,  65334,  65752,  66438,  66285,  66565,
        67616,  69090,  69386,  69928,  70470,  70318,  70228,  71028,
        71197,  71827,  71712,  71312,  72013,  72878,  73398,  74038,
        75017,  76270,  76087,  75317,  75210,  75497,  75099,  75620,
        75059,  75008,  74146,  73531,  73556,  73927,  75395,  77235,
        77094,  77229,  77463,  77808,  77538,  77104,  76816,  76500,
        76310,  76331,  76889,  76293,  75626,  74966,  74871,  74950,
        74931,  74852,  74885,  75077,  75576,  76104,  76208,  75387,
        74971,  75878,  76311,  76566,  77014,  77205,  77231,  77456,
        77983,  78379,  78793,  78963,  79154,  79710,  80777,  82547,
        85164,  88944,  91269,  92438,  93646,  94836,  96071,  97918,
       100244, 102011, 103553, 104624, 104961, 105354, 105646, 105866,
       106367, 106361, 106461, 106659, 106933, 107055, 106903, 107028,
       107080, 107404, 107631, 108022, 108194, 108261, 108519, 109023,
       109349, 109873, 110373, 110919, 111796, 112587, 113219, 114143,
       115161, 115733, 116531, 117615, 118338, 119414, 120492, 121332,
       122387, 123824, 124938, 126113, 127465, 128857, 130411, 131869,
       133016, 133585, 134442, 135772, 136440, 136828, 137200, 137418,
       137705, 137976, 138167, 138481, 138788, 138937, 139194, 139357,
       139375, 139583, 139924, 140201, 140716, 140971, 141285, 141680,
       141837, 141975, 142260, 142567, 142774, 143154, 143533, 143853,
       144521, 145182, 145832, 147978, 149006, 150026, 151535, 152753,
       152922, 152960, 152990, 152991, 153000, 152995, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000,
       153000, 153000, 153000, 153000, 153000, 153000, 153000, 153000],
      dtype=uint64)

Maintenant, trouvez où ces colonnes ne totalisent pas 153 000 :

np.nonzero(153000-colsums)                                                                 

Cela ressemble à ceci :

(array([156, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
        170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182,
        183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195,
        196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208,
        209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
        222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234,
        235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247,
        248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260,
        261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273,
        274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286,
        287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299,
        300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312,
        313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325,
        326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
        339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351,
        352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364,
        365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377,
        378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390,
        391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403,
        404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416,
        417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429,
        430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442,
        443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455,
        456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 469]),)

Ainsi, la ligne du haut qui ne se compose pas entièrement de pixels blancs est la ligne 156 (la première entrée) et la ligne du bas qui ne se compose pas entièrement de pixels blancs est la ligne 469 (la dernière entrée).

Maintenant, faites la somme sur l'autre axe (axe =1) et répétez la même chose pour obtenir les extrêmes gauche et droit.