简单图像分割 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)
我在图像分割/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)