“PK creative Spring Festival, I am participating in the” Spring Festival Creative submission contest “, please see: Spring Festival Creative Submission Contest”

· Pixelated images celebrating the Year of the Tiger using Python

1. Show the effect

2. Split the steps

2.1 Understanding Pixels

In our lives, we often hear that the resolution of a certain electronic device is 2K, or 4K and higher. And this among them 2K, 4K refers to what on earth?

2K and 4K are both a description of the resolution of an electronic product. 2K refers to 1920 pixels in the horizontal direction and 1080 pixels in the vertical direction. 4K refers to 3840 pixels in the horizontal direction and 2160 pixels in the vertical direction of the electronic product.

The electronic image is also composed of pixels in the horizontal direction and pixels in the vertical direction one by one. When we display the electronic image on electronic products, if the image is magnified by more than 100%, the larger the amplification ratio, the more blurred the image will be. As shown below:

2.2 Understand the pixelated style

Pixelated style is commonly seen in early video games, such as Dinosaur Kombat, King of Fighters, Street Fighter, etc. Early electronic products do not have the resolution of 2K and 4K as now, so they can only provide more images for players in a limited resolution. As shown below:

2.3 Idea of pixelation effect

At this point, we have to think about the question, how do we pixelate the image without changing the resolution of the image? Since the resolution cannot be changed, that is, the number of pixels in the horizontal and vertical directions of the image remains the same. Under the condition that the number remains unchanged, we can divide the image into blocks one by one, and set the color of all pixels in this block to the color of pixels with the most same color in this block, so as to achieve pixelation.

2.4 Achieve image pixelation effect

Against 2.4.1 install Pillow

Pillow is a module in Python dedicated to image processing, and everything we do next is based on the various functions provided in this module. From the command line window, we enter the following statement to install this module.

pip install pillow
Copy the code

2.4.2 Import the image processing module in PIL

from PIL import Image
Copy the code

2.4.3 Write a function to modify the color of pixels in an image

Modify a pixel of the image
# x represents the horizontal position of the pixel, y represents the vertical position of the pixel, color represents the color of the pixel, and image represents the picture
def putPixel(x, y, color, image) :
    image.putpixel((x, y), color)
Copy the code

2.4.4 Write a function to modify the color of all pixels in a certain block of the image

# Change the color of all pixels in a block of the image
# startX indicates the horizontal position of the block, startY indicates the vertical position of the block, blockSize indicates the blockSize, and image indicates the 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) Get the color of the pixel that appears most often
    for i in range(startX, startX + blockSize):
        for j in range(startY, startY + blockSize):
            putPixel(i, j, color, image)
Copy the code

2.4.5 Write functions to determine whether image boundary pixels need to be processed

The reason why we need to write a function to determine whether to process image boundary pixels is that we used square blocks to change the color of all pixels in a certain block at the beginning. Using square blocks can better realize image pixelation, as shown in the following figure:

In this figure, purple is the image pixel to be processed, red is the image pixel to be processed, orange is the pixel where the initial horizontal and vertical coordinates of the processed image block are located, and blue is the image boundary pixel that has been processed

In above, if the level of the image pixels and the number of vertical pixels is not 1 to 1, then there will be an image pixel cannot be pixelated, if the level of the image pixels for five (5), vertical pixels for four, and we set the size of the block of 4 (level for four pixels, vertical pixels to 4), Then the last column of pixels in the image cannot be processed.

Similarly, if the image has 5 horizontal pixels and 4 vertical pixels, and we set the block size to 4 (4 horizontal pixels and 5 vertical pixels), then the last line of the image cannot be processed.

Therefore, we need to determine whether unprocessed image boundary pixels need to exist.

# Determine whether there are unprocessed image boundary pixels
# blockSize indicates the blockSize and image indicates the image
def shouldHandleOtherWidthAndHeight(blockSize, image) :
    width, height = image.size
    shouldHandleOtherWidth = False
    shouldHandleOtherHeight = False
    ifwidth % blockSize ! =0:
        shouldHandleOtherWidth = True
    ifheight % blockSize ! =0:
        shouldHandleOtherHeight = True

    return shouldHandleOtherWidth, shouldHandleOtherHeight
Copy the code

So how to deal with unprocessed image boundary pixels?

For the unprocessed image boundary pixel, we can calculate the initial horizontal coordinate and initial vertical coordinate of the block closest to the boundary pixel, and modify the color of all pixel points in this block in the image.

If the unprocessed column pixels in the image are processed, the initial horizontal coordinates of the block of the image to be processed will not change, and the initial vertical coordinates will change. As we see in the image above, the orange pixels are the pixels where the initial horizontal and vertical coordinates of the processing image block are located.

If the unprocessed row pixels in the image are processed, the initial horizontal coordinates of the block of the image to be processed will change, and the initial vertical coordinates will remain unchanged. As we see in the image above, the orange pixels are the pixels where the initial horizontal and vertical coordinates of the processing image block are located.

3. Complete code (based on Python3)

from PIL import Image


Modify a pixel of the image
# x represents the horizontal position of the pixel, y represents the vertical position of the pixel, color represents the color of the pixel, and image represents the picture
def putPixel(x, y, color, image) :
    image.putpixel((x, y), color)


Determine whether there are unprocessed image pixels
# blockSize indicates the blockSize and image indicates the image
def shouldHandleOtherWidthAndHeight(blockSize, image) :
    width, height = image.size
    shouldHandleOtherWidth = False
    shouldHandleOtherHeight = False
    ifwidth % blockSize ! =0:
        shouldHandleOtherWidth = True
    ifheight % blockSize ! =0:
        shouldHandleOtherHeight = True

    return shouldHandleOtherWidth, shouldHandleOtherHeight


# Change the color of all pixels in a block of the image
# startX indicates the horizontal position of the block, startY indicates the vertical position of the block, blockSize indicates the blockSize, and image indicates the 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) Get the color of the pixel that appears most often
    for i in range(startX, startX + blockSize):
        for j in range(startY, startY + blockSize):
            putPixel(i, j, color, image)


Image pixelation function
# imagePath specifies the imagePath and blockSize specifies the blockSize
def run(imagePath, blockSize) :
    image = Image.open(imagePath)
    width, height = image.size
    shouldHandleOtherWidth, shouldHandleOtherHeight = shouldHandleOtherWidthAndHeight(blockSize, image)

    # Calculate the number of blocks the image can hold
    widthTimes = (int)(width / blockSize)
    heightTimes = (int)(height / blockSize)

    # Process image pixels within blocks
    for i in range(0, widthTimes):
        startX = i * blockSize
        for j in range(0, heightTimes):
            startY = j * blockSize
            handleBlockPixel(startX, startY, blockSize, image)

    # Process unprocessed image pixels
    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)
    
    Save the pixelated image
    image.save('result.jpg')

# main function
if __name__ == "__main__":
    run("image.jpg".5)
Copy the code

Author: Toyo

Copyright notice: This article is an original article, shall not be reproduced without my permission.