图像二值化
简单阈值(OTSU和Triangle)
cv2.threshold
简单阈值
threshold(src, thresh, maxval, type[, dst]) -> retval, dst
- src:表示的是图片源
- thresh:表示的是阈值(起始值)
- maxval:表示的是最大值
- type:表示的是这里划分的时候使用的是什么类型的算法
注意:
- 当选择计算方式(如:cv.THRESH_OTSU)之后,前面所定义的thresh会不起作用
- type参数:图像处理方式 | 阈值计算方法
- 图像处理方式:cv.THRESH_BINARY、cv.THRESH_BINARY_INV、cv.THRESH_TOZERO等
- 阈值计算方法:cv.THRESH_OTSU、cv.THRESH_TRIANGLE
THRESH_BINARY 二进制阈值化 -> 大于阈值为1 小于阈值为0
THRESH_BINARY_INV 反二进制阈值化 -> 大于阈值为0 小于阈值为1
THRESH_TRUNC 截断阈值化 -> 大于阈值为阈值,小于阈值不变
THRESH_TOZERO 阈值化为0 -> 大于阈值的不变,小于阈值的全为0
THRESH_TOZERO_INV 反阈值化为0 -> 大于阈值为0,小于阈值不变
参考链接:
示例
def image_binary(image):
"""图像二值化(简单阈值)"""
image_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 这个函数的第一个参数就是原图像,原图像应该是灰度图。
# 第二个参数就是用来对像素值进行分类的阈值。
# 第三个参数就是当像素值高于(有时是小于)阈值时应该被赋予的新的像素值
# 第四个参数指定阈值类型(图像处理方式 | cv.THRESH_OTSU、cv.THRESH_TRIANGLE表示使用OTSU、TRIANGLE的阈值计算方法)
ret, binary = cv.threshold(image_gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
# ret, binary = cv.threshold(image_gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_TRIANGLE)
# ret, binary = cv.threshold(image_gray, 0, 255, cv.THRESH_TOZERO | cv.THRESH_OTSU)
print("threshold value: %s" % ret)
cv.imshow("threshold_demo", binary)
cv.imshow("image", image)
结果:
示例
def image_threshold(image):
"""显示多种图像二值化方法(简单阈值)"""
image_gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, thresh1 = cv.threshold(image_gray, 127, 255, cv.THRESH_BINARY)
ret, thresh2 = cv.threshold(image_gray, 127, 255, cv.THRESH_BINARY_INV)
ret, thresh3 = cv.threshold(image_gray, 127, 255, cv.THRESH_TRUNC)
ret, thresh4 = cv.threshold(image_gray, 127, 255, cv.THRESH_TOZERO)
ret, thresh5 = cv.threshold(image_gray, 127, 255, cv.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [image, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2, 3, i + 1), plt.imshow(images[i], cmap='gray') # 将图像按2x3铺开,以灰度图的方式显示
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
结果:
注意: plt.imshow默认是带点绿色的图,想要显示灰度图需要传参cmap=‘gray’。
参考链接:
自适应阈值
在前面的部分我们使用是全局阈值,整幅图像采用同一个数作为阈值。
当时这种方法并不适应与所有情况,尤其是当同一幅图像上的不同部分的具有不同亮度时。这种情况下我们需要采用自适应阈值。此时的阈值是根据图像上的每一个小区域计算与其对应的阈值。
因此在同一幅图像上的不同区域采用的是不同的阈值,从而使我们能在亮度不同的情况下得到更好的结果。
cv2.adaptiveThreshold
自适应阈值
adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C[, dst]) -> dst
-
adaptiveMethod:Int类型的,这里有两种选择,不过这两种方法最后得到的结果要减掉参数里面的C值。
- ADAPTIVE_THRESH_MEAN_C(通过平均的方法取得平均值)———阈值取自相邻区域的平均值)
- ADAPTIVE_THRESH_GAUSSIAN_C(通过高斯取得高斯值)———阈值取自相邻区域的加权和
-
thresholdType:同type,见threshold
-
blockSize:Int类型的,这个值来决定像素的邻域块有多大。
-
C:偏移值调整量,计算adaptiveMethod用到的参数。
注意: 这里的blockSize的值要为奇数,否则会给出这样的提示:
Assertion failed (blockSize % 2 == 1 && blockSize > 1) in cv::adaptiveThreshold
参考链接:
示例
def threshold_adaptive(image):
"""图像二值化(自适应阈值)"""
img = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# 中值滤波
img = cv.medianBlur(img, 5)
ret, th1 = cv.threshold(img, 127, 255, cv.THRESH_BINARY)
# 11 为 Block size, 2 为 C 值
th2 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_MEAN_C, cv.THRESH_BINARY, 11, 2)
th3 = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 11, 2)
titles = ['Original Image', 'Global Threshold (v = 127)', 'Adaptive Mean Threshold', 'Adaptive Gaussian Threshold']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2, 2, i + 1), plt.imshow(images[i], cmap='gray')
plt.title(titles[i])
plt.xticks([]), plt.yticks([])
plt.show()
结果:
手动计算阈值
手动将图像二值化
示例
def threshold_custom(image):
"""手动将图像二值化"""
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
h, w = gray.shape[:2]
m = np.reshape(gray, [1, w*h])
mean = m.sum() / (w*h) # 求出整个灰度图像的平均值
print("mean:", mean)
ret, binary = cv.threshold(gray, mean, 255, cv.THRESH_BINARY)
cv.imshow("threshold_custom", binary)
结果:
大图像二值化
示例
def big_image_threshold(image):
"""大图像二值化"""
print(image.shape)
cw = 256 # cw、ch定义分隔的小块的大小
ch = 256
h, w = image.shape[:2]
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# cv.imshow("big_image_gray", gray)
for row in range(0, h, ch): # 分割图片
for col in range(0, w, cw):
roi = gray[row:row+ch, col:col+cw] # 获取ROI(坐标为row,col的256*256的小矩形)
# 对ROI区域进行图像二值化(自适应阈值),127是256/2,将ROI分割成四个小区域分别进行
dst = cv.adaptiveThreshold(roi, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 127, 2)
gray[row:row + ch, col:col + cw] = dst
print(np.std(dst), np.mean(dst)) # 打印ROI区域的标准差和平均值
cv.imwrite("result_big_image.jpg", gray) # 保存图像
结果:
分辨率:1000 * 1398(在windows上显示的宽高,OpenMV里是1398 * 1000)
分辨率:2362 * 3425(在windows上显示的宽高,OpenMV里是3425 * 2362)
对该算法进行优化:
def big_image_threshold_pro(image):
"""优化大图像二值化"""
print(image.shape)
cw = 600 # cw、ch定义分隔的小块的大小
ch = 600
h, w = image.shape[:2]
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# cv.imshow("big_image_gray", gray)
for row in range(0, h, ch): # 分割图片
for col in range(0, w, cw):
roi = gray[row:row + ch, col:col + cw] # 获取ROI(坐标为row,col的256*256的小矩形)
# 对ROI区域进行图像二值化(自适应阈值)
dst = cv.adaptiveThreshold(roi, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, cv.THRESH_BINARY, 299, 2)
if np.std(dst) <= 60:
gray[row:row + ch, col:col + cw] = 255
else:
gray[row:row + ch, col:col + cw] = dst
print(np.std(dst), np.mean(dst)) # 打印ROI区域的标准差和平均值
cv.imwrite("result_big_imagePro.jpeg", gray) # 保存图像
个人优化后结果(与优化前做对比):