Recovery of text concealed by pixelation (mosaic) or blur.
Redacting text by pixelating or blurring it does not render it confidential; both transformations are reversible. UnPixel reconstructs the concealed text. It is a pure-Go port of Bishop Fox's unredacter (see why pixelation is inadequate).
Given an image containing a pixelated or blurred line of text, UnPixel determines the redaction parameters automatically — block size, blur magnitude, font, and language — and reports its best estimate of the original text.
It operates by reconstructing the redaction rather than by sharpening the image: it renders candidate text, applies the same blur or pixelation, and retains whichever candidate reproduces the redacted pixels exactly. See How it works for the method in detail.
Command-line tool:
go install github.com/oioio-space/unpixel/cmd/unpixel@latestGo library:
go get github.com/oioio-space/unpixelGo 1.26 or later is required. For building from source, see the getting-started guide.
Experimental: ML tier (-tags ml). The default build is pure-Go heuristic priors. Append -tags ml for an opt-in trained tier: softmax font-ID classifier (7/9 top-3 on bundled fonts) + per-glyph emission reranker. Both are pure Go, no weights shipped; the ML tier is experimental and currently targets monospace redactions.
go install -tags ml github.com/oioio-space/unpixel/cmd/unpixel@latestNew here? The
examples/directory has one runnable folder per feature — each with a plain-language README (CLI and Go code), amain.goyou cango run, and sample images. Start withexamples/01-basic-recovery.
1. Automatic recovery — UnPixel detects all parameters from the image:
unpixel redacted.png2. With a known font. Supplying the exact font substantially improves results on real-world images:
unpixel --font Consolas.ttf --font-size 24 redacted.png2b. Blind font prior — when the font is unknown, a pixelated-signature prior ranks the 9 bundled fonts, so the likeliest font is tried first (faster):
unpixel --font-prior redacted.png # order fonts by blind prior
unpixel --font-prior --font-prior-top-k 3 redacted.png # decode only top-3 (even faster, but riskier)3. Blur instead of pixelation:
unpixel --redaction blur redacted.png4. Decoder ensemble — run multiple decoders and select by exact image-distance (no-regression guarantee):
unpixel --decoder ensemble redacted.png5. Multi-frame decode — combine sub-pixel-jittered frames of the same redaction. When frame offsets are unknown, they are auto-detected per-frame (luma-variance grid-phase detection):
unpixel --frame frame1.png --frame frame2.png --frame frame3.png redacted.png6. DID context-aware emission — fix boundary blocks by rendering glyphs with neighbours:
unpixel --decoder did --did-context redacted.pngThe best estimate is written to standard output (so that it pipes cleanly); ranked
alternatives and progress information are written to standard error. Add --format json
for machine-readable output.
In Go, recovery is a single call:
import (
"context"
"fmt"
"github.com/oioio-space/unpixel"
"github.com/oioio-space/unpixel/mosaictext"
_ "github.com/oioio-space/unpixel/defaults" // wires the default pipeline
)
// Single-frame:
res, _ := unpixel.RecoverFile(context.Background(), "redacted.png")
fmt.Println(res.BestGuess)
// Decoder ensemble (multi-decoder with exact re-score):
ens, _ := mosaictext.DecodeEnsemble(ctx, img)
fmt.Println(ens.BestGuess)
// Multi-frame decode (requires sub-pixel-jittered frames):
multi, _ := mosaictext.DecodeMultiFrame(ctx, []image.Image{frame1, frame2, frame3})
fmt.Println(multi.BestGuess)
// DID with context-aware emission:
did, _ := mosaictext.DecodeDID(ctx, img, mosaictext.WithDIDContext(true))
fmt.Println(did.BestGuess)
// In-memory decode (from []byte), narrowing the alphabet + pinning the band of a
// redaction embedded in a larger screenshot:
res, _ := unpixel.RecoverBytes(ctx, pngData,
unpixel.WithCharset(unpixel.CharsetDigits), // narrow to digits for PINs (faster)
unpixel.WithCrop(band), // decode a redaction embedded in a larger image
)
// Verify proposed candidates against a redaction (LLM-propose / physical-verify):
verdicts, _ := unpixel.VerifyBytes(ctx, pngData, []string{"hunter2", "swordfish"},
unpixel.WithVerifyThreshold(0.05), // stricter Match threshold (default 0.10)
)
for _, v := range verdicts {
if v.Match {
fmt.Println("confirmed:", v.Text)
}
}For photos taken at angles, with undetected colorspace, or when a known prefix is present, use these opt-in flags:
Auto-recovery flags:
unpixel --auto redacted.png # auto-crop + auto-colorspace + auto-calibrate
unpixel --auto-crop redacted.png # align to mosaic grid boundaries
unpixel --auto-colorspace redacted.png # detect sRGB vs linear-light pixelation
unpixel --auto-calibrate redacted.png # infer font size and x-stretch
unpixel --rectify redacted.png # decode photos taken at an angleConstrained recovery (when part of the text is known):
unpixel --prefix "https://" redacted.png # lock first N characters
unpixel --prefix "admin" --visible-region redacted.png # known prefix + calibrate from visible textCalibration from visible text (when the image contains both clear and redacted text in the same font):
unpixel --visible-text "Username:" --visible-region "50,50,150,70" redacted.png # text + bbox
unpixel --calibrate-geometry --visible-text "Login" --visible-region "10,10,80,30" redacted.png # recover font size + x-stretchCalibration from a separate font sample (when a clean sample of the target font exists elsewhere):
unpixel --font-sample sample.png --font-sample-text "The quick brown fox" redacted.pngGeometry calibration (recover exact font size and horizontal stretch before decoding):
unpixel --calibrate-geometry --visible-text "Sample text" --visible-region "x,y,w,h" redacted.png # requires a sharp visible crop with enough textCaveat: --calibrate-geometry needs an ink-tight visible crop with sufficient text width (large white margins or very short text degrade the fit).
In Go, pass options to unpixel.Recover:
res, _ := unpixel.Recover(ctx, img,
unpixel.WithAuto(), // enables auto-crop + auto-colorspace + auto-calibrate
unpixel.WithPrefix("https://"), // constrain to known prefix
unpixel.WithAutoCalibrate(), // infer grid phase and x-stretch
)Caveat: --auto* flags and multi-decoder options (--decoder ensemble, --frame, --did-context) target real captures and boundary cases. Synthetic fixtures in the test panel already decode without them (panel remains 17/17 unchanged); their value lies in zero-config real-world use and tackling edge cases (JPEG boundaries, sub-pixel jitter, context-dependent pixelation).
UnPixel ships an MCP server (cmd/unpixel-mcp, pure Go)
that exposes the engine as an agent-callable toolbox so an LLM can drive recovery
conversationally — inspect an image, pick a decoder, score its own candidate guesses, and
render results for visual comparison.
go install github.com/oioio-space/unpixel/cmd/unpixel-mcp@latest
unpixel-mcp # speaks MCP over stdio; point your MCP client at itTools: unpixel_analyze (inspect → recommend decoder/quad), unpixel_decode (13 methods
behind one method enum; async for long runs; multi-frame auto-detects per-frame phase when offsets are 0), unpixel_verify_candidates (LLM proposes
strings, UnPixel scores them by physical re-pixelation; accepts rerank_weight to blend
a language prior into candidate ranking, with 0 = physical order and Pick staying a pure physical match;
for a REAL redaction, pass the physical-calibration hints — crop (analyze's redaction_bbox), font
(from unpixel_rank_fonts), linear_light, font_size/x_scale — to recover it end-to-end, as
unpixel_analyze → unpixel_rank_fonts → propose → verify),
unpixel_verify_image (physics-verifies a restored image against a redaction by re-applying the forward
operator; anti-hallucination gate for external diffusion restorers; see library entry unpixel.VerifyImage and
docs/sidecar-protocol.md for the restorer contract),
unpixel_render, unpixel_rank_fonts
(now supports blind histogram ranking without known_text), unpixel_calibrate; resources
unpixel://{fonts,charsets,methods,operating-envelope}. Custom fonts upload via
font_path/font_base64. unpixel_decode accepts font_prior_top_k to run a blind font-prior
sweep (orders the bundled-font search by pixelated-signature ranking) and expected_format
(digits|credit_card|iban|date|phone_fr|phone_us|phone_e164) to prune the engine search to a
structured secret (Luhn/mod-97/date/phone-validated). See docs for the full schema.
UnPixel recovers synthetic redactions reliably (text redacted with a known font and
subsequently recovered). Blind per-character recovery of real-world images is
considerably more difficult; success depends primarily on matching the exact font, and
supplying --font is the single most significant factor. But real redactions are
recoverable by propose-and-verify: given a candidate string and calibration
(font, block, colourspace, crop), unpixel.Verify / unpixel_verify_candidates confirms
it by whole-string physical re-pixelation — it recovers the real hello-world.png GIMP
mosaic at distance 0.0000 and ranks the truth #1 on 10/10 sick and 6/9 context secrets
(mise run verifymeasure). The limitations page documents the
operating envelope candidly and should be consulted before relying on the tool.
| Objective | Reference |
|---|---|
| Install and run the common cases | Getting started |
| Understand the method | How it works |
| Recover mosaic versus blur | Mosaic vs. blur |
| Configure the font | Fonts & calibration |
| Select a decoder | Decoders |
| Decode a photo taken at an angle | Decoders → --rectify |
| Review the limitations | Limits |
| Look up a CLI flag | CLI reference |
| Use the Go API | API reference |
| Browse the full documentation | docs/ |
The project roadmap and decision log are maintained in PROGRESS.md. A
comparison with the original Bishop Fox tool is provided in
comparison.
- Original work: Bishop Fox's unredacter and the article Never use pixelation to redact text.
- Fonts and libraries: the bundled Liberation, Carlito, Caladea, Source Code Pro,
and JetBrains Mono families (SIL OFL / Apache 2.0),
golang.org/x/image, andorisano/pixelmatch. The complete list and research references appear in docs/reference/references.md.
GPL-3.0-or-later — see LICENSE. UnPixel is a derivative work of Bishop
Fox's unredacter (GPL-3.0); the copyleft license is preserved. © the UnPixel authors;
original © Bishop Fox.