From-scratch reference implementation of a tiered on-device object-removal pipeline, CPU and GPU
Project description
object_remove
A from scratch implementation of an on device object removal pipeline (see
docs/ARCHITECTURE.md for the full design): a
polygon/scanline mask layer, four fill engines of increasing
sophistication, two interchangeable blend backends, GPU acceleration
(CUDA + Apple MPS) that engages automatically when available, and a
locked/queued session persistence layer.
Everything here is implemented from primitives (FAST corners, BRIEF descriptors, Hamming matching, a quad-tree patch-grid solver, PatchMatch, Laplacian/Poisson blending) — no OpenCV, no learned weights.
Quick start
pip install object-remove
import numpy as np
from object_remove import RemoveObjectJob, EngineTier
result = RemoveObjectJob(image_rgb).run(mask, tier=EngineTier.AUTO,
progress=lambda stage, frac: ...)
removed = result.image # float32 RGB in [0, 1]
print(result.tier_used) # which engine AUTO picked
image_rgb is any (H, W, 3) array (uint8 or float); mask is (H, W) with
nonzero marking the object to remove. Every engine and blender defaults to
device="auto": use a GPU (CUDA on NVIDIA, MPS on Apple Silicon) when one is
available, otherwise the plain NumPy path — no separate install, no manual
opt in. Pass "cuda"/"mps"/"cpu" to force a specific torch backend, or
device=None to force plain NumPy regardless of hardware — see
GPU acceleration below.
New to the package? Start with docs/USAGE.md — it walks
through loading an image, building a mask three different ways (brush stroke,
polygon selection, tap-to-auto-select), refining it, running the removal, and
undo/session history, with runnable snippets for each.
To run from a source checkout instead of the installed package:
pip install -r requirements.txt # numpy, scipy, Pillow, torch
PYTHONPATH=. python examples/demo.py # writes before/after PNGs to examples/out/
PYTHONPATH=. python -m pytest -q # tests
Module layout
| Stage | Concern | This repo |
|---|---|---|
| A — mask | selection, brush, auto select | object_remove/mask/ |
| B/D — scale/tiles | pyramid + tile scheduling | object_remove/pyramid/ |
| C0 — legacy CPU fill | segment aware patch comparator | fillengines/patch_comparator.py |
| C1 — general solver | quad-tree patch-grid solve | fillengines/primary/ |
| C2 — texture synthesis | self similar rigid shift | fillengines/self_similar_shift.py |
| C3 — patch match | multi scale PatchMatch | fillengines/patchmatch/ |
| E — blend | multi band + Poisson | object_remove/render/ |
| F — session | undo / persistence | object_remove/session/ |
| — GPU runtime | device resolution | object_remove/runtime/device.py |
| — orchestration | tier selection, progress, cancellation | pipeline/remove_object_job.py |
The four fill engines
- Tier 1 comparator —
SegmentAwarePatchFillEngine— greedy onion peel exemplar fill scored by segment aware SSD with byte mask validity. Cheap, correct on small holes; the low end / tiny hole fallback. - Tier 1 primary —
PrimaryFillEngine— the general purpose solver: a quad-tree patch search (fillengines/primary/patch_search_tree.py) over a sparse grid of patch centres (not a dense per-pixel field), solved in onion peel order with adaptive, blur-tested scale selection instead of a fixed pyramid formula. - Tier 2 —
SelfSimilarShiftEngine— the fast default for repetitive texture. FAST corners + 256 bit BRIEF + Hamming matching find a rigid self similar offset; a 1D DP seam cuts the copied block in cleanly, run for x then y via a transpose trick. - Tier 3 —
PatchMatchEngine— multi scale, multi group PatchMatch inpainting (random init → propagation → random search, then patch voting reconstruction, coarse to fine). Available for explicit selection.
EngineTier.AUTO sends truly tiny holes to tier 1 comparator, otherwise
tries the cheap tier 2 rigid shift first and falls back to tier 1 primary
when the shift's boundary continuity score is poor.
GPU acceleration
torch ships as a regular dependency — one pip install object-remove gets
both CPU and GPU support, nothing extra to install. Every fill engine and
both blenders accept a device kwarg, defaulting to "auto": use a GPU
(CUDA on NVIDIA, MPS on Apple Silicon) when one is available, otherwise the
plain NumPy path. Explicit "cuda"/"mps"/"cpu" force that exact torch
backend (and raise clearly if it isn't available); device=None forces
plain NumPy regardless of hardware.
from object_remove.render.multiband_blender import MultiBandBlender
from object_remove.fillengines import PrimaryFillEngine
blender = MultiBandBlender(bands=3) # device="auto" by default
engine = PrimaryFillEngine(device=None) # force plain NumPy
Notes on the GPU path:
- The multi-band blender is a straight port (pure array math). The Poisson blender solves the identical linear system via matrix-free conjugate gradient instead of an exact sparse solve. The PatchMatch solver uses red-black checkerboard propagation instead of raster order, so GPU output legitimately differs pixel for pixel from the CPU path (same quality bar, different but equally valid traversal order).
- The torch path runs in float32 throughout (Apple MPS has weak/no float64 support) — a deliberate precision tradeoff vs. the plain NumPy path's float64.
The two blend backends
MultiBandBlender— Laplacian pyramid blend with a fixed 4 tap kernel,[0.15439, 0.15133, 0.14252, 0.12896], and the mask feathered per pyramid level.PoissonBlend/PoissonBlend2— gradient domain blend (sigma,multiplier,algo_n1|2 variant), solved as a sparse Poisson system. Poisson is the safe default for single image inpainting because it reads only the (valid) hole boundary; the compositor repairs the background under the hole so no backend ever samples the object being removed.
Session persistence
SessionManager gives each edit a session id and an RLE mask history. Directory
removal is dispatched onto a background queue and run under a lock — never
an inline delete from the caller. A separate EmptySessionSweeper GCs orphaned
empty session dirs left by aborted/crashed edits.
Beyond object removal
A handful of adjacent features share the same primitives as object removal rather than each reimplementing their own:
object_remove/cv/— reusable classic CV building blocks: ORB (FAST + BRIEF + Hamming, also used by tier 2), Harris/Shi-Tomasi corners, Canny edges, a distance transform, a separable max filter / non-max suppression, a generic sigma-configurable Gaussian/box blur, nearest-opaque fill, frequency separation.object_remove/effects/—background_blur(selection-distance driven falloff, since no depth model ships) andFourPointPerspectiveCorrector(4-point homography + inverse warp).fillengines/clone_stamp.py— manual clone stamp: a user-specified source offset instead of a searched one, composing withPatchCompositorlike any other engine.fillengines/wire_removal.py— wire/cable removal: a derived (Hessian ridge traced), not painted, mask acquisition model, feeding the same fill engines used for object removal.
Status & scope
Implemented and tested: the full pipeline across all stages, CPU (NumPy) and
optional GPU (PyTorch: CUDA + Apple MPS) backends for every fill engine and
blender, plus the adjacent features above. Auto select is explicitly an
interface + CPU fallback: a real segmentation model can be dropped in
behind AutoSelectModel, which ships a working color flood fallback so tap
to select runs end to end without one.
object_remove/
mask/ brush_rasterizer, polygon_selection, selection_enhancer,
connected_components, auto_select
pyramid/ image_pyramid, scale_scheduler, tile_scheduler, torch_pyramid
fillengines/ patch_comparator (T1 comparator), primary/ (T1 general solver),
self_similar_shift (T2), patchmatch/ (T3),
clone_stamp, wire_removal
each fill engine has a *_gpu.py / torch_*.py sibling module
render/ multiband_blender(+_gpu), poisson_blender(+_gpu), compositor,
fused_patch_blend
cv/ orb, harris, canny, distance_transform, max_finder,
symmetric_convolution, box_filter, nearest_opaque,
frequency_separation
effects/ background_blur, perspective_correction
runtime/ device (GPU device resolution)
session/ undo_session_manager, empty_session_sweeper
pipeline/ remove_object_job
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file object_remove-0.2.2.tar.gz.
File metadata
- Download URL: object_remove-0.2.2.tar.gz
- Upload date:
- Size: 92.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b523655989e2a51f54f342892870ee77547993eef4b14d80894f4299c9b80d3d
|
|
| MD5 |
7ddc475628797bad4bbe69c065074b4a
|
|
| BLAKE2b-256 |
d2faed3ed65b74c29a0e6578d18c3d3098e608db554c21a4b2d1ad9bac09046c
|
Provenance
The following attestation bundles were made for object_remove-0.2.2.tar.gz:
Publisher:
release.yml on shalaga44/object_remove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
object_remove-0.2.2.tar.gz -
Subject digest:
b523655989e2a51f54f342892870ee77547993eef4b14d80894f4299c9b80d3d - Sigstore transparency entry: 2164999498
- Sigstore integration time:
-
Permalink:
shalaga44/object_remove@4441f6ee07d14670f7fc520cd1d3e99a34b0af3c -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/shalaga44
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4441f6ee07d14670f7fc520cd1d3e99a34b0af3c -
Trigger Event:
push
-
Statement type:
File details
Details for the file object_remove-0.2.2-py3-none-any.whl.
File metadata
- Download URL: object_remove-0.2.2-py3-none-any.whl
- Upload date:
- Size: 107.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff6c7437f5471d4dac90807575524b098ad8f164190313327047959834a39805
|
|
| MD5 |
5adfb07385d0e130cf4f98f102cc006b
|
|
| BLAKE2b-256 |
ac7d90cd65f90d59eb8226f7453c1104ac9558b6e1ac53d9178d2a2eb9e259ac
|
Provenance
The following attestation bundles were made for object_remove-0.2.2-py3-none-any.whl:
Publisher:
release.yml on shalaga44/object_remove
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
object_remove-0.2.2-py3-none-any.whl -
Subject digest:
ff6c7437f5471d4dac90807575524b098ad8f164190313327047959834a39805 - Sigstore transparency entry: 2164999510
- Sigstore integration time:
-
Permalink:
shalaga44/object_remove@4441f6ee07d14670f7fc520cd1d3e99a34b0af3c -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/shalaga44
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4441f6ee07d14670f7fc520cd1d3e99a34b0af3c -
Trigger Event:
push
-
Statement type: