Metadata-Version: 2.4
Name: minosse
Version: 0.1.0
Summary: Detects AI-generated or AI-edited images using signal-processing artifact analysis — no ML model.
Project-URL: Homepage, https://github.com/cesp99/Minosse
Project-URL: Issues, https://github.com/cesp99/Minosse/issues
Author: cesp99
License-Expression: MIT
License-File: LICENSE
Keywords: ai-detection,diffusion,image-forensics,signal-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 :: Only
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 :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Security
Requires-Python: >=3.9
Requires-Dist: numpy
Requires-Dist: pillow
Requires-Dist: scipy
Description-Content-Type: text/markdown

# Minosse

Detects AI-generated or AI-edited images using three complementary,
hand-crafted artifact signals — **no ML model, just signal processing.**

Minosse looks for tell-tale traces that diffusion models and neural
upsamplers leave behind: ringing halos around text, noise in flat
regions that shouldn't have any, and a synthetic signature in the
high-frequency spectrum.

## Signals

| Signal | What it catches | How it works |
|---|---|---|
| **ringing** | Diffusion VAE decoder halos | Radial profile of band-pass energy vs. distance from strong edges. Real edges decay fast; ringing persists 10–40 px out. |
| **noisy-flats** | AI "film grain" on solid panels | RMS residual noise inside coarse-flat regions. Real screenshots are truly flat; JPEG smooths photo flats. |
| **synthetic-noise** | Upsampled / generated noise | Spectral energy at the Nyquist ring vs. mid frequencies. Real sensor noise stays hot at the highest frequencies; synthetic noise dies off. |

An image is flagged if **any** signal fires; the fired signals are reported
along with the raw feature values.


## Quick start

```bash
python -m venv .venv
.venv/bin/pip install -r requirements.txt

# Analyse a single image
.venv/bin/python -m minosse path/to/image.png

# Analyse multiple images with visual proof sheets
.venv/bin/python -m minosse --proof results/ image1.png image2.jpg
```

### `--proof` output

The `--proof` flag writes `<name>_proof.png`: a side-by-side sheet with
the original image next to one panel per fired signal, with the artifact
evidence burned in as a **red heatmap**.

Heat is scaled absolutely (calibrated to the detection thresholds), so
clean images render dark — the glow itself is the proof. For ringing,
the map only shows oscillation in regions that *should* be smooth (8–40 px
from strong edges, locally flat at coarse scale), so legitimate texture
doesn't light up.

## Python API

```python
from minosse import analyze, analyze_path, Result

# From a file path
r = analyze_path("image.png")
print(r.verdict)       # "likely AI-generated/edited" or "likely clean"
print(r.reasons)       # e.g. ["ringing", "synthetic-noise"]
print(r.decay)         # ringing feature value
print(r.vhf_mf)        # spectral ratio feature value
print(r.flat_noise)    # flat-region noise feature value
print(r.resid_std)     # overall residual noise

# From a PIL Image
from PIL import Image
img = Image.open("image.png")
r = analyze(img)
```

### `Result` fields

| Field | Type | Description |
|---|---|---|
| `verdict` | `str` | `"likely AI-generated/edited"` or `"likely clean"` |
| `reasons` | `list[str]` | Fired signal names: `ringing`, `noisy-flats`, `synthetic-noise` |
| `score` | `float` | Confidence score `[0, 1]` (0.75 per firing signal, capped at 1.0) |
| `decay` | `float` | Ringing persistence: mid-distance / near-edge band energy |
| `halo_vs_bg` | `float` | Mid-distance energy vs. image background |
| `vhf_mf` | `float` | Spectral ratio: Nyquist ring / mid frequencies |
| `resid_std` | `float` | Standard deviation of the noise residual |
| `flat_noise` | `float` | RMS residual inside coarse-flat regions |

### Visual proof from Python

```python
from minosse import analyze, evidence
from PIL import Image

img = Image.open("image.png")
r = analyze(img)
sheet = evidence.render(img, r)
sheet.save("proof.png")
```

## Project structure

```
minosse/
├── minosse/
│   ├── __init__.py      # Public API: analyze, analyze_path, Result
│   ├── __main__.py      # python -m minosse entry point
│   ├── cli.py           # Argument parsing and CLI loop
│   ├── detector.py      # Feature extraction + threshold logic
│   └── evidence.py      # Visual proof sheet renderer
├── results/             # Default --proof output directory
├── requirements.txt     # numpy, pillow, scipy
└── README.md
```

## How it works (briefly)

1. **Preprocessing** — the image is converted to RGB and downscaled if
   its longest edge exceeds 1600 px.
2. **Feature extraction** (`detector._features()`) — three independent
   signal-processing pipelines compute scalar features from the
   grayscale luminance.
3. **Thresholding** — each feature is compared against a tuned threshold;
   if it exceeds the threshold the corresponding signal is flagged.
4. **Verdict** — if any signal fires, the image is marked likely
   AI-generated/edited; otherwise it's likely clean.

For a deeper explanation of each signal, see [docs/signals.md](docs/signals.md).

## Caveats

The thresholds are calibrated on a small set of examples and should be
re-validated as more AI samples are added. See [docs/tuning.md](docs/tuning.md)
for instructions on re-tuning.

## License

MIT