春节创意投稿·使用Python对虎年庆贺图像进行像素化

528 阅读7分钟

“ PK创意闹新春,我正在参加「春节创意投稿大赛」,详情请看:春节创意投稿大赛

春节创意投稿·使用Python对虎年庆贺图像进行像素化

1. 展示效果

pic1.jpg

pic2.jpg

2. 步骤拆分

2.1 理解像素

生活中,我们可以常常听见某个电子产品的分辨率是2K,或者是4K以及更高的分辨率数值。而这其中的2K、4K指的究竟是什么呢?

2K、4K等都是对电子产品的分辨率的一个描述,2K指的是此电子产品的水平方向上有1920个像素点,垂直方向上有1080个像素点,4k即指此电子产品的水平方向上有3840个像素点,垂直方向上有2160个像素点。

而电子图像也是由水平方向上的像素以及垂直方向上的像素一个一个组成的,当我们在电子产品上展示电子图像时,若对图像进行超过100%的放大时,放大的比例越大时,我们可以看到图像会越模糊。如下图所示:

pic3.jpg

pic4.jpg

2.2 了解像素化风格

像素化风格常见于早期的电子游戏中,如恐龙快打、拳皇、街头霸王等游戏,早期的电子产品并没有有像现在的动辄2K、4K的分辨率,因此只能在有限的分辨率中尽可能的为玩家提供的更多的图像。如下图所示:

pic5.jpg

2.3 实现像素化效果思路

至此,我们要思考一个问题,我们如何在不改变图像分辨率的情况下将图像进行像素化?既然不能改变分辨率,即图像的水平方向上的像素点与垂直方向上的像素点数量不变。在数量不变的情况下,我们可以为图像划分成一个一个的区块,并将此区块所有的像素点颜色全部设置为此区块中出现最多相同颜色的像素点的颜色,从而来实现像素化。

pic6.jpg

2.4 实现图像像素化效果

2.4.1 安装Pillow

Pillow是Python中专门用于图像处理的一个模块,我们接下来的任何操作都是基于此模块中提供的各种函数来完成。在命令行窗口中,我们输入以下的语句对此模块进行安装。

pip install pillow

2.4.2 导入PIL中处理图像的模块

from PIL import Image

2.4.3 编写修改某个图像的像素点的颜色的函数

# 修改图像的某个像素点
# x表示像素点的水平位置,y表示像素点的垂直位置,color表示像素的颜色,image表示图片
def putPixel(x, y, color, image):
    image.putpixel((x, y), color)

2.4.4 编写修改图像某个区块中所有像素点颜色的函数

# 修改图像的某个区块中所有像素点的颜色
# startX表示区块的水平位置,startY表示区块的垂直位置,blockSize表示区块大小,image表示图片
def handleBlockPixel(startX, startY, blockSize, image):
    rgbList = []
    for i in range(startX, startX + blockSize):
        for j in range(startY, startY + blockSize):
            rgbList.append(image.getpixel((startX, startY)))

    color = max(rgbList, key=rgbList.count) # 获取出现次数最多的像素点颜色
    for i in range(startX, startX + blockSize):
        for j in range(startY, startY + blockSize):
            putPixel(i, j, color, image)

2.4.5 编写判断是否需要处理图像边界像素点的函数

之所以需要编写判断是否处理图像边界像素的函数,是因为一开始我们是采用正方形的区块来修改某个区块中所有像素点颜色,采用正方形的区块可以更好地来实现图像的像素化,观察下图:

pic7.jpg

此图中紫色待处理的图像像素点,红色已处理的图像像素点,橙色为处理图像区块的起始水平坐标和起始垂直坐标所在的像素点,蓝色为已处理的图像边界像素点

在上图,若图像的水平像素点与垂直像素点的个数不是1比1的话,那么就会出现某个图像的像素点无法被像素化,若图像的水平像素点为5个,垂直像素点为4个的话,且我们设置区块的大小为4(水平像素点为4个,垂直像素点为4个)的话,那么就会出现图像的最后一列像素点无法被处理

相同的,若图像的水平像素点为5个,垂直像素点为4个的话,且我们设置区块的大小为4(水平像素点为4个,垂直像素点为5个)的话,那么就会出现图像的最后一行像素点无法被处理

而因此,我们需要判断是否需要存在未被处理的图像边界像素点。

# 判断是否存在未被处理的图像边界像素点
# blockSize表示区块大小,image表示图片
def shouldHandleOtherWidthAndHeight(blockSize, image):
    width, height = image.size
    shouldHandleOtherWidth = False
    shouldHandleOtherHeight = False
    if width % blockSize != 0:
        shouldHandleOtherWidth = True
    if height % blockSize != 0:
        shouldHandleOtherHeight = True

    return shouldHandleOtherWidth, shouldHandleOtherHeight

那么如何处理未被处理的图像边界像素点?

针对于未被处理的图像边界像素点,我们可以计算出距离边界像素最近的区块的起始水平坐标以及起始垂直坐标,并修改图像中这个区块中所有像素点颜色即可。

如果处理图像中未被处理的列像素点时,那么需要处理的图像的区块的起始水平坐标不变,起始的垂直坐标会变。如前面我们所看到的图像,其中橙色的像素点即为处理图像区块的起始水平坐标和起始垂直坐标所在的像素点。

如果处理图像中未被处理的行像素点时,那么需要处理的图像的区块的起始水平坐标会变,起始的垂直坐标不变。如前面我们所看到的图像,其中橙色的像素点即为处理图像区块的起始水平坐标和起始垂直坐标所在的像素点。

3. 完整代码(基于Python3)

from PIL import Image


# 修改图像的某个像素点
# x表示像素点的水平位置,y表示像素点的垂直位置,color表示像素的颜色,image表示图片
def putPixel(x, y, color, image):
    image.putpixel((x, y), color)


# 判断是否存在未被处理的图像像素点
# blockSize表示区块大小,image表示图片
def shouldHandleOtherWidthAndHeight(blockSize, image):
    width, height = image.size
    shouldHandleOtherWidth = False
    shouldHandleOtherHeight = False
    if width % blockSize != 0:
        shouldHandleOtherWidth = True
    if height % blockSize != 0:
        shouldHandleOtherHeight = True

    return shouldHandleOtherWidth, shouldHandleOtherHeight


# 修改图像的某个区块中所有像素点的颜色
# startX表示区块的水平位置,startY表示区块的垂直位置,blockSize表示区块大小,image表示图片
def handleBlockPixel(startX, startY, blockSize, image):
    rgbList = []
    for i in range(startX, startX + blockSize):
        for j in range(startY, startY + blockSize):
            rgbList.append(image.getpixel((startX, startY)))

    color = max(rgbList, key=rgbList.count) # 获取出现次数最多的像素点颜色
    for i in range(startX, startX + blockSize):
        for j in range(startY, startY + blockSize):
            putPixel(i, j, color, image)


# 图像像素化函数
# imagePath表示图片路径,blockSize表示区块大小
def run(imagePath, blockSize):
    image = Image.open(imagePath)
    width, height = image.size
    shouldHandleOtherWidth, shouldHandleOtherHeight = shouldHandleOtherWidthAndHeight(blockSize, image)

    # 计算出图像能容纳多少个区块的个数
    widthTimes = (int)(width / blockSize)
    heightTimes = (int)(height / blockSize)

    # 处理区块内的图像像素点
    for i in range(0, widthTimes):
        startX = i * blockSize
        for j in range(0, heightTimes):
            startY = j * blockSize
            handleBlockPixel(startX, startY, blockSize, image)

    # 处理未被处理的图像像素点
    if (shouldHandleOtherHeight):
        for i in range(0, heightTimes):
            startY = i * blockSize
            handleBlockPixel(width - blockSize, startY, blockSize, image)

    if (shouldHandleOtherWidth):
        for i in range(0, widthTimes):
            startX = i * blockSize
            handleBlockPixel(startX, height - blockSize, blockSize, image)
    
    # 保存完成像素化的图片
    image.save('result.jpg')

# 主函数
if __name__ == "__main__":
    run("image.jpg", 5)

作者:通雄

版权声明:本文为原创文章,未经本人允许不得转载。