我目前正在学习Python和OpenCV,我正在尝试使用轮廓来定位图像中每个形状的中心。但是,当我实现代码时,我会为每个形状获取多个 cx 和 cy 值。理想情况下,我只需要图像中每个闭合形状的一组 cx 和 cy 坐标。我怀疑轮廓算法也在考虑图像中的线条,我想排除这些线条。为了解决这个问题,我尝试通过关注白色来过滤掉这些线条,但我不确定这是否是正确的方法。下面是我正在使用的代码片段:
import os
from PIL import Image
from xml.etree.ElementTree import Element, SubElement, tostring
import cv2 as cv
import numpy as np
from IPython.display import Image as IPImage, display
def extract_color_code(image, x, y):
color = image[y, x]
r, g, b = color
return '#{:02x}{:02x}{:02x}'.format(b, g, r)
def generate_X_Y(image_path):
image = cv.imread(image_path)
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
blur = cv.GaussianBlur(gray, (5, 5), 0)
ret, thresh = cv.threshold(blur, 200, 255, cv.THRESH_BINARY_INV)
cv.imwrite("image2.jpg", thresh)
contours, hierarchies = cv.findContours(thresh, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
blank = np.zeros(thresh.shape[:2], dtype='uint8')
cv.drawContours(blank, contours, -1, (255, 0, 0), 1)
cv.imwrite("Contours.png", blank)
zones = []
for i in contours:
M = cv.moments(i)
if M['m00'] != 0:
cx = int(M['m10'] / M['m00'])
cy = int(M['m01'] / M['m00'])
color = extract_color_code(image, cx, cy)
#print(str(color))
if str(color) == '#ffffff':
#print(str(color))
cv.drawContours(image, [i], -1, (0, 255, 0), 2)
cv.circle(image, (cx, cy), 3, (0, 0, 255), -1)
#cv.putText(image, "center", (cx - 20, cy - 20), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 2)
zones.append({'x': cx, 'y': cy})
#print(f"x: {cx} y: {cy}")
cv.imwrite("image.png", image)
display(IPImage("image.png")) # Display the image in Colab
if __name__ == '__main__':
image_path = '/content/pic_24.png' # Provide the correct path in Colab
generate_X_Y(image_path)
我得到以下输出,有一个我不想要的带有蓝色方块的圆圈。简而言之,我只想要每个 cx 和 cy 。
colab 链接 gcolab 分别找到 cx 和 cy
我很难理解你的代码的逻辑。因为您似乎要做的是提取轮廓(即某些区域周围的线条),然后尝试获取封闭区域的中心。但这不是轮廓。轮廓与封闭区域无关。这是关于线条的。您可能会说,很多时候都是同样的事情,但并不总是如此,正如您所看到的(有些线并不分隔区域)。
加上您以这种方式计算的中心是线的重心,而不是封闭区域的重心。根据您想要做什么,这可能并不重要(甚至可能是您想要的),但了解这一点很重要。例如,如果您的轮廓是德克萨斯州之一,则计算中心将向南移动,因为南部边界更复杂(没有关于南部边界的政治双关语,我听说这确实是一个复杂的问题)美国)比北方的。严格来说,南部沿海边界甚至是一个分形,并且具有无限的长度,因此中心的重量也是无限的。 在你的情况下,这一点更加突出,因为
approx
的事情,这会将北部直线(已经比南部分形线短)总结为只有 2 个点......
所以,如果我正确理解你的目标,我会做的就是专注于连通区域检测
# Binarisation of the image. The color we are interested in (the one of the
# regions) is the background, 26 in your image
# *255 is just so that it is easier to visualize with any image viewer
binRaw=(img==26).astype(np.uint8)*255
# Get the regions image (just for display), the bounding box and area (for
# filtering) and the barycenters of each connected white (26) region
_, outImg, bb, centers = cv2.connectedComponentsWithStats(binRaw)
# Filter out the outer area (x=0) and the background area (here, background=
# contours), and the one with too small area (some isolated pixels)
mask=(bb[:,0]>0) & (bb[:,4]>10) # not touching x=0, and area>10 pixels
mask[0]=False # and we don't want the "black region" (contour)
plt.imshow((outImg*1236).astype(np.uint8), cmap=plt.cm.hsv) # Just to illustrate region separation
# Note the *1236.astype(np.uint8) is just a lazy way to pseudo-randomly shuffle the colors
plt.plot(centers[mask,0], centers[mask,1])
plt.show()
仍然不完美。因为并非所有区域都是凸面,并且非凸面区域的重心可能最终位于该区域之外(想象一下新月:中心在新月之外) 但是,我认为,根据您想要做什么,需要进行一些手动调整,或者需要更深入地重新思考您真正想要的点是什么。