Millow — M (for MoonBit) + illow (a nod to Python's Pillow).
A zero-FFI, cross-platform image-processing library for MoonBit. millow
works entirely on in-memory RGBA8 buffers (Array[Byte], laid out H × W × 4)
and builds on every backend: wasm-gc, wasm, js, and native.
中文 | English
| Input | to_grayscale |
tint(100,150,200) |
gaussian_blur(σ=2) |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
sharpen(1.0) |
sobel |
equalize_histogram |
threshold_otsu |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
rotate_any(45°) |
find_contours |
pipeline |
|---|---|---|
![]() |
![]() |
![]() |
cmd/main contains a lightweight demo that generates a synthetic gradient
image and applies 30+ millow operations — no external dependencies required:
moon run cmd/main
For the full-featured demo (PNG/JPEG I/O, 30 output images), use the
standalone examples/ project:
cd examples
moon run .
- Core image type —
Imagewith construction, pixel access, cloning, channel split/merge, and sub-images. - Color — grayscale (flat & weighted), invert, tint, BGR, alpha flatten,
HSV/YCbCr conversions, LUT application, and
overcompositing. - Geometry — crop, flips, 90/180/270 rotation, arbitrary rotation, translation, affine transform, shear, resize (nearest / bilinear / bicubic), rescale, fit/cover, thumbnails, and padding.
- Enhancement — brightness, contrast, gamma, normalize, auto-contrast, standardize, sharpen, and unsharp mask.
- Threshold & histogram — fixed threshold, Otsu, Sauvola, histogram (gray & color), equalization, CLAHE, and histogram matching.
- Filters & edges — convolution, box/Gaussian blur, median/min/max, bilateral filter, Sobel, Scharr, Prewitt, Laplacian, and Canny.
- Morphology — erode, dilate, open, close, gradient, top-hat, black-hat, skeletonize, and hit-or-miss.
- Feature detection — LBP, HOG, Harris corner, and Shi-Tomasi corner.
- Measurement — connected components, find contours, moments, Hu moments, region properties, and pixel counting.
- Data augmentation — random crop, flip, rotate, brightness/contrast/gamma adjustment, Gaussian/salt-pepper noise, color jitter, composable pipelines with weighted random choice.
- Metrics — MSE, PSNR, SSIM.
- Drawing — pixels, lines, rectangles, circles, ellipses, polygons, and flood fill.
- I/O — PPM/PGM serialization plus a pluggable
Encoder/Decoderregistry.
millow/
├── src/ # implementation package (megemini/millow/src)
├── millow.mbt # root facade: re-exports the public API (megemini/millow)
├── test/ # blackbox test package exercising the public API
├── test_alignment/ # alignment tests against Python (skimage/Pillow) reference
├── cmd/main/ # lightweight synthetic-image demo (millow-only)
└── examples/ # standalone full demo (PNG/JPEG I/O via mizchi/image)
The root package is a thin facade over src, so downstream users just
import "megemini/millow" and reach the whole API through @millow.
moon add megemini/millow
Then import it in your package's moon.pkg:
import {
"megemini/millow" @millow,
}
///|
test "build, transform and inspect an image" {
// A 64×64 canvas with a filled rectangle drawn on it.
let base = Image::from_pixel(64, 64, 30, 60, 90, 255)
let canvas = draw_rect(base, 8, 8, 40, 40, 220, 40, 40, 255, true, 1)
// Grayscale → blur → edges.
let gray = to_grayscale(canvas)
let blurred = gaussian_blur(gray, 1.5)
let edges = sobel(blurred)
assert_eq(edges.shape(), (64, 64))
// Otsu adaptive threshold.
let (_, binary) = threshold_otsu(blurred)
assert_eq(binary.shape(), (64, 64))
// Downscale and serialize to PPM.
let thumb = resize(canvas, 16, 16, Nearest)
let ppm = to_ppm(thumb)
assert_eq(ppm[0], 'P'.to_int().to_byte())
}In the examples above the API is called unqualified because they run inside the
millowpackage itself. From another module, prefix each name with the import alias, e.g.@millow.to_grayscale(img).
Compose multiple augmentations into a single pass with augment_pipeline,
which applies each Augmentation variant left-to-right:
///|
test "augment_pipeline example" {
let img = Image::from_pixel(64, 64, 30, 60, 90, 255)
let out = augment_pipeline(img, [
FlipHorizontal,
Rotate(15.0),
Brightness(1.2),
Contrast(1.3),
NoiseGaussian(8.0),
])
assert_true(out.h > 0 && out.w > 0)
}Available Augmentation variants: Crop(y, x, h, w), Resize(dst_h, dst_w),
FlipHorizontal, FlipVertical, Rotate(angle), Brightness(factor),
Contrast(factor), Gamma(g), NoiseGaussian(std), NoiseSaltPepper(prob),
ColorJitter(b, c, s, h). augment_pipeline may raise on invalid crop/resize
arguments.
To sample one augmentation from a weighted distribution, use
augment_random_choice:
///|
test "augment_random_choice example" {
let img = Image::from_pixel(64, 64, 30, 60, 90, 255)
let out = augment_random_choice(img, [
(0.4, FlipVertical),
(0.4, Gamma(0.8)),
(0.2, ColorJitter(0.2, 0.2, 0.0, 0.0)),
])
assert_eq(out.shape(), img.shape())
}millow uses a single uniform coordinate convention across the entire API:
-
Dimension order:
(h, w)— height first, width second.Image::new(h, w),resize(img, dst_h, dst_w, interp),crop(img, y, x, h, w),Image::shape() -> (h, w). -
Coordinate order:
(y, x)— row first, column second.yis the vertical axis (increases downward),xis the horizontal axis (increases rightward). The origin(0, 0)is the top-left corner. -
Drawing centers:
draw_circle(img, cy, cx, radius, ...),draw_ellipse(img, cy, cx, ry, rx, ...). -
Translation:
translate(img, dy, dx, interp). -
Contours:
find_contoursreturns(y, x)tuples.
adjust_brightness(img, factor) uses a multiplicative factor:
factor = 1.0returns the original imagefactor = 0.0returns a black image- Values greater than 1.0 brighten the image
- Values less than 1.0 darken the image
adjust_contrast(img, factor) adjusts contrast relative to the image's average luma:
factor = 1.0returns the original imagefactor = 0.0returns a solid gray image equal to the image's mean- Values greater than 1.0 increase contrast
- Values less than 1.0 decrease contrast
flatten_alpha(img, r, g, b) composites the image over a solid background color, using floating-point blending for smooth results.
Several operations support a mode parameter that controls how border pixels are handled:
Replicate— extends the nearest edge pixel outwardReflect— mirrors pixels across the edgeWrap— tiles the image periodicallyConstant(r, g, b, a)— fills border regions with a constant color
Functions supporting an optional mode parameter include affine_transform and shear. Most other operations use replicate (clamp) border handling internally.
bilateral_filter(img, d, sigma_color, sigma_space) applies edge-preserving smoothing:
dis the diameter of the pixel neighborhood (use 0 to auto-compute based on sigma_space)sigma_colorcontrols how similar colors must be to influence each other (larger = more smoothing)sigma_spacecontrols how close pixels must be spatially to influence each other (larger = wider neighborhood)
affine_transform(img, matrix, dst_h, dst_w, interp, mode) applies a general affine transformation using a 6-element matrix [a, b, c, d, e, f] representing:
x' = a*x + b*y + c
y' = d*x + e*y + f
Use rotate_any and translate for common transformations.
random_noise_gaussian(img, std) adds Gaussian noise with the specified standard deviation.
random_noise_salt_pepper(img, amount) adds salt-and-pepper noise with the specified amount (fraction of pixels affected).
millow contains no foreign function calls. It is verified to build on
wasm-gc, wasm, js, and native, and the test suite passes on each.
moon test # run every test
moon test --target native # pick a backend
moon run cmd/main # run the synthetic demo (no external deps)
cd examples && moon run . # run the full demo with JPEG I/O
test_alignment/ verifies millow's output against a Python reference
(numpy / skimage / Pillow) that implements the same algorithms. The workflow
is:
test_alignment/generate_fixtures.pycomputes expected output bytes for small test images and writes them asArray[Int]literals infixtures_test.mbt.- The MoonBit tests construct
Images from those fixtures, run each millow operation, and compare byte-for-byte (exact for integer ops, ±1 tolerance for floating-point rounding).
Regenerate the fixtures after changing an algorithm:
source $HOME/venv310/bin/activate
python test_alignment/generate_fixtures.py
moon test
See docs/roadmap.md for the full version plan and upcoming features.
Apache-2.0. See LICENSE.










