提问者:小点点

简单图像分割OpenCV-分水岭


我在图像分割/OpenCV场景中还是个新手,希望你能帮我。目前,我正在尝试计算这张照片中2种液体的百分比

应该是这样的(作为例子)

我认为opencv分水岭可以帮助我,但我无法做到这一点。我试图手动设置标记,但我得到以下错误:(-215:断言失败)src. type()==CV_8UC3

如果有人能帮我(也许有更好的方法),我将不胜感激

这是我使用的代码:

import cv2
import numpy as np

img = cv2.imread('image001.jpg')
# convert the image to grayscale and blur it slightly
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray", gray)

# read image
#img = cv2.imread('jeep.jpg')
hh, ww = img.shape[:2]
hh2 = hh // 2
ww2 = ww // 2

# define circles
radius = hh2
yc = hh2
xc = ww2

# draw filled circle in white on black background as mask
mask = np.zeros_like(gray)
mask = cv2.circle(mask, (xc,yc), radius, (255,255,255), -1)

# apply mask to image
result = cv2.bitwise_and(gray, mask)
cv2.imshow("Output", result)

ret, thresh = cv2.threshold(result,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
cv2.imshow("ret 1", thresh)

markers = cv2.circle(thresh, (xc,50), 5, 1, -1)
markers = cv2.circle(thresh, (xc,yc+50), 5, 2, -1)
markers = cv2.circle(thresh, (15,15), 5, 3, -1)
cv2.imshow("marker 1", markers)

markers = cv2.watershed(img, markers)
img[markers == -1] = [0,0,255]
cv2.imshow("watershed", markers)
cv2.waitKey(0)

共1个答案

匿名用户

首先,您会获得一个异常,因为OpenCV的流域()函数期望标记数组由32位整数组成。来回转换将消除错误:

markers = markers.astype(np.int32)
markers = cv2.watershed(img, markers)
markers = markers.astype(np.uint8)

但是,如果您现在执行代码,您会发现结果不是很好,分水岭算法会将液体区域与不需要的区域合并。为了使您的代码像这样工作,您应该为图像中的每个特征提供一个标记。这将是非常不切实际的。

让我们首先提取我们感兴趣的图像区域,即两个液体圆圈。您已经尝试过进行掩蔽操作,我在下面的代码中通过使用OpenCV的HoughCircle()函数自动检测圆圈来改进它。然后分水岭算法将需要一个图像,每个区域有一个标记。我们将用零填充标记图像,在每个液体区域放置一个标记,一个标记作为背景。将所有这些放在一起,我们获得了下面的代码和结果。

考虑到你的图像质量(反射,压缩伪影等),我个人认为结果相当好,我不确定另一种方法会给你更好的。另一方面,如果你能得到质量更好的图像,基于颜色空间的分割方法可能更合适(正如Christoph Rackwitz所指出的)。

import cv2
import numpy as np

img = cv2.imread('image001.jpg')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Detect circle
circles = cv2.HoughCircles(img_gray, cv2.HOUGH_GRADIENT, 1.3, 100)

# only one circle is detected
circle = circles[0,0,:]
center_x, center_y, radius = circles[0,0,0], circles[0,0,1], circles[0,0,2]

img_circle = img.copy()
cv2.circle(img_circle, (center_x, center_y), int(radius), (0, 255, 0), 3)
cv2.imshow("circle", img_circle)

# build mask for this circle
mask = np.zeros(img_gray.shape, np.uint8)
cv2.circle(mask, (center_x, center_y), int(radius), 255, -1)

img_masked = img.copy()
img_masked[mask == 0] = 0
cv2.imshow("image-masked", img_masked)

# create markers
markers = np.zeros(img_gray.shape, np.int32)
markers[10, 10] = 1                                        # background marker
markers[int(center_y - radius*0.9), int(center_x)] = 100   # top liquid
markers[int(center_y + radius*0.9), int(center_x)] = 200   # bottom liquid

# do watershed
markers = cv2.watershed(img_masked, markers)
cv2.imshow("watershed", markers.astype(np.uint8))

cv2.waitKey(0)