其实生成马赛克的方法有很多种,最简单的方法是用Opencv来生成,用Opencv生成马赛克的方法会在文末列出,只有几行代码。
这里我们用numpy和PIL(Python图像库),主要是学习编程思路和几个关键方法的应用:
比如以下方法:
编程思路:
以下是实现的代码,重要的位置用中文注释
import sys, os, random, argparse
from PIL import Image
import numpy as np
def getAverageRGB(image):
"""
return the average RGB value of an image
"""
# 将图像转换为numpy数组
im = np.array(image)
# 得到每个输入图像的形状
w, h, d = im.shape
# 求平均值
ava_color = np.trunc((np.average(im.reshape(w * h, d), axis=0)))
# 转换为元组,元组中的元素为整数
ava_color = tuple(int(item) for item in ava_color)
return ava_color
def splitImage(image, size):
"""
given the image and dimensions(rows,cols), return an m*n list of images
"""
W, H = image.size[0], image.size[1]
m, n = size
w, h = int(W / n), int(H / m)
# 图像列表
imgs = []
for j in range(m):
for i in range(n):
# 添加裁剪后的图像到列表
imgs.append(image.crop((i * w, j * h, (i + 1) * w, (j + 1) * h)))
return imgs
def getArray(img_shape, avg_color):
"""
get a numpy array with average RGB value
"""
avg = avg_color # 输入的图像颜色值
# 创建一个指定形状和类型的数组
splitArray = np.zeros((img_shape[0], img_shape[1], 3), dtype=np.uint8)
splitArray[:, :, :] = avg # 以指定颜色值填充整个数组
return splitArray
def creatImageGrid(images, dims):
"""
given a list of images and a grid size(m,n), return a new image
"""
m, n = dims
# assert断言,如果条件为假,则终止程序
assert m * n == len(images)
# 得到第一张图像的宽度和高度
width = images[0].shape[0]
height = images[0].shape[1]
# 这里不假设所有图像的大小相同,所以需要遍历所有图像,找到最大的宽度和高度,我们这里是相同的,
# 为了方便,我们可以假设所有图像的大小相同,注释掉下面两行代码
# width = max([img.shape[0] for img in images])
# height = max([img.shape[1] for img in images])
# 创建一个新的图像,模式为RGB,大小为n*width*m*height
grid_image = Image.new('RGB', (n * width, m * height))
for j in range(m):
for i in range(n):
# 粘贴图像到ixj网格的指定位置,左上角坐标为(i * width, j * height),Image.fromarray()将numpy数组转换为PIL图像
grid_image.paste(Image.fromarray(images[j * n + i]), (i * width, j * height))
grid_image.show()
return grid_image
def createPhotomosaic(target_image, grid_size):
"""
create photomosaic given the target image and grid size
"""
print("正在分割输入的图像...")
# 分割图像
target_images = splitImage(target_image, grid_size)
print("正在生成输出图像...")
output_images = []
# 计数器
count = 0
batch_size = int(len(target_images) / 10)
for img in target_images:
# 计算每个图像的平均RGB值
avg_color = getAverageRGB(img)
img = np.array(img) # 将PIL图像转换为numpy数组
array = getArray(img.shape, avg_color) # 生成指定形状和颜色的numpy数组
output_images.append(array) # 将生成的numpy数组添加到输出图像列表中
# 用户反馈
if count > 0 and batch_size > 10 and count % batch_size == 0: # 每处理10%的图像,打印一次进度
print('完成 %d 之 %d...' % (count, len(target_images)))
count += 1
print('正在生成马赛克...')
# 创建马赛克图像
mosaic_image = creatImageGrid(output_images, grid_size)
# 显示图像
return mosaic_image
# main 函数
def main():
# command line arguments are in sys.argv[1], sys.argv[2] ...
# sys.argv[0] is the name of the script itself and can be ignored
# 创建参数解析器
parser = argparse.ArgumentParser(description='Create a photomosaic.')
# 添加参数
parser.add_argument('--target-image', dest='targetImage', required=True)
parser.add_argument('--grid-size', nargs=2, dest='grid_size', required=True)
parser.add_argument('--output-file', dest='output_file', required=False)
# 解析参数
args = parser.parse_args()
###### INPUTS ######
# 目标图像
target_image = Image.open(args.targetImage)
# 网格大小
grid_size = (int(args.grid_size[0]), int(args.grid_size[1]))
# 输出图像
output_filename = 'mosaic.png'
if args.output_file:
output_filename = args.output_file
###### END INPUTS ######
print('开始创建马赛克图像...')
# 调用函数创建马赛克图像
mosaic_image = createPhotomosaic(target_image, grid_size)
# 保存图像
mosaic_image.save(output_filename, 'PNG')
print('保存图像至 %s' % output_filename)
print('完成')
# 调用main函数
if __name__ == '__main__':
main()
python .\newphotomosaic.py --target-image .\R-C.jpg --grid-size 100 100
import cv2 as cv
import sys
import numpy as np
if __name__ == '__main__':
img = cv.imread('R-C.jpg')
if img is None:
print('读取图片失败')
sys.exit()
else:
print('原始图像的形状:{}\n元素数据类型:{}\n图像通道数:{}\n像素总数:{}'
.format(img.shape, img.dtype, img.ndim, img.size))
# 缩小图像,插值方法为双线性插值
mosaic_m=50 #水平方向上的马赛克像素数
mosaic_n=100 #垂直方向上的马赛克像素数
# 缩小图像,插值方法为双线性插值
small_image = cv.resize(img, (mosaic_m, mosaic_n), interpolation=cv.INTER_LINEAR)
img1 = np.repeat(small_image, int(img.shape[0]/mosaic_n), axis=0) # 沿着列(垂直)方向重复,高度尺寸接近原图像
mosaic_image = np.repeat(img1, int(img.shape[1]/mosaic_m), axis=1) # 沿着行(水平)方向重复,宽度尺寸接近原图像
print('放大图像的形状:{}\n元素数据类型:{}\n图像通道数:{}\n像素总数:{}'
.format(mosaic_image.shape, mosaic_image.dtype, mosaic_image.ndim, mosaic_image.size))
cv.namedWindow('mosaic1', cv.WINDOW_AUTOSIZE)
cv.imshow('mosaic1', mosaic_image)
cv.imwrite('mosaic1.jpg', mosaic_image)
cv.waitKey(0)
cv.destroyAllWindows()