本文主要讲述的是数字图像处理中对图像背景的处理以及图片噪点的去除,运用到的方法有高斯模糊、灰度二值化、连通域检检测
引用Wikipedia的两句话:
Mathematically, applying a Gaussian blur to an image is the same as convolving the image with a Gaussian function.
数学上讲,对图像做高斯模糊等同于将图像与高斯函数卷积。
Since the Fourier transform of a Gaussian is another Gaussian, applying a Gaussian blur has the effect of reducing the image’s high-frequency components; a Gaussian blur is thus a low pass filter.
由于高斯分布的傅里叶变换仍然是高斯分布,使用高斯模糊就减少了图像的高频分量,因此高斯模糊是低通滤波器。
关于高斯模糊的原理可以参考:高斯模糊原理
使用openCV只需要一行代码即可以实现高斯模糊:
blur = cv2.GaussianBlur(img,(5,5),0)
这里(5, 5)表示高斯矩阵的长与宽都是5,标准差取0时OpenCV会根据高斯矩阵的尺寸自己计算。概括地讲,高斯矩阵的尺寸越大,标准差越大,处理过的图像模糊程度越大。
先解释一下灰度:
灰度是指只含亮度信息,不含色彩信息的图像。黑白照片就是灰度图,特点是亮度由暗到明,变化是连续的
用个例子来说明吧:一个256级灰度的图象,如果RGB三个量相同时,如:RGB(100,100,100)就代表灰度为100,RGB(50,50,50)代表灰度为50。
原理参考:图像灰度化
在OpenCV中的用法:
gray = cv2.cvtColor(pic,cv2.COLOR_BGR2GRAY)
再解释一下二值化:
图像的二值化是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果。
原理说起来很简单,就是灰度大于 阈值(Threshold) 的时候将灰度值设为255,小于则设为0,当然还有很多其他的二值化方法,参考博客:几种不同的Threshold类型
在OpenCV中的用法:
ret,im_treshold = cv2.threshold(gray,Threshold,maxval,cv2.THRESH_BINARY)
threshold参数表示阈值。
maxval参数表示与THRESH_BINARY和HRESH_BINARY_INV阈值类型一起使用设置的最大值。
1.Canny边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。
2.Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:
好的检测- 算法能够尽可能多地标识出图像中的实际边缘。
好的定位- 标识出的边缘要尽可能与实际图像中的实际边缘尽可能接近。
最小响应- 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。
3.算法步骤:
①高斯模糊 - GaussianBlur
②灰度转换 - cvtColor
③计算梯度 – Sobel/Scharr
④非最大信号抑制
⑤高低阈值输出二值图像
重要的是需要理解,高斯卷积核大小的选择将影响Canny检测器的性能。尺寸越大,检测器对噪声的敏感度越低,但是边缘检测的定位误差也将略有增加。一般5x5是一个比较不错的trade off。
推荐博客(写的很好):边缘检测Canny原理
在OpenCV中的用法:
canny1 = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
第一个中括号中的可以省略,具体这里为什么会有两个Threshold看推荐博客中的原理。
本文使用的是cv2.connectedComponentsWithStats()
进行连通域检测的,具体的原理有篇博客写的很好:连通域检测原理,在这里就不赘述了,简单来说一般用两种方法可以实现:1)Two-pass;2)Seed-Filling种子填充。
要知道的有两种连通方式:
4连通 | 8连通 |
还需要注意的是cv2.connectedComponentsWithStats()
的返回值,在这里给出官方文档。
- input 8-bit single-channel (binary)
- OutputArray labels, // output label map
- OutputArray stats, // Nx5 matrix (CV_32S) of statistics:
// [x0, y0, width0, height0, area0;
// … ; x(N-1), y(N-1), width(N-1),
// height(N-1), area(N-1)]- OutputArray centroids, // Nx2 CV_64F matrix of centroids
要注意的是:stats一共有五位,分别对应各个轮廓的x,y,width,height和面积。注意0的区域标识的是background
先推荐个好东西,当许多参数不确定的时候,可以用这两个函数:
cv2.createTrackbar('THRESHOLD','image',0,255,callback)
THRESHOLD = cv2.getTrackbarPos('THRESHOLD','image')
因为不同的图片各个参数往往不同,这样方便测试最优参数,当然不要忘记先cv2.namedWindow('image')
创建一个窗口。
首先我测试的是cv2.GaussianBlur()
中的blockSize和c
然后测试的是cv2.threshold()
中的阈值以及cv2.adaptiveThreshold()
中的blockSize和c
最好效果为:
treshold.jpg | adThreshold.jpg |
可以发现已经去除掉了图片背景中的沁墨,仅剩下少部分噪点。
然后通过cv2.Canny()
得到的图(用的普通的theshold):
通过连通域检测的结果去除少数几个噪点
_, labels, stats, centroids = cv2.connectedComponentsWithStats(canny1)
for istat in stats:
if istat[4]<18:
cv2.rectangle(im_treshold,tuple(istat[0:2]),tuple(istat[0:2]+istat[2:4]) , (255,255,255),thickness=-1)
cv2.rectangle(canny1,tuple(istat[0:2]),tuple(istat[0:2]+istat[2:4]) , 0,thickness=-1)
代码因为测试缘故,迭代了好几个版本,这里不删掉注释是为了方便你们调试:
import cv2
from matplotlib import pyplot as plt
#from skimage import measure, color, morphology
def callback(object):
pass
pic = cv2.imread('original.jpg')
#cv2.namedWindow('image')
gray1 = cv2.cvtColor(pic,cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray1, (5,5), 0)
cv2.imwrite('gray.jpg',gray)
#cv2.createTrackbar('THRESHOLD','image',0,255,callback)
#cv2.createTrackbar('blockSize','image',3,11,callback)
#cv2.createTrackbar('c','image',1,10,callback)
#while(True):
#cv2.imshow('gray',gray)
#THRESHOLD = cv2.getTrackbarPos('THRESHOLD','image')
#blockSize = cv2.getTrackbarPos('blockSize','image')
#c = cv2.getTrackbarPos('c','image')
#THRESHOLD = 135
#if blockSize % 2 == 1:
#gray = cv2.GaussianBlur(gray1, (blockSize,blockSize), c)
ret,im_treshold = cv2.threshold(gray,135,255,cv2.THRESH_BINARY)
cv2.imwrite('treshold.jpg',im_treshold)
#if blockSize % 2 == 1:
#测试得出c=8,blockSize=5效果最好
im_at_mean = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C , cv2.THRESH_BINARY, 5, 8)
cv2.imwrite('adThreshold.jpg',im_at_mean)
canny1=cv2.Canny(im_treshold,100,200)
cv2.imwrite('canny.jpg',canny1)
#cv2.imshow('canny',canny1)
#stats:一共有五位,分别对应各个轮廓的x,y,width,height和面积。注意0的区域标识的是background
_, labels, stats, centroids = cv2.connectedComponentsWithStats(canny1)
#if cv2.waitKey(10) & 0xFF == ord('q'):
#break
for istat in stats:
if istat[4]<18:
cv2.rectangle(im_treshold,tuple(istat[0:2]),tuple(istat[0:2]+istat[2:4]) , (255,255,255),thickness=-1)
cv2.rectangle(canny1,tuple(istat[0:2]),tuple(istat[0:2]+istat[2:4]) , 0,thickness=-1)
#cv2.imshow('THRESHOLD',im_treshold)
cv2.imshow('result',im_treshold)
cv2.imwrite('cv_gray_result.jpg',im_treshold)
#label_img = measure.label(im_at_mean)
#label_img = morphology.remove_small_objects(label_img,min_size=3000,connectivity=1,in_place=True)
#dst = color.label2rgb(label_img)
#cv2.imshow('remove_small_objects',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()