Metadata-Version: 2.5
Name: image-provenance-inspector
Version: 0.2.0
Summary: Inspect image provenance and create privacy-sanitized copies without removing provenance.
Project-URL: Homepage, https://github.com/KageRyo/image-provenance-inspector
Project-URL: Repository, https://github.com/KageRyo/image-provenance-inspector
Project-URL: Issues, https://github.com/KageRyo/image-provenance-inspector/issues
Author: Image Provenance Inspector contributors
License: MIT License
        
        Copyright (c) 2026 Chien-Hsun Chang
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: c2pa,content-credentials,fastapi,image-metadata,provenance
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Multimedia :: Graphics
Classifier: Topic :: Security
Requires-Python: <3.13,>=3.12
Requires-Dist: fastapi<1,>=0.115
Requires-Dist: pillow<12,>=10.4
Requires-Dist: python-multipart<1,>=0.0.9
Requires-Dist: uvicorn<1,>=0.30
Provides-Extra: analysis
Requires-Dist: imagehash<5,>=4.3; extra == 'analysis'
Requires-Dist: numpy<3,>=2.1; extra == 'analysis'
Requires-Dist: opencv-python-headless<5,>=4.10; extra == 'analysis'
Requires-Dist: scikit-image<1,>=0.24; extra == 'analysis'
Provides-Extra: c2pa
Requires-Dist: c2pa-python<1,>=0.19; extra == 'c2pa'
Provides-Extra: dev
Requires-Dist: httpx<1,>=0.27; extra == 'dev'
Requires-Dist: pytest-cov<7,>=5; extra == 'dev'
Requires-Dist: pytest<9,>=8.3; extra == 'dev'
Requires-Dist: ruff<1,>=0.6; extra == 'dev'
Provides-Extra: ui
Requires-Dist: streamlit<2,>=1.39; extra == 'ui'
Description-Content-Type: text/markdown

# Image Provenance Inspector

[![CI](https://github.com/KageRyo/image-provenance-inspector/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/KageRyo/image-provenance-inspector/actions/workflows/ci.yml)
[![Python 3.12](https://img.shields.io/badge/python-3.12-3776AB.svg?logo=python&logoColor=white)](https://www.python.org/)
[![License: MIT](https://img.shields.io/badge/license-MIT-yellow.svg)](LICENSE)

Image Provenance Inspector is an open-source tool for inspecting image metadata, C2PA Content Credentials, and editing provenance. It turns the evidence available inside an image into a small, human-readable report without collapsing uncertain origin and editing signals into one overconfident label.

It is designed to answer:

> What AI-related or editing information did this image actually retain?

It is not an AI detector and it does not try to defeat platform labels. A missing manifest or metadata field is not proof that an image is original.

## What it checks

- EXIF, XMP, and IPTC presence
- Camera model, capture time, and editing software when available
- C2PA Content Credentials, claim generator, validation state, and actions
- SHA-256, dimensions, format, and file size
- Optional original-versus-edited comparison using SSIM, perceptual hash, and a difference mask

The rule-based assessment reports two independent dimensions:

| Dimension | Values | Meaning |
| --- | --- | --- |
| `origin` | `CAMERA_CAPTURED`, `SYNTHETIC`, `UNKNOWN` | Evidence about where the asset may have originated. |
| `editing` | `NONE_DETECTED`, `TRADITIONAL`, `GENERATIVE`, `UNKNOWN` | Evidence about editing actions retained in the asset. |

Camera metadata is reported as medium-confidence `CAMERA_CAPTURED` evidence, not as proof that pixels are untouched. A generative action without a known generator remains `origin=UNKNOWN`, because a real source image may have lost its camera metadata. These values describe evidence, not certainty; provenance can be stripped, lost during export, or incomplete.

## Quick start

The project targets Python 3.12 and uses `uv` for reproducible environments.

```bash
uv sync --extra dev --extra c2pa --extra ui
uv run uvicorn app.main:app --reload
```

For the optional native acceleration used by larger comparison workloads, add
the `analysis` extra. The API also has a Pillow/standard-library fallback:

```bash
uv sync --extra dev --extra c2pa --extra ui --extra analysis
```

The API is available at <http://127.0.0.1:8000>. Interactive OpenAPI documentation is at <http://127.0.0.1:8000/docs>.

For the Streamlit interface, start the API dependencies in the same environment and run:

```bash
uv run streamlit run ui/streamlit_app.py
```

ExifTool is used when installed for broader metadata coverage. Pillow is the built-in fallback, so local development and tests do not require the system executable. On Debian/Ubuntu:

```bash
sudo apt-get install libimage-exiftool-perl
```

## API

### Analyze one image

```bash
curl -X POST http://127.0.0.1:8000/api/v1/analyze \
  -F "image=@photo.jpg"
```

The response is a fixed Pydantic-validated JSON contract:

```json
{
  "file": {
    "format": "JPEG",
    "width": 4032,
    "height": 3024,
    "size_bytes": 1842930,
    "sha256": "..."
  },
  "capture": {
    "camera": "Apple iPhone",
    "captured_at": "2026-08-20T15:32:11"
  },
  "metadata": {
    "exif": true,
    "xmp": true,
    "iptc": false,
    "software": "Adobe Photoshop"
  },
  "c2pa": {
    "present": true,
    "valid": true,
    "claim_generator": "Adobe Photoshop Generative Fill",
    "actions": ["c2pa.edited"],
    "generative_edit": true
  },
  "assessment": {
    "origin": {
      "type": "CAMERA_CAPTURED",
      "confidence": "medium",
      "reason": [
        "Camera capture metadata detected",
        "Metadata can be copied, rewritten, or retained after editing"
      ]
    },
    "editing": {
      "type": "GENERATIVE",
      "confidence": "high",
      "reason": [
        "Generative editing or AI provenance evidence detected",
        "C2PA generative action detected"
      ]
    }
  }
}
```

`c2pa.valid` is `null` when no validation state is available. The API does not convert absent evidence into a positive or negative origin claim.

### Compare an original and edited image

```bash
curl -X POST http://127.0.0.1:8000/api/v1/compare \
  -F "original=@original.jpg" \
  -F "edited=@edited.jpg"
```

The response includes `ssim` (0–1), `phash_distance`, `changed_area_ratio` (0–1), and a base64-encoded PNG difference mask.

### Privacy-sanitized copy

Create a local-use copy with selected privacy-sensitive EXIF fields removed:

```bash
curl -X POST http://127.0.0.1:8000/api/v1/sanitize \
  -F "image=@photo.jpg" \
  -F "remove=gps" \
  -F "remove=device-identifiers" \
  -F "remove=owner" \
  -F "remove=comments" \
  -F "remove=private-thumbnails" \
  -F "remove=timestamps"
```

If `remove` is omitted, all supported privacy groups are selected. The endpoint
returns the sanitized image as base64 together with a report of removed fields,
preserved metadata, C2PA state, and pixel comparison metrics.

The sanitizer is intentionally provenance-preserving: it does not remove or
rewrite C2PA, XMP, IPTC, software fields, or other provenance-bearing blocks.
When C2PA Content Credentials are detected, the input is returned unchanged so
that a signed asset is not silently invalidated. This is a privacy metadata
utility, not an AI-label remover.

The same operation is available from the CLI:

```bash
uv run image-provenance-inspector sanitize photo.jpg \
  --remove gps \
  --remove device-identifiers \
  --output photo.sanitized.jpg
```

All validation and expected upload errors use the same Pydantic `ErrorResponse` shape:

```json
{
  "code": "invalid_image",
  "message": "The uploaded file is not a supported image.",
  "details": []
}
```

The default upload limit is 20 MiB. Set `IPI_MAX_UPLOAD_BYTES` to change it for a deployment.

### Validation fixtures

The repository includes a pinned official C2PA JPEG fixture under
[`fixtures/`](fixtures/). Its test expects a signed `c2pa.created` manifest but
keeps both origin and editing classification as `UNKNOWN`; a credential is
evidence of provenance data, not automatic proof of camera capture or AI
generation. See [`fixtures/README.md`](fixtures/README.md) before adding
real-world vendor samples.

## Docker

Build and run the API container:

```bash
docker build -t image-provenance-inspector .
docker run --rm -p 8000:8000 image-provenance-inspector
```

The image includes ExifTool and the API runtime. It does not store uploaded images after a request completes.

## Development

Install development and UI dependencies, then run the checks locally:

```bash
uv sync --extra dev --extra c2pa --extra ui --extra analysis
uv run ruff check .
uv run ruff format --check .
uv run pytest --cov=app --cov-report=term-missing --cov-fail-under=80
```

The GitHub Actions workflow runs the same lint, formatting, coverage, lock-file,
wheel-build, and Docker-build gates on Python 3.12. `uv.lock` is committed so
CI and deployments resolve the same dependency graph.

The project also exposes a CLI using the same service and Pydantic result model:

```bash
uv run image-provenance-inspector photo.jpg
```

For contribution and maintainer workflows, see
[`CONTRIBUTING.md`](CONTRIBUTING.md).

## Project layout

```text
app/
├── api/             API route helpers
├── analyzers/       image, metadata, C2PA, and comparison readers
├── models/          public Pydantic schemas
├── provenance/      explainable classification rules
├── cli.py           command-line entry point and privacy sanitizer
├── main.py          FastAPI application
└── service.py       shared analysis orchestration
tests/               unit and API contract tests
fixtures/            traceable C2PA and real-world validation assets
ui/                  Streamlit interface
```

## Scope and limitations

This project inspects, validates, explains, and compares evidence. It deliberately does not:

- remove C2PA credentials or invisible watermarks;
- bypass Facebook, Instagram, or other platform detection;
- guarantee that an image is AI-generated or camera-original;
- infer provenance from pixels alone.

Uploaded content may contain sensitive metadata such as GPS coordinates. Run the service locally or review and sanitize metadata before sending images to a hosted deployment. The v0.2 sanitizer removes selected privacy EXIF fields while preserving provenance-bearing blocks.

## Responsible use

> ⚠️ **Responsible use**
>
> Use this project only with images and metadata that you are authorized to
> inspect. Do not use it to facilitate unlawful activity, privacy violations,
> fraud, copyright infringement, or attempts to evade platform safeguards.
>
> If an image is AI-generated or AI-edited, follow the applicable laws, license
> terms, disclosure requirements, and content policies of the platform where it
> is published. Do not remove, falsify, or bypass C2PA credentials, labels,
> watermarks, or other provenance signals.
>
> This project provides technical evidence, not a legal determination. You are
> responsible for obtaining the rights and permissions needed for image use
> and for complying with applicable laws and platform policies.

## License

Released under the MIT License. See [LICENSE](LICENSE).
