python学opencv|读取图像(三十八 )阈值自适应处理
【1】引言
前序学习了5种阈值处理方法,包括(反)阈值处理、(反)零值处理和截断处理,相关文章链接为:
python学opencv|读取图像(三十三)阈值处理-灰度图像-CSDN博客
python学opencv|读取图像(三十四)阈值处理-彩色图像-CSDN博客
python学opencv|读取图像(三十五)反阈值处理-CSDN博客
python学opencv|读取图像(三十六)(反)零值处理-CSDN博客
python学opencv|读取图像(三十七 )截断处理-CSDN博客
经过对比,会发现零值处理获得的图像效果更为明显。
但实际上,无论是哪种处理方法,内部的阈值开关都是自由设定的,因此,难以确认零值处理是否效果总是相对较好?
这时候,我们刚好发现了一个自适应处理函数:cv.adaptiveThreshold(),这个函数允许图像按照方块大小,逐个做阈值处理,相当于是更为精细化的阈值处理方式。
【2】官网教程
点击下述链接,可以直达cv.adaptiveThreshold()函数的官网教程:
OpenCV: Miscellaneous Image Transformations
在官网,可以看到cv.adaptiveThreshold()函数的参数说明:
图1
具体的参数解释为:
void cv::adaptiveThreshold ( InputArray src, #输入图像
OutputArray dst, #输出图像
double maxValue, #阈值上限
int adaptiveMethod, #自适应方法
int thresholdType, #阈值处理方法
int blockSize, #方块大小
double C ) #均值或者加权均值减去的常量,一般是正整数
官网指出,自适应放大法adaptiveMethod只有两种选择:cv.THRESH_BINARY和cv.THRESH_BINARY_INV。
【3】代码测试
cv.adaptiveThreshold()函数只能对灰度图进行自适应处理,这里我们先回忆一下灰度图生成技巧:
python学opencv|读取图像(十一)彩色图像转灰度图的两种办法_opencv读取png图片为灰度图片-CSDN博客
因此我们在设计程序时,也设计了两种灰度图转化方法,以作为对前述学习知识的复习:
import cv2 as cv # 引入CV模块
import numpy as np #引入numpy模块
# 读取图片-直接转化灰度图
src = cv.imread('srcf.png',0) #读取图像
dst=src#输出图像
# 读取图片-函数转化灰度图
src1 = cv.imread('srcf.png') #读取图像
dst1=cv.cvtColor(src1,cv.COLOR_BGR2GRAY) #转化为灰度图
dstt=np.hstack((dst,dst1)) #两种灰度图拼接在一起
由于我们已经知晓了这两种方法转化后的图像效果一样,因此直接进行自适应处理,并且把零值处理加进来做对比:
#自适应处理
dstt1=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,5,3) #
dstt2=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,5,3) #
dsttv1=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY_INV,5,3) #
dsttv2=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY_INV,5,3) #
#零值处理
t2,dst2=cv.threshold(src,58,158,cv.THRESH_TOZERO) #零值-阈值开关58,阈值上限158
#dstt3=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_TOZERO,5,3) #
dsttt=np.hstack((dstt1,dstt2)) #两种灰度图拼接在一起
dstttv=np.hstack((dsttv1,dsttv2)) #两种灰度图拼接在一起8,cv.THRESH_TOZERO) #零值-阈值开关58,阈值上限158
然后对图像进行呈现和保存:
#阈值和零值处理
#t1,dst1=cv.threshold(src,58,158,cv.THRESH_BINARY) #阈值-阈值开关58,阈值上限158
#t2,dst2=cv.threshold(src,58,158,cv.THRESH_TOZERO) #零值-阈值开关100,阈值上限255
#截断处理
#t3,dst3=cv.threshold(src,58,158, cv.THRESH_TRUNC) #截断-阈值开关158,阈值上限200
#t2,dst2=cv.threshold(src,100,255,cv.THRESH_TOZERO) #阈值开关100,阈值上限255
#t3,dst3=cv.threshold(src,0,255,cv.THRESH_TOZERO) #阈值开关0,阈值上限255
#反阈值和反零值处理
#tt1,dstt1=cv.threshold(src,58,158,cv.THRESH_BINARY_INV) #阈值开关100,阈值上限255
#tt2,dstt2=cv.threshold(src,58,158,cv.THRESH_TOZERO_INV) #阈值开关100,阈值上限255
#tt1,dstt1=cv.threshold(src,58,158,cv.THRESH_TOZERO_INV) #阈值开关58,阈值上限158
#tt2,dstt2=cv.threshold(src,100,255,cv.THRESH_TOZERO_INV) #阈值开关100,阈值上限255
#tt3,dstt3=cv.threshold(src,0,255,cv.THRESH_TOZERO_INV) #阈值开关0,阈值上限255
#和原图对比
#ttt1=np.hstack((dst,dst1,dst3)) #原图-阈值-截断对比
#ttt2=np.hstack((dst,dst2,dst3)) #原图-零值-截断对比
#ttt3=np.hstack((dst,dstt1,dst3)) #原图-反阈值-截断对比
#ttt4=np.hstack((dst,dstt2,dst3)) #原图-反零值-截断对比
#ttt5=np.hstack((dst,dst1,dst2)) #原图-阈值-零值对比
#ttt6=np.hstack((dst3,dstt1,dstt2)) #截断-反阈值-反零值对比
#ttt7=np.vstack((ttt5,ttt6)) #原图-阈值-零值-截断-反阈值-反零值对比
#展示图像
#cv.imshow('srcft0', dst) # 在屏幕展示效果
cv.imshow('srcft', dstt) # 在屏幕展示效果
cv.imshow('srcft2', dst2) # 在屏幕展示效果
cv.imshow('srcftt1', dsttt) # 在屏幕展示效果
cv.imshow('srcftt2', dstttv) # 在屏幕展示效果
#cv.imshow('srcfttv1', dsttv1) # 在屏幕展示效果
#cv.imshow('srcfttv2', dsttv2) # 在屏幕展示效果
#cv.imshow('srcftt3', dstt3) # 在屏幕展示效果
#cv.imshow('srcft3', ttt3) # 在屏幕展示效果
#cv.imshow('srcft4', ttt4) # 在屏幕展示效果
#cv.imshow('srcft5', ttt7) # 在屏幕展示效果
#显示BGR值
#print("原图-dst像素数为[100,100]位置处的BGR=", dst[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("阈值-dst1像素数为[100,100]位置处的BGR=", dst1[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("零值-dst2像素数为[100,100]位置处的BGR=", dst2[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("截断-dst3像素数为[100,100]位置处的BGR=", dst3[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dstt1像素数为[100,100]位置处的BGR=", dstt1[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dsttv1像素数为[100,100]位置处的BGR=", dsttv1[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dstt2像素数为[100,100]位置处的BGR=", dstt2[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dsttv2像素数为[100,100]位置处的BGR=", dsttv2[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("dstt3像素数为[100,100]位置处的BGR=", dstt3[100, 100]) # 获取像素数为[100,100]位置处的BGR
#保存图像
cv.imwrite('srcf-dstt.png', dstt) # 保存图像
cv.imwrite('srcf-dst1.png', dst2) # 保存图像
cv.imwrite('srcf-dsttt.png', dsttt) # 保存图像
cv.imwrite('srcf-dstttv.png', dstttv) # 保存图像
#cv.imwrite('srcf-ttt1.png', dsttv1) # 保存图像
#cv.imwrite('srcf-ttt2.png', dsttv2) # 保存图像
#cv.imwrite('srcf-ttt3.png', dstt3) # 保存图像
#cv.imwrite('srcf-JC-t3-VC-ttt3.png', ttt3) # 保存图像
#cv.imwrite('srcf-JC-t3-VC-ttt4.png', ttt4) # 保存图像
#cv.imwrite('srcf-JC-t3-VC-ttt7.png', ttt7) # 保存图像
cv.waitKey() # 图像不会自动关闭
cv.destroyAllWindows() # 释放所有窗口
此处使用的原始图像为:
图2
转化后的两种灰度图为:
图3 两种方法转化的灰度图
按照自适应转化后的图像为:
图4 左cv.ADAPTIVE_THRESH_MEAN_C右cv.ADAPTIVE_THRESH_GAUSSIAN_C-cv.THRESH_BINARY阈值处理方法
图4显示了cv.ADAPTIVE_THRESH_MEAN_C和cv.ADAPTIVE_THRESH_GAUSSIAN_C应用cv.THRESH_BINARY阈值处理方法获得的两种图像,实际上看不出显著差别。
图5 左cv.ADAPTIVE_THRESH_MEAN_C右cv.ADAPTIVE_THRESH_GAUSSIAN_C-cv.THRESH_BINARY_INV阈值处理方法
图5显示了cv.ADAPTIVE_THRESH_MEAN_C和cv.ADAPTIVE_THRESH_GAUSSIAN_C应用cv.THRESH_BINARY_INV阈值处理方法获得的两种图像,实际上也看不出显著差别。
图4和图5相对来说,因为是按照相反的阈值处理方法,所以在颜色黑白上有明显的差别,相对来说,图4代表的阈值处理方法保存的细节相对更多。
之后大家一起看一下零值处理的图像:
图6 零值处理
相对来说,零值处理保留的细节多,自适应处理保留轮廓更多。
此时的完整代码为(下述代码附带了很多注释,调整“#”出现的位置,可以用来辅助测试其他阈值处理方法):
import cv2 as cv # 引入CV模块
import numpy as np #引入numpy模块
# 读取图片-直接转化灰度图
src = cv.imread('srcf.png',0) #读取图像
dst=src#输出图像
# 读取图片-函数转化灰度图
src1 = cv.imread('srcf.png') #读取图像
dst1=cv.cvtColor(src1,cv.COLOR_BGR2GRAY) #转化为灰度图
dstt=np.hstack((dst,dst1)) #两种灰度图拼接在一起
#自适应处理
dstt1=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,5,3) #
dstt2=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,5,3) #
dsttv1=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY_INV,5,3) #
dsttv2=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY_INV,5,3) #
#零值处理
t2,dst2=cv.threshold(src,58,158,cv.THRESH_TOZERO) #零值-阈值开关58,阈值上限158
#dstt3=cv.adaptiveThreshold(src,158,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_TOZERO,5,3) #
dsttt=np.hstack((dstt1,dstt2)) #两种灰度图拼接在一起
dstttv=np.hstack((dsttv1,dsttv2)) #两种灰度图拼接在一起
#阈值和零值处理
#t1,dst1=cv.threshold(src,58,158,cv.THRESH_BINARY) #阈值-阈值开关58,阈值上限158
#t2,dst2=cv.threshold(src,58,158,cv.THRESH_TOZERO) #零值-阈值开关100,阈值上限255
#截断处理
#t3,dst3=cv.threshold(src,58,158, cv.THRESH_TRUNC) #截断-阈值开关158,阈值上限200
#t2,dst2=cv.threshold(src,100,255,cv.THRESH_TOZERO) #阈值开关100,阈值上限255
#t3,dst3=cv.threshold(src,0,255,cv.THRESH_TOZERO) #阈值开关0,阈值上限255
#反阈值和反零值处理
#tt1,dstt1=cv.threshold(src,58,158,cv.THRESH_BINARY_INV) #阈值开关100,阈值上限255
#tt2,dstt2=cv.threshold(src,58,158,cv.THRESH_TOZERO_INV) #阈值开关100,阈值上限255
#tt1,dstt1=cv.threshold(src,58,158,cv.THRESH_TOZERO_INV) #阈值开关58,阈值上限158
#tt2,dstt2=cv.threshold(src,100,255,cv.THRESH_TOZERO_INV) #阈值开关100,阈值上限255
#tt3,dstt3=cv.threshold(src,0,255,cv.THRESH_TOZERO_INV) #阈值开关0,阈值上限255
#和原图对比
#ttt1=np.hstack((dst,dst1,dst3)) #原图-阈值-截断对比
#ttt2=np.hstack((dst,dst2,dst3)) #原图-零值-截断对比
#ttt3=np.hstack((dst,dstt1,dst3)) #原图-反阈值-截断对比
#ttt4=np.hstack((dst,dstt2,dst3)) #原图-反零值-截断对比
#ttt5=np.hstack((dst,dst1,dst2)) #原图-阈值-零值对比
#ttt6=np.hstack((dst3,dstt1,dstt2)) #截断-反阈值-反零值对比
#ttt7=np.vstack((ttt5,ttt6)) #原图-阈值-零值-截断-反阈值-反零值对比
#展示图像
#cv.imshow('srcft0', dst) # 在屏幕展示效果
cv.imshow('srcft', dstt) # 在屏幕展示效果
cv.imshow('srcft2', dst2) # 在屏幕展示效果
cv.imshow('srcftt1', dsttt) # 在屏幕展示效果
cv.imshow('srcftt2', dstttv) # 在屏幕展示效果
#cv.imshow('srcfttv1', dsttv1) # 在屏幕展示效果
#cv.imshow('srcfttv2', dsttv2) # 在屏幕展示效果
#cv.imshow('srcftt3', dstt3) # 在屏幕展示效果
#cv.imshow('srcft3', ttt3) # 在屏幕展示效果
#cv.imshow('srcft4', ttt4) # 在屏幕展示效果
#cv.imshow('srcft5', ttt7) # 在屏幕展示效果
#显示BGR值
#print("原图-dst像素数为[100,100]位置处的BGR=", dst[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("阈值-dst1像素数为[100,100]位置处的BGR=", dst1[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("零值-dst2像素数为[100,100]位置处的BGR=", dst2[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("截断-dst3像素数为[100,100]位置处的BGR=", dst3[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dstt1像素数为[100,100]位置处的BGR=", dstt1[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dsttv1像素数为[100,100]位置处的BGR=", dsttv1[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dstt2像素数为[100,100]位置处的BGR=", dstt2[100, 100]) # 获取像素数为[100,100]位置处的BGR
print("dsttv2像素数为[100,100]位置处的BGR=", dsttv2[100, 100]) # 获取像素数为[100,100]位置处的BGR
#print("dstt3像素数为[100,100]位置处的BGR=", dstt3[100, 100]) # 获取像素数为[100,100]位置处的BGR
#保存图像
cv.imwrite('srcf-dstt.png', dstt) # 保存图像
cv.imwrite('srcf-dst1.png', dst2) # 保存图像
cv.imwrite('srcf-dsttt.png', dsttt) # 保存图像
cv.imwrite('srcf-dstttv.png', dstttv) # 保存图像
#cv.imwrite('srcf-ttt1.png', dsttv1) # 保存图像
#cv.imwrite('srcf-ttt2.png', dsttv2) # 保存图像
#cv.imwrite('srcf-ttt3.png', dstt3) # 保存图像
#cv.imwrite('srcf-JC-t3-VC-ttt3.png', ttt3) # 保存图像
#cv.imwrite('srcf-JC-t3-VC-ttt4.png', ttt4) # 保存图像
#cv.imwrite('srcf-JC-t3-VC-ttt7.png', ttt7) # 保存图像
cv.waitKey() # 图像不会自动关闭
cv.destroyAllWindows() # 释放所有窗口
【4】细节说明
自适应处理函数cv.adaptiveThreshold():
只能处理单通道的灰度图;
阈值处理方法只能选用cv.THRESH_BINARY和cv.THRESH_BINARY_INV两种阈值处理方法。
图7
【5】总结
掌握了python+opencv实现图像自适应处理的技巧。