我试图根据像素强度将灰度图像分割成三个区域。由于照明和斑点,这项任务比我预期的更具挑战性......
这是原始图像:
明亮区域占据了图像的大部分。在它的下面,有一个灰色区域,黑色区域占据了图像底部的狭窄部分。
多 Otsu 阈值处理不是很有效,因为直方图没有显示尖锐的峰值...
from skimage.filters import threshold_multiotsu
thresholds = threshold_multiotsu(image)
plt.hist(image.ravel(), bins=255)
for thresh in thresholds:
plt.axvline(thresh, color='r')
regions = np.digitize(image, bins=thresholds)
plt.imshow(regions, cmap='jet')
我认为我需要的是具有多个标签的大规模自适应阈值,但我不知道如何实现这一点。
(编辑)
在阈值化之前应用模糊显示出更有希望的结果。
blur = cv2.GaussianBlur(image, (0, 0), 8)
thresholds = threshold_multiotsu(blur)
regions = np.digitize(blur, bins=thresholds)
plt.imshow(regions, cmap='jet')
但是
x > 1100
中的“白色”和“灰色”区域之间的边界仍然不准确。
同时,对模糊图像应用 Sobel 滤波器会返回相当准确的边界。
grad_x = cv2.Sobel(blur, cv2.CV_16S, 1, 0, ksize=3)
grad_y = cv2.Sobel(blur, cv2.CV_16S, 0, 1, ksize=3)
abs_grad_x = cv2.convertScaleAbs(grad_x)
abs_grad_y = cv2.convertScaleAbs(grad_y)
grad = cv2.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0)
plt.imshow(grad, cmap="gray")
那么问题就变成了:如何结合Sobel结果和Otsu结果来获得准确的mask?
使用 k 均值聚类怎么样?如有必要,您可以在聚类之前应用平滑过滤器。下面,我使用了您在聚类之前使用的相同平滑过滤器。我不确定我是否满足了您使用 sobel 和 otsu 的真正要求。只是想提出另一种可能的分割方式。
import numpy as np
import cv2 as cv
img = cv.imread('input.png')
img = cv.GaussianBlur(img, (0, 0), 8)
Z = img.reshape((-1,3))
# convert to np.float32
Z = np.float32(Z)
# define criteria, number of clusters(K) and apply kmeans()
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 3
ret,label,center=cv.kmeans(Z,K,None,criteria,10,cv.KMEANS_RANDOM_CENTERS)
# Now convert back into uint8, and make original image
center = np.uint8(center)
res = center[label.flatten()]
res2 = res.reshape((img.shape))
cv.imwrite('output.png', res2)