天天看点

数字图像处理学习笔记2:图像直方图及空域处理和常见python编程问题

本次学习内容是记录基本的图像增强,滤波或者直方图处理的编程实现以及相关python常见错误,涉及numpy,matplotlib,opencv等库。以上内容基于第一次作业。

目录

  1. 直方图读取显示
  2. 直方图均衡化
  3. plt绘图技巧说明
  4. 直方图拉伸
  5. 平滑滤波
  6. 图像锐化
  7. 图像二值化

1.直方图读取显示

函数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)
           

2.直方图均衡化

此操作意在增强图片对比度,使各个灰度级均匀分布

对比度可以简单的解释为图像矩阵中像素的最大值和最小值之差。

视觉看来就是灰蒙蒙的和深黑色的区别

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:目标图像。

3. plt绘图技巧说明和python编程说明

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中要注意缩进,缩进对齐的相当于一个个代码块,和大括号相当,缩进的语句是从属于未缩进的前句的,再条件结构中要注意

4.直方图拉伸

可以采用查找表的方式实现(即映射)

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为反转函数

5.平滑滤波

图像平滑是滤掉高频分量,从而达到减少图象噪声,使图片变得有些模糊。

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
           

6.图像锐化

如果说平滑是取均值等加和类似方法(积分)进行,则锐化是通过微分(即像素作差)的方式达到反效果,可以联系到各种微分算子,比如梯度算子,拉普拉斯算子等

锐化就是通过增强高频分量来减少图象中的模糊,因此又称为高通滤波。锐化处理在增强图象边缘的同时增加了图象的噪声。

图像锐化时需要使用模板滤波,模板可以用numpy中的array函数实现

kernel_3x3 = np.array([[1, -2, 1],#将括号里的内容矩阵化
                   [-2,  9, -2],
                   [1, -2, 1]])
           

obel算子是一种常用的边缘检测算子,是一阶的梯度算法。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。它进行处理的模板如下:

数字图像处理学习笔记2:图像直方图及空域处理和常见python编程问题

采用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
           

7.图像二值化

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")
           

继续阅读