Overview: NumArt is an end-to-end system that transforms a user’s uploaded image
into a printable paint-by-numbers kit and, optionally, dispenses the required paint colors using a
hardware mixer. The software pipeline handles color quantization, segmentation, and vectorization to
produce a clean, scalable SVG output, while the hardware setup uses Raspberry Pi–controlled
peristaltic pumps to mix and dispense paint according to the generated palette. The project combines
computer vision, clustering algorithms, and embedded systems for a full digital-to-physical creative
workflow.
Key Features:
Image Quantization: Uses K-Means clustering (via scikit-learn) to reduce an
input image to k representative colors. Each pixel is assigned to the nearest cluster
centroid, creating a simplified color palette ideal for painting.
Label Map Generation: The quantized image is converted into a 2D label map,
where each pixel corresponds to its color index. This map is cleaned using majority filters and
region-merging algorithms to remove noise and ensure paintable areas.
Vectorization & SVG Export: The cleaned label map is converted into vector
paths, with each region assigned a number and lightly filled for visibility. The final SVG
includes a color legend and can be printed directly as a paint-by-numbers template.
Hardware Integration: A Raspberry Pi 4 controls five 12V peristaltic pumps
connected to color reservoirs (Red, Green, Blue, White, Black). The pumps precisely dispense
paint volumes to recreate the digital palette physically.
Python Driver & FastAPI Backend: A Python FastAPI service coordinates the
process—from receiving an image upload to processing it through the quantization pipeline and
sending dosing instructions to the Pi for paint mixing. The Modbus or GPIO driver handles
real-time pump control and calibration.
Physical Assembly: Pumps are mounted in an enclosure and connected via silicone
tubing to color reservoirs and a magnetic stirrer. The Raspberry Pi, relay board, and 12V PSU
are wired with shared ground and secured for safety and reliability.
Technology Used: Python 3.12, FastAPI, NumPy, Pillow, scikit-learn (K-Means),
OpenCV, svgwrite, Raspberry Pi 4, peristaltic pumps, relay modules, and 12V DC power electronics.
Process: The project begins with image validation and downsizing before applying
K-Means to extract dominant colors. The palette and label map are refined with region cleanup and
distance transforms to ensure each zone is large enough to paint. The resulting vector outlines are
exported as a numbered SVG. In parallel, the Raspberry Pi interprets the same palette and activates
each pump for a calibrated duration to produce corresponding paint mixtures in small beakers with
magnetic stirrers. The system can handle up to five base colors and generate custom blends
automatically.
Challenges: Achieving clean segmentation for complex images, balancing speed versus
accuracy in K-Means clustering, preventing color bleeding between small regions, and synchronizing
hardware control for consistent dosing were key engineering hurdles. Additionally, ensuring
repeatable pump calibration and stable power distribution on the Pi relay circuit required careful
tuning and testing.
Overview: This project involved the design and implementation of a custom RISC-style
processor with 8 general-purpose registers and a compact instruction set architecture. The goal was
to build a minimal yet functional CPU capable of executing arithmetic, logic, and branch
instructions, including support for 16-bit integer and 16-bit IEEE-754 floating-point data
conversions and addition/subtraction. We used the processor's custom instruction set to write a
program in Assembly Language to handle the conversion and computations. A custom compiler for the
instruction set, to convert the program from ASM to machine code was also created using C++.
Key Features:
Register-register / Load-store architecture with direct and indirect addressing
Optimized register file design, prioritizing registers 0 to 3 for frequently accessed
operations, while 4 to 7 are utilized for storage. Special handling of 0 as the implied
destination for certain branch comparisons.
LUT-based conditional branching and control flow, storing up to 32 jump locations
Custom instructions for logical shifts, bit manipulation, and subroutine handling
Logical shifts can span one or two registers.
Support for 16-bit integer and 16-bit IEEE-754 floating-point data types
8-bit wide, 256-byte deep memory, to support indirect addressing during memory access
instructions
The ALU also supports a comparison operation. Given two registers (R0 and R1), it will compare
the data in these registers and return one of three values..
Control unit to decode the instruction code (op-code) and manage the flow of data across the
data path.
Process: The processor architecture was designed from the ground up, starting with
the instruction set and register file. An ALU module was built to support both standard arithmetic
and custom bitwise operations. A memory subsystem handled instruction and data memory independently.
Testing was performed with custom testbenches and waveform outputs to validate correctness at each
stage.
Challenges: Addressing edge cases in floating-point conversion, handling operand
ordering in memory instructions, and eliminating infinite loop scenarios in early branch logic
prototypes.
Development Process: Followed Agile methodologies with sprint planning, issue
tracking, code reviews, and iterative delivery through GitHub Projects and pull requests.
Challenges: Managing state across dynamic components, ensuring consistent data
synchronization between frontend and API layers, and maintaining high code quality through
continuous integration workflows.
Accomplishments: Deployed a production-ready, mobile-responsive web application with
a scalable architecture, developer-friendly tooling, and seamless user experience tailored to the
TCG community.