# TopCodes in Rust
[![Build](https://github.com/tangibl/topcodes-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/tangibl/topcodes-rs/actions/workflows/ci.yml)
This is a direct reimplementation of TopCodes in Rust. The original source by
Michael Horn can be found [here](https://github.com/TIDAL-Lab/TopCodes).
Using the image crate, the scanner can be used as follows:
```rust
use image::io::Reader as ImageReader;
let (mut scanner, buffer) = {
let img = ImageReader::open("assets/photo.png")
.unwrap()
.decode()
.unwrap();
let (width, height) = (img.width() as usize, img.height() as usize);
let buffer = img.into_rgb8().into_raw();
(Scanner::new(width, height), buffer)
};
let topcodes = scanner
.scan(&buffer, |buffer, index| {
(
buffer[index * 3] as u32,
buffer[index * 3 + 1] as u32,
buffer[index * 3 + 2] as u32,
)
});
```
You are free to use any abstraction as long as you can provide the scanner with
a reference to the raw image buffer (currently assumes an RGB [u8] slice).
## Demo
An example using this library in WASM can be found in the
[topcodes-wasm-rs](https://github.com/tangibl/topcodes-wasm-rs/) repository.
## Thresholding
For a peak into how the scanner works, we start with an image such as the
following:
It runs the thresholding algorithm which produces the following data in the
alpha channel (visualized as a greyscale image):
## Scanning
After the thresholding, TopCodes are determined from this black and white map.
First the candidate TopCode is ensured not to overlap existing TopCodes
(opportunity for a BVH or similar data structure to determine collisions
quickly), then unit size (width of ring) is determined, and finally the actual
code is determined. There is a checksum to ensure that the code's number of 1's
bits is equal to five to reduce the number of valid TopCodes (and thus decrease
the error rate).
## Performance
The benchmark below uses the `assets/photo.png` image and is performed on a
computer with the following specs:
CPU: Intel Core i7-9700K @ 3.60 GHz
RAM: 16.0 GB
While performance benchmarks aren't entirely reliable, this will help with
development moving forward as I don't want to regress accuracy or performance
for an improved API.
![Density function](docs/pdf.svg)
![Average iteration time](docs/iteration_times.svg)
## Plans
The goal of this package is to be as agnostic of the platform as possible. All
dependencies that are not explicitly required will be feature-gated to ensure
that the default dependencies of this project are as close to zero as possible.
Ideally, this version of the project should be able to run on most/all
platforms that are supported by Rust out of the box.
I plan to create a separate repository for providing a dynamic library from this
source, so that it can be pulled in from other languages, as well.