• Stars
    star
    497
  • Rank 88,652 (Top 2 %)
  • Language
    C
  • License
    MIT License
  • Created over 4 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A new algorithm for retrieving topological skeleton as a set of polylines from binary images

Skeleton Tracing

A new algorithm for retrieving topological skeleton as a set of polylines from binary images.

Available in all your favorite languages: C, C++, Java, JavaScript, Python, Go, C#/Unity, Swift, Rust, Julia, WebAssembly, Haxe, Processing, OpenFrameworks.

[Online Demo]

About the Chinese characters in the test image

Introduction

Traditionally, skeletonization (thinning) is a morphological operation to reduce a binary image to its topological skeleton, returning a raster image as result. However, sometimes a vector representation (e.g. polylines) is more desirable. Though contour-finding can be used to further trace the results, they usually give enclosing outlines instead of single strokes, and are prone to slight variations in stroke width caused by imperfection in the skeletonization process. In this demo we present a parallelizable divide-and-conquer based algorithm for skeleton tracing, which converts binary images into a set of polylines, i.e. arrays of (x,y) coordinates along the skeleton, in real time.

Algorithm Description

Define a binary image to be a 2D matrix consisting of 0-pixels(background) and 1-pixels(foreground). The algorithm can be summarized as follows:

  1. Given a binary image, first skeletonize it with a traditional raster skeletonization algorithm, e.g. Zhang-Suen 1984 is used in this demo. (Without this step, the algoirthm still works to a certian extent, though the quality is generally reduced.)
  2. If the width and height of the image are both smaller than a small, pre-determined size, go to step 7.
  3. Raster scan the image to find a row or column of pixels with qualities that best match the following:
    • Has the least amount of 1-pixels on itself.
    • The 2 submatrices divided by this row or column do not have 1-pixels on their four corners.
    • When two or more candidates are found, pick the one that is closer to the center of the image.
  4. Split the image by this column or row into 2 submatrices (either left and right, or top and bottom depending on whether row or column is selected in the previous step).
  5. Check if either of the 2 submatrices is empty (i.e. all 0-pixels). For each non-empty submatrix, recursively process it by going to step 2.
  6. Merge the result from the 2 submatrices, and return the combined set of polylines.
    • For each polylines from one submatrix whose either endpoint coincide with the splitting row or column, find another polyline in the other submatrix whose endpoint meets it. If the matrix was split horizontally, then the x-coordinate of the endpoints should differ by exactly 1, and y-coordinate can differ between 0 to about 4 (depending on the steepness of the stroke potrayed), The reverse goes for vertical splitting.
  7. Recursive bottom. Walk around the 4 edges of this small matrix in either clockwise or ant-clockwise order inspecting the border pixels.
    • Initially set a flag to false, and whenever a 1-pixel is encountered whilst the flag is false, set the flag to true, and push the coordinate of the 1-pixel to a stack.
    • Whenever a 0-pixel is encountered whilst the flag is true, pop the last coordinate from the stack, and push the midpoint between it and the current coordinate. Then set the flag to false.
    • After all border pixels are visited, the stack now holds coordinates for all the "outgoing" (or "incoming") pixels from this small image section. By connecting these coordinates with the center coordinate of the image section, an estimated vectorized representation of the skeleton in this area if formed by these line segments. We further improve the estimate using the following heuristics:
    • If there are exactly 2 outgoing pixels. It is likely that the area holds a straight line. We return a single segment connecting these 2 pixels.
    • If there are 3 or more outgoing pixels, it is likely that the area holds an intersection, or "crossroad". We do a convolution on the matrix to find the 3x3 submatrix that contains the most 1-pixels. Set the center of all the segments to the center of the 3x3 submatrix and return.
    • If there are only 1 outgoing pixels, return the segment that connects it and the center of the image section.

Implementations

Click on links below to see each implementation's documentation and code.

  • C99 (parallelized with pthreads, libpng for reading and X11 for display)
  • C++ (thinly wrapped from C version)
  • JavaScript (WebAssembly compiled from C++ using emscripten)
  • Vanilla JS (Pure JavaScript implementation)
  • Pure Python (slow)
  • Python using C API (compiled from C using SWIG, compatible with numpy and opencv)
  • Java (includes a Processing demo)
  • OpenFrameworks addon (friendly wrapper on C++ version)
  • C# (demo script for Unity Engine)
  • Go (parallelized with goroutines)
  • Swift (demo with NSImage and AppKit)
  • Rust (simple rust implementation)
  • Julia (julia implementation with array views)

Benchmarks

The benchmarks below are produced on MacBook Pro Mid 2015 (2.5 GHz Intel Core i7, 16GB 1600 MHz DDR3). The input image is test_images/opencv-thinning-src-img.png (300x149px).

All the times refer to pure or "vanilla" implemenations, not wrappers of C/C++. Wrappers on C/C++ should be comparable to the performance of C plus some overhead. Exception is WebAssembly performance which depends heavily on browser and number of open tabs.

All the times refer to the "tracing" operation only, excluding the raster thinning step (which is not my algorithm (problem), but note that practically, raster thinning often takes longer than the tracing, especially for images with lots of white pixels).

For compiled languages, the highest possible optimization level is selected, unless otherwise specified.

Ordered by fastest to slowest.

Language Seconds/1000 Runs FPS % C speed Note
C 0.759748 1316 100% -O3
Go 1.1165248 895 68%
Rust 1.39878 714 54% -O
Java 1.722 580 44%
Swift 1.795619 556 42% -O
JavaScript 1.948 513 38% Node.js v12.10
C# 4.266101 234 17% Unity 2018.4.8f1
Julia 4.722 (2.791 amortized) 211 16% First frame takes 2 sec, the rest 0.002 sec
Python 1015.04818 1 0% Python 3.7

Developed at Frank-Ratchye STUDIO for Creative Inquiry at Carnegie Mellon University.

More Repositories

1

shan-shui-inf

Procedurally generated Chinese landscape painting.
HTML
5,488
star
2

fishdraw

procedurally generated fish drawings
JavaScript
2,200
star
3

qiji-font

齊伋體 - typeface from Ming Dynasty woodblock printed books
Python
1,296
star
4

rrpl

Describing Chinese Characters with Recursive Radical Packing Language (RRPL)
JavaScript
884
star
5

wax

A tiny programming language that transpiles to C, C++, Java, TypeScript, Python, C#, Swift, Lua and WebAssembly 🚀
C
770
star
6

linedraw

Convert images to vectorized line drawings for plotters.
Python
755
star
7

q5xjs

A small and fast alternative (experimental) implementation of p5.js
JavaScript
541
star
8

nonflowers

Procedurally generated paintings of nonexistent flowers.
JavaScript
509
star
9

cope

A modern IDE for writing classical Chinese poetry 格律诗编辑程序
JavaScript
455
star
10

ndwfc

🌊💥 N-dimensional Wave Function Collapse with infinite canvas
JavaScript
311
star
11

psvg

Programmable Scalable Vector Graphics -- drawings that draw themselves
TypeScript
297
star
12

legumes

🎼 A sheet music to polylines renderer
TypeScript
230
star
13

magic-square-poems

Discovering magic squares in Tang Dynasty poems
C
188
star
14

handpose-facemesh-demos

🎥🤟 8 minimalistic templates for tfjs mediapipe handpose and facemesh
JavaScript
187
star
15

Hermit

A man. A horse. A nature.
Python
168
star
16

chinese-hershey-font

Convert Chinese Characters to Single-Line Fonts using Computer Vision
Python
129
star
17

Processing-Demos-for-The-Pocket-Handbook-of-Image-Processing-Algorithms

Processing Demos made when reading the book *The Pocket Handbook for Image Processing Algorithms in C*
Processing
127
star
18

edges2calligraphy

Using pix2pix to convert scribbles to Chinese calligraphy
JavaScript
115
star
19

tk-fangsong-font

剔骨仿宋: Experimental Fang Song style Chinese font
Python
112
star
20

grand-timeline

Interactive grand unified timeline of 30,800 ancient Chinese people / 古人全表
JavaScript
111
star
21

hfmath

Render LaTeX math with Hershey Fonts
TypeScript
94
star
22

VisionOSC

PoseOSC + FaceOSC + HandOSC + OcrOSC + CatOSC + DogOSC
Objective-C++
88
star
23

wechit

WeChat in Terminal (微信终端版)
Python
88
star
24

wasm-fun

Non-trivial programs in hand-written WebAssembly
WebAssembly
78
star
25

PoseOSC

📹🤸‍♂️🤾‍♀️🤺 PoseNet + OSC: send realtime human pose estimation data to your apps
JavaScript
77
star
26

ci-ren

Generative Chinese poetry
Python
75
star
27

r1b

A thermal-printer-oriented, 1-bit graphics rasterizer for 2D and 3D
C
73
star
28

squiggy

vector brushstroke library
TypeScript
65
star
29

asciimare

3D engine powered by ASCII art
Python
63
star
30

Okb.js

Procedural generation toolkit for Javascript - noises, randomness, curves, and more
HTML
60
star
31

ofxPoissonFill

Poisson filling shader for OpenFrameworks
C++
56
star
32

p5-hershey-js

p5.js Hershey Vector Font Library
JavaScript
53
star
33

pmst

🎨 Poor Man's Style Transfer - Painting an image with the style of another, without machine learning
C++
48
star
34

Loshu.js

A linear algebra library for JavaScript 🔢
JavaScript
47
star
35

zdic-cli

An offline command-line interface to zdic.net dictionary (漢典)
JavaScript
47
star
36

interesting-polygon-archive

Collection of polygon data in various formats for testing computational geometry algorithms.
Processing
46
star
37

ttf2hershey

Convert True Type Fonts (.ttf) to Hershey vector fonts
Python
43
star
38

skeletonization-js

Javascript implementation of image skeletonization
JavaScript
42
star
39

t43

A tiny 3D slicer written from scratch
C
32
star
40

fv

An experimental approach to expressing vector math in js (tagged template literals)
JavaScript
31
star
41

LingDong-

Automatically keep my Github profile README updated with a python script and Github Actions
Python
28
star
42

PContour

Processing/Java library for finding contours in binary images
HTML
28
star
43

srcsnap

screenshot-driven version tracking
JavaScript
22
star
44

dbn.js

Recreation of John Maeda's "Design By Numbers" programming environment in JavaScript
JavaScript
18
star
45

wax4vscode

Extension for the wax programming language in VS Code (highlight + transpile + run)
TypeScript
17
star
46

TrackpadOSC

💻👋✌️👉Send mac's multitouch trackpad read-out through OSC
Objective-C
17
star
47

xcessing

Friendly Processing-like interface to X11/Xlib in C
C
16
star
48

svg2pl

convert svg to polylines
C
15
star
49

lbll

tiny experimental language for limited environments
C
14
star
50

fast-many-face-detection-with-cpp-or-openframeworks-on-mac-using-neural-networks

Fast Many Face Detection with C++/OpenFrameworks on macOS using Neural Networks
C++
14
star
51

galiano-drawing

A procedurally generated drawing
JavaScript
11
star
52

machining-projection-map

JavaScript
8
star
53

avrlass

AVR Lightweight Assembler (and disassembler)
JavaScript
6
star
54

60-212

JavaScript
5
star
55

teapot.lua

1 path tracer written in pure lua, 1 file, 0 dependencies.
Lua
5
star
56

Hello-World

Lorem Ipsum
3
star
57

cvDictUI

opencv-python tool for generating interactive GUI from any python dictionary
Python
3
star
58

lingdong

LingDong's project links
JavaScript
2
star
59

galiano-lidar-render

Semi-realistic rendering of LiDAR data from the entire Galiano Island, BC
C
1
star