As a backend project, fogleman/primitive has picked up 13.2k stars on GitHub (Go). Reproducing images with geometric primitives.
Snapshot summary built from the project's own GitHub metadata — there's no written TopGit review yet. The page will update automatically when a full review is published.
WHY NO REVIEW YET
TopGit writes full reviews for the most-starred, most-requested repositories. This page is a snapshot until then — see the READ ME tab for the original README in full.
A target image is provided as input. The algorithm tries to find the single most optimal shape that can be drawn to minimize the error between the target image and the drawn image. It repeats this process, adding one shape at a time. Around 50 to 200 shapes are needed to reach a result that is recognizable yet artistic and abstract.
Primitive for macOS
Now available as a native Mac application!
https://primitive.lol/
Twitter
Follow @PrimitivePic on Twitter to see a new primitive picture every 30 minutes!
The Twitter bot looks for interesting photos using the Flickr API, runs the algorithm using randomized parameters, and
posts the picture using the Twitter API.
You can tweet a picture to the bot and it will process it for you.
Command-line Usage
Run it on your own images! First, install Go.
go get -u github.com/fogleman/primitive
primitive -i input.png -o output.png -n 100
Small input images should be used (like 256x256px). You don't need the detail anyway and the code will run faster.
add N extra shapes each iteration with reduced search (mostly good for beziers)
nth
1
save every Nth frame (only when %d is in output path)
r
256
resize large input images to this size before processing
s
1024
output image size
a
128
color alpha (use 0 to let the algorithm choose alpha for each shape)
bg
avg
starting background color (hex)
j
0
number of parallel workers (default uses all cores)
v
off
verbose output
vv
off
very verbose output
Output Formats
Depending on the output filename extension provided, you can produce different types of output.
PNG: raster output
JPG: raster output
SVG: vector output
GIF: animated output showing shapes being added - requires ImageMagick (specifically the convert command)
For PNG and SVG outputs, you can also include %d, %03d, etc. in the filename. In this case, each frame will be saved separately.
You can use the -o flag multiple times. This way you can save both a PNG and an SVG, for example.
Progression
This GIF demonstrates the iterative nature of the algorithm, attempting to minimize the mean squared error by adding one shape at a time. (Use a ".gif" output file to generate one yourself!)
Static Animation
Since the algorithm has a random component to it, you can run it against the same input image multiple times to bring life to a static image.
Creative Constraints
If you're willing to dabble in the code, you can enforce constraints on the shapes to produce even more interesting results. Here, the rectangles are constrained to point toward the sun in this picture of a pyramid sunset.
Shape and Iteration Comparison Matrix
The matrix below shows triangles, ellipses and rectangles at 50, 100 and 200 iterations each.
How it Works, Part II
Say we have a Target Image. This is what we're working towards recreating. We start with a blank canvas, but we fill it with a single solid color. Currently, this is the average color of the Target Image. We call this new blank canvas the Current Image. Now, we start evaluating shapes. To evaluate a shape, we draw it on top of the Current Image, producing a New Image. This New Image is compared to the Target Image to compute a score. We use the root-mean-square error for the score.
Current Image + Shape => New Image
RMSE(New Image, Target Image) => Score
The shapes are generated randomly. We can generate a random shape and score it. Then we can mutate the shape (by tweaking a triangle vertex, tweaking an ellipse radius or center, etc.) and score it again. If the mutation improved the score, we keep it. Otherwise we rollback to the previous state. Repeating this process is known as hill climbing. Hill climbing is prone to getting stuck in local minima, so we actually do this many different times with several different starting shapes. We can also generate N random shapes and pick the best one before we start hill climbing. Simulated annealing is another good option, but in my tests I found the hill climbing technique just as good and faster, at least for this particular problem.
Once we have found a good-scoring shape, we add it to the Current Image, where it will remain unchanged. Then we start the process again to find the next shape to draw. This process is repeated as many times as desired.
Primitives
The following primitives are supported:
Triangle
Rectangle (axis-aligned)
Ellipse (axis-aligned)
Circle
Rotated Rectangle
Combo (a mix of the above in a single image)
More shapes can be added by implementing the following interface:
Hill Climbing or Simulated Annealing for optimization (hill climbing multiple random shapes is nearly as good as annealing and faster)
Scanline rasterization of shapes in pure Go (preferable for implementing the features below)
Optimal color computation based on affected pixels for each shape (color is directly computed, not optimized for)
Partial image difference for faster scoring (only pixels that change need be considered)
Anti-aliased output rendering
Inspiration
This project was originally inspired by the popular and excellent work of Roger Johansson - Genetic Programming: Evolution of Mona Lisa. Since seeing that article when it was quite new, I've tinkered with this problem here and there over the years. But only now am I satisfied with my results.
It should be noted that there are significant differences in my implementation compared to Roger's original work. Mine is not a genetic algorithm. Mine only operates on one shape at a time. Mine is much faster (AFAIK) and supports many types of shapes.
Examples
Here are more examples from interesting photos found on Flickr.
The most recent commit recorded on fogleman/primitive was 2.1 years ago, based on the GitHub push timestamp. The repository has 648 forks — one of the better signals of community interest.
Is fogleman/primitive open source?
Yes — fogleman/primitive ships under the MIT license, which makes its source code freely readable (and, depending on license terms, forkable and reusable). Source: github.com/fogleman/primitive.
What license does fogleman/primitive use?
fogleman/primitive is released under the MIT license. Always verify the LICENSE file directly on GitHub for the authoritative terms — license strings can be edited out of sync with a project's actual stance.
What topics is fogleman/primitive associated with?
GitHub's repository topics for fogleman/primitive: "art", "go", "graphics", "primitives". TopGit's editorial category is Backend.
Where can I see fogleman/primitive in action?
The project maintains a homepage at https://primitive.lol/. The README tab on this page also usually contains screenshots and a quickstart.
Where do I read more about fogleman/primitive?
This TopGit page is a snapshot — the READ ME tab shows the project's own README content (links stripped, images preserved). The GitHub repository at github.com/fogleman/primitive is the definitive source.
Read full README in the tab above.
Still deciding about primitive?
One click hands the question to an AI along with this page — see what it says about primitive.