如何从OpenCV中已标记的图像中获取区域属性?

时间:2019-05-14 18:20:01

标签: python opencv image-segmentation connected-components labeling

我正在OpenCV中使用分水岭算法来标记图像(类似于本教程:https://docs.opencv.org/3.4/d3/db4/tutorial_py_watershed.html),以便最后获得一个标签数组,其中每个区域都具有与其标签相对应的整数值。现在,我想获取每个区域的边界框和区域的坐标。

我知道使用skimage.measure.regionprops()很容易做到这一点,但是考虑到执行速度,我希望在不导入skimage的情况下实现这一点,理想情况下直接使用OpenCV。

我尝试使用cv2.connectedComponentsWithStats(),但它似乎仅在图像为二进制时有效,而不是在标签已定义的情况下。

我尝试对标记的图像进行二值化处理,然后按如下所示用connectedComponentsWithStats()重新标记(请注意,在这种情况下背景的标签为1,我想将其删除):

segmented = cv2.watershed(image.astype('uint8'), markers)

segmented_bin = segmented.copy()
segmented_bin[segmented < 2] = 0
segmented_bin[segmented > 1] = 255
num_labels, label_image, stats, centroids = cv2.connectedComponentsWithStats(segmented_bin.astype('uint8'), 4, cv2.CV_32S)

但是这种方法会合并没有被背景分开的区域,这不是理想的效果。

本质上,我想知道是否有类似于connectedComponentsWithStats()的功能来处理已标记的图像?

2 个答案:

答案 0 :(得分:2)

由于(如果我没记错的话)每个标签都代表一个连续的区域,因此我们可以遍历所有非背景标签。

for i in range(2, marker_count + 1):

对于每个标签,我们可以使用numpy.where创建一个相应的二进制掩码(带有该标签的像素变为255,其他所有像素变为0)。

    mask = np.where(segmented==i, np.uint8(255), np.uint8(0))

由于cv2.boundingRect也可以处理单通道图像,因此我们可以使用它直接从蒙版确定边界框。

    x,y,w,h = cv2.boundingRect(mask)

标签的面积只是具有给定标签的像素的计数(即,蒙版中所有非零像素)。我们可以简单地使用cv2.countNonZero。既然我们已经知道边界框,那么只处理相应的ROI就可以节省一些工作。

    area = cv2.countNonZero(mask[y:y+h,x:x+w])

我们完成了。

    print "Label %d at (%d, %d) size (%d x %d) area %d pixels" % (i,x,y,w,h,area)

控制台输出

Label 2 at (41, 14) size (47 x 49) area 1747 pixels
Label 3 at (111, 30) size (48 x 47) area 1719 pixels
Label 4 at (71, 51) size (56 x 48) area 1716 pixels
Label 5 at (152, 61) size (48 x 47) area 1676 pixels
Label 6 at (25, 75) size (47 x 48) area 1719 pixels
Label 7 at (109, 76) size (49 x 49) area 1748 pixels
Label 8 at (192, 82) size (49 x 48) area 1774 pixels
Label 9 at (64, 97) size (48 x 49) area 1695 pixels
Label 10 at (1, 114) size (47 x 48) area 1720 pixels
Label 11 at (139, 114) size (52 x 48) area 1727 pixels
Label 12 at (97, 132) size (48 x 48) area 1745 pixels
Label 13 at (181, 133) size (48 x 47) area 1667 pixels
Label 14 at (41, 140) size (47 x 48) area 1733 pixels
Label 15 at (129, 167) size (45 x 47) area 1666 pixels
Label 16 at (5, 169) size (50 x 48) area 1713 pixels
Label 17 at (72, 176) size (46 x 48) area 1745 pixels
Label 18 at (171, 177) size (50 x 49) area 1772 pixels
Label 19 at (35, 205) size (46 x 47) area 1702 pixels
Label 20 at (106, 207) size (55 x 49) area 1909 pixels
Label 21 at (155, 219) size (43 x 47) area 1537 pixels
Label 22 at (65, 237) size (51 x 48) area 1713 pixels
Label 23 at (25, 251) size (50 x 49) area 1818 pixels
Label 24 at (108, 264) size (48 x 47) area 1730 pixels
Label 25 at (155, 264) size (46 x 47) area 1711 pixels

图片

  • 输入
    Input image
  • 彩色标签
    Colored labels
  • 带标签的边框
    Labeled bounding boxes

完整的脚本

import numpy as np
import cv2

# START of original watershed example
# from https://docs.opencv.org/3.4/d3/db4/tutorial_py_watershed.html

img = cv2.imread('water_coins.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

# noise removal
kernel = np.ones((3,3),np.uint8)
opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2)
# sure background area
sure_bg = cv2.dilate(opening,kernel,iterations=3)
# Finding sure foreground area
dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5)
ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0)
# Finding unknown region
sure_fg = np.uint8(sure_fg)
unknown = cv2.subtract(sure_bg,sure_fg)

# Marker labelling
marker_count, markers = cv2.connectedComponents(sure_fg)
# Add one to all labels so that sure background is not 0, but 1
markers = markers+1
# Now, mark the region of unknown with zero
markers[unknown==255] = 0

segmented = cv2.watershed(img,markers)

# END of original watershed example

output = np.zeros_like(img)
output2 = img.copy()

# Iterate over all non-background labels
for i in range(2, marker_count + 1):
    mask = np.where(segmented==i, np.uint8(255), np.uint8(0))
    x,y,w,h = cv2.boundingRect(mask)
    area = cv2.countNonZero(mask[y:y+h,x:x+w])
    print "Label %d at (%d, %d) size (%d x %d) area %d pixels" % (i,x,y,w,h,area)

    # Visualize
    color = np.uint8(np.random.random_integers(0, 255, 3)).tolist()
    output[mask!=0] = color
    cv2.rectangle(output2, (x,y), (x+w,y+h), color, 1)
    cv2.putText(output2,'%d'%i,(x+w/4, y+h/2), cv2.FONT_HERSHEY_SIMPLEX, 0.4, color, 1, cv2.LINE_AA)

cv2.imwrite('wshseg_colors.png', output)
cv2.imwrite('wshseg_boxes.png', output2)

答案 1 :(得分:0)

如果其他人有兴趣,由于无法获得void Game::drawGame(int fruitxpos, int fruitypos, std::vector<int>& xposition, std::vector<int>& yposition, int snakesize){ system("cls"); int printedflag = 0; for(int j = 1; j <= ysize; j++){ if(j == 1 || j == ysize){ for(int i = 1; i <= xsize; i++){ std::cout << "#"; } std::cout << "\n"; } else{ for(int i = 1; i <= xsize; i++){ if(i == 1 || i == xsize){ std::cout << "#"; } else{ for(int n = 0; n <= snakesize; n++){ if(i == xposition[n] && j == yposition[n]){ std::cout << "o"; printedflag = 1; } else{ printedflag = 0; } } if(!printedflag){ if(i == fruitxpos && j == fruitypos){ std::cout << "F"; } else{ std::cout << " "; } } } } std::cout << "\n"; } } } ,我最终返回了skimage.measure.regionprops()。每个图像的时间开销只有几十毫秒。