Create Excel Pixel Art With Python

Sharing is caring!

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.

Required Libraries

For this project, we’ll use two libraries: xlwings and opencv.

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 opencv.

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.

Dragonball Goku Pixel Art

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.

Black And White Picture Seen By A Computer

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).

RGB Color Palette In Excel

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 opencv. The 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 cv2.imread() method:

  • 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?

GBR color mode

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.

Convert GBR to RGB

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.

Prepare Image

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 x and 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 xlwings library.

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[0]

xw.Range((1,1),(1,col+1)).column_width = 0.3
xw.Range((1,1),(row+1,1)).row_height = 3
time.sleep(3)

Paint Direction

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[2],i[1],i[0])
                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

The (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.

Each 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[0]

    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[2],i[1],i[0])
                c += 1
            r += 1

Leave a Reply

Your email address will not be published. Required fields are marked *