Metadata-Version: 2.4
Name: pickblur
Version: 0.3.1
Summary: Interactively pick which faces to blur across a folder of images.
Author-email: LordJFR <jorfonrev@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/LordJFR/pickblur
Project-URL: Documentation, https://github.com/LordJFR/pickblur#readme
Project-URL: Issues, https://github.com/LordJFR/pickblur/issues
Project-URL: Source, https://github.com/LordJFR/pickblur
Keywords: face,blur,privacy,anonymization,opencv,gui,image-processing
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Security
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: opencv-python<5,>=4.5
Requires-Dist: numpy>=1.21
Requires-Dist: Pillow>=9.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Dynamic: license-file

# pickblur

**Selectively blur faces across a folder of photos, by clicking on the ones you want.**

Point pickblur at a folder of images. It auto-detects every face and outlines
it in **green**. Click a face to mark it for blurring — the box turns **red**.
Move through the folder with Next/Previous, zoom in on tricky images, and when
you're done, pickblur writes the results to a new `blurred_images/` folder,
blurring only the faces you picked.

```
┌──────────────────────────────────────────┐
│  Image 2 / 8  |  family_photo.jpg         │
│  3 face(s) detected, 1 marked for blur    │
│                                            │
│      ┌──────┐        ┌──────┐             │
│      │green │        │ red  │   ┌──────┐  │
│      │ box  │        │ box  │   │green │  │
│      └──────┘        └──────┘   └──────┘  │
│                                            │
│  [Prev] [Next] [Zoom+] [Zoom-] [Reset]     │
│                        [Finish & Save All] │
└──────────────────────────────────────────┘
```

## Why this exists

Most face-blurring tools do one of two things: blur *every* face (dashcam /
street-photography privacy tools), or require you to already have reference
photos of the people you want to match against (face-recognition pipelines).
pickblur is for the common in-between case: you have a folder of group
photos and you know, by eye, which specific faces should be blurred — a
minor in the frame, a bystander, someone who didn't consent to being
published — and you just want to click them and move on.

## Installation

```bash
pip install pickblur
```

This installs the `pickblur` command and the `pickblur` Python package.
Dependencies: `opencv-python`, `numpy`, `Pillow`. Tkinter is also required
for the GUI — it ships with most standard Python installations, but on some
minimal Linux setups you may need `sudo apt-get install python3-tk` (Debian/
Ubuntu) or the equivalent for your distro.

## Windows: a double-click .exe (no terminal needed)

If you'd rather not use a terminal at all, pickblur includes a small
graphical launcher that can be packaged into a standalone `pickblur.exe`:
a window with a "Photo folder" field (type or Browse) and a Start button,
which then opens the exact same review window as the CLI.

**This has to be built on Windows** (PyInstaller doesn't reliably
cross-compile). See [`launcher/README.md`](launcher/README.md) for the
one-command build script and full instructions.

## Usage

### Command line

```bash
pickblur /path/to/your/photos
```

This opens the review window. When you finish (or click past the last
image), results are saved to `/path/to/your/photos/blurred_images/`.

Options:

```bash
pickblur /path/to/your/photos --output-dir ./redacted --strength 55
```

| Flag | Description | Default |
|---|---|---|
| `-o`, `--output-dir` | Where to save results | `<input_dir>/blurred_images` |
| `-s`, `--strength` | Blur intensity (higher = blurrier) | `35` |
| `--version` | Print the installed version | |

`--strength` scales smoothly: moderate values (25-60) give a typical soft
blur, and very high values (a few hundred and up, relative to how large
the detected face is in pixels) converge to a fully solid, zero-detail
patch over the face -- useful if you want a guaranteed "completely
unrecognizable" result rather than a merely soft blur.

### Controls in the window

- **Click a green box** → marks that face for blurring (turns red)
- **Click a red box** → unmarks it (turns back to green)
- **Right-click any box** → deletes it entirely (works whether it was
  auto-detected or manually drawn — useful for false-positive detections,
  or to remove a manual box drawn in the wrong spot)
- **Add Missed Face** → toggles draw mode. While active, click-and-drag on
  the canvas to draw a box around a face the detector missed. New boxes
  are shown with a **dashed** outline (to distinguish them from
  auto-detected ones) and are **marked for blurring by default** (red),
  since the whole point of drawing one is almost always to blur a face
  that wasn't caught automatically. Click "Stop Adding" to go back to
  normal mode. Tiny/accidental drags (under ~8px) are ignored rather
  than creating junk boxes.
- **Draw mode automatically switches back to normal click-to-toggle mode
  whenever you move to another image** via Next/Previous — so you never
  accidentally start drawing a box on an image you haven't looked at yet.
- **Next / Previous** → move between images; your selections, manually
  drawn boxes, and deletions are all remembered
- **Zoom In / Zoom Out / Reset Zoom**, or mouse wheel over the image → zoom,
  useful for small or crowded faces, and for drawing precise manual boxes.
  Scrollbars appear so you can pan around while zoomed in.
- **Clear selections (this image)** → unmark everything on the current image
  (does not delete any boxes, manual or detected)
- **Finish & Save All** → blur every marked face across every image in the
  folder and write results to the output folder. Images with no faces
  marked are copied over unchanged (byte-for-byte -- not re-encoded).

### Improving detection accuracy

Haar Cascade (the default detector) is fast and dependency-light, but it
can miss angled, small, poorly lit, or partially occluded faces. Two ways
to improve recall:

**1. Tune the detector's sensitivity** (more false positives, but catches
more real faces):

```python
from pickblur import FaceDetector
detector = FaceDetector(scale_factor=1.05, min_neighbors=3, min_size=(20, 20))
```

**2. Use "Add Missed Face"** in the GUI to manually box anything the
detector still misses -- this is the recommended complement to (1) rather
than trying to tune the detector to catch 100% of faces, which usually
isn't achievable with Haar Cascade alone.

If you need significantly better automatic recall (e.g. for large batches
where manual review isn't practical), consider swapping in a stronger
detector backend -- see `detector.py`'s docstring for where to plug one in.

### As a library

You can also use the detection/blurring pieces directly, without the GUI:

```python
import cv2
from pickblur import FaceDetector, blur_selected_faces

detector = FaceDetector()
image = cv2.imread("group_photo.jpg")

boxes = detector.detect(image)          # [(x, y, w, h), ...]
chosen = [boxes[0], boxes[2]]           # pick whichever ones you want blurred
result = blur_selected_faces(image, chosen, strength=40)

cv2.imwrite("group_photo_blurred.jpg", result)
```

Or launch the GUI programmatically instead of via the CLI:

```python
from pickblur import run_app

run_app(input_dir="photos/", output_dir="photos/blurred_images", strength=40)
```

## Supported image formats

`.jpg`, `.jpeg`, `.png`, `.bmp`, `.webp`, `.tif`, `.tiff`

## How detection works

pickblur uses OpenCV's Haar Cascade classifier (`haarcascade_frontalface_default.xml`),
bundled directly inside the package (see `src/pickblur/data/`) so detection
works immediately after `pip install`, without depending on the installed
OpenCV wheel to also ship its own copy of that file. It works well for
mostly-frontal, reasonably well-lit faces; it can miss faces that are
heavily angled, partially occluded, or very small in the frame. If you need
higher accuracy, you can swap in your own cascade or detector by passing a
custom `FaceDetector(cascade_path=...)`, or by extending `detector.py`
(e.g. to wrap a DNN-based or MediaPipe-based detector instead).

## Troubleshooting

**`ModuleNotFoundError: No module named 'tkinter'`**
Tkinter isn't part of the `pip`-installable dependency tree — it's a system
package tied to your Python installation. Install it via your OS package
manager, e.g. `sudo apt-get install python3-tk` on Debian/Ubuntu, then
reinstall/re-run pickblur.

**`AttributeError: module 'cv2' has no attribute 'CascadeClassifier'`**
This has been observed with `opencv-python==5.0.0.93` on some platforms,
where the `objdetect` module (which provides `CascadeClassifier`) appears to
be missing from that particular wheel. pickblur's dependency is pinned to
`opencv-python>=4.5,<5` to avoid this; if you've installed pickblur into an
environment that already has a newer/broken `opencv-python`, downgrade it:

```bash
pip install "opencv-python>=4.5,<5" --force-reinstall
```

**No faces detected on an image that clearly has faces**
Haar Cascade is a lightweight, older detector — it can miss small, angled,
or poorly lit faces. Use the **"Add Missed Face"** button in the GUI to
draw a box manually around anything it missed (see "Improving detection
accuracy" above); it defaults to marked-for-blur.

## Project layout

```
pickblur/
├── pyproject.toml          # Package metadata & build configuration (PEP 621)
├── MANIFEST.in             # Extra files to include in the sdist
├── README.md                # This file
├── LICENSE                  # MIT license
├── CHANGELOG.md             # Version history
├── requirements.txt         # Plain pip requirements (for non-package installs)
├── .gitignore
├── src/
│   └── pickblur/
│       ├── __init__.py      # Public API: FaceDetector, blur_region,
│       │                    # blur_selected_faces, run_app
│       ├── _version.py      # Single source of truth for the version string
│       ├── detector.py       # FaceDetector: Haar-cascade-based face detection
│       ├── blur_utils.py     # blur_region / blur_selected_faces
│       ├── gui.py            # PickBlurApp: the Tkinter review window
│       ├── cli.py            # `pickblur` command-line entry point
│       └── data/
│           ├── __init__.py
│           ├── haarcascade_frontalface_default.xml   # bundled cascade
│           └── README.md     # attribution/license note for the cascade
├── tests/
│   ├── conftest.py           # shared pytest fixtures
│   ├── test_detector.py
│   └── test_blur_utils.py
├── conda-recipe/
│   └── meta.yaml              # recipe for building a local/conda-forge package
├── launcher/
│   ├── pickblur_launcher.py   # Tkinter front-end: pick a folder, click Start
│   ├── build_exe.bat          # Windows: bundles the launcher into pickblur.exe
│   └── README.md               # build instructions & troubleshooting
└── docs/
    └── usage.md                # extended usage notes / API reference
```

Why a `src/` layout: it prevents accidentally importing the package from
your working directory instead of the installed version, which is a common
source of confusing bugs when developing a package (see the
[Python Packaging Authority's guidance](https://packaging.python.org/en/latest/tutorials/packaging-projects/)
on `src` layouts).

## Development

```bash
git clone https://github.com/LordJFR/pickblur.git
cd pickblur
python -m venv .venv && source .venv/bin/activate     # Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pytest
```

`pytest` runs the non-GUI unit tests (`detector.py`, `blur_utils.py`). The
GUI module (`gui.py`) is deliberately imported lazily elsewhere in the
package specifically so these tests, and any other use of the detection/
blurring utilities, work in headless CI environments without Tkinter
installed.

## Publishing this package

### 1. Push it to GitHub

```bash
cd pickblur
git init
git add .
git commit -m "Initial commit: pickblur v0.1.0"
git branch -M main
git remote add origin https://github.com/LordJFR/pickblur.git
git push -u origin main
```

Before pushing: replace the placeholder author name/email and GitHub URLs
in `pyproject.toml`, and update the copyright name in `LICENSE`.

Tag releases so PyPI/conda-forge versions map to a specific commit:

```bash
git tag v0.1.0
git push origin v0.1.0
```

Consider also adding, if you want a more complete open-source repo:
- A GitHub Actions workflow (`.github/workflows/tests.yml`) that runs
  `pytest` on push/PR across a few Python versions.
- A `CONTRIBUTING.md` if you expect outside contributors.
- Screenshots or a short screen recording in the README (GitHub renders
  images/GIFs directly in `README.md`).

### 2. Publish to PyPI (so `pip install pickblur` works for everyone)

First, register an account at https://pypi.org (and, for testing,
https://test.pypi.org). Then, from the project root:

```bash
pip install build twine
python -m build                      # creates dist/pickblur-0.1.0.tar.gz and .whl

# Optional but recommended: upload to TestPyPI first and verify
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ pickblur

# Once you're happy, upload for real
twine upload dist/*
```

`twine upload` will prompt for PyPI credentials (or an API token, which is
the recommended method — generate one under PyPI Account Settings → API
tokens, then use `__token__` as the username and the token as the
password). Note that **`pickblur` may already be taken as a package name**
on PyPI — check https://pypi.org/project/pickblur/ before you commit to
the name, and rename in `pyproject.toml` (the `[project] name` field) if
needed.

After this, anyone can run `pip install pickblur`.

### 3. Publish to conda / conda-forge (so `conda install pickblur` works)

There are two levels of "getting it into conda":

**A. Build and use a local conda package (quick, no review process)**

A starter recipe is included at `conda-recipe/meta.yaml`. With
[`conda-build`](https://docs.conda.io/projects/conda-build/) installed:

```bash
conda install conda-build
conda build conda-recipe/
```

This builds a `.conda`/`.tar.bz2` package on your machine (conda will print
its exact path). You can then install it directly:

```bash
conda install --use-local pickblur
```

or share that build file with others, who can install it via
`conda install /path/to/pickblur-0.1.0-py_0.tar.bz2`.

**B. Publish to conda-forge (public, for everyone, requires review)**

This is the "real" way to make `conda install -c conda-forge pickblur`
work for anyone. High-level steps (conda-forge's process changes
occasionally, so check https://conda-forge.org/docs/maintainer/adding_pkgs.html
for the current details):

1. Publish `pickblur` to PyPI first (conda-forge recipes for pure-Python
   packages typically build from the PyPI sdist).
2. Fork https://github.com/conda-forge/staged-recipes.
3. Add a new folder under `recipes/pickblur/` containing a `meta.yaml`
   (you can adapt the one in `conda-recipe/meta.yaml`, updating the
   `sha256` to match your published PyPI sdist — get it via
   `pip download --no-binary :all: --no-deps pickblur` then `sha256sum`
   the downloaded file, or from the PyPI project page's "Download files"
   section).
4. Open a pull request against `staged-recipes`. Conda-forge's automated
   linter and maintainers will review it; once merged, a bot creates a
   dedicated `pickblur-feedstock` repository and publishing new versions
   from then on is just a matter of updating the version/sha256 there
   (often done automatically by conda-forge's regro-cf-autotick-bot when
   it notices a new PyPI release).

## License

MIT — see [`LICENSE`](LICENSE). The bundled Haar cascade file is
redistributed from the OpenCV project; see
[`src/pickblur/data/README.md`](src/pickblur/data/README.md) for its
attribution note.
