使用Python OpenCV查找图像中的极端外部点

时间:2019-06-26 22:20:37

标签: python image opencv image-processing computer-vision

我有这个雕像像。

enter image description here

我试图找到雕像上最顶部,最底部,最左侧和最右侧的点。有没有一种方法可以测量每边的边缘以确定雕像上最外面的点?我想获得每一边的(x,y)坐标。我尝试使用cv2.findContours()cv2.drawContours()来获得雕像的轮廓。

import cv2

img = cv2.imread('statue.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

contours = cv2.findContours(gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[0]
cv2.drawContours(img, contours, -1, (0, 200, 0), 3)

cv2.imshow('img', img)
cv2.waitKey()

5 个答案:

答案 0 :(得分:8)

这是一种潜在的方法:

  • 将图像转换为灰度并模糊图像
  • 获取二进制图像的阈值
  • 找到轮廓
  • 获取外部坐标

转换为灰度并模糊图像后,我们阈值以获取二进制图像

现在,我们使用cv2.findContours()查找轮廓。由于OpenCV使用Numpy数组对图像进行编码,因此轮廓只是(x,y)坐标的Numpy数组。我们可以对Numpy数组进行切片,并使用argmin()argmax()来确定像这样的外部左,右,上和下坐标

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

这是结果

  

左:(162,527)

     

右:(463,467)

     

顶部:(250,8)

     

底部:(381,580)

import cv2
import numpy as np

image = cv2.imread('2.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]

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)

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

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()

答案 1 :(得分:3)

这可能是对nathancy's answer的改进,它是大多数代码的来源,也是使用np.argmax的主要思想。因此,请先查看该答案!


由于我们已经有一个来自cv2.threshold的二值化图像,因此输入图像的(白色)背景被设置为零,因此我们可以使用cv2.boundingRect的功能来“点集的右上边界矩形或灰度图像的非零像素” 。该方法返回一个元组(x, y, w, h),其中(x, y)的左上点以及边界矩形的宽度w和高度h。从那里,可以在left图像的相应切片上使用right轻松获得提到的点np.argmaxthresh等。

完整代码如下:

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()

图像输出看起来像是纳坦西的答案。

尽管如此,结果之一还是有些不同:

  

左:(162,527)

     

右:(463,461)(而不是(463,467))

     

顶部:(250,8)

     

底部:(381,580)

如果我们仔细查看thresh图像,我们会发现对于第463列,461 ... 467范围内的所有像素的值均为{ {1}}。因此,对于右边缘,没有唯一的极值。

在nathancy的方法中找到的轮廓255依次保持两个点c(463, 467),这样(463, 461)会首先找到np.argmax。在我的方法中,检查第(463, 467)列是从4630,这样(height of image)会首先找到np.argmax

从我的角度来看,两个(或什至两者之间的所有其他点)都是合适的结果,因为在处理多个极端点时没有其他约束。

至少在使用(463, 461)的一些简短测试中,使用cv2.boundingRect可以节省两行代码,并且执行速度更快。


披露:同样,大多数代码和主要思想都来自nathancy's answer

答案 2 :(得分:3)

与其检查每个元素(并为每个像素使用if语句使CPU停顿),不如将所有元素加总到每一列上可能更快。它们应为600 * 255,如果全部为白色,则应为153,000。因此,然后找到153,000减去列总数的非零值。第一个和最后一个将是雕像的顶部和底部。

然后在各行中重复以找到左右极值。

因此,从灰度图像开始,将每一行向下累积总计像素:

import numpy as np

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

每列的总和现在看起来像这样:

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)

现在找到那些列的总和不超过153,000:

np.nonzero(153000-colsums)                                                                 

看起来像这样:

(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]),)

因此,不完全由白色像素组成的第一行是第156行(第一个条目),不完全由白色像素组成的下一行是469行(最后一个条目)。

现在将另一个轴(轴= 1)上的和求和,并再次执行相同的操作以获取左右极值。

答案 3 :(得分:1)

import cv2
import numpy as np
img = cv2.imread('statue.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY_INV)[1]
sz=thresh.shape
top=divmod(np.flatnonzero(thresh)[0], sz[0])[::-1]
botton=divmod(np.flatnonzero(thresh)[-1], sz[0])[::-1]
thresh=thresh.T
left=divmod(np.flatnonzero(thresh)[0], sz[1])
right=divmod(np.flatnonzero(thresh)[-1], sz[1])
print(left, right, top, botton, sep='\n')

答案 4 :(得分:0)

您不需要昂贵的代码,例如 public class foo { public Guid id { get; set; } } public class foo2 { public Guid id { get; set; } } public IReadOnlyCollection<T> GetDocumentAsync<T>(Guid id) where T: class { var searchResponse = _client.Search<T>(s => s .Query(q => q .Match(m => m .Field(f => f.id) //f.id is not a property of T .Query(id.ToString()) ) ) ); return searchResponse.Documents; } 。您只需要从外到内4个面逐行扫描图像,直到找到第一个非白色像素即可。

从左开始,从左上到左下扫描。如果找不到白色像素,请向右移动1像素,然后再次从上到下移动。找到非白色像素后,即为findContours

对所有方面都做同样的事情。