计算机视觉编程——基本的图像操作和处理

基本的图像操作和处理

1 Python图像处理类库

PIL(Python Imagine Library Python,图像处理类库)提供了通用的图像处理功能以及大量有用的基本图像操作,比如图像缩放、裁剪、旋转、颜色转换等。

如下介绍一个简单的例子来介绍PIL函数:

from PIL import Image
from pylab import *
pil_im = Image.open('jimei.jpg').convert('L')
pil_im.show()

在这段代码中,利用PIL中的函数从图像格式的文件中读取数据然后写入最常见的图像格式文件中。PIL中最重要的模块就是Image,可以使用open()函数读取一幅图像,返回值pil_im是一个PIL图像对象,使用convert()来实现颜色的转换。运行结果如下图所示。
在这里插入图片描述

图1 简单的PIL例子

1.1 转换图像格式

通过save()方法,PIL可以将图像保存成多种格式的文件,即保存图像到具有指定文件名的文件。

from PIL import Image
I = Image.open('jimei.jpg')
I.save('new_jimei.jpg')

在这个例子中,我们将工作空间路径下的图片以重命名的方式保存在了同一路径下。PIL是一个足够智能的库,可以根据文件扩展名来判断图像的格式。PIL函数会进行简单的检查,如果文件不是JPEG格式,会自动将其转换成JPEG格式;如果转换失败,会在控制台输出一条报告失败的消息。

1.2 创建缩略图

使用PIL可以很方便的创建图像的缩略图。thumbnail()方法接受一个元组参数(该参数指定生成缩略图的大小),然后将图像转换成符合元组参数指定大小的缩略图。例如,创建最长边为128像素的缩略图,可以使用如下代码:

from PIL import Image

img = Image.open('jimei.jpg')

img.thumbnail((128, 128))

img.save('jimei_thumb.jpg')

在这里插入图片描述

图2 thumbnail方法实例

1.3 复制和粘贴图像区域

使用crop()方法可以从一幅图像中裁剪指定区域:

box = (100, 100, 400, 400)
region = pil_im.crop(box)

该区域使用四元组来指定。四元组的坐标依次是(左,上,右,下)。PIL中指定坐标系的左上角坐标为(0, 0)。同时可以旋转上面代码中获取的区域,然后使用paste()方法将该区域放回去,具体实现如下:

region = region.transpose(Image.ROTATE_180)
pil_im.paste(region, box)

下边是一个简单的crop实例:

from PIL import Image
from pylab import *

pil_im = Image.open('jimei.jpg')

box = (pil_im.size[0]/4,pil_im.size[1]/4,pil_im.size[0]*3/4,pil_im.size[1]*3/4)	
region = pil_im.crop(box)

region.show()

在这里插入图片描述

图3 crop方法实例
上图就是从通过crop方法原图像中截取的一部分,接下来使用ROTATE方法旋转这部分区域再使用paste方法将旋转后的区域放回原图像中。
from PIL import Image
from pylab import *

pil_im = Image.open('jimei.jpg')

box = (pil_im.size[0]//4,pil_im.size[1]//4,pil_im.size[0]*3//4,pil_im.size[1]*3//4)

region = pil_im.crop(box)
region = region.transpose(Image.ROTATE_180)
pil_im.paste(region, box)

pil_im.show()

在这里插入图片描述

图4 paste方法实例

1.4 调整尺寸和旋转

要调整一幅图像的尺寸,可以调用resize()方法,其类似于创建一幅缩略图。该方法的参数是一个元组,用来指定新图像的大小:

out = pil_im.resize((128, 128))

要旋转一幅图像,可以使用逆时针方式表示旋转角度,然后调用rotate()方法:

out = pil_im.rotate(45)

使用如下代码调整图像尺寸,结果如图:

from PIL import Image
from pylab import *

pil_im = Image.open('jimei.jpg')

region = pil_im.resize((128, 128))
region.save('jimei_resize.jpg')

region.show()

在这里插入图片描述

图5 resize方法实例

使用如下代码旋转图像,结果如图:

from PIL import Image
from pylab import *

pil_im = Image.open('jimei.jpg')	

pil_im = pil_im.rotate(180)
pil_im.save('jimei_rotate.jpg')

pil_im.show()

在这里插入图片描述

图6 rotate方法实例

2 Matplotlib

在Python中,Matplotlib是一个很好地类库,在处理数学运算、绘制图表,或者在图像上绘制点、直线和曲线时,具有比PIL更强大的功能。

2.1 绘制图像、点和线

调用方式为:

plot(x, y, [fmt], data=None, **kwargs)

点和线的坐标由参数x,y提供。可选参数fmt是一个快捷字符串,用于定义颜色、标记符合和线条形状,例如:

>>> plot(x, y)        # 使用默认颜色和形状
>>> plot(x, y, 'bo')  # 使用蓝色(blue)、圆点型绘图
>>> plot(y)           # 绘制y坐标,x坐标使用列表0..N-1
>>> plot(y, 'r+')     # 同上,但使用红色(red)+号形状

支持的颜色命令如下:

字符 颜色
‘b’ blue 蓝色
‘g’ green 绿色
‘r’ red 红色
‘c’ cyan 青色
‘m’ magenta 紫红色
‘y’ yellow 黄色
‘k’ black 黑色
‘w’ white 白色

支持的marker缩写如下:

字符 描述
‘.’ point marker 点
‘,’ pixel marker 像素
‘o’ circle marker 圆形
‘v’ triangle_down marker 下三角
‘s’ square marker 方形
‘p’ pentagon marker 五角形
‘*’ star marker 星型
‘+’ plus marker 加号
‘x’ x marker ×型
‘D’ diamond marker 钻石型
‘’ vline marker 竖线型
‘_’ hline marker 横线型

支持的line缩写如下:

字符 描述
‘-’ solid line style 实线
‘–’ dashed line style 虚线
‘-.’ dash-dot line style 交错点线
‘:’ dotted line style 点线

简单的绘图实例如下:

from PIL import Image
import matplotlib.pyplot as plt
from pylab import *

im = array(Image.open('jimei.jpg'))

imshow(im)

x = [100, 100, 400, 400]
y = [200, 300, 200, 300]

plot(x, y, 'r*')
plot(x[:2], y[:2])

title('Plotting: "jimei.jpg"')

show()

在上述代码中,在x和y列表中给定点的x坐标和y坐标上绘制出红色星状点标记,然后在两个列表表示的前两个点之间绘制一条线段(默认为蓝色)。在PyLab中,约定图像的左上角为坐标原点,图像的坐标轴是一个很有用的调试工具,若不想显示坐标轴可以使用如下命令:

axis('off')

使用上述代码绘制的结果如下图所示:
在这里插入图片描述

图6 Matplotlib绘图实例

2.2 图像轮廓和直方图

下边来看两个特别的绘图实例:图像的轮廓和直方图。

绘制轮廓需要对每个坐标[x, y]的像素值施加一个阈值,所以首先需要将图像灰度化。直方图用来表征该图像像素的分布情况。用一定数目的小区间来指定表征像素值的范围,每个小区间会得到落入该小区间表示的像素数目。该图像的直方图可以使用hist()函数绘制。hist()函数的第二个参数指定小区间的数目。需要注意的是,hist()只接受一维数组作为输入,所以在绘制直方图之前,必须先对图像进行压平处理。flatten()方法将任意数组按照行优先准则转换成一维数组。

from PIL import Image
import matplotlib.pyplot as plt
from pylab import *

im = array(Image.open('jimei_grey.jpg'))

figure()

gray()

contour(im, origin = 'image')

axis('equal')
axis('off')

figure()

hist(im.flatten(), 128)

show()

所绘制出的轮廓图和直方图如下:
在这里插入图片描述

图7 Matplotlib绘图图像等轮廓线和直方图

2.3 交互式标注

有时用户和某些应用交互,例如在一幅图像中标记一些点,或者标记一些训练数据。PyLab中的ginput()函数就可以实现交互式标注。

from PIL import Image
import matplotlib.pyplot as plt
from pylab import *

im = array(Image.open('jimei_grey.jpg'))

imshow(im)

print ('Please click 3 points')

x = ginput(3)

print ('you clicked:' ,x)

show()

输出图像后等待用户在绘图窗口的图像区域点击三次,程序将这些点击的坐标[x, y]自动保存在x列表里。
在这里插入图片描述

图8 Matplotlib中交互式标注的简单实例

3 NumPy

NumPy是非常有名的Python科学计算工具包,其中包含了大量有用的思想,比如数组对象以及线性代数函数。数组对象可以完成比如矩阵乘积、转置、解方程系统、向量乘积和归一化,这为图像变形、对变化进行建模、图像分类、图像聚类等提供了基础。

3.1 图像数组表示

NumPy中的数组对象是多维的,可以用来表示向量、矩阵和图像。一个数组对象很像一个列表,但是数组中的所有元素必须具有相同的数据类型。除非创建数组对象时指定数据类型,否则数据类型会按照数据的类型自动确定。

例如:

im = array(Image.open('jimei.jpg'))
print(im.shape, im.dtype)

im = array(Image.open('jimei.jpg').convert('L'), 'f')
print(im.shape, im.dtype)

控制台输出结果为:(330, 600, 3) uint8 (330, 600) float32
每行的第一个元组表示图像数组的大小(行、列、颜色通道),紧接着的字符串表示数组元素的数据类型。因为图像通常被编码成无符号八位整数,所以在第一种情况下载入图像并将其转换到数组中,数组的数据类型为“uint8”。在第二种情况,对象进行灰度化处理,并且在创建数组时使用额外的参数“f”,该参数将数据类型转换为浮点型。

数组中的元素可以使用下标访问。位于坐标i,j,以及颜色通道k的像素值可以这样访问:

value = im[i, j, k]

多个数组元素可以使用数组切片方式访问。切片方式返回的是以指定间隔下标访问该数组的元素值。

im[i, :] = im[j, :]	    # 将第j行的数值赋值给第i行
im[:, i] = 100	        # 将第i行的所有数值设为100
im[:100, :50].sum()	    # 计算前100行、前50列所有数值的和
im[50: 100, 50: 100]	# 50~100行,50~100列(不包括第100行和第100列)
im[i].mean()	        # 第i行所有数值的平均值
im[:, -1]	            # 最后一行
im[-2, :](or im[-2])	# 倒数第2行

若仅使用一个下标,则该下标为行下标,负数切片表示从最后一个元素逆向计数。

3.2 灰度变换

将图像读入NumPy数组对象后,可以对它们执行任意数学操作。一个简单例子就是图像的灰度变换。考虑任意函数f,它将0…255区间映射到自身,即输出区间的范围和输入区间的范围相同。

from PIL import Image
from numpy import *

im = array(Image.open('jimei_grey.jpg'))

im2 = 255 - im
im3 = (100.0 / 255) * im + 100
im4 = 255.0 * (im / 255.0) ** 2

可以使用如下命令查看图像中的最小和最大像素值:

print(int(im.min()), int(im.max()))

程序代码如下:

from PIL import Image
from numpy import *
from pylab import *

im = array(Image.open('jimei.jpg').convert('L'))
# print(int(im.min()), int(im.max()))

im2 = 255 - im
# print(int(im2.min()), int(im2.max()))

im3 = (100.0 / 255) * im + 100
# print(int(im3.min()), int(im3.max()))

im4 = 255.0 * (im / 255.0) ** 2
# print(int(im4.min()), int(im4.max()))

subplot(221)
title('f(x) = x')
gray()
imshow(im)

subplot(222)
title('f(x) = 255 - x')
gray()
imshow(im2)

subplot(223)
title('f(x) = (100/255)*x + 100')
gray()
imshow(im3)

subplot(224)
title('f(x) =255 *(x/255)^2')
gray()
imshow(im4)

show()

运行后的结果如图所示:
在这里插入图片描述

图9 灰度变换的简单实例

在这里插入图片描述
图10 不同灰度变换下最大最小像素值对比

在上述实验中,第二个例子将灰度图像进行反相处理;第三个例子将图像的像素值变换到100…200区间;第四个例子对图像使用二次函数变换,使较暗的像素变得更小。

3.3 图像缩放

NumPy的数组对象使我们处理图像和数据的主要工具。想要对图像进行缩放处理没有现成简单的方法。可以使用之前PIL对图像对象转换的操作来完成。

from PIL import Image
from numpy import *
from pylab import *
import numpy as np

im = array(Image.open('jimei.jpg'))

pil_im = Image.fromarray(im)

pil_im = np.array(pil_im.resize((128, 128)))

imshow(pil_im)

show()

在这里插入图片描述

图11 使用PIL方法对NumPy数组对象的缩放

3.4 直方图均衡化

图像灰度变换中一个非常有用的例子就是直方图均衡化。直方图均衡化是指将一幅图像的灰度直方图变平,使变换后的图像中每个灰度值的分布概率都相同。在对图像作进一步处理之前,直方图均衡化通常是对图像灰度值进行归一化的一个非常好的方法,并且可以增强图像的对比度。

下边是直方图均衡化的具体实现:

def histeq(im, nbr_bins = 256)
	imhist, bins = histogram(im.flatten(), nbr_bins, normed = True)
	cdf = imhist.cumsum()
	cdf = 255 * cdf / cdf[-1]
	im2 = interp(im.flatten(), bins[:-1], cdf)
	return im2.reshape(im.shape), cdf

这个函数仅有两个参数,一个是灰度图像,一个是直方图中使用小区间的数目。函数返回直方图均衡化后的图像,以及用来做像素值映射的累积分布函数。注意,函数中使用到的累积分布函数的最后一个元素(下标是-1),目的是将其归一化到0…1范围,可以使用这个函数:

from PIL import Image
from numpy import *

im = array(Image.open('jimei_grey.jpg'))
im2, cdf = imtools.histeq(im)

使用如下程序完成直方图的绘制:

from PIL import Image 
from pylab import *
 
im = array(Image.open('jimei.jpg').convert('L'))
 
subplot(231)
 
hist(im.flatten(),256)
 
imhist,bins = histogram(im.flatten(),256,normed=True)
cdf = imhist.cumsum()
cdf = cdf*255/cdf[-1]
 
subplot(232)
 
plot(bins[:256],cdf)
 
im2 = interp(im.flatten(),bins[:256],cdf)
 
im2 = im2.reshape(im.shape)
 
subplot(233)
 
hist(im2.flatten(),256)
 
gray()
 
subplot(234)
 
imshow(im)

subplot(236)
 
imshow(im2)
 
show()

在这里插入图片描述

图12 直方图均衡化实例

如图为直方图均衡化后的结果,上边为直方图均衡化之前和之后的灰度直方图,以及累计概率分布函数映射图像。可以看到,直方图均衡化之后图像的对比度增强了,原先图像灰色区域的细节变得清晰。

3.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)

	return array(averageim, 'uint8')

除此之外,还可以使用mean()函数计算平均图像。mean()需要将所有图像堆积到一个数组中,会占很大的内存。

3.6 图像的主成分分析(PCA)

PCA是一个非常有用的降维技巧,可以在使用尽可能少维数的前提下,尽量多的保持训练数据的信息,其产生的投影矩阵可以被视为将原始坐标变换成现有的坐标系,坐标系中的各个坐标按照重要性递减排列。

为了对图像数据进行PCA变换,图像需要转换成一维向量表示,可以使用NumPy类库中的flatten()方法进行变换。PCA操作函数如下:

from PIL import Image
from numpy import *

def pca(X):
	num_data, dim = X.shape
	
	mean_X = X.mean(axis = 0)
	X = X - mean_X
	
	if dim > num_data:
		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:
		U, S, V = linalg.svd(X)
		V = V[:num_data]
			
	return V, S, mean_X

3.7 使用pickle模块

pickle模块接受几乎所有的Python对象,并且将其转换成字符串表示。该过程叫做封装。从字符串表示中重构该对象,称为拆封。这些字符串表示可以方便地存储和传输。

使用如下代码保存平均图像和主成分:

f = open('font_pca_modes.pkl', 'wb')
pickle.dump(immean, f)
pickle.dump(V, f)
f.close()

pickle中有很多不同的协议可以生成.pkl文件,如果不确定的话最好以二进制文件的形式读取和写入。

打开文件并保存:

with open('font_pca_modes.pkl', 'wb') as f
pickle.dump(immean, f)
pickle.dump(V, f)

打开文件并载入:

with open('font_pca_modes.pkl', 'wb') as f
immean = pickle.load(f)
V = pickle.load(f)

作为pickle的一种替代方式,NumPy具有读写文本文件的简单函数,即:

savetxt('test.txt', x, '%i')
x = loadtxt('test.txt')

4 SciPy模块

SciPy是建立在NumPy基础上,用于数值运算的开源工具包,可以实现数值积分、优化、统计、信号处理以及图像处理功能。

4.1 图像模糊

SciPy有用来做滤波操作的scipy.ndimage.filters模块。该模块使用快速一维分离的方式来计算卷积。

from PIL import Image
from numpy import *
from pylab import *
from scipy.ndimage import filters

im = array(Image.open('jimei_grey.jpg'))
im2 = filters.gaussian_filter(im, 2)
im3 = filters.gaussian_filter(im, 5)
im4 = filters.gaussian_filter(im, 10)

subplot(2, 2, 1)
gray()
axis('off')
imshow(im)

subplot(2, 2, 2)
gray()
axis('off')
imshow(im2)

subplot(2, 2, 3)
gray()
axis('off')
imshow(im3)

subplot(2, 2, 4)
gray()
axis('off')
imshow(im4)

show()

上边guassian_filter()函数的最后一个参数表示标准差,随着σ的增加,图像的模糊程度就越大,细节丢失也越多。得到的结果如下图所示:
在这里插入图片描述

图13 使用scipy.ndimage.filters模块进行高斯模糊

若要对一幅彩色图像进行模糊,只需要对每一个颜色通道进行高斯模糊:

im = array(Image.open('jimei.jpg'))
im2 = zeros(im.shape)
for i in range(3)
	im2[:, :, i] = filter.guassian_filter(im[:, :, i], 5)
im2 = uint2(im2)

4.2 图像导数

图像的强度变化可以用灰度图像I的x和y方向的导数Ix和Iy进行描述,图像的导数大多通过卷积来实现。通常选用Prewitt滤波器和Sobel滤波器。这些导数滤波器可以使用scipy.ndimage.filters模块的标准卷积操作来简单实现:

from PIL import Image
from numpy import *
from pylab import *
from scipy.ndimage import filters

im = array(Image.open('jimei_grey.jpg'))

imx = zeros(im.shape)
filters.sobel(im, 1, imx)

imy = zeros(im.shape)
filters.sobel(im, 0, imy)

magnitude = sqrt(imx ** 2 + imy ** 2)

subplot(2, 2, 1)
gray()
axis('off')
imshow(im)

subplot(2, 2, 2)
gray()
axis('off')
imshow(imx)

subplot(2, 2, 3)
gray()
axis('off')
imshow(imy)

subplot(2, 2, 4)
gray()
axis('off')
imshow(magnitude)

show()

上段代码使用Sobel滤波器计算x和y的方向导数以及梯度大小。sobel()函数的第二个参数表示选择x或者y方向导数,第三个参数保存输出的变量。下图显示了用Sobel滤波器计算出的导数图像,在两个导数图像中,正导数显示为亮的像素,负导数显示为暗的像素,灰色区域表示导数的值接近于零。
在这里插入图片描述

图14 使用Sobel导数滤波器计算导数图像

上述计算图像导数的方法存在一定的缺陷:在该方法中,滤波器的尺度需要随着图像分辨率的变化而变化。为了在处理图像噪声方面更加稳健,以及在任意尺度上计算导数,可以使用高斯导数滤波器。用于模糊的filters.gaussian)filter()函数可以接受额外的参数用来计算高斯导数,可以按照下边的方式处理:

from PIL import Image
from numpy import *
from pylab import *
from scipy.ndimage import filters

im = array(Image.open('jimei_grey.jpg'))

sigma = 2

imx = zeros(im.shape)
filters.gaussian_filter(im, (sigma, sigma), (0, 1), imx)

imy = zeros(im.shape)
filters.gaussian_filter(im, (sigma, sigma), (1, 0), imy)

subplot(1, 2, 1)
gray()
axis('off')
imshow(imx)

subplot(1, 2, 2)
gray()
axis('off')
imshow(imy)

show()

该函数的第三个参数指定对每个方向计算哪种类型的导数,第二个参数为使用的标准差。下图显示了在标准差为2的高斯导数下的模糊,相较Sobel导数下模糊程度要更大。
在这里插入图片描述

图15 使用高斯导数滤波器计算导数图像(标准差为2)

4.3 形态学:对象计数

形态学是度量和分析基本形状的图像处理方法的基本框架与集合。形态学通常用于处理二值图像,但是也能够用于灰度图像。

scipy.ndimage中的morphology模块可以实现形态学操作,可以使用measurements模块实现二值图像的计数和度量功能。

在一个二值图像中,计算该图像中的对象个数可以通过下边代码实现:

from PIL import Image
from numpy import *
from pylab import *
from scipy.ndimage import measurements, morphology

im = array(Image.open('jimei_grey.jpg'))
im = 1 * (im < 128)

labels, nbr_objects = measurements.label(im)
print("Number of objects:", nbr_objects)

首先载入图片,通过阈值化方式来确保该图像是二值图像。通过和1相乘将布尔数组转换成二进制表示,然后使用label()函数寻找单个的物体,并且按照它们属于哪个对象将整数标签给像素赋值。若是一些对象之间有一些小的连接可以使用开操作将其移出:

im_open = morphology.binary_opening(im, ones((9, 5)), iterations = 2)

label_open, nbr_objects_open = measurements.label(im_open)
print("Number of objects:", nbr_objects)

binart_opening()函数的第二个参数指定一个数组结构元素。该数组表示以一个像素为中心时,使用哪些相邻元素。在这种情况下,我们在y方向上使用9个像素,在x方向上使用5个像素。binary_closing()函数实现相反的操作。

4.4 一些有用的Scipy模块

Scipy中包含一些用于输入和输出的实用模块,例如io和misc。

4.4.1 读写.mat文件

如有一些数据以Matlab的.mat文件格式存储,可以使用scipy.io模块来读取。

data = scipy.io.loadmat('test.mat')

上面代码中,data对象包含一个字典,字典中的键对应于保存在原始.mat文件中的变量名。由于这些变量是数组格式的,因此可以很方便的保存到.mat文件中,只需要创建一个字典然后使用savemat()函数:

data = {}
data['x'] = x
scipy.io.savemat('test.mat', data)

因为上面脚本保存的是数组x,所以当读入到Matlab中时,变量的名字仍为x。

4.4.2 以图像形式保存数组

因为需要对图像进行操作,并且需要使用数组对象来做运算,所以将数组直接保存为图像文件非常有用。

imsave()函数可以从scipy.misc模块中载入,要将数组im保存到文件中,可以使用下面的命令:

from scipy.misc import imsave
imsave('test.jpg', im)

scipy.misc模块同样包含了Lena测试:

lena = scipy.misc.lena()

该脚本返回一个512 * 512的灰度图像数组。

5 实验:图像去噪

图像去噪是在去除图像噪声的同时,尽可能地保留图像细节和结构的处理技术。在接下来的实验中使用ROF去噪模型。ROF去噪模型具有 很好的性质:是处理后的图像更平滑,同时保持图像边缘和结构信息,其理论在本质上是使去噪后的图像像素值“平坦”变换,但是在图像区域的边缘上,允许去噪后的图像像素值“跳跃”变化。

实现ROF模型的代码如下:

from numpy import *
 
def denoise(im,U_init,tolerance=0.1,tau=0.125,tv_weight=100):   
    m,n = im.shape  
    
    U = U_init
    Px = im 
    Py = im 
    error = 1
 
    while(error>tolerance):
        Uold = U

        GradUx = roll(U,-1,axis=1)-U    
        GradUy = roll(U,-1,axis=0)-U    

        PxNew = Px + (tau/tv_weight)*GradUx
        PyNew = Py + (tau/tv_weight)*GradUy
        NormNew = maximum(1,sqrt(PxNew**2+PyNew**2))
 
        Px = PxNew/NormNew  
        Py = PyNew/NormNew  
 
        RxPx = roll(Px,1,axis=1)    
        RyPy = roll(Py,1,axis=0)    
 
        DivP = (Px-RxPx) + (Py-RyPy) 
        U = im + tv_weight*DivP 

        error = linalg.norm(U - Uold)/sqrt(n*m)
 
    return U,im-U 

在这段代码中使用了roll()函数,在坐标轴上循环滚动数组中的元素值。linalg.norm()函数可以衡量两个数组之间的差异。

5.1 对合成的噪声图像去噪

from  PIL import Image
import numpy as np
from pylab import *
from numpy import *
from numpy import random
from scipy.ndimage import filters
import rof
 
figure()
gray()

im = zeros((500,500))
im[100:400,100:400] = 128
im[200:300,200:300] = 255
im = im + 30*random.standard_normal((500,500))
subplot(1,3,1)
imshow(im)
axis("off")
 
U,T = rof.denoise(im,im)
G = filters.gaussian_filter(im,10)
subplot(1,3,2)
imshow(U)
axis("off")

subplot(1,3,3)
imshow(G)
axis("off")

show()

原始图像和去噪结果如下所示:
在这里插入图片描述

图16 对合成噪声去噪

从上图中不难看出,ROF算法去噪后的图像很好地保留了图像的边缘信息。

5.1 对实际图像去噪

from  PIL import Image
import numpy as np
from pylab import *
from numpy import *
from numpy import random
from scipy.ndimage import filters
import rof
 
figure()
gray()

im = array(Image.open('jimei_grey.jpg'))
subplot(1,3,1)
imshow(im)
axis("off")
 
U,T = rof.denoise(im,im)
G = filters.gaussian_filter(im,10)
subplot(1,3,2)
imshow(U)
axis("off")

subplot(1,3,3)
imshow(G)
axis("off")

show()

经过ROF去噪后的图像如下图所示:
在这里插入图片描述

图17 对实际图像去噪

可以明显看出,ROF模型去噪后的图像保留了边缘和图像的结构信息,同时模糊了“噪声”。

总结

在本章主要学习了Matplotlib、NumPy和SciPy三种工具。

Matplotlib是Python中最著名的绘图系统,很多其他的绘图都是由其封装而成。可以通过pyplot,pylab绘制散点图,折线图,条形图,直方图,饼状图等。
Numpy是一个基础的数学计算模块,数据结构为ndarray,可以看做一个N维数组容器,来存储和处理大型矩阵,比Python自身的嵌套列表(nested list structure)结构要高效的多。这个是很基础的扩展,其余的扩展都是以此为基础。
SciPy是基于Numpy的一个科学计算函数库,提供方法(函数库)直接计算结果,封装了一些高阶抽象和物理模型。方便、易于使用、专为科学和工程设计,它包括统计,优化,整合,线性代数模块,傅里叶变换,信号和图像处理,常微分方程求解器等等,基本可以代替Matlab。

猜你喜欢

转载自blog.csdn.net/weixin_42262128/article/details/107609096