目录
一、读取并显示图片的三种方式(Pillow、matplotlib、opencv)
一、读取并显示图片的三种方式(Pillow、matplotlib、opencv)
1.1 Pillow方式读取显示图片
实现代码:
from PIL import Image
pil_im = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
pil_im.show()
测试结果:
1.2 matplotlib方式读取显示图片
实现代码:
import matplotlib.pyplot as plt
im = plt.imread('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
plt.imshow(im)
plt.show()
测试结果:
1.3 opencv方式读取显示图片
实现代码:
import cv2
img = cv2.imread('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
cv2.imshow('尚大楼', img)
cv2.waitKey(0)
测试结果:
从测试结果可以看出:图像只显示一小部分。
这是因为当图像尺寸大于屏幕可显示大小时,通过上述代码,显示的图片仅为原始图片的一部分。这是由于上述代码默认的窗口属性为cv2.WINDOW_AUTOSIZE(按照图片大小自动调整窗口大小),则当图片尺寸小于屏幕大小时,按图片尺寸设置窗口大小,显示一个完整的图片。当图片尺寸大于屏幕大小时,窗口依旧按照图片尺寸设置,则我们的屏幕不能显示整个窗口,从而产生图片显示不全的问题。这时,我们可以通过设置窗口属性,保证图片显示完整。
修改代码:
import cv2
im = cv2.imread('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
cv2.namedWindow('img',cv2.WINDOW_NORMAL)
cv2.imshow('img',im)
cv2.waitKey(0)
cv2.destroyAllWindows()
测试结果:
通过cv2.namedWindow(‘img’,cv2.WINDOW_NORMAL)设置窗口属性可以任意调整窗口大小,图片能够在屏幕上完整显示,但拖动窗口时会造成图像比例失真。
图片显示不全的主要原因是图片过大。顺着这个思路,我们可以通过主动调整图片大小,使其符合屏幕大小从而解决该问题。
修改代码:
import cv2
im = cv2.imread('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
#cv2.namedWindow('img',cv2.WINDOW_NORMAL)
im = cv2.resize(im,(1920,1039))
cv2.imshow('img',im)
cv2.waitKey(0)
cv2.destroyAllWindows()
测试结果:
二、Pillow库(PIL)的用法介绍
在Python2中,PIL(Python Imaging Library)是一个非常好用的图像处理库,但PIL不支持Python3,所以有人(Alex Clark和Contributors)提供了Pillow,可以在Python3中使用。
2.1 安装Pillow
pip install pillow
Pillow库安装成功后,导包时要用PIL来导入,而不能用pillow或Pillow。
import PIL
from PIL import Image
在Pillow库中,除了有二十多个模块,还支持非常多的插件。其中最常用的是Image模块中同名的Image类,其他很多模块都是在Image模块的基础上对图像做进一步的特殊处理,Image模块中会导入部分来使用。
2.2 显示图片
原图:(以下实验都使用此图)
代码实现:
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
image.show()
运行结果:
2.3 转换图片格式
Pillow 库支持多种图片格式,可以直接使用 open() 方法来读取图片,并且无须考虑图片是何种类型。同时,Pillow 能够很轻松地实现图片格式之间的转换。下面是两种图片格式之间转换的方法。
2.3.1 save()方法
save() 方法用于保存图像,当不指定文件格式时,它会以默认的图片格式来存储;如果指定图片格式,则会以指定的格式存储图片。
save() 的语法格式如下:
Image.save(fp, format=None)
具体参数说明如下:
fp:图片的存储路径,包含图片的名称,字符串格式;
format:可选参数,可以指定图片的格式。
实验代码:
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
image.save('E:\Junior_N\computerVision\imgTest\Lab_1\saveTestImg.bmp')
image.show()
运行结果:
2.3.2 convert() 方法
由于save()方法有些格式无法成功转换,故提供含有多个参数,比如 mode、matrix、dither 等的convert()方法,其中最关键的参数是 mode。
语法格式如下:
convert(mode,parms**)
mode:指的是要转换成的图像模式;
params:其他可选参数。
实验代码:
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\Lab_1\IMG20190831164243.png')
image = image.convert('RGB')
image.save('E:\Junior_N\computerVision\imgTest\Lab_1\saveTestImg2.jpg')
image.show()
运行结果:
此外,图像的颜色转换也可以使用convert()方法实现。要读取一幅图像,并将其转换成灰度图像,只需要加上convert('L')。
实验代码:
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
image = image.convert('L')
image.save('E:\Junior_N\computerVision\imgTest\Lab_1\convertTest1.jpg')
image.show()
运行结果:
2.4 创建缩略图
使用PIL可以很方便地创建图像的缩略图。thumbnail()方法接受一个元组参数(该元组参数指定生成缩略图的大小),然后将图像转换成符合元组参数指定大小的缩略图。
语法格式如下:
thumbnail(size,resample)
创建一个指定大小(size)的缩略图,需要注意的是,thumbnail方法是原地操作,返回值是None。第一个参数是指定的缩略图的大小,第二个是采样的方法。
例如,创建最长边为128像素的缩略图,可以使用下列命令:
image.thumbnail((128,128))
实验代码:
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
image.thumbnail((128,128))
image.save('E:\Junior_N\computerVision\imgTest\Lab_1\ thumbnailTest.jpg')
image.show()
运行效果:
2.5 复制和粘贴图像区域
2.5.1 crop()方法的使用
Image 类提供的 crop() 函数允许我们以矩形区域的方式对原图像进行裁剪,函数的语法格式如下:
imageCopy = image.crop(box=None)
box:表示裁剪区域,默认为 None,表示拷贝原图像。
注意:box 是一个有四个数字的元组参数 (x_左上,y_左下,x1_右上,y1_右下),分别表示被裁剪矩形区域的左上角 x、y 坐标和右下角 x,y 坐标。默认 (0,0) 表示坐标原点,宽度的方向为 x 轴,高度的方向为 y 轴,每个像素点代表一个单位。
实验代码:(box值为默认None)
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
imageCopy = image.crop(box=None)
imageCopy.save('E:\Junior_N\computerVision\imgTest\Lab_1\imageCopyNoneTest.jpg')
imageCopy.show()
运行结果:
实验代码:box = (100,100,400,400)
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
box = (100,100,400,400)
imageCopy = image.crop(box)
imageCopy.save('E:\Junior_N\computerVision\imgTest\Lab_1\imageCopyTest.jpg')
imageCopy.show()
运行结果:
2.5.2 paste()方法
paste()方法在原图上修改它的 Image 对象,它不会返回粘贴后图像的 Image 对象。如果想调用 paste(),但还要保持原始图像的未修改版本,就需要先复制图像,然后在副本上调用 paste()。
paste() 方法语法格式如下所示:
paste(image, box=None, mask=None)
该函数的作用是将一张图片粘贴至另一张图片中。注意,粘贴后的图片模式将自动保持一致,不需要进行额外的转换。参数说明如下:
image:指被粘贴的图片。
box:指定图片被粘贴的位置或者区域,其参数值是长度为 2 或者 4 的元组序列,长度为 2 时,表示具体的某一点 (x,y);长度为 4 则表示图片粘贴的区域,此时区域的大小必须要和被粘贴的图像大小保持一致。
mask:可选参数,为图片添加蒙版效果。
实验代码:
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
imageCopy = image.copy()#复制一张图片副本
imageCopy = imageCopy.crop((0,0,200,100)) #对副本进行裁剪
# #创建一个新的图像作为蒙版,L模式,单颜色值
# image_new = Image.new(‘L’, (200, 100), 200)
imageCopy.paste(imageCopy,(100,100,300,200)) #mask=image_new
imageCopy.save('E:\Junior_N\computerVision\imgTest\Lab_1\imagePasteTest.jpg')
imageCopy.show()
运行效果:
2.6 调整尺寸和旋转
2.6.1 resize()方法
resize()方法的参数是一个元组,用来指定新图像的大小。
实验代码:
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
imageResize = image.resize((300,300))
imageResize.save('E:\Junior_N\computerVision\imgTest\Lab_1\imageResizeTest.jpg')
imageResize.show()
运行效果:
2.6.2 rotate()方法
rotate()方法可以实现一幅图像的旋转,其参数使用逆时针方式表示旋转角度。
实验代码:(以逆时针旋转60°为例)
from PIL import Image
image = Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg')
imageRotate = image.rotate(60)
imageRotate.save('E:\Junior_N\computerVision\imgTest\Lab_1\imageRotateTest.jpg')
imageRotate.show()
运行效果:
三、Matplotlib库的用法介绍
3.1 绘制图像、点和线
3.1.1 imshow()绘制图像
实验代码:
from PIL import Image
from pylab import *
#读取图像到数组中
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg'))
#绘制图像
imshow(image)
#显示绘制的图像
show()
运行效果:
3.1.2 plot(x,y,' ')绘制点
在使用plot(x,y,' ')绘制点前需要先给出一些点的坐标,例如
x = [100,200,300,400]
y = [200,300,400,500]
plot(x,y,' ')绘制点语法:
plot(x,y,**kwargs)
具体参数解释如下:
x,y表示需要绘制点的坐标。
**kwargs可以控制其颜色和样式。其部分短命令如下:
plot(x,y) #默认为蓝色实线
plot(x,y,'r*') #红色形状标记
plot(x,y,'go-') #带有圆圈标记的绿线
plot(x,y,'ks:') #带有正方形标记的黑色点线
实验代码:
#读取图像到数组中
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg'))
#绘制图像
imshow(image)
#需要绘制点的坐标
x = [100,200,300,400]
y = [200,300,400,500]
plot(x,y,'bo') #使用蓝色圆圈状标记绘制点
#显示绘制的图像
show()
运行效果:
3.1.3 plot(x[:4],y[:4])绘制线
实验代码:
from PIL import Image
from pylab import *
#读取图像到数组中
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg'))
#绘制图像
imshow(image)
#需要绘制点的坐标
x = [100,200,300,400]
y = [200,300,400,500]
plot(x,y,'bo') #使用蓝色圆圈状标记绘制点
plot(x[:4],y[:4]) #绘制连线将前四个点连成线
title('Plotting: "imagePlotTest.jpg"')
#显示绘制的图像
show()
运行效果:
在PyLab库中约定图像的左上角为坐标原点,图像的坐标轴是一个很有用的调试工具,但也可以加上下列命令使坐标轴不显示:
axis('off')
其效果如下:
3.2 图像轮廓和直方图
3.2.1 contour()方法实现图像轮廓提取
绘制轮廓需要对每个坐标[x,y]的像素值施加同一个阈值,所以首先将图像灰度化。前面也提到使用PIL的convert()方法可以将图像转化为灰度图像。
实验代码:
from PIL import Image
from pylab import *
#将图像转化为灰度图并读取到数组中
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg').convert('L'))
#新建一个图像
figure()
#不使用颜色信息
gray()
#在原点的左上角显示轮廓图像
contour(image,origin='image')
axis('equal')
axis('off')
show()
运行结果;
3.2.2 hist()函数绘制直方图
图像的直方图用来表征该图像像素的分布情况。用一定数目的小区间(bin)来指定表征像素值的范围,每个小区间会得到落入该小区间表示范围的像素数目。该(灰度)图像的直方图使用hist()函数绘制,hist()函数的第二个参数指定小区间的数目。需要注意的是,因为hist()只接受一维数组作为输入,所以我们在绘制图像直方图之前,必须先对图像进行压平处理。flatten()方法将任意数组按照行优先准测转换成一维数组。
实验代码:(结合上一部分得到的灰度图)
from PIL import Image
from pylab import *
#将图像转化为灰度图并读取到数组中
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg').convert('L'))
#新建一个图像
figure()
#不使用颜色信息
gray()
#在原点的左上角显示轮廓图像
contour(image,origin='image')
axis('equal')
axis('off')
figure()
hist(image.flatten(),128)
show()
运行结果:
3.3 交互式标注
有时用户需要和某些应用交互,例如在一幅图像中标记一些点,或者标注一些训练数据。PyLab库中的ginput()函数就可以实现交互式标注。
实验代码:
from PIL import Image
from pylab import *
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg'))
imshow(image)
print ('please click 4 points')
x = ginput(4)
print ('you clicked:',x)
show()
上面的代码首先绘制一幅图像,然后等待用户在绘制窗口的图像区域点击4次。程序将这些点击的坐标[x,y]自动保存在x列表里。
运行效果:
四、NumPy库的用法介绍
4.1 图像数组表示
当载入图像时,通过array()方法将图像转换成NumPy的数组对象。NumPy中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表(或者是列表的列表),但是数组中所有的元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。
PyLab实际上包含NumPy的一些内容,如数组类型。
测试代码:
from PIL import Image
from pylab import *
# from numpy import *
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg'))
print(image.shape,image.dtype)
image = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg').convert('L'),'f')
print(image.shape,image.dtype)
show()
运行结果:
每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数(uint8), 所以在第一种情况下,载入图像并将其转换到数组中,数组的数据类型为“uint8"。 在第二种情况下,对图像进行灰度化处理,并且在创建数组时使用额外的参数“f";该参数将数据类型转换为浮点型。注意,由于灰度图像没有颜色信息,所以在形状元组中,它只有两个数值。
数组中的元素可以使用下标访问。位于坐标i、j,以及颜色通道k的像素值可以像下面这样访问:
value = image[i,j,k]
多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。下面是有关灰度图像的一些例子:
image[i,:] = image[j,:] #将第j行的数值赋值给第i行
image[:,i] = 100 #将第i列的所有数值设为100
image[:100, :50].sum() #计算前100行、前50列所有数值的和
image[50: 100, 50:100] # 50~100行, 50~100列(不包括第100行和第100列)
image[i].mean() #第i行所有数值的平均值
image[:,-1] #最后一列
image[-2,:] (or image[-2]) #倒数第二行
注意,示例仅仅使用一个下标访问数组。如果仅使用一个下标,则该下标为行下标。注意,在最后几个例子中,负数切片表示从最后一个元素逆向计数。我们将会频繁地使用切片技术访问像素值,这也是一个很重要的思想。
4.2 灰度变换
实验代码:
from PIL import Image
from pylab import *
from numpy import *
im = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg').convert('L'))
gray()
print(int(im.min()), int(im.max()))
im2 = 255 - im # 对图像进行反相处理
print(int(im2.min()), int(im2.max()))
im3 = (100.0/255) * im + 100 # 将图像像素值变换到 100...200 区间
print(int(im3.min()), int(im3.max()))
im4 = 255.0 * (im/255.0)**2 # 对图像像素值求平方后得到的图像
print(int(im4.min()), int(im4.max()))
#figure()
subplot(2,2,1)
axis('off')
title('im')
imshow(im)
subplot(2,2,2)
axis('off')
title('f(x)= (100.0/255) * im + 100')
imshow(im2)
subplot(2,2,3)
axis('off')
title('f(x)= 255 - im')
imshow(im3)
subplot(2,2,4)
axis('off')
title('f(x)= 255.0 * (im/255.0)**2')
imshow(im4)
show()
运行结果:
第一张是灰度图;第二张图是将灰度图像进行反相处理;第三张图将图像的像素值变换到 100...200区间;第四张图是对图像使用二次函数变换,使较暗的像素值变得更小。
4.3 图像缩放
def imresize(im,sz):
""" 使用 PIL 对象重新定义图像数组的大小 """
pil_im = Image.fromarray(uint8(im))
return array(pil_im.resize(sz))
4.4 直方图均衡化
def histeq(im,nbr_bins=256):
""" 对一幅灰度图像进行直方图均衡化 """
# 计算图像的直方图
imhist,bins = histogram(im.flatten(),nbr_bins,normed=True)
cdf = imhist.cumsum() # cumulative distribution function
cdf = 255 * cdf / cdf[-1] # 归一化
# 使用累积分布函数的线性插值,计算新的像素值
im2 = interp(im.flatten(), bins[:-1], cdf)
return im2.reshape(im.shape), cdf
im = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg').convert('L'))
im2,cdf = imtools.histeq(im)
figure()
subplot(2,2,1)
axis('off')
gray()
title("im")
imshow(im)
subplot(2,2,2)
axis('off')
title("im2")
imshow(im2)
subplot(2,2,3)
axis('off')
title("hist im")
hist(im.flatten(),128,density=True)
subplot(2,2,4)
axis('off')
title("hist im2")
hist(im2.flatten(),128,density=True)
show()
可以看到,直方图均衡化后图像的对比度增强了,原先图像灰色区域的细节变得清晰。
4.5 图像平均
def compute_average(imlist):
""" 计算图像列表的平均图像 """
# 打开第一幅图像,将其存储在浮点型数组中
averageim = array(Image.open(imlist[0]), 'f')
for imname in imlist[1:]:
try:
averageim += array(Image.open(imname))
except:
print(imname + '...skipped')
averageim /= len(imlist)
# 返回 uint8 类型的平均图像
return array(averageim, 'uint8')
4.6 图像的主成分分析(PCA)
#####################PCA主成分分析#########
from PIL import Image
from numpy import *
def pca(X):
""" 主成分分析:
输入:矩阵X,其中该矩阵中存储训练数据,每一行为一条训练数据
返回:投影矩阵(按照维度的重要性排序)、方差和均值 """
# 获取维数
num_data,dim = X.shape
# 数据中心化
mean_X = X.mean(axis=0)
X = X - mean_X
if dim>num_data:
# PCA- 使用紧致技巧
M = dot(X,X.T) # 协方差矩阵
e,EV = linalg.eigh(M) # 特征值和特征向量
tmp = dot(X.T,EV).T # 这就是紧致技巧
V = tmp[::-1] # 由于最后的特征向量是我们所需要的,所以需要将其逆转
S = sqrt(e)[::-1] # 由于特征值是按照递增顺序排列的,所以需要将其逆转
for i in range(V.shape[1]):
V[:,i] /= S
else:
# PCA- 使用 SVD 方法
U,S,V = linalg.svd(X)
V = V[:num_data] # 仅仅返回前 nun_data 维的数据才合理
# 返回投影矩阵、方差和均值
return V,S,mean_X
4.7 使用pickle模块
五、SciPy库的用法介绍
5.1 图像模糊
![](https://img-blog.csdnimg.cn/8f3d402f35254e8b80124118413b31f8.png)
from scipy.ndimage import filters
im = array(Image.open('E:\Junior_N\computerVision\imgTest\IMG20190831164243.jpg').convert('L'))
figure()
gray()
axis('off')
subplot(1,4,1)
axis('off')
title(u'原图',fontproperties=font)
imshow(im)
for bi,blur in enumerate([2,5,10]):
im2=zeros(im.shape)
im2=filters.gaussian_filter(im,blur)
im2=np.uint8(im2)
imNum=str(blur)
subplot(1,4,2 + bi)
axis('off')
title(u'标准差为'+imNum,fontproperties=font)
imshow(im2)
5.2 一些有用的SciPy模块
data = scipy.io.loadmat('test.mat')
data = {}data['x'] = xscipy.io.savemat('test.mat',data)
from scipy.misc import imsaveimsave('test.jpg',im)
lena = scipy.misc.lena()
5.3 高级示例:图像去噪
from numpy import *
def denoise(im,U_init,tolerance=0.1,tau=0.125,tv_weight=100):
""" 使用 A. Chambolle(2005)在公式(11)中的计算步骤实现 Rudin-Osher-Fatemi(ROF)去噪模型
输入:含有噪声的输入图像(灰度图像)、U 的初始值、TV 正则项权值、步长、停业条件
输出:去噪和去除纹理后的图像、纹理残留 """
m,n = im.shape # 噪声图像的大小
# 初始化
U = U_init
Px = im # 对偶域的x 分量
Py = im # 对偶域的y 分量
error = 1
while (error > tolerance):
Uold = U
# 原始变量的梯度
GradUx = roll(U, -1, axis=1) - U # 变量 U 梯度的x 分量
GradUy = roll(U, -1, axis=0) - U # 变量 U 梯度的y 分量
# 更新对偶变量
PxNew = Px + (tau / tv_weight) * GradUx
PyNew = Py + (tau / tv_weight) * GradUy
NormNew = maximum(1, sqrt(PxNew ** 2 + PyNew ** 2))
Px = PxNew / NormNew # 更新x 分量(对偶)
Py = PyNew / NormNew # 更新y 分量(对偶)
# 更新原始变量
RxPx = roll(Px, 1, axis=1) # 对x 分量进行向右x 轴平移
RyPy = roll(Py, 1, axis=0) # 对y 分量进行向右y 轴平移
DivP = (Px - RxPx) + (Py - RyPy) # 对偶域的散度
U = im + tv_weight * DivP # 更新原始变量
# 更新误差
error = linalg.norm(U - Uold) / sqrt(n * m);
return U, im - U # 去噪后的图像和纹理残余