使用 OpenCv 的阈值?

Threshold using OpenCv?

如问题所述,我想对我的图像应用双向自适应阈值技术。也就是说,我想找到邻域中的每个像素值,如果小于或大于邻域的平均值减去一个常数c,则将其设置为255。

以这张图片为例,作为像素的邻域。需要保留的像素区域是第三个和第六个方块的上半部分(从左到右和从上到下)以及八个和十二个方块的上半部分的较暗区域。

显然,这一切都取决于设置的常量值,但理想情况下,与邻域的平均像素值明显不同的区域将被保留。不过,我可以担心自己的调整。

你的问题和评论是矛盾的:保持一切(显着)brighter/darker 比邻域的平均值(+/- 常数)(问题)与保持一切在平均值 +/- 常数之内(评论) .我假设第一个是正确的,我会尽力给出答案。

使用cv2.adaptiveThreshold当然有用;参数化可能很棘手,尤其是考虑到示例图像。首先,让我们看一下输出:

我们看到,给定图像中的强度值范围很小。第三和第六方格的上半部分与它们的邻域并没有什么不同。不太可能在那里找到适当的差异。 #8 和 #12 的上半部分(或 #10 的下半部分)更有可能被发现。

顶行现在显示更多 "global" 参数(blocksize = 151c = 25),底行显示更多 "local" 参数(blocksize = 51c = 5).中间列是比邻域更暗的所有内容(相对于参数),右列是比邻域更亮的所有内容。我们看到,在更多 "global" 的情况下,我们得到了正确的上半部分,但几乎没有 "significant" 较暗的区域。查看更多 "local" 的情况,我们看到一些较暗的区域,但我们找不到完整的 upper-/lower-halfs 问题。那只是因为不同的三角形是如何排列的。

在技术方面:您需要两次调用 cv2.adaptiveThreshold,一次使用 cv2.THRESH_BINARY_INV 模式来查找更暗的所有内容,一次使用 cv2.THRESH_BINARY 模式来查找更亮的所有内容。此外,您必须为两种不同的情况提供 c-c

完整代码如下:

import cv2
from matplotlib import pyplot as plt
from skimage import io          # Only needed for web grabbing images

plt.figure(1, figsize=(15, 10))

img = cv2.cvtColor(io.imread('https://i.stack.imgur.com/dA1Vt.png'), cv2.COLOR_RGB2GRAY)

plt.subplot(2, 3, 1), plt.imshow(img, cmap='gray'), plt.colorbar()

# More "global" parameters
bs = 151
c = 25
img_le = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, bs, c)
img_gt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, bs, -c)
plt.subplot(2, 3, 2), plt.imshow(img_le, cmap='gray')
plt.subplot(2, 3, 3), plt.imshow(img_gt, cmap='gray')

# More "local" parameters
bs = 51
c = 5
img_le = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, bs, c)
img_gt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, bs, -c)
plt.subplot(2, 3, 5), plt.imshow(img_le, cmap='gray')
plt.subplot(2, 3, 6), plt.imshow(img_gt, cmap='gray')

plt.tight_layout()
plt.show()

希望有所帮助 - 不知何故!

-----------------------
System information
-----------------------
Python:      3.8.1
Matplotlib:  3.2.0rc1
OpenCV:      4.1.2
-----------------------

另一种看待这个问题的方法是,当 abs(mean - image) <= c 时,你希望它变成白色,否则你希望它变成黑色。在 Python/OpenCV/Scipy/Numpy 中,我首先使用统一的 51x51 像素块平均滤波器(boxcar 平均)计算局部统一均值(平均值)。如果需要,您可以使用一些加权平均法,例如高斯平均法。然后我计算 abs(mean - image)。然后我使用 Numpy 阈值。注意:您也可以只在 abs(平均图像)结果上使用一个简单的阈值(cv2.threshold)来代替两个 numpy 阈值。

输入:

import cv2
import numpy as np
from scipy import ndimage

# read image as grayscale
# convert to floats in the range 0 to 1 so that the difference keeps negative values
img = cv2.imread('squares.png',0).astype(np.float32)/255.0

# get uniform (51x51 block) average
ave = ndimage.uniform_filter(img, size=51)

# get abs difference between ave and img and convert back to integers in the range 0 to 255
diff = 255*np.abs(ave - img)
diff = diff.astype(np.uint8)

# threshold
# Note: could also just use one simple cv2.Threshold on diff
c = 5
diff_thresh = diff.copy()
diff_thresh[ diff_thresh <= c ] = 255
diff_thresh[ diff_thresh != 255 ] = 0


# view result
cv2.imshow("img", img)
cv2.imshow("ave", ave)
cv2.imshow("diff", diff)
cv2.imshow("threshold", diff_thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()

# save result
cv2.imwrite("squares_2way_thresh.jpg", diff_thresh)


结果: