简单图像分割 OpenCV-Watershed

Simple Image Segmentation OpenCV-Watershed

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

应该是这样的(举个例子)

我认为 opencv watershed 可以帮助我,但我做不好。我正在尝试手动设置标记,但出现以下错误:(-215:Assertion failed) src.type() == CV_8UC3 && dst.type() == CV_32SC1 in function 'cv::watershed' (可能我的标记全错了)

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

这是我使用的代码:

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)

首先,你得到一个异常,因为OpenCV的watershed()函数expects markers array to be made of 32-bit integers。来回转换将消除错误:

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

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

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

考虑到您的图像质量(反射、压缩伪影等),我个人认为结果非常好,我不确定另一种方法是否会比这更好。另一方面,如果你能得到更好质量的图像,基于颜色space的分割方法可能更合适(正如所指出的)。

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)