Skip to content

OpenCV script to turn images into color-by-number pages (2018)

License

Notifications You must be signed in to change notification settings

ptrgags/color-by-numbers

Repository files navigation

Color by Numbers (2018)

This is a Python script that uses OpenCV and Jinja2 to turn photos into coloring book pages. The result is a printout page that one can print and color!

Overview

This Python script currently has 2 algorithms for generating coloring printouts:

  1. "Downscale" - This algorithm shrinks the image to a really low resolution. it then draws a numbered grid on a page that describes the brightness of each pixel.
  2. "Shapes" - This algorithm overlaps a bunch of circles and regular polygons on a page. Each shape is assigned a brightness value after sampling pixels from the input image.

Output is always in PostScript format. These files can be converted to PDF format by programs such as GhostScript's ps2pdf if needed.

Purpose

This is just a fun little Python project I made to turn photos into coloring pages. The idea for this project came from a coloring book series, Querkles by Thomas Pavitte. Be sure to check out his coloring books if you like this project!

This is also an exercise for me to learn about Docker. This script is designed to run in a Docker container. Two directories are mounted so the images and output are stored on the host, not in the container.

This is also an exercise in learning the PostScript programming language. It's a language used by printers to lay out a page of text and vector graphics.

Quickstart (Docker)

This assumes you already have Docker installed.

  1. Clone this Git repo.
  2. Run launch.sh. This script does the following:
    1. Pull the latest Docker container for color-by-numbers from Docker Hub
    2. Mount the input/ and output/ directories of this Git repo.
    3. Start Bash within the container. Now we are ready to process some images!
  3. (Optional) Put an image into the input/ directory on the host machine. You can ignore this step if you want to use one of the sample images provided in the repo.
  4. Run the python script inside the Docker container. Some examples:
    ./main.py downscale input/gears.jpg output/gears_downscale.ps
    ./main.py shapes input/gears.jpg output/gears_shapes.ps
    
    There are many options to main.py, use the -h flag to learn more!
  5. (Optional) Convert the PostScript output to a PDF file. GhostScript is already installed in the container, so simply run a command like this inside the Docker container:
    # from /app inside the container
    ps2pdf output/gears_downscale.ps output/gears_downscale.pdf
    
    # it's a lot simpler if we enter the output folder:
    cd output
    #  this creates gears_downscale.pdf automatically
    ps2pdf gears_downscale.ps
    

Running Without Docker

This script is really designed for use inside Docker. However, if you want to run the Python code from the repo directly, follow these instructions.

Note: I have not tested this myself.

Installing Dependencies

  • Install Python modules with the usual pip install -r requirements.txt
  • If you want to be able to convert PostScript -> PDFs, install GhostScript

Caveats of Running Outside Docker

  • Input images MUST go in the input/ directory of this repo. The Python script assumes that there will be directories called input/ and output/ in the same directory as main.py. In the Docker container, those two two directories are created by mounting 2 directories on the host machine.
  • The output directory MUST be the output/ directory of this repo for the same reasons.

Coloring Rules

Downscale

Here are the coloring rules:

  • There are --num-colors colors. Let's number them with integers from [0, N).
  • 0 is the darkest color, N-1 is the brightest color
  • The artist decides a gradient of any N colors. this could be varying shades of a single color, or many different colors. The only important choice is that the gradient must go from dark to light.
  • Start coloring!

This method of coloring follows the rules of Querkles by Thomas Pavitte with some slight modifications:

  1. My script lets you pick how many colors to use. Querkles always uses 5 colors plus the white background color
  2. My coloring pages do not use a background color. If you want part of the image to be white, don't color in the brightest color (N - 1)
  3. As a programmer, I started the numbering at 0 instead of 1.

Shapes

When I designed the Shapes algorithm, I wanted to have a similar nummbering system to Downscale. However, I realized that it would be impossible to read the numbers with so many overlapping shapes. Therefore, I came up with these modified rules:

  • Shapes with black outlines are always the darkest color
  • Shapes with red outlines are always the second darkest color
  • As you go through the color wheel from red -> green -> blue, they correspond to brightness values. These colors are evenly spaced on the color wheel.

For example, for --num-colors 6 (the default), you get shapes with the following outline color:

Outline Color Brightness
Black 0 - Darkest color in gradient
Red 1
Yellow-green 2
Green 3
Blue 4
Red-violet 5 - Brightest color in gradient

How it Works

Downscale Algorithm

This algorithm does the following:

  1. Read the command line arguments. (see ./main.py downscale --help)
  2. Read in the input image and convert it to grayscale.
  3. We want to subdivide the image into a grid of squares. Using the image size, the page size (--page-size), the margin size (--margin), and the desired size per square(--square-size), calculate how many pixels wide each grid square is on the input image.
  4. Shrink the image, converting squares of the calculated size into single pixels. The average color is taken for each square.
  5. Bucket the colors of this downsampled image into the number specified by the user (--num-colors).
  6. Scale down these values from [0, 255] to [0, num_colors).
  7. Use Jinja2 to template a PostScript file that draws a grid with a number per cell. These numbers correspond to the numbers we assigned in the previous step.
  8. Write the PostScript file to the output directory.

Here is an example I colored

Shapes Algorithm

This algorithm does the following:

  1. Read the command line arguments
  2. Read in the image and convert it to grayscale
  3. Given the page size (--page-size), margin size (--margin) and the size of the image, calculate how many points of the output file are needed per pixel of the input image.
  4. Now let's start covering the page with shapes! Repeat the following for every iteration from 0 to --iterations:
    1. Compute the diameter of the circles for this iteration. Start with about half the print area's (page - margins) shorter side. Then do 1/4, 1/8, etc., halving the diameter at each iteration.
    2. Calculate how many shapes we need to approximately cover the print area. I use the formula img.rows * img.cols / circle_diameter ** 2 I'm using the bounding box rather than the circle itself, it's close enough.
    3. Randomly pick the shape types. Circles and regular polygons from 3-8 sides are all equally likely. Note that even the polygons will be colored from a circular region of the input image. The result of this is an array of PostScript commands for the different shapes.
    4. Calculate the colors for each shape. This involves the following:
      1. Randomly select a slice out of the image of size circle_diameter x circle_diameter. Keep track of the positions of these slices in the image.
      2. Compute the average color using a circularly-shaped kernel of the same size as the circle.
      3. Quantize the colors so there are only --num-colors values
      4. Assign each value a color. 0 is always assigned black. 1 is always assigned red. all the other --num-colors - 2 colors have evenly spaced hues around the color wheel
    5. Calculate the centers and radii of the circles in points so we know where and how big the shapes are in PostScript
    6. Generate lines of PostScript code that look like this: hue saturation brightness x y r shape_command
  5. Finally, gather up the lines of PostScript code and use Jinja2 to insert them into a PostScript template.

Examples:

(Note: DeviantArt doesn't show the preview image on the page. See here for pictures of my colored printouts)

About

OpenCV script to turn images into color-by-number pages (2018)

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published