Last Updated on February 9, 2022 by Jay
In this tutorial, I’ll show you how to create Excel pixel art with Python. With a little technical skill, you can become a professional Excel artist.
Take a look at the below short video to get a sense of what we are about to achieve.
For this project, we’ll use two libraries:
xlwings– one of the best Python libraries to automate Excel files
opencv– a library for computer vision
Opencv is not required as the library is really intended for machine learning applications. We can use something like
Pillow to replace it. However, I already had it that’s why I’m sticking with
What Is A Picture?
Basically, an image as we know consists of thousands of pixels with different colors. Each pixel has one color. However, once we zoom out far enough and the size of a pixel becomes very small, we don’t see the pixels and all we see is a picture.
How Does The Excel Art Program Work?
At a high level, we use an Excel sheet as our “canvas” with each cell being a single “pixel” on the canvas. Then we create the artwork by filling each cell (pixel) with a different color. For instance, the picture I showed in the video has a size of 1,257 * 700 which translates into 879,900 pixels. Once we fill colors for all the 879.9k cells, then zoom out far enough, an image will appear.
How Do Computers Understand Color?
The color of each pixel has a meaning to humans, like red, green, blue, etc.
However, to a computer, those colors are just a bunch of integer numbers.
Black And White Picture
Usually, we can use just a single integer to represent a color. We use 0 to represent the black color, and 255 to represent white. Any integers in between are some kind of grey color (between black and white).
Take the below B&W picture as an example. It’s a handwritten number 3 with writing in white color on a black background. Once read into Python, what computer “sees” is a matrix of integer numbers from 0 to 255.
Color Picture & RGB Model
Color pictures are usually represented using three separate color channels. One of the most common color models is the RGB model which contains Red, Green, and Blue channels. Similar to the B&W color, each of the RGB color channels also can take on integer values between 0 and 255. So in theory, we can use the RGB model to create a total of 256 ^ 3 = 16.777 million different colors! Unfortunately, our eyes can’t tell the subtle differences between many colors…
Using Excel as our color palette to illustrate how to use RGB to create colors. As shown below, we can create the “cyan” color by mixing green and blue colors together while leaving out the red (0, 255, 255).
The intersection of Red, Green, and Blue is White, with an RGB value of (255,255,255).
Now we have a good understanding of pictures and colors. It’s time to practice our art skills!
Working With Pictures In OpenCV
Let’s now load a picture into Python with
matplotlib library is only for displaying the image for demonstration purposes, our actual program doesn’t require it.
import cv2 import numpy as np import matplotlib.pyplot as plt img = cv2.imread(r'one-punch-man.jpg', cv2.IMREAD_COLOR)
With the above 4 lines of simple code, we have loaded an image file into Python. In the
- The 1st argument is the path of the image file
- The 2nd argument specifies that our image file is a color image.
Let’s see what it looks like:
As expected, just a bunch of numbers. Note that these are groups of 3 integers, with each group representing an RGB value.
Further checking the
img object, we realize it’s a
numpy.ndarray object, with a size of (700, 1257, 3).
- 700 means the number of pixels vertically (i.e. # of rows)
- 1257 means the number of pixels horizontally (i.e. # of columns)
- 3 means each element in the ndarray is again a list, with 3 items inside
Let’s use the
matplotlib to display this image. Hmm, Saitama seems cold… what’s happening?
It turned out that
opencv actually expects BGR instead of RGB, so the R and B are swapped. Whereas
matplotlib uses the conventional RGB format. What we need is to swap the R and B channels back, which is easy to do using a list reversal.
Transfer Pixel Colors From Python To Excel
I named this program “Picasso bot” since it’s quite talented in painting. Let’s create a function for it.
def picasso_bot(img_path, direction= None, x=1, y=1): img = cv2.imread(img_path,cv2.IMREAD_COLOR) if x != 1 and y !=1: img_resize = cv2.resize(img, None, fx=x, fy=y) else: img_resize = img row, col, _ = img_resize.shape
We’ll also give it a nice optional feature to resize the image. This is because for large pictures it will be difficult to show inside Excel (Excel zoom out max is at 10%), and it will take a long time to paint.
We can use the
cv2.resize to change the picture size, remember to keep the argument
y to be the same values to maintain the aspect ratio of the image.
Next, we’ll find out the height (rows) and length (columns) of the image and store them inside two variables called row, and col. Note there’s a third value for shape once we unpack it, and that is the 3 (for RGB). We don’t care about this value from the .shape attribute, so we can just ignore it and set it to “underscore”.
Prepare Excel Sheet For Art
Now the picture data (i.e. each information for each pixel) is inside a matrix. We can start painting cells. To make this Excel art “live painting”, we’ll use the
First, we’ll open up a new workbook. Then, we’ll set the column width and cell heights to be appropriate values such that each cell appears to be a square. I found the 0.3 and 3 to be good values for width and height, feel free to try other value combinations.
We’ll also add a sleep timer to pause the program for 3 seconds so that we have time to maximize the Excel window and zoom out on the cells.
book = xw.Book() sheet = book.sheets xw.Range((1,1),(1,col+1)).column_width = 0.3 xw.Range((1,1),(row+1,1)).row_height = 3 time.sleep(3)
Notice we have a
direction argument in the function? Our “Picasso” Excel art bot is capable of painting in different order/styles. Here, I’m only showing how to paint horizontally line by line. Feel free to come up with your own creative painting styles. You can paint vertically, or even diagonally!
if direction == 'horizontal': r = 1 for row in img_resize: c = 1 for i in row: if not (i == 255).all(): sheet.cells[r,c].color = (i,i,i) c += 1 r += 1
Here we use a nested loop. We’ll:
- Start painting cells on the top-left corner of the picture, paint each cells for the first row from left to right
- Then move on to the second row, paint all cells from left to right
- And so on
(i == 255).all() checks if a pixel is a white color, then it will skip painting it. If not a white color, i.e. not all of the RGB values are 255, then we’ll paint that pixel.
i in the loop represents the RGB value for a single pixel, and it should be a list with three integers. Also, remember we have to swap the R and B so that BGR becomes RGB?
Putting It All Together
The below provides a shell program for creating Excel art pieces, feel free to copy it and come up with your own creative versions!
import cv2 import xlwings as xw import time def picasso_bot(img_path, direction= None, x=1, y=1): img = cv2.imread(img_path,cv2.IMREAD_COLOR) if x != 1 and y !=1: img_resize = cv2.resize(img, None, fx=x, fy=y) else: img_resize = img row, col, _ = img_resize.shape book = xw.Book() sheet = book.sheets xw.Range((1,1),(1,col+1)).column_width = 0.3 xw.Range((1,1),(row+1,1)).row_height = 3 time.sleep(3) if direction == 'horizontal': r = 1 for row in img_resize: c = 1 for i in row: if not (i == 255).all(): sheet.cells[r,c].color = (i,i,i) c += 1 r += 1