PixelLab — Image Processing Lab 📸
A React Native (Expo) app that captures a photo and applies classic digital image-processing algorithms, each with a built-in "How it works" explainer. Black & orange interface. Every filter is implemented from scratch in TypeScript (no OpenCV) so the math matches the course material.
Algorithms are drawn from the syllabus decks:
- Ch. 3 — Intensity Transformations & Spatial Filtering
- Ch. 5 — Image Restoration & Reconstruction
- Feature Extraction & Segmentation
Features
- 📷 Capture with the camera or pick from the library.
- 🎛️ 35 filters across 7 categories, with live sliders for parameters.
- 🧠 Info panel for every filter — summary, formula, algorithm steps, use cases, source chapter.
- 👆 Hold-to-compare before/after, plus undo / reset and non-destructive filter stacking.
- 💾 Save the result to your photo library.
Filter catalogue
| Category | Filters |
|---|---|
| Intensity | Negative · Log · Power-Law (Gamma) · Contrast Stretch · Intensity Slicing · Bit-Plane Slicing |
| Histogram | Histogram Equalization (YCbCr, colour-preserving) |
| Smoothing / Restoration | Box (Mean) · Gaussian · Median · Min · Max · Midpoint · Geometric Mean · Harmonic Mean · Contraharmonic Mean · Alpha-Trimmed Mean · Adaptive Median |
| Sharpening | Laplacian · Laplacian Sharpen · Unsharp / High-Boost |
| Edges | Sobel · Prewitt · Laplacian Edges · Canny · Laplacian of Gaussian · Difference of Gaussians |
| Features / Segmentation | Global Threshold · Otsu · Adaptive Threshold · Local Binary Pattern · Harris Corners · Hough Lines |
| Noise (for restoration demos) | Add Gaussian Noise · Add Salt & Pepper |
How the image pipeline works
React Native has no built-in raw-pixel access, so:
expo-image-manipulatorresizes the photo (longest edge ≤MAX_DIM, 620px) and returns base64 JPEG.jpeg-jsdecodes it to anRGBAImage(Uint8ClampedArray).- The pure-TS filter runs on the pixel array (
src/ip/filters/*). jpeg-jsencodes the result back to adata:URI shown in<Image>.- Saving writes the base64 to a cache file and hands it to
expo-media-library.
jpeg-jsneeds a NodeBuffer;src/polyfills.tsprovides one and is imported first inindex.ts.
Project layout
src/
ip/
types.ts RGBAImage, Filter, FilterDoc definitions
core.ts clamp, grayscale, convolution, histogram, Gaussian kernels …
codec.ts jpeg-js decode / encode
pipeline.ts load photo → RGBA, render RGBA → data URI
registry.ts every Filter: metadata + docs + apply()
filters/ intensity · histogram · smoothing · sharpening · edges · features · noise
components/ ParamSlider, InfoSheet
screens/ CameraScreen, EditorScreen
theme.ts black & orange design tokens
scripts/
test-filters.ts Node smoke test for every algorithm + codec round-tripDevelop / run
Requires Node 18+. (This machine used a conda env:
conda activate rnip.)
npm install
npm run typecheck # tsc --noEmit — should be clean
npm run test:filters # runs all 35 filters on a synthetic image
npm start # Expo dev server — scan the QR with Expo Go
# or: npm run android / npm run iosCamera + saving need a physical device (Expo Go) or a dev build — the simulator has no camera. Library-pick works everywhere.
Notes
- Processing is on-device and synchronous; a spinner shows while a filter runs.
MAX_DIMinsrc/ip/pipeline.tstrades quality for speed. - Corner/line overlays (Harris, Hough) are drawn in the brand orange.