python 简单图像处理(5) 缩放
好啦,在这里我要介绍图像的缩放啦
图像比例缩放是指将给定的图像在x轴方向按比例缩放,在y轴方向也按比例缩放,从而获得一幅新的图像。
如果两个方向上缩放比例相等,则为全比例缩放,否则为非全比例缩放
比例缩放用矩阵形式可表示为
代数式为
我们先来看图像的比例缩小变换
介绍两种简单的缩小变换方法:
- 基于等间隔采样的图像缩小方法
- 基于局部均值的图像缩小方法
基于等间距采样的图像缩小方法的设计思路是,对画面像素均匀采样来保持所选像素仍旧可以保持图像的特征
说白了,就是选择一些点,舍弃一些点,用选择的点组成一幅图,使它和原图差不多
若比例因子为m和n,原图的矩阵为f[M×N],缩小后的矩阵为g[(M*m)×(N*n)]
由于原图像素点多,映射到g后会出现多点对一点的现象
所以我们从g映射到f,
则有g(i,j) = f(i/m,j/n)
我们可以很快写出测试程序:
import cv
def Reduce(image,m,n):
H = int(image.height*m)
W = int(image.width*n)
size = (W,H)
iReduce = cv.CreateImage(size,image.depth,image.nChannels)
for i in range(H):
for j in range(W):
x = int(i/m)
y = int(j/n)
iReduce[i,j] = image[x,y]
return iReduce
image = cv.LoadImage('lena.jpg',1)
iReduce1 = Reduce(image,0.7,0.6)
iReduce2 = Reduce(image,0.8,0.8)
cv.ShowImage('image',image)
cv.ShowImage('iReduce1',iReduce1)
cv.ShowImage('iReduce2',iReduce2)
cv.WaitKey(0)
这里我们一个是非全比例缩放,一个是全比例缩放
运行效果如下:
第二种缩放方法是基于局部均值的图像缩放方法
前面一种方法舍弃了很多点的信息,而局部均值法可以解决这个问题
我们说过f到g是一个多对一的映射
前面的方法,我们简单的选取了其中的一个来映射到g
而这里,我们将找出所有映射到g(i,j)上的点,然后对他们的值取均值
好啦,具体思路如下
此时g(i,j)不只是简单映射到f(i/m,j/n)了
而是可以映射到如下矩阵
我们对矩阵中的元素取平均值就是g(i,j)的值了
好了,我们得到的程序如下:
import cv
def JReduce(image,m,n):
H = int(image.height*m)
W = int(image.width*n)
size = (W,H)
iJReduce = cv.CreateImage(size,image.depth,image.nChannels)
for i in range(H):
for j in range(W):
x1 = int(i/m)
x2 = int((i+1)/m)
y1 = int(j/n)
y2 = int((j+1)/n)
sum = [0,0,0]
for k in range(x1,x2):
for l in range(y1,y2):
sum[0] = sum[0]+image[k,l][0]
sum[1] = sum[1]+image[k,l][1]
sum[2] = sum[2]+image[k,l][2]
num = (x2-x1)*(y2-y1)
iJReduce[i,j] = (sum[0]/num,sum[1]/num,sum[2]/num)
return iJReduce
image = cv.LoadImage('lena.jpg',1)
iJReduce1 = JReduce(image,0.7,0.6)
iJReduce2 = JReduce(image,0.8,0.8)
cv.ShowImage('image',image)
cv.ShowImage('iJReduce1',iJReduce1)
cv.ShowImage('iJReduce2',iJReduce2)
cv.WaitKey(0)
运行效果如下:
和刚才的差不多
那两种方法到底有没有差别呢,对比了才知道:
我们用一副大图,用0.1的全比例缩放。
查看结果对比
能看出差别吗?左边为第二种方法,右图为第一种方法
其实还是很明显的,第一种方法图片比较生硬,很多锯齿。第二种方法就比较平滑,但会有轻微的模糊效果。
第二种算法和后面要学的空域低通滤波算法很相似,所以会出现模糊的效果。以后会讲到。
好了,现在我们再来讲讲放大的简单算法
放大的一般算法也是两种:最近领域法和线性插值法
简单的说,最近领域法是直观的放大
放大后会变成
好了,没什么好说的,直接编程序有:
import cv
def Zoom(image,m,n):
H = int(image.height*m)
W = int(image.width*n)
size = (W,H)
iZoom = cv.CreateImage(size,image.depth,image.nChannels)
for i in range(H):
for j in range(W):
x = int(i/m)
y = int(j/n)
iZoom[i,j] = image[x,y]
return iZoom
image = cv.LoadImage('lena3.jpg',1)
iZoom1 = Zoom(image,2,3)
iZoom2 = Zoom(image,2.5,2.5)
cv.ShowImage('image',image)
cv.ShowImage('iZoom1',iZoom1)
cv.ShowImage('iZoom2',iZoom2)
cv.WaitKey(0)
事实上这个程序和第一个缩小程序是一样的,呵呵
只需要把m,n设为大于1的数就可以了
看看效果吧
原图是最小的那个,那看了马赛克了吧,哈哈
下面我们来讲讲线性插值法:
我们仍用f(M×N)和g((M*m)×(N*n))来代表原图和处理后的图片
不同的是这次g比f更大
g对f映射时,会出现下面的情况
投射到(x,y)上后,位于ABCD四个整点中间,若是直接取整,得到的和第一种方法一样了
我们采用插值法
(x,y) = q(1-p)*A+(1-q)(1-p)*B+p(1-q)*C+pq*D
好了,我们来编写程序为:
import cv
import math
def JZoom(image,m,n):
H = int(image.height*m-m)
W = int(image.width*n-n)
size = (W,H)
iZoom = cv.CreateImage(size,image.depth,image.nChannels)
sum = [0,0,0]
for i in range(H):
for j in range(W):
x1 = int(math.floor((i+1)/m-1))
y1 = int(math.floor((j+1)/n-1))
p = (i+0.0)/m - x1
q = (j+0.0)/n - y1
for k in range(3):
sum[k] = int(image[x1,y1][k]*(1-p)*(1-q)+image[x1+1,y1][k]*p*(1-q)+image[x1,y1+1][k]*(1-p)*q+image[x1+1,y1+1][k]*p*q)
iZoom[i,j] = (sum[0],sum[1],sum[2])
return iZoom
image = cv.LoadImage('lena3.jpg',1)
iZoom1 = JZoom(image,2,3)
iZoom2 = JZoom(image,2.5,2.5)
cv.ShowImage('image',image)
cv.ShowImage('iZoom1',iZoom1)
cv.ShowImage('iZoom2',iZoom2)
cv.WaitKey(0)
这里简化了映射点恰好在整点的情况,所以在H和W中分别减去了m和n
我们看看运行结果吧
照例来对比一下吧
仔细看看眼睛,鼻子,嘴,肩膀
能看出差别了吧
好了。图像的缩放就讲到这里了吧