[PYTHON] A story of creating 16 * 16 dots from a Digimon photo

Introduction

I'm a programming beginner (a hobbyist, non-engineer). It is created while studying. I referred to the following page. Thank you very much.

https://postd.cc/image-processing-101/

environment

Windows10 Python 3.8 OpenCV 4.0.1 notebook

Motivation and flow to start

I make Digimon apps as a hobby. (↓ like this) スクリーンショット 2021-01-04 181541.png

Digimon to be incorporated into the app is Digimon Pendulum Z

  1. Nurture
  2. Take a photo
  3. Make pixel art by hand I prepared it in the flow.

The ** dot picture making ** part of these 3 became very troublesome.

Because the actual Digimon itself is drawn in 16x16. Even if you draw it as it is, it will be blurred if you stretch it (Left: Agumon of the actual machine Right: Agumon drawn with 32 x 32)

Besides, I want to express the line between the dots of the actual machine **, so I drew it as 1 dot 7px (1px between dots) with 8x 128x128.

This ~~ due to useless commitment ~~ Dot striking became very troublesome. I wondered if I could make a pixel art from a photo **.

Start

In the flow Original image → Threshold value binarization → Regeneration Start thinking about that. (Mostly just trace the pages that I referred to)

1. Image preparation

apocaly_0.jpg This time I prepared Apocalymon (apocaly_0.jpg). It has been trimmed in the area of ​​16x16 that you want to create.

#Various imports
import cv2, matplotlib
import numpy as np
import matplotlib.pyplot as plt

#Loading images
img = cv2.imread('images/apocaly_0.jpg')

2. Threshold processing

#Grayscale
gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

#Binarized at threshold 128
_, threshold_img = cv2.threshold(gray_img, 128, 255, cv2.THRESH_BINARY)
 
#Display image by returning grayscale to RGB
threshold_img = cv2.cvtColor(threshold_img, cv2.COLOR_GRAY2RGB)
plt.imshow(threshold_img)

スクリーンショット 2021-01-04 180123.png

It was nicely divided into black and white. I tried some threshold adjustments and settled on 128.

Divide the image into 16x16 and calculate the area value

#16 divisions of height and width of the image itself
h, w, _ = img.shape
cell_width = w/16
cell_height = h/16

#Make the delimiter position an array
col_cell = list(range(17))
row_cell = list(range(17))

for i in range(17):
    col_cell[i] = round(cellWidth * col_cell[i])
    row_cell[i] = round(cellHeight * row_cell[i])

I created an array of delimiters as.

print(col_cell)
#result[0, 26, 53, 79, 106, 132, 159, 185, 212, 238, 264, 291, 317, 344, 370, 397, 423]
print(row_cell)
#result[0, 28, 56, 84, 112, 139, 167, 195, 223, 251, 279, 307, 334, 362, 390, 418, 446]

Now that we have a grid of 16x16 area, we can finally calculate.

# [line i,jth column]Element acquisition of
for i in range(16):
    for j in range(16):
        #Cut out as area cell
        cell = threshold_img[row_cell[i]:row_cell[i+1]-1, col_cell[j]:col_cell[j+1]-1]
       
        #Row average in cell
        ave_per_row = np.average(cell,axis=0)

        #Row average average=Overall average
        ave_color = np.average(ave_per_row, axis=0)
        ave_value = np.average(ave_color)
        
        #Stored as white → 0 black → 1
        if ave_value >= 220:
            dot[i][j] = 0
        else:
            dot[i][j] = 1

If the average value ave_value is close to 255, white → 0 is substituted, otherwise it is close to black, so 1 is substituted.

print(dot)
#result
[[0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0], 
 [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0], 
 [1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1], 
 [0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0], 
 [1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1], 
 [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], 
 [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], 
 [0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0],
 [0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
 [0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0],
 [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0],
 [0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0],
 [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]

I was able to express Apocalymon safely with 01. I thought it was very Digimon-like here.

Regeneration

Finally regenerated

#Create a 128 x 128 area
width = 128
height = 128
dot_img = np.zeros((height,width,3), dtype=np.uint8)

for i in range(16):
    for j in range(16):
        if dot[i][j] == 0:
            #White → 255
            dot_img[i*8:i*8+8,j*8:j*8+8] = 255
        else:
       #Black → 0
            dot_img[i*8:i*8+8,j*8:j*8+8] = 0

plt.imshow(dot_img)

スクリーンショット 2021-01-04 180031.png

I was able to do it safely!

The 1px line between the dots and the white part are transparent and made into png, etc. Although it is still in the middle, the goal is achieved for the time being.

at the end

Thank you for reading this far.

I'm writing this article I made white → 0 black → 1 on the way, but I thought it would be smarter to make it white → 1 black → 0 and make it dot [i] [j] * 255 when regenerating. It's important to look back.

Recommended Posts

A story of creating 16 * 16 dots from a Digimon photo
The story of launching a Minecraft server from Discord
The story of creating a VIP channel for in-house chatwork
A story about creating a UNIX / Linux compatible OS from scratch
The story of a Django model field disappearing from a class
The story of creating a database using the Google Analytics API
The story of writing a program
The story of creating a site that lists the release dates of books
DataFrame of pandas From creating a DataFrame from two lists to writing a file
A story of a person who started aiming for data scientist from a beginner
The story of blackjack A processing (python)
A story about creating a program that will increase the number of Instagram followers from 0 to 700 in a week
The story of Django creating a library that might be a little more useful
A memorandum of calling Python from Common Lisp
DJango Memo: From the beginning (creating a view)
Flow of creating a virtual environment with Anaconda
The story of making a mel icon generator
The story of moving from Pipenv to Poetry
The story of switching from WoSign to Let's Encrypt for a free SSL certificate
A story that reduces the effort of operation / maintenance
Calculate volume from the two-dimensional structure of a compound
Make a copy of a Google Drive file from Python
Generate a vertical image of a novel from text data
A story of trying out pyenv, virtualenv and virtualenvwrapper
Make your cursor a photo of your choice on Linux
Installation of TensorFlow, a machine learning library from Google
Python points from the perspective of a C programmer
The story of making a music generation neural network
DJango Note: From the beginning (creating a view from a template)
Steps from installing Python 3 to creating a Django app
A story about changing the master name of BlueZ
Zip 4 Gbyte problem is a story of the past
A story that analyzed the delivery of Nico Nama.
Now in Singapore The story of creating a LineBot and wanting to do a memorable job
The story of creating a "spirit and time chat room" exclusively for engineers in the company