python3-PIL二值化

发布时间:2021-09-13 23:44:59编辑:run阅读(7365)

    这是一种点操作,通过将阀值以下的所有像素变为0,阀值以上的所有像素变为1,从灰度级的图像创建二值图像。如果g(x,y)是f(x,y)在全局阀值T处的二值函数,则可以表示为:g(x,y)=1,f(x,y)>T   0,其他.


    为什么需要转化为二值图像?主要是这样一些原因:可能对将图像分为前景和背景感兴趣;图像需要用黑白打印机打印出来(所有灰色阴影只需要用黑白圆点表示);需要用形态学操作对图像进行预处理,等等。


    固定阀值的二值化,使用PIL的point()函数以固定阀值进行二值化处理。

    from PIL import Image
    import matplotlib.pylab as pylab
    import numpy as np
    
    pylab.rcParams['font.sans-serif'] = ['KaiTi']
    pylab.rcParams['axes.unicode_minus'] = False
    im = Image.open(r'D:\image_processing\image4\4.jpg').convert('L')
    pylab.hist(np.array(im).ravel(), bins=256, range=(0, 256), color='g')
    pylab.xlabel('像素值', size=20)
    pylab.ylabel('频率', size=20)
    pylab.title('像素值的直方图', size=20)
    pylab.show()

    image.png


    阀值不同的效果,不同灰度阀值的二值图像的阴影处理不得当,导致了人工痕迹显著的伪轮廓。

    from PIL import Image
    import matplotlib.pylab as pylab
    import numpy as np
    
    pylab.rcParams['font.sans-serif'] = ['KaiTi']
    pylab.rcParams['axes.unicode_minus'] = False
    def plot_image(image, title=''):
        pylab.title(title, size=20)
        pylab.imshow(image)
        pylab.axis('off')
    im = Image.open(r'D:\image_processing\image4\4.jpg').convert('L')
    pylab.hist(np.array(im).ravel(), bins=256, range=(0, 256), color='g')
    pylab.xlabel('像素值', size=20)
    pylab.ylabel('频率', size=20)
    pylab.title('像素值的直方图', size=20)
    pylab.show()
    pylab.figure(figsize=(12, 18))
    pylab.gray()
    pylab.subplot(221)
    plot_image(im, '原始图像')
    th = [0, 50, 100, 150, 200]
    for i in range(2, 5):
        im1 = im.point(lambda x:x > th[i])
        pylab.subplot(2, 2, i)
        plot_image(im1, f'阀值为{str(th[i])}的二值图像')
    pylab.show()

    image.png


    半色调二值化,在阀值化(二值化)中,一种减少伪轮廓的方法是在量化前对输入图像加入均匀分布的白噪声。具体的做法是,对于灰度图像的每个输入像素f(x,y),添加一个独立的均匀分布于[-128,128]内的随机数,然后进行二值化处理,这种技术称为半色调二值化.

    from PIL import Image
    import matplotlib.pylab as pylab
    import numpy as np
    
    pylab.rcParams['font.sans-serif'] = ['KaiTi']
    pylab.rcParams['axes.unicode_minus'] = False
    def plot_image(image, title=''):
        pylab.title(title, size=20)
        pylab.imshow(image)
        pylab.axis('off')
    im = Image.open(r'D:\image_processing\image4\4.jpg').convert('L')
    im = Image.fromarray(np.clip(im + np.random.randint(-128, 128,
    (im.height, im.width)), 0, 255).astype(np.uint8))
    pylab.figure(figsize=(20, 15))
    pylab.subplot(221)
    plot_image(im, "带有噪声的原始图像")
    th = [0, 50, 100, 150, 200]
    for i in range(2, 5):
        im1 = im.point(lambda x:x > th[i])
        pylab.subplot(2, 2, i)
        plot_image(im1, f'阀值为{str(th[i])}的二值图像')
    pylab.show()

    image.png


    基于误差扩散的Floyd-Steinberg抖动。为了防止大尺度的图样模式(如伪轮廓)的出现,我们特意采用一种噪声应用形式来随机量化误差,这个过程称为抖动。Floyd-Steinberg算法使用误差扩散技术来实现抖动,将像素的剩余量化误差推加到相邻像素上,再做处理。

    该算法从左到右,从上到下扫描图像,依法对像素值进行量化,每次量化误差分布在相邻的像素之间(待扫描),而不影响已经量化的像素。因此,如果对多个像素向下取整,那么接下来的像素更有可能被算法向上取整,从而使得平均量化误差接近于零。

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    
    plt.rcParams['font.sans-serif'] = ['KaiTi'] # 指定默认字体
    plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
    
    img_gray0 = cv2.imread(r"D:\image_processing\image4\4.jpg", cv2.IMREAD_GRAYSCALE)
    img_gray0 = 255 - img_gray0
    h, w= img_gray0.shape
    
    img_gray0 = cv2.resize(img_gray0, (w//2, h//2))
    
    h, w= img_gray0.shape
    pylab.figure(figsize=(20, 15))
    plt.subplot(221)
    plt.imshow(img_gray0, vmin=0, vmax=255, cmap=plt.get_cmap("Greys"))
    plt.title("原始图像", size=25)
    
    img_gray_eq = img_gray0
    
    img_dither = np.zeros((h+1, w+1), dtype=np.float)
    img_undither = np.zeros((h, w), dtype=np.uint8)
    
    threshold = 128
    
    for i in range(h):
        for j in range(w):
            img_dither[i, j] = img_gray_eq[i, j]
            if img_gray_eq[i, j] > threshold:
                img_undither[i, j] = 255
    
    for i in range(h):
        for j in range(w):
            old_pix = img_dither[i, j]
            if (img_dither[i, j] > threshold):
                new_pix = 255
            else:
                new_pix = 0
    
            img_dither[i, j] = new_pix
    
            quant_err = old_pix - new_pix
    
            if j > 0:
                img_dither[i+1, j-1] = img_dither[i+1, j-1] + quant_err * 3 / 16
            img_dither[i+1, j] = img_dither[i+1, j] + quant_err * 5 / 16
            img_dither[i, j+1] = img_dither[i, j+1] + quant_err * 7 / 16
            img_dither[i+1, j+1] = img_dither[i+1, j+1] + quant_err * 1 / 16
    
    img_dither = img_dither.astype(np.uint8)
    img_dither = img_dither[0:h, 0:w]
    
    plt.subplot(222)
    plt.imshow(img_dither, vmin=0, vmax=255, cmap=plt.get_cmap("Greys"))
    plt.title("半色调二值化图像", size=25)
    plt.subplot(223)
    plt.imshow(img_undither, vmin=0, vmax=255, cmap=plt.get_cmap("Greys"))
    plt.title("经Floyd-Steinberg算法处理后的二值图像", size=25)
    
    plt.show()

    image.png



关键字