目录
一、计算机视觉和图像处理
1.1 什么是计算机视觉?
1.2 什么是图像处理?
1.3 计算机视觉和图像处理的关系
二、图像处理工具包
2.1 PIL:Python图像处理类库
2.1.1 Python安装PIL
2.1.2 Image对象
2.1.3 转换图像格式
2.1.4 创建缩略图
2.1.5 复制和粘贴图像区域
2.1.6 调整尺寸和旋转
2.2 Matplotlib类库
2.2.1 绘制图像、点和线
2.2.2 绘制图像的格式
2.3 OpenCV视觉库
2.3.1 基本操作
3.2 OpenCV读取和PIL读取的差异
三、图像直方图与直方图均衡化
3.1 图像直方图
3.1.1 图像直方图的定义
3.1.2 图像直方图的性质
3.1.3 绘制直方图
3.2 直方图均衡化
3.2.1 直方图均衡化的定义
3.2.2 基本思想
3.2.3 编程实现
四、高斯滤波
4.1 什么是高斯滤波?
4.2 基本原理
4.3 使用示例
从20世纪中期至今,计算机视觉不断发展,研究经历了从二维图像到三维到视频再到真实空间的探知,操作方法从构建三维向特征识别转变,算法由浅层神经网络到深度学习,数据的重要性逐渐被认知,伴随着计算机从理论到应用的速度加快,高质量的各种视觉数据不断沉淀,无论在社会经济农业还是工业领域,还是视频直播、游戏、电商不断发展,有计算机视觉应用层出不穷,为我们生活添加了丰富色彩,那么计算机视觉到底是什么?
计算机视觉是一门研究如何使机器“看”的科学,更进一步的说,就是指用摄影机和电脑代替人眼对目标进行识别、跟踪和测量等机器视觉,并进一步做图形处理,使电脑处理成为更适合人眼观察或传送给仪器检测的图像。计算机视觉也可以看作是研究如何使人工系统从图像或多维数据中“感知”的科学。
简单一点说,计算机视觉是人工智能的一个重要分支,它要解决的问题就是:看懂图像里的内容。给出一张二维图像,计算机视觉系统必须识别出图像中的对象及其特征,如形状、纹理、颜色、大小、空间排列等,从而尽可能完整地描述该图像。
比如,如下图:
我们人类能够理解和描述图像中的场景。我们眼睛可以检测到图片里有两个人,且一男一女,他们在海边。除了这些基本信息,我们还能够看出图像中看到,女生穿着浅绿色衣服,男生穿的白色T恤,并且他们两都穿着黑色运动短裤,在沙滩上跑步。我们还可以理性地推断出图中人物是外国人,女生的头发是金色,男生的头发是褐色。我们从材质与纹理方面,还能描述出男生的裤子相较于女生的裤子比较宽松。
以上,也是计算机视觉系统需要的技能。
一幅图像可以定义成一个二维空间函数,即 I = f(x,y),I 是二维空间,x和y是空间中的坐标,f是位于二维空间中x和y坐标处的灰度值,即使在计算机中可以把图像看做一个矩阵。
图像处理(狭义)是对输入图像进行某种变换得到输出图像,是一种图像到图像的过程。主要指对图像进行各种操作以改善图像的视觉效果,或对图像进行压缩编码以减少所需存储空间或传输时间、传输通路的要求,包括图像增强、图像恢复和重建,以及图像编码。
图像处理旨在处理原始图像以应用某种变换,其目标通常是改进图像或将其作为某项特定任务的输入,而计算机视觉的目标是描述和解释图像。计算机视觉使用机器学习技术建模图像处理。计算机视觉应用机器学习来识别用于解释图像的模式。就像人类视觉的视觉推理过程一样;我们可以区分对象,对它们进行分类,根据它们的大小对它们进行排序等等。
深度学习领域,对图片视频样本的处理占了很大一部分比重。同样,对于计算机视觉来说,基本的图像处理操是重要的,对图像的处理结果的好与坏,可能会直接对研究结果产生影响,下面我将介绍一些关于图像处理的基本知识。
PIL(Python Imaging Library,图像处理类库) 提供了通用的图像处理功能,以及大量有用的基本图像操作,比如:打开显示,灰度转换,图像缩放,旋转,裁剪等。PIL功能非常强大,但API却非常简单易用。
PIL仅支持到Python2.7以下的版本,加上年久失修,于是一群志愿者在PIL的基础上创建了兼容的版本,名字叫Pillow,支持最新Python 3.x,又加入了许多新特性,因此,如果我们python的版本比较高,我们可以直接安装使用Pillow。
安装Pillow需要对应满足版本的pip,所以安装前最好更新一下pip,不然容易安装失败!
python -m pip install --upgrade pip
然后在命令行下直接通过pip安装:
pip install pillow
安装完成之后,我们就可以在命令行输入:
pip list
我们就可以看到安装好的pip和Pillow的版本了。
利用 PIL 中的函数,我们可以从大多数图像格式的文件中读取数据,然后写入最常 见的图像格式文件中。PIL 中最重要的模块为 Image,使用下列导包方式引入 Image 模块:
from PIL import Image
1)创建Image对象
使用 Image 类可以实例化一个 Image 对象,通过调用该对象的一系列属性和方法对图像进行处理。Pilow 提供了两种创建 Image 实例对象的方法,下面对它们进行简单的介绍。
方法一:open()
使用 Image 类的 open() 方法,可以创建一个 Image 对象,语法格式如下:
img = Image.open(fp, mode="r")
参数说明:
使用示例:
from PIL import Image
# 读取图片,返回一个PIL图像对象
img = Image.open('C:/Users/Administrator/Desktop/picture1.jpg')
# 显示图片,调用 show()方法
img.show()
输出结果:
方法二:new()
使用 Image 类提供的 new() 方法可以创建一个新的 Image 对象,语法格式如下:
img = Image.new(mode, size, color)
参数说明:
使用示例:
# 使用颜色的十六进制格式
img2 = Image.new(mode='RGB', size=(500,500), color="#98e9e7")
img2.show()
输出结果:
文件目录下,读取多图片:
在某一个文件夹里有多张图片,则我们可以写一个函数进行批处理:
import os
# 返回目录中所有JPG 图像的文件名列表
def get_imlist(path):
return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]
运行测试:
if __name__ == '__main__':
img_list = get_imlist('C:\\Users\\Administrator\\Desktop\\Test1')
for str in img_list:
print(str)
如下面这个文件夹,可以知道,上面运行结果是正确读取出了JPG图像,共六张。
2) Image对象属性
Image 对象有一些常用的基本属性,我们来了解一下这些图片的基本信息:
属性 | 说明 |
size | 图像的尺寸 |
format | 图片的格式 |
readonly | 图片是否为只读 |
info | 图片相关信息 |
mode | 图像模式 |
from PIL import Image
# 读取图片
img = Image.open('C:/Users/Administrator/Desktop/picture1.jpg')
# 查看图片尺寸
# 方式一: 直接打印
print(img)
# 方式二: 分别打印图片宽高
print("图像的宽:", img.width, "; 图像的高:", img.height)
# 方式三: 打印图片尺寸
print("图像的大小:", img.size)
# 查看图片的格式
print("图像的格式:", img.format)
# 查看图像是否为只读: 是返回为0, 否为1
print("图像是否为只读:", img.readonly)
# 查看图片相关信息: 返回值为字典格式
print("图像信息:", img.info)
# 查看图像模式
print("图像模式信息:", img.mode)
通过 save() 方法,PIL 可以将图像保存成多种格式的文件。save() 方法保存图像,当不指定文件格式时,它会以默认的图片格式来存储;如果指定图片格式,则会以指定的格式存储图片。save() 的语法格式如下:
Image.save(fp, format=None)
参数说明:
from PIL import Image
# 读取图片
img = Image.open('C:/Users/Administrator/Desktop/picture.jpg')
# 把图像图片格式由jpg改成png
img.save('C:/Users/Administrator/Desktop/picture.png', 'PNG')
运行结束后,生成了一个格式为png的图片。
缩略图(thumbnail image)指的是将原图缩小至一个指定大小(size)的图像。通过创建缩略图可以使图像更易于展示和浏览。
Image 对象提供了一个 thumbnail() 方法用来生图像的缩略图,该函数的语法格式如下:
thumbnail(size,resample)
参数说明:
使用示例:
from PIL import Image
# 读取图片
img = Image.open('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
# 制定缩略尺寸
img.thumbnail((500, 100))
print("缩略图尺寸:", img.size)
输出结果:
有时候,缩略图的尺寸可能与我们指定的尺寸不一致,这是因为 Pillow 会对原图像的长、宽进行等比例缩小,当指定的尺寸不符合图像的尺寸规格时,缩略图就会创建失败, 比如指定的尺寸超出了原图像的尺寸规格。
拷贝、粘贴操作几乎是成对出现的,Image 类提供了 copy() 和 paste() 方法来实现图像的复制和粘贴。
1)复制:copy()方法
使用 crop()
方法可以从一幅图像中裁剪指定区域:
box = (left, top, right, bottom)
img.crop(box) # img是读取的图片对象
参数说明:
2)粘贴:paste()方法
paste() 粘贴方法,该函数的作用是将一张图片粘贴至另一张图片中,粘贴后的图片模式将自动保持一致,不需要进行额外的转换。语法格式如下所示:
paste(image, box=None, mask=None)
参数说明:
使用示例:
from PIL import Image
# 读取图片
img = Image.open('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
# 复制原图片副本
img_copy = img.copy()
# 对副本进行裁剪
box = (1000, 1000, 3000, 3000)
region = img_copy.crop(box)
# 旋转180°上面获取的区域
region = region.transpose(Image.ROTATE_180)
# 将裁剪后的副本粘贴至副本图像上
img.paste(region, box)
# 显示粘贴后的图像
img.show()
输出结果:
1)调整尺寸
在图像处理过程中经常会遇到缩小或放大图像的情况,Image 类提供的 resize() 方法能够实现任意缩小和放大图像。
resize() 函数的语法格式如下:
resize(size, resample=image.BICUBIC, box=None, reducing_gap=None)
参数说明:
使用示例:
from PIL import Image
# 读取图片
img = Image.open('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
# 调整尺寸
img1 = img.resize((128, 128))
# 输出图片
img1.show()
输出结果:
注意:
1. resize()方法可以缩小也可以放大,而thumbnail()方法只能缩小;
2. resize()方法不会改变对象的大小,只会返回一个新的Image对象,而thumbnail()方法会直接改变对象的大小,返回值为none;
2) 图片旋转
Image 类提供了函数rotate(),可以任意角度旋转:
Image.rotate(angle, resample=PIL.Image.NEAREST, expand=None, center=None, translate=None, fillcolor=None)
参数说明:
使用示例:
from PIL import Image
# 读取图片
img = Image.open('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
# 旋转45°
img1 = img.rotate(45)
# 输出图片
img1.show()
输出结果:
我们处理数学运算、绘制图表,或者在图像上绘制点、直线和曲线时,Matplotlib是个很好的类库,具有比 PIL 更强大的绘图功能。Matplotlib可以绘制出高质量的 图表,就像本书中的许多插图一样。Matplotlib 中的 PyLab 接口包含很多方便用户创建图像的函数。
尽管 Matplotlib 可以绘制出较好的条形图、饼状图、散点图等,但是对于大多数计 算机视觉应用来说,仅仅需要用到几个绘图命令。最重要的是,我们想用点和线来表示一些事物,比如兴趣点、对应点以及检测出的物体。下面是用几个点和一条线绘制图像的例子:
from PIL import Image
from pylab import *
# 读取图像到数组中
img = array(Image.open('C:/Users/Administrator/Desktop/Test1/picture1.jpg'))
# 绘制图像
imshow(img)
# 一些点
x = [500, 500, 1000, 1000]
y = [200, 900, 200, 900]
# 使用红色星状标记绘制点
plot(x, y, 'r*')
# 绘制连接前两个点的线
plot(x[:2], y[:2])
# 添加标题,显示绘制的图像
title('Plotting: "picture1.jpg"')
show()
# 坐标轴不显示
axis('off')
绘图时,有很多选项可以控制图像的颜色和样式,使用方法见下面的例子:
用PyLab库绘图的基本颜色格式命令:
用PyLab库绘图的基本线型格式命令:
用PyLab库绘图的基本绘制标记格式命令:
在计算机视觉项目的开发中,OpenCV作为较大众的开源库,拥有了丰富的常用图像处理函数库,采用C/C++语言编写,可以运行在Linux/Windows/Mac等操作系统上,能够快速的实现一些图像处理和识别的任务。
此外,OpenCV还提供了Java、python、cuda等的使用接口、机器学习的基础算法调用,从而使得图像处理和图像分析变得更加易于上手,让开发人员更多的精力花在算法的设计上。OpenCV是用于快速处理图像处理、计算机视觉问题的工具,支持多种语言进行开发如c++、python、java等。我使用OpenCV-Python,使用python语言对图像进行处理和研究。
注意,使用PyCharm开发,要先引入cv2库,若引入不成功就引入opencv-python。
1)读入图像
cv2.imread(filepath,flags)
参数说明:
2)显示图像
cv2.imshow(wname, img)
参数说明:
ps:显示图片时注意,让程序暂停cv2.waitKey(0),否则图片一闪而过,我们来不及观察图片 。
3)保存图像
cv2.imwrite(file,img,num)
参数说明:
使用示例:
import cv2
# 读入图像
# 1的话读取全彩图片 0读取灰度图片即黑白图片
img1 = cv2.imread('C:/Users/Administrator/Desktop/Test1/picture1.jpg', 1)
img2 = cv2.imread('C:/Users/Administrator/Desktop/Test1/picture1.jpg', 0)
# 显示图像
cv2.namedWindow('img1', cv2.WINDOW_NORMAL)
cv2.namedWindow('img2', cv2.WINDOW_NORMAL)
cv2.resizeWindow('img1', 600, 450)
cv2.resizeWindow('img2', 600, 450)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.waitKey(0)
# 保存图像
# IMWRITE_JPEG_QUALITY的取值范围为0-100,下面写入jpg格式,数值越小,压缩比越高,图片失真严重
cv2.imwrite('C:/Users/Administrator/Desktop/Test1/picture1_copy.jpg', img1, [cv2.IMWRITE_JPEG_QUALITY, 0])
输出结果:
输出差异
cv2.imread()读取的是图像的真实数据。lmage.open()函数只是保持了图像被读取的状态,但是图像的真实数据并未被读取态。
通道差异
Image.open()读取的通道顺序是RGB,cv2.imread()读取的通道顺序为BGR。
import cv2
from PIL import Image
from matplotlib import pyplot as plt
# 读入图像
# OpenCV读取
img1 = cv2.imread('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
# PIL读取
img2 = Image.open('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
# 显示图像
plt.figure(dpi=120)
plt.subplot(121) # 1代表行,2代表列,所以一共有2个图,1代表此时绘制第一个图。
plt.axis('off') # 取消坐标轴
plt.title("OpenCV") # 标题
plt.imshow(img1) # 显示图片
plt.subplot(122) # 1代表行,2代表列,所以一共有2个图,2代表此时绘制第二个图。
plt.axis('off') # 取消坐标轴
plt.title("PIL") # 标题
plt.imshow(img2) # 显示图片
plt.show() # 显示窗口
我们可以看到,两种方式输出的效果是不一样,用OpenCV读取的图片显示的颜色比较奇怪,用PIL读取图片就和实际原图是一致的,造成两个输出效果不一致的原因就是通道差异,PIL中Image.open()读取的通道顺序是RGB,OpenCV中cv2.imread()读取的通道顺序为BGR。
⼀幅数字图像在范围[0, G]内总共有L个灰度级,其直⽅图定义为离散函数:
其中: 是第 级亮度,是灰度级为的图像中的像素数,
在实际处理中,图像直方图的x轴区间一般是[0, 255],对应的是8位位图的256个灰度级;y轴对应的是具有相应灰度级的像素点的个数。虽然8位的图像都具有256个灰度级(每一个像素可以有256个灰度值),但是属于不同灰度级的像素数量是很不一样的。有时为了便于表示,也会采用归一化直方图。在归一化直方图中,x轴仍然表示灰度级;y轴不再表示灰度级出现的次数,而是灰度级出现的频率。
- 直方图反映了图像中的灰度分布规律。它描述每个灰度级具有的像元个数,但不包含这些像元在图像中的位置信息。
- 任何一幅特定的图像都有唯一的直方图与之对应,但不同的图像可以有相同的直方图。
- 如果一幅图像有两个不相连的区域组成,并且每个区域的直方图已知,则整幅图像的直方图是该两个区域的直方图之和。
对于每幅图像都可做出其灰度直方图。根据直方图的形态可以大致推断图像质量的好坏。由于图像包含有大量的像元,其像元灰度值的分布应符合概率统计分布规律。假定像元的灰度值是随机分布的,那么其直方图应该是正态分布。
图像的灰度值是离散变量,因此直方图表示的是离散的概率分布。若以各灰度级的像元数占总像元数的比例值为纵坐标轴做出图像的直方图,将直方图中各条形的最高点连成一条外轮廓线,纵坐标的比例值即为某灰度级出现的概率密度,轮廓线可近似看成图像相应的连续函数的概率分布曲线。
Python的模块matplotlib.pyplot中的hist()函数能够方便地绘制直方图,我们通常采用该函数直接绘制直方图。除此以外,OpenCV中的cv2.calcHist()函数能够计算统计直方图,还可以在此基础上绘制图像的直方图。
1)使用Numpy绘制直方图
模块matplotlib.pyplot提供了一个类似于MATLAB绘图方式的框架,可以使用其中的matplotlib.pyplot.hist()函数。
matplotlib.pyplot.hist(X,BINS)
参数说明:
使用示例:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
plt.hist(img.ravel(), 256)
plt.show()
输出结果:
2)使用OpenCV绘制直方图
OpenCV提供了函数cv2.calcHist()用来计算图像的统计直方图,该函数能统计各个灰度级的像素点个数。利用matplotlib.pyplot模块中的plot()函数,可以将函数cv2.calcHist()的统计结果绘制成直方图。
hist = cv2.calcHist( images, channels, mask, histSize,ranges, accumulate)
参数说明:
使用示例:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
hist = cv2.calcHist([img], [0], None, [256], [0, 255])
# hist是一个shape为(256,1)的数组,表示0-255每个像素值对应的像素个数,下标即为相应的像素值
# plot一般需要输入x,y,若只输入一个参数,那么默认x为range(n),n为y的长度
plt.plot(hist)
plt.show()
输出结果:
把原始图像的灰度直⽅图从⽐较集中的某个灰度区间变成在全部灰度范围内的均匀分布的技术。直⽅图均衡化能起到增强图像对⽐度的作⽤。
直方图均衡化方法的基本思想是对在图像中像素个数多的灰度级进行展宽,而对像素个数少的灰度级进行缩减。从而达到清晰图像的目的。
计算步骤:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 获取直方图——计算像素值出现概率
def GetHist(img):
assert isinstance(img, np.ndarray)
prob = np.zeros(shape=(256))
for rv in img:
for cv in rv:
prob[cv] += 1
row, col = img.shape
prob = prob / (row * col)
return prob
# 直方图均衡化
def EqualHist(img, prob):
# 累计概率
prob = np.cumsum(prob)
# 像素值映射
img_map = [int(i * prob[i]) for i in range(256)]
# 像素值替换
assert isinstance(img, np.ndarray)
row, col = img.shape
for i in range(row):
for j in range(col):
img[i, j] = img_map[img[i, j]]
return img
# 画直方图
def Draw_plot(y, name):
plt.figure(num=name)
plt.bar([i for i in range(256)], y, width=1)
if __name__ == '__main__':
# 读取灰度图
img = cv2.imread("C:/Users/Administrator/Desktop/Test1/picture1.jpg", 0)
# 获取原图取直方图
prob = GetHist(img)
# 画原图直方图
Draw_plot(prob, "原图直方图")
# 直方图均衡化
img = EqualHist(img, prob)
cv2.imwrite("source_hist.jpg", img) # 保存图像
# 获取均衡化后直方图
prob = GetHist(img)
# 画均衡化后的直方图
Draw_plot(prob, "直方图均衡化结果")
plt.show()
运行结果:
原图灰度图像 均衡化后的灰度图像⾼斯滤波是⼀种线性平滑滤波,适⽤于消除⾼斯噪声,⼴泛应⽤于图像处理的减噪过程。通俗的讲,⾼斯滤波就是对整幅图像进⾏加权平均的过程,每⼀个像素点的值,都由其本身和邻域内 的其他像素值经过加权平均后得到。⾼斯滤波的具体操作是:⽤⼀个模板(或称卷积、掩模)扫 描图像中的每⼀个像素,⽤模板确定的邻域内像素的加权平均灰度值去替代模板中⼼像素点的值。
数值图像处理中,⾼斯滤波主要可以使⽤两种⽅法实现。⼀种是离散化窗⼝滑窗卷积,另⼀种⽅法是通过傅⾥叶变化。最常⻅的就是滑窗实现,只有当离散化的窗⼝⾮常⼤,⽤滑窗计算量⾮常 搭的情况下,可能会考虑基于傅⾥叶变化的实现⽅法。所以本⽂将主要介绍滑窗实现的卷积。
离散化窗⼝划船卷积时主要利⽤的是⾼斯核,⾼斯核的⼤⼩为奇数,因为⾼斯卷积会在其覆盖区 域的中⼼输出结果。常⽤的⾼斯模板有如下⼏种形式:
⾼斯模板是通过⾼斯函数计算出来的,公式如下:
opencv函数 cv2.GaussianBlur实现高斯滤波:
cv2.GaussianBlur(src, ksize, sigmaX, sigmaY, borderType)
参数详解:
使用示例:
import cv2
img = cv2.imread('C:/Users/Administrator/Desktop/Test1/picture1.jpg')
img1 = cv2.GaussianBlur(img, (3, 3), 10)
img2 = cv2.GaussianBlur(img, (3, 3), 100)
img3 = cv2.GaussianBlur(img, (3, 3), 1000)
cv2.namedWindow('img', cv2.WINDOW_NORMAL)
cv2.namedWindow('img1', cv2.WINDOW_NORMAL)
cv2.namedWindow('img2', cv2.WINDOW_NORMAL)
cv2.namedWindow('img3', cv2.WINDOW_NORMAL)
cv2.resizeWindow('img', 600, 450)
cv2.resizeWindow('img1', 600, 450)
cv2.resizeWindow('img2', 600, 450)
cv2.resizeWindow('img3', 600, 450)
cv2.imshow('img', img)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.waitKey(0)
输出结果:
下面四张图中,img为原图,其他都是经过高斯滤波后的图像,可以看出草地变得更加平滑了!