本次学习内容是记录基本的图像增强,滤波或者直方图处理的编程实现以及相关python常见错误,涉及numpy,matplotlib,opencv等库。以上内容基于第一次作业。
函数cv.calcHist(images,channels,mask,histSize,ranges [,hist [,accumulate]])
是在opencv库中用来查找直方图的,参数说明如下:
images:它是uint8或float32类型的源图像。它应该放在方括号中,即“ [img]”。
channels:也以方括号给出。它是我们计算直方图的通道的索引。例如,如果输入为灰度图像,则其值为[0]。对于彩色图像,您可以传递[0],[1]或[2]分别计算蓝色,绿色或红色通道的直方图。
mask:图像掩码。为了找到完整图像的直方图,将其指定为“无”。但是,如果要查找图像特定区域的直方图,则必须为此创建一个掩码图像并将其作为掩码。(我将在后面显示一个示例。)
histSize:这表示我们的BIN计数。需要放在方括号中。对于全尺寸,我们通过[256]。
ranges:这是我们的RANGE。通常为[0,256]。
使用示例:
hist= cv.calcHist([own], #计算图像的直方图
[0], #使用的通道
None, #没有使用mask
[256], #it is a 1D histogram
[0.0,255.0])
plt.hist(src,pixels)
src:数据源,注意这里只能传入一维数组,使用src.ravel()可以将二维图像拉平为一维数组。
pixels:像素级,一般输入256。
img = cv2.imread('test.png',cv2.IMREAD_GRAYSCALE)
plt.hist(img.ravel(),256)# plt.hist(image.ravel(), 256, [0, 256])
plt.show()
自定义的函数:
def plot_image(IMG,title):
plt.imshow(IMG, cmap='gray', interpolation='bicubic')
plt.xticks([]), plt.yticks([]) # 隐藏 x 轴和 y 轴上的刻度值
plt.title(title)
def plot_hist(image,title):
plt.hist(image.ravel(), 256, [0, 256])
plt.title(title)
此操作意在增强图片对比度,使各个灰度级均匀分布
对比度可以简单的解释为图像矩阵中像素的最大值和最小值之差。
视觉看来就是灰蒙蒙的和深黑色的区别
def hist_equl(img):
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
equal_hist=plt.hist(cl1.ravel(), 256, [0, 256])#直方图
equal_img= np.hstack((img, cl1))#比较图
return (equal_img,equal_hist)
输入是图像,返回值是两个值,这里如果需要调用其中一个:
a,_=hist_equl(img)#a为equal_img,即均衡后的图
_,b=hist_equl(img)#均衡后的直方图数据
注意:上面是自适应均衡化,全局的均衡如下
#equ = cv.equalizeHist(own)
#res = np.hstack((own, equ)) # stacking images side-by-side
#cv.imwrite('res.png', res)
#对g02和自选图像own进行均衡化
void equalizeHist(InputArray src,OutputArray dst)
src:源图像。图像必须是灰度图。
dst:目标图像。
1.plt和opencv尽量不要混用,以免出现数据或者图像格式不对的问题
2.plt.figure(1)可以先建立一个画布,在后续进行图像读取
绘图顺序必须是先确立画布,再用subplot排布,最后show出来,此时所有的图像都会显示新的画布。
plt.figure(1)#此步可省略,show函数会自己确立
plt.subplot(321);plot_hist(img1,'g01')
plt.subplot(322);plot_hist(img2,'g02')
plt.subplot(323);plot_hist(img3,'g03')
plt.subplot(324);plot_hist(img4,'g04')
plt.subplot(325);plot_hist(img5,'g05')
plt.subplot(326);plot_hist(img6,'g06')
plt.show()
3.关于plt的画图操作
4.再python中要注意缩进,缩进对齐的相当于一个个代码块,和大括号相当,缩进的语句是从属于未缩进的前句的,再条件结构中要注意
可以采用查找表的方式实现(即映射)
lut = np.zeros(256, dtype = own.dtype )#创建空的查找表
minBinNo, maxBinNo = 0, 255
#计算从左起第一个不为0的直方图柱的位置
for binNo, binValue in enumerate(hist):#binnoh和binvalue则是灰度值和频率
if binValue != 0:
minBinNo = binNo
break
#计算从右起第一个不为0的直方图柱的位置
for binNo, binValue in enumerate(reversed(hist)):
if binValue != 0:
maxBinNo = 255-binNo
break
print(minBinNo, maxBinNo)
for i,v in enumerate(lut):
print(i)
if i < minBinNo:
lut[i] = 0
elif i > maxBinNo:
lut[i] = 255
else:
lut[i] = int(255.0*(i-minBinNo)/(maxBinNo-minBinNo)+0.5)
result = cv.LUT(own, lut)#滤波
这里需要注意的是:enumerate是python里的一个函数,通常再for循环实用,所以==for i in x()==本来是i遍历x所有成分
例子:
fruits = ['banana', 'apple', 'mango']
for index in range(len(fruits)):
print '当前水果 :', fruits[index]
print "Good bye!"
当前水果 : banana
当前水果 : apple
当前水果 : mango
Good bye!
而此enumerate则是将一系列值和编号串起来遍历,reversed为反转函数
图像平滑是滤掉高频分量,从而达到减少图象噪声,使图片变得有些模糊。
blur = cv.blur(img,(5,5))#用55进行均值滤波
blurimg = np.hstack((img, blur))#左右拼接
out = gaussian_filter(img, K_size=3, sigma=1.3)#高斯滤波,标准差1.3,模板为33的
img_sp = sp_noise(img,0.1)#添加椒盐噪声,0.1是噪声比例
median = cv.medianBlur(img_sp,5)#中值滤波
其中用到的椒盐噪声和高斯滤波函数如下(需要import random)
def gaussian_filter(img, K_size=3, sigma=1.3):
if len(img.shape) == 3:
H, W, C = img.shape
else:
img = np.expand_dims(img, axis=-1)
H, W, C = img.shape
## Zero padding
pad = K_size // 2
out = np.zeros((H + pad * 2, W + pad * 2, C), dtype=np.float)
out[pad: pad + H, pad: pad + W] = img.copy().astype(np.float)
## prepare Kernel
K = np.zeros((K_size, K_size), dtype=np.float)
for x in range(-pad, -pad + K_size):
for y in range(-pad, -pad + K_size):
K[y + pad, x + pad] = np.exp(-(x ** 2 + y ** 2) / (2 * (sigma ** 2)))
K /= (2 * np.pi * sigma * sigma)
K /= K.sum()
tmp = out.copy()
# filterin
for y in range(H):
for x in range(W):
for c in range(C):
out[pad + y, pad + x, c] = np.sum(K * tmp[y: y + K_size, x: x + K_size, c])
out = np.clip(out, 0, 255)
out = out[pad: pad + H, pad: pad + W].astype(np.uint8)
return out
def sp_noise(image,prob):
'''
添加椒盐噪声
prob:噪声比例
'''
output = np.zeros(image.shape,np.uint8)
thres = 1 - prob
for i in range(image.shape[0]):
for j in range(image.shape[1]):
rdn = random.random()
if rdn < prob:
output[i][j] = 0
elif rdn > thres:
output[i][j] = 255
else:
output[i][j] = image[i][j]
return output
如果说平滑是取均值等加和类似方法(积分)进行,则锐化是通过微分(即像素作差)的方式达到反效果,可以联系到各种微分算子,比如梯度算子,拉普拉斯算子等
锐化就是通过增强高频分量来减少图象中的模糊,因此又称为高通滤波。锐化处理在增强图象边缘的同时增加了图象的噪声。
图像锐化时需要使用模板滤波,模板可以用numpy中的array函数实现
kernel_3x3 = np.array([[1, -2, 1],#将括号里的内容矩阵化
[-2, 9, -2],
[1, -2, 1]])
obel算子是一种常用的边缘检测算子,是一阶的梯度算法。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。它进行处理的模板如下:
采用sobel算子时
x = cv.Sobel(img, cv.CV_16S, 1, 0,ksize=3)
y = cv.Sobel(img, cv.CV_16S, 0, 1,ksize=3)
absX = cv.convertScaleAbs(x) # 转回uint8
absY = cv.convertScaleAbs(y)
dst = cv.addWeighted(absX, 0.5, absY, 0.5, 0)
其中sobel函数的参数含义如下
cv.CV_16S指深度
1,0分别是在x,y方向的差分阶数,例如第一行为对x的一阶偏导
ksize为模板大小,只能为1,3,5,7
cv.convertScaleAbs()
函数是将sobel算子处理过的数据负值化正值,转回uint8型
cv.addWeighted(absX, 0.5, absY, 0.5, 0)
函数是
void cvAddWeighted( const CvArr* src1, double alpha,const CvArr* src2, double beta,double gamma, CvArr* dst );
参数1:src1,第一个原数组.
参数2:alpha,第一个数组元素权重
参数3:src2第二个原数组
参数4:beta,第二个数组元素权重
参数5:gamma,图1与图2作和后添加的数值。不要太大,不然图片一片白。总和等于255以上就是纯白色了。
参数6:dst,输出图片
故代码中是x和y方向各取0.5融合,权重和添加值为0
对于滤波还可以采用算子卷积的方法
from scipy import ndimage
kernel_3x3 = np.array([[1, -2, 1],#定义模板
[-2, 9, -2],
[1, -2, 1]])
k3 = ndimage.convolve(img3, kernel_3x3).
除了sobel算子以外,拉普拉斯算子用法如下
gray_lap = cv.Laplacian(img2, cv.CV_16S,ksize=3)
dst2 = cv.convertScaleAbs(gray_lap)#转换uint8
from PIL import Image
import cv2 as cv
img = Image.open('1.png')
#模式L”为灰色图像,它的每个像素用8个bit表示,0表示黑,255表示白,其他数字表示不同的灰度。
Img = img.convert('L')
Img.save("test1.jpg")
#自定义灰度界限,大于这个值为黑色,小于这个值为白色
threshold = 200
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
#图片二值化
photo = Img.point(table, '1')
photo.save("test3.jpg")