图像处理的过程中,有时候为了加快处理速度,可以容忍一定的信息丢失,这时候就需要用到图像的二值化处理,opencv提供了一些图像二值化的API

1. 简单二值化

简单的二值化处理为,输入图像的灰度图数据,如果像素值大于阈值,则会为其指定一个值(可能为白色),否则将为其指定另一个值(可能为黑色),使用的函数为

double cv::threshold    (    
InputArray     src, //输入的灰度图
OutputArray     dst, //输出图
double     thresh,      //像素值阈值
double     maxval,      // 当阈值类型为THRESH_BINARY和THRESH_BINARY_INV时的最大值
int     type         //二值化类型
)    

二值化类型
image.png
二值化类型图解
image.png
示例代码

import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('test.jpg',0)
ret,thresh1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
ret,thresh2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
ret,thresh3 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
ret,thresh4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
ret,thresh5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
titles = ['Original Image','BINARY','BINARY_INV','TRUNC','TOZERO','TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(images[i],'gray')
    plt.title(titles[i])
    plt.xticks([]),plt.yticks([])
plt.show()

py版本的cv2.threshold返回值有两个,其中第二个参数是本次二值化所使用的阈值(上表的最后两种二值化类型是根据图像计算阈值的),第一个是二值化结果。结果:
image.png

2. 自适应二值化

在简单二值化中,我们使用全局参数作为阈值。但是,假如图像各部分光照情况不同,可能会丢失掉很多信息,得到不好的结果。在这种情况下,我们采用自适应阈值。在该算法中,对图像中的一个小区域进行阈值计算。因此,对于同一图像的不同区域,就得到了不同的阈值,对于光照不同的图像,自适应二值化能得到更好的结果。


void cv::adaptiveThreshold    (    
InputArray     src,       //8位单通道图像数据
OutputArray     dst,       //输出图像
double     maxValue,          //当condition满足时目标像素点的值,非零
int     adaptiveMethod,    //自适应算法
int     thresholdType,     //二值化类型,见上一小节中的说明,此处只能取值THRESH_BINARY或THRESH_BINARY_INV
int     blockSize,         //用来计算自适应阈值的块大小,如3、5、7...
double     C //从平均数或加权平均数中减去的常数。通常情况下,它是正的,但也可以是零,也可以是负的。
)    

自适应算法:
|算法名|说明|
|-|-|
|ADAPTIVE_THRESH_MEAN_C |自适应阈值 T(x,y)=(x,y)周边blockSizeblockSize范围内点像素值的平均值 - C|
|ADAPTIVE_THRESH_GAUSSIAN_C |自适应阈值 T(x,y)=(x,y)周边blockSize
blockSize范围内点像素值基于高斯窗口的加权平均数 - C|

示例代码:

import cv2
import numpy as np
from matplotlib import pyplot as plt

def testAdaptiveThreshold():
    img = cv2.imread('test.jpg', 0)
    img = cv2.medianBlur(img, 5)
    ret, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
    th2 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
                                cv2.THRESH_BINARY, 11, 2)
    th3 = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                cv2.THRESH_BINARY, 11, 2)
    titles = ['Original Image', 'Global Thresholding (v = 127)',
              'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
    images = [img, th1, th2, th3]
    for i in range(4):
        plt.subplot(2, 2, i + 1), plt.imshow(images[i], 'gray')
        plt.title(titles[i])
        plt.xticks([]), plt.yticks([])
    plt.show()

image.png
效果非常明显,很好的提取了轮廓部分

3. 大津法二值化

大津法二值化,本身也是一种固定阈值的二值化,只不过其阈值是根据图像直方图来计算的,这种方法对于直方图有两个波峰的图像效果比较好。
使用cv2.Threshold()函数,二值化类型传cv2.THRESH_Otsu,阈值传零即可。该算法会自动找到最优阈值,并将找到的阈值作为第二个返回值返回。如果未使用Otsu阈值,则retval与所使用的阈值相同。
下面的示例,输入图像是一个有噪声的图像。第一种情况,使用全局阈值127;第二种情况,直接使用Otsu的阈值;第三种情况,使用5x5高斯核过滤图像以去除噪声,然后应用Otsu阈值。噪声过滤结合大津法二值化,很好的优化了结果。

import cv2
import numpy as np
from matplotlib import pyplot as plt

def testOtsu():
    img = cv2.imread('noisy2.png', 0)
    # global thresholding
    ret1, th1 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
    # Otsu's thresholding
    ret2, th2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    # Otsu's thresholding after Gaussian filtering
    blur = cv2.GaussianBlur(img, (5, 5), 0)
    ret3, th3 = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
    # plot all the images and their histograms
    images = [img, 0, th1,
              img, 0, th2,
              blur, 0, th3]
    titles = ['Original Noisy Image', 'Histogram', 'Global Thresholding (v=127)',
              'Original Noisy Image', 'Histogram', "Otsu's Thresholding",
              'Gaussian filtered Image', 'Histogram', "Otsu's Thresholding"]
    for i in range(3):
        plt.subplot(3, 3, i * 3 + 1), plt.imshow(images[i * 3], 'gray')
        plt.title(titles[i * 3]), plt.xticks([]), plt.yticks([])
        plt.subplot(3, 3, i * 3 + 2), plt.hist(images[i * 3].ravel(), 256)
        plt.title(titles[i * 3 + 1]), plt.xticks([]), plt.yticks([])
        plt.subplot(3, 3, i * 3 + 3), plt.imshow(images[i * 3 + 2], 'gray')
        plt.title(titles[i * 3 + 2]), plt.xticks([]), plt.yticks([])
    plt.show()

image.png

☞ 参与评论