计算机视觉一(Python)
图像的基本操作和处理
1.直方图
2.直方图均衡化
3.高斯滤波
一 直方图
图像直方图是反映一个图像像素分布的统计表,其实横坐标代表了图像像素的种类,可以是灰色的,也可以是彩色的。纵坐标代表了每一种颜色值在图像中的像素总数或者占有像素个数的百分比。图像由像素构成,因为反映像素的直方图往往可以作为图像一个很重要的特征。hist()只接受一维数组作为输入,所以在绘制图像之前,可以用flatten()先对图像进行压平处理,将其转换成一维数组。相应代码如下:
from PIL import Image
from pylab import *
im = array(Image.open('D:/Software/Pictures/i.jpg').convert('L'))
figure()
gray()
contour(im, origin = 'image')
axis('equal')
axis('off')
figure()
hist(im.flatten(),128)
show()
图片如下:
二 直方图均衡化
直方图均衡化处理的"中心思想"是把原始图像的灰度直方图从比较集中的某个灰度区间变成在全部灰度范围内的均匀分布,直方图均衡化就是对图像进行非线性拉伸,重新分配图像像素值,使一定会度范围内的像素数量大致相同。直方图均衡化就是把给定图像的直方图分布改成“均匀分布”直方图。相应代码如下:
import cv2 # 仅用于读取图像矩阵
import matplotlib.pyplot as plt
import numpy as np
gray_level = 256 # 灰度级
def pixel_probability(img):
"""
计算像素值出现概率
:param img:
:return:
"""
assert isinstance(img, np.ndarray)
prob = np.zeros(shape=(256))
for rv in img:
for cv in rv:
prob[cv] += 1
r, c = img.shape
prob = prob / (r * c)
return prob
def probability_to_histogram(img, prob):
"""
根据像素概率将原始图像直方图均衡化
:param img:
:param prob:
:return: 直方图均衡化后的图像
"""
prob = np.cumsum(prob) # 累计概率
img_map = [int(i * prob[i]) for i in range(256)] # 像素值映射
# 像素值替换
assert isinstance(img, np.ndarray)
r, c = img.shape
for ri in range(r):
for ci in range(c):
img[ri, ci] = img_map[img[ri, ci]]
return img
def plot(y, name):
"""
画直方图,len(y)==gray_level
:param y: 概率值
:param name:
:return:
"""
plt.figure(num=name)
plt.bar([i for i in range(gray_level)], y, width=1)
if __name__ == '__main__':
img = cv2.imread('D:/Software/Pictures/f.jpg', 0) # 读取灰度图
prob = pixel_probability(img)
plot(prob, "原图直方图")
# 直方图均衡化
img = probability_to_histogram(img, prob)
cv2.imwrite("source_hist.jpg", img) # 保存图像
prob = pixel_probability(img)
plot(prob, "直方图均衡化结果")
plt.show()
图片如下:
三 高斯滤波
图像的高斯模糊是非常经典的图像卷积例子,本质上, 图像模糊就是将(灰度)图像和一个高斯核进行卷积操作。相应代码如下:
import cv2 as cv
import numpy as np
# 传入灰度图像
# sigma是高斯分布的标准差,avr是均值
def makeNoise(img, sigma, avr):
img2 = img.copy() # 使用副本,不用原本的
row, col = img2.shape # row和col都是列表类型,代表行数,列数
for x in range(0, row):
for y in range(0, col):
img2[x, y] = img2[x, y] + np.random.normal(avr, sigma ** 2, 1)[0]
return img2
# 传入灰度图像
# kernel_size为奇数。sigmax为0时是程序根据公式计算得方差,不为0时代表指定标准差为sigmax
def guassianImgFilt(img, kernel_size, sigmax):
row, col = img.shape # 获得未添加边界前的大小信息
# 下面产生卷积核
kernel = np.zeros([kernel_size, kernel_size])
center = kernel_size // 2 # 整除
# 计算标准差
if sigmax == 0:
sigma = ((kernel_size - 1) * 0.5 - 1) * 0.3 + 0.8
else:
sigma = sigmax
s = 2 * (sigma ** 2)
sum_val = 0
for i in range(0, kernel_size):
for j in range(0, kernel_size):
x = i - center
y = j - center # center-j也无所谓,反正权重是按到圆心距离算的,而且距离带平方了,正负无所谓,x**2+y**2的值代表了权重。
kernel[i, j] = np.exp(-(x ** 2 + y ** 2) / s)
sum_val += kernel[i, j]
# /(np.pi * s)
sum_val = 1 / sum_val
# 对卷积核归一化,确保卷积核之和为1
kernel = kernel * sum_val # 对于np.array来说是对应位置相乘。这里不要用除,最好是用乘以分之一的形式
# 以上是产生卷积核
# 计算图像边界需要添加的范围,扩充图像边界使得能遍历到原图像的每一个像素
addLine = int((kernel_size - 1) / 2) # 虽然addLine理应是整数,但存储类型是浮点,要转换类型
img = cv.copyMakeBorder(img, addLine, addLine, addLine, addLine, borderType=cv.BORDER_REPLICATE)
# 定位未扩充之前图像左上角元素在新图像中的下标索引,这个索引将用来遍历原图像的每一个像素点,相当于指针
source_x = addLine # 定义起始位置,即未扩充之前图像左上角元素在新图像中的下标索引
source_y = addLine # 定义起始位置,即未扩充之前图像左上角元素在新图像中的下标索引
# addLine的值是单边添加边界的大小(行数,也是列数),一共四个边添加边界
# 在添加了边界后的图像中遍历未添加边界时的原像素点,进行滤波
# 外层控制行,内层控制列
for delta_x in range(0, row):
for delta_y in range(0, col):
img[source_x, source_y] = np.sum(
img[source_x - addLine:source_x + addLine + 1, source_y - addLine:source_y + addLine + 1] * kernel)
source_y = source_y + 1
source_x = source_x + 1 # 行加1,准备进行下行的所有列的遍历
source_y = addLine # 把列归位到原始的列起点准备下轮列遍历
# 经过上面的循环后,图像已经滤波完成了
# 剥除四边添加的边界,然后返回滤波后的图片
return img[addLine:row + addLine, addLine:col + addLine]
# -----------------------------------------------------------------------------------------------------#
img = cv.imread('D:/Software/Pictures/a.jpg', cv.IMREAD_UNCHANGED) # 图像读入后是BGR形式
imgGray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) # BGR图像转为灰度图像
imgGrayWithNosie = makeNoise(imgGray, 3, 0)
imgfilted = guassianImgFilt(imgGrayWithNosie, 5, 0)
cv.imshow("origin", img)
cv.imshow("gray", imgGray)
cv.imshow("GrayWithNosie", imgGrayWithNosie)
cv.imshow("filted", imgfilted)
cv.waitKey()
cv.destroyAllWindows()
图片如下: