本文讲解基于OpenCV-python的图像二值化API及浅显原理讲解



文章目录

  • 一. 阈值
  • 1. 简单阈值
  • 2. 自适应阈值
  • 二. 图像二值化
  • 1. 全局图像二值化
  • 2. 局部图像二值化
  • 3. Otsu’s二值化
  • 4. 自定义计算阈值二值化
  • 5. 对图像的实际处理
  • 三. 超大图像二值化
  • 1. 超大图像
  • 2. 代码
  • 四. 注意点


一. 阈值

在python的OpenCV中,阈值是图像二值化的必要条件,所谓阈值就是设定一个指,对于整张图片来说,大于该值的像素点重赋值为一个值,或者小于该值的像素点重赋值为多少。一般情况下阈值的选取及选取方法决定了二值化的效果。从API层面来看,有两种阈值可选,分别为简单阈值和自适应阈值

1. 简单阈值

简单阈值顾名思义就是由我们给出一个简单的值,这种阈值是由我们给出的,具体的流程为,遍历图像,当像素高与该阈值时,则对该像素点赋值为设定好的值,否则不动。

2. 自适应阈值

自适应阈值是利用API在某部分图像自动计算出的阈值,该阈值大概率情况下效果都要好于自己设定好的阈值。

二. 图像二值化

1. 全局图像二值化

对于该方法opencv提供了我们需要的API

cv2.threshold()

它用来做全局的二值化,即用一个阈值对整个图每张像素点进行操作。
参数及效果会用代码和几张图片来讲解。

图片:

python opencv2 计算二值图像的连通域 opencv二值化python_python

import cv2 as cv
import numpy as np


def load_image():
    src=cv.imread('num.jpg')
    h,w=src.shape[:2]
    #src=cv.resize(src,(w*2,h*3))
    return src
#全局阈值
def threshold_demo(image):
    gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
    #为0则使用自动寻找阈值的选项 使用自动寻找即用|分开。
    ret,binary=cv.threshold(gray,127,255,cv.THRESH_BINARY)#|cv.THRESH_TRIANGLE
    print(ret)
    cv.imshow('same_3',binary)
    cv.waitKey(0)
    cv.destroyAllWindows() 

threshold_demo(load_image())

结果如下:

python opencv2 计算二值图像的连通域 opencv二值化python_计算机视觉_02

显然与我们需要的相差甚远,但是该方法还是需要讲解。

1. ret,binary=cv.threshold(gray,127,255,cv.THRESH_BINARY)
2. ret,binary=cv.threshold(gray,127,255,cv.THRESH_BINARY)#|cv.THRESH_TRIANGLE

参数:

  • 灰度图像:在做二值化时我们必要用灰度图像
  • 设定好的阈值
  • 当阈值大于该值时(或小于)像素点赋值为
  • 固定参数方法:cv.THRESH_BINARY 二值化,下图将包含方法及其曲线
  • 可选参数若加该参数则在最后加上一个 |cv.THRESH_TRIANGLE注意那里的|不要丢,选定该方法后,前面设置的阈值将无效,利用方法自动计算阈值
  • 返回值一:计算好或者设定好的阈值
  • 返回值二:处理好的图像
  • 其他的都已经讲过很多遍了就需要多说了。

python opencv2 计算二值图像的连通域 opencv二值化python_python_03

2. 局部图像二值化

局部图像二值化要比全局图像二值化的效果好很多,我们在全局图像二值化时,就算设定了自动计算阈值,取得较好的阈值,但是对于直方图有两个波峰的图像来说,其阈值显然只适用于部分的像素点。因此会出现大面积黑或者大面积白的情况。我们来看局部图像二值化的结果。

import cv2 as cv
import numpy as np


def load_image():
    src=cv.imread('num.jpg')
    h,w=src.shape[:2]
    #src=cv.resize(src,(w*2,h*3))
    return src

#局部阈值
def local_threshold(image):
    gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
    binary=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,25,10)
    cv.imshow('binary',binary)
    cv.waitKey(0)
    cv.destroyAllWindows() 

local_threshold(load_image())

效果如下:

python opencv2 计算二值图像的连通域 opencv二值化python_二值化_04


可以看出效果非常好,并且不难想到,它既然可以做到将图像中的文字信息找出来,那么其在目标识别中应该有很大的应用。接下来来看,API。

binary=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,25,10)

参数:

  • 灰度图
  • 设定的赋值像素点的值,即如果满足条件则付志伟255
  • 参数三一般由两种,如下:

    第一种是高斯c,即加权计算。
    第二种是均值c,即计算均值。
  • 参数4就是固定方法了,二值化方法,上面说了。
  • 参数五为选定像素点数量。
  • 参数6位c,即利用参数3计算出的值,减去这个c就是它的阈值。
  • 返回值为处理好的图像。

局部二值化与全局二值化差距很大,局部二值化可以考虑局部的直方图情况,防止了像全局二值化那样直接用一个阈值计算整体,这样的话可以防止很多有效的信收到损伤。

3. Otsu’s二值化

在第一部分我们提到最后可以利用API计算出自适应的阈值。而且后面给了设定的一个参数。即

python opencv2 计算二值图像的连通域 opencv二值化python_二值化_05


前面已经说到了我们在处理全局时可能遇到的问题,实际上,这里我们一般设置的参数为:

cv2.THRESH_OTSU

设置该参数后,算法会找到一个最优的阈值。可以尝试一下

#图像的二值化 


import cv2 as cv
import numpy as np


def load_image():
    src=cv.imread('num.jpg')
    h,w=src.shape[:2]
    #src=cv.resize(src,(w*2,h*3))
    return src
#全局阈值
def threshold_demo(image):
    gray=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
    #为0则使用自动寻找阈值的选项 使用自动寻找即用|分开。
    ret,binary=cv.threshold(gray,127,255,cv.THRESH_BINARY|cv.THRESH_OTSU)#
    print(ret)
    cv.imshow('same_3',binary)
    cv.waitKey(0)
    cv.destroyAllWindows() 

threshold_demo(load_image())

python opencv2 计算二值图像的连通域 opencv二值化python_计算机视觉_06


虽然结果依旧不如人意,但是确实好了很多。接下里我们来看利用这些操作可以做出一些有意思的处理。

4. 自定义计算阈值二值化

还记得上面的均值吗,我们也可以自己做一个这样的算法,本身并没有特别难。

#图像的二值化 


import cv2 as cv
import numpy as np


def load_image():
    src=cv.imread('guan.jpg')
    h,w=src.shape[:2]
    src=cv.resize(src,(w//2,h//2))
    return src

#自定义均值二值化   
def custom_threshold(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('input',binary)
    cv.waitKey(0)
    cv.destroyAllWindows()

custom_threshold(load_image())

我们看看效果。

python opencv2 计算二值图像的连通域 opencv二值化python_二值化_07

没有特别理想,不过无所谓了,重点是代码,效果可以自己调试出来。参数解释在其那里已经非常清楚了。

5. 对图像的实际处理

看了图片的功能之后能想到,它不仅可以做学术研究,还可以做出一些有意思的p图。如下:

python opencv2 计算二值图像的连通域 opencv二值化python_python_08


这是一次全局二值化的结果,可能这还没有特别有趣,但是如果我们设置为局部二值化呢。

python opencv2 计算二值图像的连通域 opencv二值化python_二值化_09


是不是还充斥了一种莫名的美感。😂也可以用它对我们的人像进行处理。

python opencv2 计算二值图像的连通域 opencv二值化python_二值化_10

关神(小声逼逼)这是我们实验室大二组心中的神。

python opencv2 计算二值图像的连通域 opencv二值化python_opencv_11

三. 超大图像二值化

1. 超大图像

所谓的超大图像,就是字面意思,即尺寸很大的图片,这样的图片我们用一般的屏幕是显示不出来吗,大多只能显示出一部分图像。其实对于这部分图像,我们可以直接用局部二值化处理的,但是由于部分算法原因以及一些其他的原因,我们更加建议对超大型图像进行快快的遍历,对每个模块进行自己的处理,同时由于部分模块的信息可能完全无效,因此可以直接赋值,不必做多余的运算,这样的话会节省很多的时间。

2. 代码

#超大图像二值化
import cv2 as cv
import numpy as np


def load_image():
    image=cv.imread('num.jpg')
    return image
def big_binary(image):
    print('message:',image.shape)
    sh=sw=40
    h,w=image.shape[:2]
    grey=cv.cvtColor(image,cv.COLOR_BGR2GRAY)
    for row in range(0,h,sh):
        for col in range(0,w,sw):
            roi=grey[row:row+sh,col:col+sw]
            #val,dist=cv.threshold(roi,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
            if np.std(roi)<15 :
                roi[row:row+sh,col:col+sw]=255
            else:
                val,dist=cv.threshold(roi,0,255,cv.THRESH_BINARY|cv.THRESH_OTSU)
                grey[row:row+sh,col:col+sw]=dist
    cv.imshow('score.jpg',grey)
    cv.waitKey(0)
    cv.destroyAllWindows()
    print('finished')
big_binary(load_image())

np.stu():方差
np.array.sums():总和
np.mean():均值

不难想象如果图像很大的话,可能我们遍历的某个块之中的信息完全无效因此,我们设置若是方差小于15的话就视为其信息无效。这样就可以一遍过不需要多余的计算。



四. 注意点

在处理图像时,一定要注意对一些图像可能需要平滑操作(模糊操作),因为很多信息会被噪音污染。