本文讲解基于OpenCV-python的图像二值化API及浅显原理讲解
在python的OpenCV中,阈值是图像二值化的必要条件,所谓阈值就是设定一个指,对于整张图片来说,大于该值的像素点重赋值为一个值,或者小于该值的像素点重赋值为多少。一般情况下阈值的选取及选取方法决定了二值化的效果。从API层面来看,有两种阈值可选,分别为简单阈值和自适应阈值
简单阈值顾名思义就是由我们给出一个简单的值,这种阈值是由我们给出的,具体的流程为,遍历图像,当像素高与该阈值时,则对该像素点赋值为设定好的值,否则不动。
自适应阈值是利用API在某部分图像自动计算出的阈值,该阈值大概率情况下效果都要好于自己设定好的阈值。
对于该方法opencv提供了我们需要的API
cv2.threshold()
它用来做全局的二值化,即用一个阈值对整个图每张像素点进行操作。
参数及效果会用代码和几张图片来讲解。
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())
显然与我们需要的相差甚远,但是该方法还是需要讲解。
参数:
局部图像二值化要比全局图像二值化的效果好很多,我们在全局图像二值化时,就算设定了自动计算阈值,取得较好的阈值,但是对于直方图有两个波峰的图像来说,其阈值显然只适用于部分的像素点。因此会出现大面积黑或者大面积白的情况。我们来看局部图像二值化的结果。
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())
效果如下:
可以看出效果非常好,并且不难想到,它既然可以做到将图像中的文字信息找出来,那么其在目标识别中应该有很大的应用。接下来来看,API。
binary=cv.adaptiveThreshold(gray,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,cv.THRESH_BINARY,25,10)
参数:
局部二值化与全局二值化差距很大,局部二值化可以考虑局部的直方图情况,防止了像全局二值化那样直接用一个阈值计算整体,这样的话可以防止很多有效的信收到损伤。
在第一部分我们提到最后可以利用API计算出自适应的阈值。而且后面给了设定的一个参数。即
前面已经说到了我们在处理全局时可能遇到的问题,实际上,这里我们一般设置的参数为:
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())
虽然结果依旧不如人意,但是确实好了很多。接下里我们来看利用这些操作可以做出一些有意思的处理。
还记得上面的均值吗,我们也可以自己做一个这样的算法,本身并没有特别难。
#图像的二值化
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())
没有特别理想,不过无所谓了,重点是代码,效果可以自己调试出来。参数解释在其那里已经非常清楚了。
看了图片的功能之后能想到,它不仅可以做学术研究,还可以做出一些有意思的p图。如下:
这是一次全局二值化的结果,可能这还没有特别有趣,但是如果我们设置为局部二值化呢。
是不是还充斥了一种莫名的美感。也可以用它对我们的人像进行处理。
关神(小声逼逼)这是我们实验室大二组心中的神。
所谓的超大图像,就是字面意思,即尺寸很大的图片,这样的图片我们用一般的屏幕是显示不出来吗,大多只能显示出一部分图像。其实对于这部分图像,我们可以直接用局部二值化处理的,但是由于部分算法原因以及一些其他的原因,我们更加建议对超大型图像进行快快的遍历,对每个模块进行自己的处理,同时由于部分模块的信息可能完全无效,因此可以直接赋值,不必做多余的运算,这样的话会节省很多的时间。
#超大图像二值化
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的话就视为其信息无效。这样就可以一遍过不需要多余的计算。
在处理图像时,一定要注意对一些图像可能需要平滑操作(模糊操作),因为很多信息会被噪音污染。
高斯滤波
简单模糊操作