图像放大缩小原理

图像放大

放大图像的关键是增加像素。

图像放大后原像素点的相对位置关系应该是不变的,所以对于小图中的每一个点,只需要将他们的坐标乘以对应的放大倍数(横纵),就可以得到小图中的点在放大后图像中的坐标。此时图像中存在大量未被填充的区域,所以需要通过一定的算法将这些区域填充完毕。

例如将一幅图片放大3*3倍,未被填充的区域先用黑色代替。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import cv2
import numpy

img_address = 'C:/Users/kyle/Desktop/lena.png'
img = cv2.imread(img_address)

height = img.shape[0]
width = img.shape[1]
x_rate = 6
y_rate = 6
bigger_pic = numpy.zeros([height*y_rate, width*x_rate, 3], numpy.uint8)

for i in range(height):
for j in range(width):
bigger_pic[i*y_rate][j*x_rate] = img[i][j]

cv2.imwrite('C:/Users/kyle/Desktop/lena1.png', bigger_pic)

原图:

test

结果:

test1

最邻近点插值法

​ 在未填充的区域,计算离该点最近的已填充像素点,然后使用它的颜色信息给该点赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 最邻近点插值法
def Nearest_Neighbor(former_img, rate_y, rate_x):
for i in range(former_img.shape[0]):
for j in range(former_img.shape[1]):
if i//rate_y == 0 and j//x_rate == 0: # 已赋值的点
continue # 跳过
if i % rate_y > rate_y/2:
y_index = i + rate_y - i % rate_y
else:
y_index = i - i % rate_y
if j % rate_x > rate_x/2:
x_index = j + rate_x - j % rate_x
else:
x_index = j - j % rate_x
if y_index >= former_img.shape[0]:
y_index -= rate_y
if x_index >= former_img.shape[1]:
x_index -= rate_x
former_img[i][j] = former_img[y_index][x_index]

结果:

test2

这种算法虽然运行速度较快,但是当放大倍数增大时,很容易出现类似“马赛克”的效果,整体比较僵硬。

双线性差值法

​ 最邻近点插值法的问题在于用于不够自然,一个人颜色到另一个颜色的变化来的太突然了,所以如果可以将填充部分的过度更加自然,那么放大的效果就会比较好。

​ 因此对于点的赋值不仅仅只和一个点有关,而与附近的点都有关。对于一个待填充点,考察将它包围的四个已填充点的像素值,离得越近的点,相关程度越大。最后按权相加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def Bilinear_Interpolation(former_img, rate_y, rate_x):
for i in range(former_img.shape[0]):
for j in range(former_img.shape[1]):
if i//rate_y == 0 and j//x_rate == 0: # 已赋值的点
continue # 跳过
# 左上角包围点的坐标
y = i - i % rate_y
x = j - j % rate_x
# print(y,x)

# 先考虑边界情况,在已经赋值的点中,最大纵坐标为former_img.shape[0]-rate_y-1,最大横坐标为former_img.shape[1]-rate_x-1
if y + rate_y >= former_img.shape[0]-rate_y: # 只有上方两个点
if x + rate_x >= former_img.shape[1]-rate_x: #是最右下角一小块,直接用左上角点赋值
former_img[i][j] = former_img[y][x]
else: # 根据和两个点横坐标距离比例计算比例,与左上角点横坐标距离为(j%rate_x),与右上角点横坐标距离为rate_x - j % rate_x
former_img[i][j] = (rate_x - j % rate_x)/rate_x * former_img[y][x] + \
j % rate_x/rate_x * former_img[y][x+rate_x]
elif x + rate_x >= former_img.shape[1]-rate_x:
former_img[i][j] = (rate_y - i % rate_y)/rate_y * former_img[y][x] + \
i % rate_y/rate_y * former_img[y + rate_y][x]

else: # 正常情况,将四个点分成上下两对,先分别计算出横向的比例结果,在计算纵向的比例结果。
tmp1 = (rate_x - j % rate_x)/rate_x * former_img[y][x] + j % rate_x/rate_x * former_img[y][x+rate_x]
tmp2 = (rate_x - j % rate_x)/rate_x * former_img[y+rate_y][x] + j % rate_x/rate_x * former_img[y+rate_y][x+rate_x]
former_img[i][j] = tmp1 * (rate_y - i % rate_y)/rate_y + tmp2 * (i%rate_y)/rate_y

结果:

test20

整体看上去比最临近插值法好很多。

双立方插值法

与双线性插值法类似,双立方插值法运用周围的16个点来进行权值计算,效果更优,代码略。

图像缩小

​ 与放大图像相反,图像缩小时需要舍弃很多像素,相比于放大简单,但肯定会造成失真。

​ 一种最简单的方法就是根据缩小的比例在一个区域中任取一个点。这种方法简单又快速,但是考虑到图像中可能会存在噪声点,因此这种方法得到的缩小图像可能也会有噪声点。

​ 第二个方法就是取一个区域中的平均值,这样可以大幅减小图像中噪声点的影响,相比于第一种方式得到的结果也会更加平滑。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!