Artistic halftone generation library
pip install git+https://github.com/setanarut/halftonism
from halftonism import Project
p = Project("example.ora", repeat=16, waveform="triangle")
p.save_GIF("example.gif", scale=0.25, miliseconds=70, colors=30, resample=3)
You can open the example.ora file in example folder with the Krita. (Openraster format). In the example you can see the color layers obtained using fastLayerDecomposition repository. After installing the halftonism package, the folder2ora
command line tool is installed. Using this, you can convert the folder full of PNG layers obtained with fastLayerDecomposition into an ORA file. Saves it with the name output.ora
.
$ folder2ora ~/Downloads/layers_folder
# saved -> ~/Downloads/layers_folder/output.ora
Alternatively you can decompose and save image as an ORA file with the decompose package. decompose package separates very quickly using pytorch but the colors are inaccurate a little bit depending on the palette.
$ decomp ~/Desktop/img.png
# Decomposer mask generation...
# Decomposer processing alpha layers...
# Decomposer Done!
# ORA saved: img.ora
# 7
# Palette saved: img_palette.png
Also you can paint it by hand without layer decompositing. Play around with layer orders and alpha levels for a more color balanced halftone outputs.
Before processing the ORA file with Python, you have to follow the template below.
-
Each color layer should contain only a single color.
-
The bottom background layer (base color) must be solid color.
-
There should be a grayscale fractal heightmap at the top layer for halftone patterns. Top layer can be a computer generated heightmaps or real DEM images or any suitable grayscale gradient.
- Procedural terrains based on simplified landscape evolution model (LEM) github.com/TadaTeruki/fastlem-random-terrain
- Diffusion-limited aggregation
- Mandelbrot Fractals
- Hydraulic erosion simulation across a heightfield (Noise)
Halftone animation is created with 256 pixels linear grayscale gradient cycling, also known as palette shifting or palette animation. The halftone pattern and color layer are mixed with the Hard Mix blending mode (mix halftone gradient pattern and color layer by 50%. If the alpha value is greater than 128, set the color to 255, otherwise set the color to 0).
Waveform of gradient. Project()
has parameters waveform="sawtooth"
, waveform="triangle"
and waveform="sine"
. The effects are shown below, respectively.
This number determines how many times gradient will be divided. The formula (256 / repeat) / frame_skipping
gives the number of animation frames. (frame_skipping is 1 by default, meaning it is disabled). For example, repeat=8
will give 32 frames. (256/8/1 = 32 frames).
Let's save all sawtooth gradient repetitions from 1 to 16 as image. It will help to understand. Height increased from 256x1 to 256x32 and image rotated 90 degree for visibility.
from PIL import Image
import numpy as np
from halftonism.utils import gradient_vstack, gradient
stack = gradient_vstack(gradient(1, "sawtooth"), 32)
for repeat in range(2, 17):
stack = np.vstack((stack, gradient_vstack(gradient(repeat, "sawtooth"), 32)))
im = Image.fromarray(stack)
im.rotate(90, expand=True).save("gradient_repeats.png")
You can skip frames to reduce GIF/APNG file size. This also reduces the frame jump effect in repeat numbers where the number 256 is not divisible, such as 3,7,10.
For antialiasing, you can downscale image with bicubic sampling. For example, you can start with 2000x2000 and downscale to 500x500 for final output (500 / 2000 = 0.25 scale). The save_GIF()
save_APNG()
and save_frame()
methods have scale
and resample arguments.
Resampling example with NEAREST
on the left and BICUBIC
on the right. (scale 0.25)
p.save_GIF("output.gif", scale=0.25, resample=Image.BICUBIC)
p.save_frame(0, "01_frame.png", scale=0.25., resample=3)