实验目的
- 了解图片切换以及应用实现
- 实现中值区分算法
实验工具
python 3.7.0 pycharm
实验内容
实验一
题目
实现一种视频切换,将一张图片慢慢覆盖另一张图片,并通过一个开放的圆圈慢慢显现。写出公式,并在两个图片中获得正确的像素点以实现这种功能,只需要写出红色通道的答案即可。
算法概述
假设两张图片分别为A、B,列与行大小分别为col,row,每一个像素点都是由24位组成的,所以可以得到图片中心点O的坐标为(row/2, col/2) 不考虑奇偶的关系,同时假设图上的任意一个点表示为S,坐标为[x,y]
- 将一个图片慢慢覆盖另外一张图片,其实也就是像素点的替换过程
- 而因为替换过程是一个慢慢放大的圆环,因为这是一个动态的过程。所以可以分为两个步份来实现
- 一个圆环:假设圆环的半径为l
- 一个圆环中点的元素的替代,实际上是通过计算图像上每一个点到O点的距离与l的比值大小关系,也就是(y-col/2)*(y-col/2) + (x-row/2)*(x-row/2) 与 l^2的关系
- 如果大于,则不进行替换操作。
- 如果小于等于,则进行对应通道的赋值操作,因为是24色,一般体现为RGB,所以如果都需要替换则直接整体赋值,而比如需要替换R通道则对列表中的[0]进行替换
- 慢慢放大:也就是意味着圆的半径l是在不断变大的。
- 可以根据要求产生递增序列,以这一个递增序列作为圆的半径变化,然后每一次只需要更新一个圆环(也就是以序列中相邻两个数字为半径构成的圆环)
- 一个圆环:假设圆环的半径为l
- 所以公式如下:也就是距离公式
- 伪代码:
for l in seq for x in row for y in col if (y-col/2)*(y-col/2) + (x-row/2)*(x-row/2) <= l*l im[x][y].Red = im2[x][y].Red
代码实现
from PIL import Image
from numpy import array
import imageio
im = Image.open('诺贝尔.jpg')
im2 = Image.open('lena.jpg')
#利用numpy的array将图片转化为数组
matrix = array(im)
matrix2 = array(im2)
img = []
#对图片进行替换操作
for i in range(16):
for j in range(408):
for k in range(408):
根据图片像素点到圆心的距离判断是否进行替换
if (j-204)*(j-204)+(k-204)*(k-204) <= 400*i*i:
matrix[j][k] = matrix2[j][k]
#将数组重新转化为图片并存进img中
im3 = Image.fromarray(matrix)
img.append(im3)
gifs = []
#将图片转化为numpy数组类型后存进gifs中
for i in range(len(img)):
gifs.append(array(img[i]))
#生成gif图片
imageio.mimsave("problem1.gif", gifs, "GIF", duration=0.5)
实验效果
构成GIF图片的每一帧图片截图:
GIF图片见:https://share.weiyun.com/5vcS9Z2
实验二
题目
对于颜色查找表,在一幅图片上试试中值区分算法,简要解释该算法若用在一幅红苹果图像上,在红颜色中,为什么把更多的颜色等级放在那些需要24位彩色图像的地方。
算法概述
中值区分算法的实现步骤如下:
- 将图片中的矩阵读取出来记为M,将M作为一整个方块。同时创建一个副本为M’
- 将这一个方块M根据当前所选择的通道(有R、G、B三个通道),找出对应的通道的中位数:
- 将相应通道大于该值的放入S1方块中,
- 而小于该值的放入S2方块中
- 等于的则先临时存储起来,然后待全部分割完之后再填充进S1与S2中使得S1与S2大小一样。
- 经过以上步骤之后M便被分割为S1与S2这两个方块,接着对S1与S2以及后续的分割后的方块重复使用以上的步骤直到分割为256个方块。
- 计算每一个方块的颜色的平均值,作为改方块中颜色的替代颜色。也就是对M’中相应的区域的颜色替代为平均值。
- 同时这256种颜色也就构成了颜色查找表。而M’可以生成变换后的图像。
可以知道这一个划分的过程相当于一个递归过程,因为每一个切割后的区域还需要每个单独进行切割,可以知道第n层递归产生的块数为2n,所以切分为256块,需要进行8层递归实现。所以到达第八层递归就是递归的终止条件。
红色颜色更多的原因:
- 因为中值区分算法实际上是一种颜色量化算法,其试图选择一个颜色均衡的颜色表。而这个颜色表是根据划分的颜色块有关的
- 而颜色更为集中的区块,也就意味着这一种颜色在一个图像中占有的比例更高,所以就会有更多的块含有这一种颜色。换句话说,出现更频繁的颜色将得到优先处理。这种算法考虑到具体图象的色彩分布,所得到的图象显示层次感丰富,较为常用。
- 因此根据中值区分算法的实现原理可以知道红颜色在查找表中也会相应的更多。
代码实现
import numpy
from PIL import Image
from numpy import array
from numpy import mean
from numpy import zeros
from numpy import median
#颜色查找表
global palette
#颜色查找表当前的大小
global size
#图片像素的cpoy
global matrixcopy
palette = []
size = 0
class BaseColor:
x = 0
y = 0
color = [0, 0, 0]
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
def main():
global palette
global size
global matrixcopy
im = Image.open("redapple.jpg")
matrix = array(im)
matrix2 = []
matrixcopy = matrix
for i in range(len(matrix)):
for j in range(len(matrix[0])):
matrix2.append(BaseColor(i, j, matrix[i][j]))
slice(matrix2, 0)
print(len(palette))
im = Image.fromarray(matrixcopy)
im.save("256.jpg")
palette_matrix = array(zeros([256, 100, 3], numpy.uint8))
for i in range(256):
for j in range(100):
palette_matrix[i][j] = palette[i]
im2 = Image.fromarray(palette_matrix)
im2.show()
im2.save("palette.jpg")
# 递归分割获取颜色查找表
def slice(slice_colors, depth):
global palette
global size
global matrixcopy
if depth > 7:
size = size + 1
r, g, b = [], [], []
for i in range(len(slice_colors)):
r.append(slice_colors[i].color[0])
g.append(slice_colors[i].color[1])
b.append(slice_colors[i].color[2])
mean_color = [mean(r, dtype=int), mean(g, dtype=int), mean(b, dtype=int)]
for i in range(len(slice_colors)):
matrixcopy[slice_colors[i].x][slice_colors[i].y] = mean_color
palette.append(mean_color)
return
currentrgb = []
for i in range(len(slice_colors)):
currentrgb.append(slice_colors[i].color[depth%3])
currentrgbmedian = median(currentrgb)
zero, one = [], []
wait_slice = []
#根据颜色与中值颜色的大小比较
#若颜色大于中值颜色,则插入one中,隐式序列号
#若颜色小于中值颜色,则插入zero中
#若颜色等于中值颜色,则插入临时数组之中以待后续操作
for i in range(len(slice_colors)):
if slice_colors[i].color[depth%3] > currentrgbmedian:
one.append(slice_colors[i])
elif slice_colors[i].color[depth%3] == currentrgbmedian:
wait_slice.append(slice_colors[i])
else:
zero.append(slice_colors[i])
#计算出每一个数组应有的大小
num2 = int(len(slice_colors)/2)
#将wait_slice中的数据分割出去,使得zero和one的大小一致
for i in range(len(wait_slice)):
if(len(one) < num2):
one.append(wait_slice[i])
else:
zero.append(wait_slice[i])
#递归
slice(one, depth + 1)
slice(zero, depth + 1)
return
if __name__ == "__main__":
main()
实验效果
原图片为:
变化后的256色图如下:
颜色查找表如下: