Metadata-Version: 2.4
Name: imdiff
Version: 2.1.0
Summary: Compare image files in different directories
Author-email: "John T. Goetz" <theodore.goetz@gmail.com>
Project-URL: homepage, https://gitlab.com/johngoetz/imdiff
Keywords: image-diff,directory-comparison,visual-regression,golden-files,test-artifacts,qa
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Natural Language :: English
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Multimedia :: Graphics :: Viewers
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: pillow
Requires-Dist: ttkbootstrap
Provides-Extra: dev
Requires-Dist: ruff; extra == "dev"
Requires-Dist: basedpyright; extra == "dev"
Dynamic: license-file

# ImDiff: Compare Directories of Images

ImDiff compares either two image files or two directory trees that contain image
files with matching relative paths. It is intended for workflows where generated
images need visual review as part of test development, golden-file updates, or
artifact triage.

The project exposes the same comparison engine through two interfaces:

- `python -m imdiff LEFT RIGHT` opens a Tk-based review window when the GUI
  stack is available, or warns and falls back to summary mode when it is not.
- `python -m imdiff --summary LEFT RIGHT` prints a non-interactive summary and
  exits with a non-zero status when differences are found without importing any
  GUI-only modules.

When both arguments are directories, ImDiff walks the union of both directory
trees, pairs files by relative path, and classifies each entry as:

- `identical`: byte-identical files or text files with no unified diff output
- `similar`: images whose normalized RMSE is below the project threshold
- `different`: images or text files whose contents differ materially
- `different-size`: image pairs with different dimensions
- `missing` or `new`: a file exists on only one side
- `failed-to-load`: a file exists but Pillow could not decode it as an image

The interactive directory view combines a file tree, image panes, and file
operations so a reviewer can inspect left, right, and diff images, switch zoom
policies, and copy or delete files directly from the comparison session.

This utility requires that the Tk Python packages be installed on the system. Hint for
those running Ubuntu:

```
apt install python3-pil python3-pil.imagetk python3-tk
```

The application also depends on `numpy` and `ttkbootstrap` for image math and
themed Tk widgets.

## Usage

Compare two image files:

```bash
python -m imdiff path/to/expected.png path/to/actual.png
```

Compare two directories and print a CI-friendly summary:

```bash
python -m imdiff --summary tests/baseline-images tests/generated-images
```

By default the normalized RMSE is measured over the whole image, which tolerates
the many tiny GPU/driver rendering variations that show up across large images.
To instead catch small but concentrated differences, select a sliding-window
size with `--window`; the reported score becomes the worst-scoring window of that
size:

```bash
python -m imdiff --window medium tests/baseline-images tests/generated-images
```

The available window sizes are `global` (the default, whole image), `small`
(32x32), `medium` (64x64), `large` (128x128), and `xlarge` (256x256). You can
also pass an explicit pixel size as `WIDTH,HEIGHT`, for example
`--window 1024,1024`, when none of the presets fit. A window larger than the
image is clamped to the image size. The flag applies to both the interactive
windows and `--summary` mode.

When the GUI cannot be imported or Tk cannot start, the default interactive
command warns on `stderr` and continues in the same summary mode used by
`--summary`. This makes headless CI environments safe without changing the CLI
command line.

## Programmatic comparison

The same comparison core is available to other Python code, such as test
harnesses that compare a captured screenshot against a stored reference:

```python
from imdiff import ImageComparator, RmseWindow

comparator = ImageComparator(reference_png, actual_png, RmseWindow.Medium)

status = comparator.diff_info  # 'identical', 'missing', 'different-size', ...
if isinstance(status, float):  # a same-size pair: status is the normalized RMSE
    assert status <= 0.03, f'normalized RMSE {status:g} exceeded threshold'

comparator.diff                # grayscale difference image (PIL.Image)
comparator.high_contrast_diff  # histogram-stretched difference image
```

`diff_info` returns a symbolic status string for presence, size, and load
problems, or the normalized RMSE (range `[0, 1]`) when both images decode at the
same size. The metric honors the `window` passed to the constructor: `Global`
scores the whole image, while any other `RmseWindow` reports the largest score
found across every window position of that size. The `window` may also be an
explicit `(width, height)` tuple instead of a preset. A window larger than the
image is clamped to the image size. Decoded images and derived results are
cached on first use and recomputed after `copy_left_to_right`,
`copy_right_to_left`, `delete_files`, or an explicit `clear`.

The `normalized_rmse(left_data, right_data, window)` function is also exported
for callers that already hold equally sized RGBA arrays.

## Repository Layout

- `imdiff/image_comparator.py` is the comparison core: the `RmseWindow` sizes,
  the `normalized_rmse` metric, and the lazy `ImageComparator` model shared by
  the CLI and GUI.
- `imdiff/list_files.py` pairs files across directory trees.
- `imdiff/cli/` contains the command-line entry points and summary printing.
- `imdiff/gui/` contains the Tk windows, canvases, menus, and directory browser.
- `developing/architecture.md` describes the component relationships in more
  detail.
