AI-Created Software

Last Updated: October 13, 2026

The following is AI-generated (aka vibe-coded) software. The software is specific to my needs and my system and may not work for you or your system. Support is limited, but if you report a bug I'll certainly look into it. All code is provided in a zip file for you to review should you choose.

Image Compare

A desktop tool for comparing two images, or two folders of images, pixel for pixel. You can view a pair with a draggable split divider, stack the two images and flip between them with a click, or show their pixel difference. It also has zoom, pan and a magnifying loupe for close inspection.

Built with Python, Tkinter and Pillow.

Contents

Features

  • Slider mode. The left image is on one side of a divider and the right image is on the other. Drag the divider to reveal more of either one.
  • Overlay mode. The two images are stacked. A single click (or Space) flips between them, which makes small shifts and color changes easy to spot.
  • Difference mode. Shows the absolute per-pixel difference between the two images. An optional amplification factor (×1–32) makes faint differences visible.
  • Loupe. A 360-pixel magnifier (at 100% display scaling) at 4× that follows the cursor. It shows the image-space coordinates of the pixel under the cursor.
  • Zoom and pan. Zoom with the mouse wheel around the cursor, or use the Fit / 50% / 100% / 200% presets. Both images always move together.
  • Folder comparison. Pairs up images with the same name (or the same relative path, in recursive mode) across two folders, and lists the images found on one side only.
  • How pairs differ. Each pair is measured on its whole images: the share of pixels that differ, the largest difference and the average. The Pairs panel marks identical pairs, and Shift+← / → skips them. See How pairs differ.
  • Opens in the background. Pairs open without freezing the window, and the next pair is opened ahead, so moving to it is instant.
  • Size-mismatch handling. Pairs whose resolutions differ are flagged. You can optionally scale them onto the same grid.
  • Drag and drop. Drop files or folders onto the path fields or the image area (requires tkinterdnd2).
  • Color-managed. Images with an embedded ICC profile (Adobe RGB, Display P3, ProPhoto and others) are converted to sRGB before they are shown or compared. See Color profiles.
  • Camera RAW files. CR2, CR3, NEF, ARW, DNG, RAF, ORF, RW2 and other RAW files open with a default rendering (requires rawpy). See Camera RAW files.
  • Remembers your setup. Paths, options, window size and position are saved between sessions.
  • Tooltips. Rest the pointer on a button or switch to see what it does.
  • Neutral gray interface. Every color in the interface has R = G = B, so nothing next to the images tints how you see them. The two sides are told apart by a filled square (left) and a hollow square (right), and by their position, not by color.
  • Display scaling. Everything is drawn to match the display scaling set in Windows or Linux, not only the text. See Display scaling.

Requirements

  • Python 3.8 or newer, with Tkinter. Tkinter is included with the standard Windows and macOS installers. On Debian/Ubuntu, install it with sudo apt install python3-tk.
  • Pillow
  • Optional: tkinterdnd2 for drag-and-drop. Without it, the app works normally with the Folder… / File… buttons, and the status bar notes that drag-and-drop is unavailable.
  • Optional: rawpy to open camera RAW files. Without it, RAW files in folders aren’t listed or paired; two RAW files chosen with File… still pair, and opening them says what to install.
pip install Pillow
pip install tkinterdnd2   # optional
pip install rawpy         # optional

Running

python image-compare.py

With no arguments, the app reopens the last paths you used. You can also pass paths on the command line:

# Compare two folders
python image-compare.py --left-file-or-folder-path ./before --right-file-or-folder-path ./after

# Compare two specific files (their names don't need to match)
python image-compare.py --left-file-or-folder-path a.png --right-file-or-folder-path b.png

# Match images in sub-folders too
python image-compare.py --left-file-or-folder-path ./before --right-file-or-folder-path ./after --recursive TRUE

# Check the image functions and the installed features, without opening a window
python image-compare.py --selftest TRUE

Command-line options

Option Description
--left-file-or-folder-path PATH File or folder for the left side.
--right-file-or-folder-path PATH File or folder for the right side.
--recursive TRUE Include images in sub-folders, matched by relative path; FALSE leaves them out. Blank: the choice saved last time.
--working-folder-path FOLDER The folder for the settings file (image-compare.ini) and temporary files, created if needed. The default is %APPDATA%\image-compare on Windows (see Settings). A folder that can’t be created is an error (exit status 2).
--selftest TRUE Check the image functions and report the optional features (RAW support, drag and drop, Tkinter), then exit. See Testing.

Paths given on the command line take priority over the saved ones.

Every option is named and takes a value, written --name VALUE or --name=VALUE, in any order.
Yes/no options take TRUE or FALSE (also 1/0, YES/NO, ON/OFF, in any case); script launchers that read the options (such as Scriptary) offer them as buttons.
python image-compare.py --help lists the options with their defaults. A mistake on the command line (an unknown or shortened option, an option given twice or without its value, or a path without an option) stops the app with a message and exit status 2; started without a console, as with pythonw.exe, the message is shown in a message box instead.

How pairs are matched

  • Two files: they are compared directly, whatever their names.
  • Two folders: only images whose file names match on both sides are paired. With Recursive on, the whole folder tree is searched and images are paired by relative path (sub/dir/photo.png on the left pairs with sub/dir/photo.png on the right). Pairs are sorted by path, case-insensitively.
  • A folder and a file: not supported. The app asks for either two folders or two files.

Supported formats are .jpg, .jpeg, .png, .bmp, .gif, .tiff, .tif and .webp, and, with rawpy installed, camera RAW files (see Camera RAW files). Images are converted to 8-bit sRGB for display (see Color profiles). 16-bit grayscale files are scaled from 0–65535 down to 0–255; a 16-bit file whose values all fit in 0–255 is shown as it is.

Photos are turned upright from their EXIF orientation tag, as cameras and phones set it, before they are shown or compared. Resolutions, and the size check below, are for the photo turned upright, so a portrait photo stored sideways with a rotation tag pairs cleanly with an upright copy of it.

The window

From top to bottom, the window contains:

  1. Path row. Left and Right path fields, each marked with its side’s square, with Folder… / File… browse buttons, then:
    • Folder… and File… open where that side’s field points: in its folder, or in a file’s folder with the file chosen. A path that no longer exists opens at its nearest folder that does. With the field empty, they open at the last folder that side used, which is kept for next time. On Windows they use Windows’ own Open dialog directly, because Tk’s changes some paths on the way (see Troubleshooting). Each side’s dialog also has its own memory of where it was last, so one side never opens where the other side’s last was.
    • Recursive: On/Off. Changing it rescans immediately.
    • Rescan: re-read both paths and rebuild the pair list. Use this after typing a new path.

    The two path fields share what’s left of the row’s width.

  2. Toolbar. When the window is too narrow for it on one row, Difference and Loupe move to a second row.
    • ◀ Prev / Next ▶: move between pairs. Prev is grayed out on the first pair and Next on the last, so with two files (one pair) both are.
    • View: Fit / 50% / 100% / 200% presets, − / + zoom buttons and the current zoom level. A preset is lit only while the zoom matches it.
    • Difference: On/Off, plus the amplification factor (×1–32).
    • Loupe: On/Off.

    On every switch, clicking the option that is already lit does nothing.

  3. Info line. The left file, the pair name with its position (e.g. 3 / 20), and the right file, each with its native resolution and how its colors were read (see Color profiles). A bold ⚠ note appears here when the two sizes differ.
  4. Pairs panel. Lists every pair, one row each. The selected row is raised and bold, and expands to show thumbnails of both images. At the right of each row, a ⚠ marks a pair whose sizes differ, same an identical pair (its name dimmed), and a percentage the share of pixels that differ; these are worked out in the background.
    Below the pairs, Only on the left and Only on the right list the images with no match on the other side, with their extensions (so shot.CR2 with no shot.CR2 on the right shows there even when there is a shot.jpg); they can’t be opened, having nothing to be compared with. The line below the list shows how many pairs are mismatched and identical, how many images are on one side only, and scanning… while the background checks are still going. Drag the line between the panel and the Compare area to make the panel wider or narrower (from 160 to 700 pixels at 100% display scaling, always leaving the Compare area at least 400); it keeps that width when the window is resized, and next time.
  5. Compare area. The Slider | Overlay switch, a live readout (divider position or which side is showing, the zoom level, and the image-space point at the center of the view), how the pair differs at the right (see How pairs differ), and the image itself. Before a pair is open, it says how to open one, or that no image has the same name on both sides. A pair that takes more than 0.15 seconds to open shows Opening… meanwhile.
  6. Status line. Shortcut hints for the current mode, the pair count and drag-and-drop availability.

Controls

Mouse

Action Slider mode Overlay mode
Left-click / drag on image Move the divider Flip between left and right
Mouse wheel Zoom around the cursor Zoom around the cursor
Middle-drag, right-drag or Shift + left-drag Pan Pan
Click a row in the Pairs panel Open that pair (← and → then work at once, even after typing a path) Open that pair (likewise)
Drag the line beside the Pairs panel Widen or narrow the panel Widen or narrow the panel

Keyboard shortcuts

Key Action
← / → Previous / next pair (stops at the first and last)
Shift+← / Shift+→ Previous / next pair that isn’t identical (pairs not yet measured count as changed; stops at the first and last)
+ or = Zoom in
− Zoom out
0 Fit to window
1 100% (one screen pixel per image pixel)
[ / ] Nudge the divider left / right by 1% (Slider mode)
Space Flip between left and right (Overlay mode)

Shortcuts are ignored while you’re typing in a path field or the × box, and while a dialog is open.

Drag and drop

When tkinterdnd2 is installed:

  • Drop a file or folder onto the Left or Right path field to set that side. The field lights up while a file is over it.
  • Drop onto the image area to set the left side (left half) or the right side (right half).

The pair list rescans automatically once both sides have a path.

Comparison modes

Slider (the default) splits the view at the divider, with the left image on the left and the right image on the right. Each side’s folder or file name is shown in a caption in its top corner, with the side’s square.

Overlay shows one full image at a time, in the same position and at the same zoom. Click anywhere on the image, or press Space, to flip to the other one. Only the caption for the image currently showing is displayed, in that side’s usual corner, and the Compare readout says showing LEFT or showing RIGHT. Switching back and forth quickly is often the fastest way to catch small shifts, crops or color changes.

Difference takes precedence over both. When it is on, the view shows |left − right| for each pixel: black where the images are identical, brighter where they differ. Raise the × factor to multiply the difference and make subtle changes visible.
Difference needs both images on the same pixel grid, so it turns Match sizes on automatically if the resolutions differ.

The slider/overlay choice is remembered between sessions. Difference and Loupe always start off.

Loupe

With Loupe on, a 360 × 360 pixel magnified inset (at 100% display scaling) follows the cursor. It appears just beside the outlined area it is sampling, so it never covers the spot you’re looking at. It moves to the other side of the cursor, or above/below it, near the edges of the view.

What the loupe shows depends on the mode:

  • Slider: split down the middle, with the left image on the left and the right image on the right, both at the same point.
  • Overlay: whichever image is currently showing. It flips along with the view.
  • Difference: the difference image.

A caption strip along the bottom of the loupe shows the magnification and the pixel coordinates of the cursor in the source image.

To change the loupe, edit these constants near the top of the script:

LOUPE_SIZE = 360   # on-screen size, in pixels at 100% display scaling
LOUPE_GAP = 16     # gap between the sampled area and the loupe, likewise
LOUPE_ZOOM = 4.0   # magnification relative to the current view

How pairs differ

Every pair is measured on its whole images, as they are shown: in sRGB, upright, at full size. The Compare area’s header says how the open pair differs:

  • Identical: no pixel differs when every pixel of the two images is the same.
  • Otherwise, for example 1.0% of pixels differ · largest difference 190 · average 0.42: the share of pixels where any of red, green or blue differs, the largest difference in any channel (out of 255), and the average difference per channel over the whole image.

A pair whose sizes differ isn’t measured, since resizing one image to the other’s size changes every pixel; the ⚠ says the sizes differ instead.

The Pairs panel shows the same share for each row, and same for identical pairs. The open pair is measured once it is on screen. The others are measured one at a time in the background, starting from the selected pair, and only while no pair is being opened, so they never slow down the pair you are looking at. Measuring needs each image decoded: for a pair of 6000 × 4000 images, measuring took 0.5 seconds on top of opening them, and a folder of large or RAW pairs takes a while to measure in full. Rescan measures everything again, in case files changed.

Mismatched sizes

When the two images in a pair have different resolutions, the app flags it in the info line and marks the row with ⚠ in the Pairs panel. A mismatch usually means you’re comparing the wrong things, so the app points it out instead of silently hiding it.

  • Match sizes On (default): the right image is resized (Lanczos) to the left image’s resolution so the two line up.
  • Match sizes Off: each image is shown at its native size, anchored to the top-left, so the comparison is not aligned. The Difference switch is dimmed in this state because a pixel difference can’t be computed. Clicking it anyway turns Match sizes back on and explains why.

Color profiles

Each image is converted to sRGB from the ICC profile embedded in it, so the views, the loupe, the thumbnails and Difference all compare colors, not raw values in different color spaces. An image saved in Adobe RGB and the same image saved in sRGB look the same and show a black Difference.

The info line says how each side’s colors were read, in small text next to its resolution. A long note wraps onto a second line rather than being cut off.

Note Meaning
No embedded profile, assumed sRGB The file has no embedded profile. Its values are shown as sRGB, unchanged.
Embedded profile: and its name, such as sRGB IEC61966-2.1 The file is already sRGB, so it is shown unchanged.
Converted from the profile’s name to sRGB The file’s colors were converted from its profile to sRGB.
Embedded profile could not be read, assumed sRGB The embedded profile couldn’t be read, so the values are shown unchanged.
CMYK without a profile, converted naively; readings are approximate A CMYK file with no profile, converted with a simple formula.
RAW decoded with camera white balance and a default rendering, not your darktable edit A camera RAW file, decoded straight to sRGB (see Camera RAW files).

Conversion uses the relative colorimetric intent. Colors outside sRGB, which Adobe RGB, Display P3 and ProPhoto can hold, are clipped to the nearest sRGB color, so two images that differ only in those colors can show as identical.

In testing, images converted from sRGB into Adobe RGB, Display P3 and ProPhoto and saved as PNG came back within 1 level of the original (0.2 on average); the same images saved as JPEG at quality 97 were within 0.6 on average, the rest being compression. Converting takes time: a 6000 × 4000 JPEG with an Adobe RGB profile opened in 0.88 seconds, against 0.16 seconds without a profile. A 6000 × 4000 16-bit grayscale PNG opened in 0.32 seconds, against 0.13 seconds for an 8-bit one. Thumbnails are made from the images already opened, so they add almost nothing.

Camera RAW files

With rawpy installed, camera RAW files (.cr2, .cr3, .crw, .nef, .nrw, .arw, .srf, .sr2, .dng, .raf, .orf, .rw2, .pef, .srw, .3fr, .iiq, .x3f, .erf, .mrw, .kdc) are decoded with the camera’s white balance and LibRaw’s default rendering, straight to 8-bit sRGB, turned upright, at full size. This is not your darktable edit: .xmp sidecar files are ignored. To compare edits, export them from darktable and compare the exports.

Pairs are matched by the whole file name, extension included, so shot.CR2 pairs with shot.CR2, not with shot.jpg. To compare a RAW file with an export of it, choose the two files with File….

The size check behind the ⚠ marks reads a RAW file’s size with rawpy, not from the preview image many RAW files carry: for a Canon 5D Mark II CR2, the preview is 5616 × 3744 but the decoded image is 5634 × 3752.

Decoding is slow. That 21-megapixel CR2 took 1.9 seconds to decode, and a pair of two of them 4.6 seconds to open. The window keeps responding meanwhile, and the info line says opening RAW….
The next pair is opened ahead while you look at one, so moving through a folder in order waits only when you move faster than that. Its thumbnails in the Pairs panel are made from the decoded images, in 0.07 seconds.

Without rawpy, RAW files in folders aren’t listed or paired, and the File… dialog lists them only under All files. Two RAW files chosen with File… still pair: opening them clears the compare area and says what to install (pip install rawpy), or to export the photo as an sRGB JPEG, PNG or TIFF instead, and the info line shows ⚠ can't open the left image (or right). The same happens for any file that can’t be opened.

Zoom and rendering

At 200% and above, images are scaled with nearest-neighbor sampling so individual pixels stay crisp. Below that, Lanczos resampling is used to avoid aliasing. Only the visible region is resized, so zooming into very large images stays responsive.

While the view is changing (zooming with the wheel or the buttons, panning, resizing the window, or a new pair opening), a quick preview is drawn at once with a faster filter, and the Lanczos version replaces it once the view has stayed the same for 0.4 seconds. The preview differs from the final view by about 3 levels out of 255 on average. For a pair of 6000 × 4000 images at Fit in a 1357 × 671 view, the preview took about 160 ms and the full version about 460 ms in testing. At 100% and from 200% the two are the same, so there is nothing to wait for.
Dragging the divider and flipping in Overlay reuse the views already drawn, so they are immediate.

The view resets to Fit whenever you open a new pair.
Zoom ranges from 2% to 6400%.

On Windows, the app declares itself DPI-aware, so 100% really means one screen pixel per image pixel, even when display scaling is set to 125% or 150%.

Display scaling

The interface follows the display scaling set in Windows or Linux.
Sizes are designed for a 100% (96 dpi) display and scaled to match: at 150%, the text, buttons, spacing, the rows of the Pairs panel, the thumbnails, the divider handle and the loupe are all half as large again. The images themselves are not scaled this way: 100% zoom stays one screen pixel per image pixel.

The toolbar fits on one row from about 1250 pixels at 100% with Linux’s default font (about 1800 at 150%), less than the minimum width, so it stays on one row there; with a larger font, Difference and Loupe move to a second row when it doesn’t fit. The window can’t be made narrower than 1400 pixels at 100% (2100 at 150%), which leaves the info line room for long file names on both sides. The window opens at 1560 × 820 at 100% the first time, scaled likewise.

Settings

Settings are saved when you close the window, as image-compare.ini in the app’s working folder: %APPDATA%\image-compare on Windows, ~/Library/Application Support/image-compare on macOS, and ~/.config/image-compare (or $XDG_CONFIG_HOME/image-compare) elsewhere.
python image-compare.py --help shows the folder on your computer. Use --working-folder-path to put it somewhere else. If the default folder can’t be created, the app still runs, without saving.

The saved settings are:

  • [paths]: the left and right paths, and the last folder each side used (left_folder, right_folder)
  • [compare]: Recursive, Match sizes, the Difference factor (×1–32) and the comparison mode (Slider or Overlay)
  • [window]: the window’s size, position and maximized state
  • [panels]: the width of the Pairs panel, where its divider was left

The file is plain text and can be edited by hand. A value that can’t be read, or is out of range, falls back to its default, so a damaged file never stops the app from starting.

Older versions saved image_compare_settings.json beside the script. The first time this version starts without an image-compare.ini, it brings those settings over, including the older path_left / path_right and folder_left / folder_right keys. The old file is left as it was.

Window icon (optional)

image-compare.ico, next to the script (or the frozen executable), is the icon of the title bar and the taskbar. On Windows the .ico file is used as it is, so each place takes the size it needs; elsewhere every size in it (16 to 256 pixels) is handed to Tk. If the file is missing, the app runs with Tk’s own icon.

On Windows the app has its own taskbar ID, infolific.image-compare, so its button groups and pins as Image Compare rather than as Python. Versions before this one used a different ID: if you pinned the app to the taskbar, pin it again.

image-compare-generate-icons.py draws the icon file, the PNGs in resources/ and the light and dark previews in the root folder with Pillow.

Testing

python image-compare.py --selftest TRUE checks the image functions without opening a window, printing one line per check and All checks passed. at the end. It exits with status 0 when every check passes and 1 otherwise. It checks:

  • pairing: two folders by name (GIF files too), recursively by relative path, two files, and a folder with a file;
  • color profiles: an image without one, an Adobe RGB image converted back to within 1 level of its sRGB colors, and a damaged profile;
  • a photo stored sideways with an EXIF orientation tag, shown upright;
  • a 16-bit grayscale ramp scaled to 8 bits;
  • RAW files recognized by their extension and, without rawpy, the message saying to install it;
  • settings saved as INI in the working folder and read back, a bad value falling back to its default, and an older version’s JSON settings brought over once.

It then says which optional features are installed. It takes about 0.2 seconds, works in a temporary folder, and doesn’t touch your settings. It runs without Tkinter too, so it can tell you Tkinter is missing.

python -m pytest runs the GUI tests: they open the real app on images they generate, work its controls with real mouse and key events, and check what it shows on screen. They need pytest (pip install pytest) as well as Pillow and Tkinter. Without a working $DISPLAY they start Xvfb (and openbox, if installed) themselves; with no display at all they are skipped.
python -m pytest -k zoom runs the tests whose names contain zoom. The 35 tests cover:

  • Slider: each image on its side of the divider, dragging the divider, and the [ ] keys;
  • Overlay: a click and Space flip between the images;
  • Difference: black where the images match, the difference where they don’t, and the × factor;
  • mismatched sizes: the ⚠ marks and count, Match sizes, and Difference dimmed then turning Match sizes back on;
  • zoom: the View presets, 100% as one screen pixel per image pixel, − and +, and wheel zoom around the cursor;
  • the quick preview while zooming, replaced by the full-quality view once the zoom has stayed the same for 0.4 seconds;
  • the loupe: left and right halves, placed beside the area it samples, and gone when the cursor leaves;
  • the Pairs panel and keys: clicking a row, thumbnails, ← → stopping at the first and last pair, Prev and Next grayed out there and after a Rescan or a folder change, and no navigation while typing in a path; a click in the list taking the focus from a path field, so → works at once;
  • images on one side only: listed by side under the pairs, counted in the footer, not opened by a click, and pointed to when there are no pairs;
  • how pairs differ: the numbers in the Compare header, every pair measured in the background and marked same or with its share, and Shift+→ skipping identical pairs;
  • opening in the background: the window responding while a slow pair opens, Opening… in place of the old images, and the next pair preloaded and shown at once;
  • the divider beside the Pairs panel: dragged with the mouse, kept within its limits, keeping its width when the window is resized, and restored after a restart;
  • switches acting only on a change (Recursive’s lit option doesn’t rescan), and the empty compare area saying how to start, centered after a resize, with no old images left to draw after a Rescan finds no pairs;
  • color profiles, EXIF orientation and 16-bit grayscale, each against a plain sRGB copy of the same picture;
  • RAW without rawpy (RAW files in folders not listed, two RAW files chosen as files pairing but saying what to install, and the folders’ pair opening again) and, with rawpy, a real RAW file named by $RAW_TEST_FILE (skipped otherwise);
  • settings kept across a restart, a hand-edited settings file with bad values (or not INI at all) falling back to the defaults, and settings that can’t be saved on closing reported in a message box;
  • the command line: --working-folder-path creating the folder and keeping the settings there, the paths and --recursive TRUE given in any order, --recursive FALSE overriding a saved choice, a working folder that can’t be created, an unknown option and a bare switch each exiting with status 2, --selftest TRUE, and --help; the yes/no options’ TRUE and FALSE written out where Scriptary reads them;
  • Recursive, then Rescan set apart from it, at the end of the path row, after both path fields and with no image counts, at the minimum width and wider;
  • the toolbar on one row at the minimum width, and moving Difference and Loupe to a second row when the window is too narrow for one (the test lowers the minimum to see it), and back when it’s widened;
  • the window at its minimum size: never narrower than the toolbar on two rows, no button, switch or label cut off, the toolbar’s groups not crowding each other, at least three rows of the Pairs panel in view, and a long note in the info line wrapping rather than being cut off;
  • the side squares: filled and hollow the same size, drawn as Image to Image Color Matcher draws them, on the path row, the info line and the captions;
  • tooltips on the path row’s buttons and switch, the toolbar’s groups and Slider | Overlay, shown below a control after the pointer rests and gone when it leaves;
  • every color of the interface a neutral gray in every state (hover, press, focus, disabled, read-only), with none of the Tk theme’s own beige or blue;
  • the window icon in every size the icon file holds, and closing the window with the size check still going, a re-render pending and the full-quality view still to come, which must leave no scheduled job behind.

They take about 33 seconds, or about 41 with the RAW test, and pass at 100% and 150% display scaling. To test another copy of the script, set APP_SCRIPT=/path/to/copy.py. Each test uses its own temporary folders and working folder.

Before each change:

  • Run python image-compare.py --selftest TRUE and python -m pytest.
  • Check the Python files still parse as Python 3.8: python3 -c "import ast, glob; [ast.parse(open(f, encoding='utf-8').read(), feature_version=(3, 8)) for f in glob.glob('*.py') + glob.glob('*/*.py')]"
  • Run python3 -m pyflakes on the changed files, and python templates/tools/check_copies.py --app-folder-path . to check that the code copied from the templates still matches them.
  • Look at the change on screen too. On Linux without a display, run the app under Xvfb and take screenshots; start Xvfb with -dpi 144 to check 150% display scaling.

Troubleshooting

  • “Tkinter is not available” when starting: install Tkinter for your Python. It comes with the python.org installers for Windows and macOS; on Debian or Ubuntu, sudo apt install python3-tk.
    python image-compare.py --selftest TRUE says whether it’s there.
  • “Settings will not be saved: cannot create …” on the console: the default working folder couldn’t be created. The app still runs. Give it a folder it can use with --working-folder-path.
  • “Could not save the settings to …” when closing: the settings file couldn’t be written, often because the working folder was removed, is read-only or is full. Your paths and choices from this session are lost; the next session starts from the settings saved before. Give the app a folder it can write to with --working-folder-path.
  • Your paths and choices were forgotten after updating: this version keeps them in image-compare.ini in the working folder instead of image_compare_settings.json beside the script. It brings the old file’s settings over the first time it starts, as long as the old file is still beside the script then.
  • The taskbar button shows Python’s icon, or a pinned button opens a second one: the app’s taskbar ID changed in this version (to infolific.image-compare). Unpin the old button and pin the app again. The icon itself is image-compare.ico beside the script (see Window icon).
  • The window opens somewhere unexpected: a position saved on a monitor that’s no longer connected is ignored, and the window opens at the default place. To reset the size and position, close the app and delete the [window] section of image-compare.ini.
  • “Opening camera RAW files needs rawpy” in the compare area: pip install rawpy, or export the photo from darktable as an sRGB JPEG, PNG or TIFF and compare that.
  • Folder… or File… opens somewhere else than the path in the field (on Windows, with folders under a Cryptomator vault or another link): Tk’s own dialogs follow links in the path and can lose its drive letter, and Windows then opens the last folder you browsed.
    The app now opens Windows’ dialog itself to avoid this, and only falls back to Tk’s if that fails.
  • Drag and drop does nothing: it needs pip install tkinterdnd2. The status line says whether it’s available; the Folder… and File… buttons work either way.
  • A pair looks different here than in another viewer: both images are converted to sRGB from their embedded profiles, and colors outside sRGB are clipped (see Color profiles). The info line says how each side’s colors were read.
  • The view looks slightly soft for a moment after zooming: that’s the quick preview; the full-quality view replaces it 0.4 seconds after the zoom stops (see Zoom and rendering).

Limitations

  • Images are shown as 8-bit sRGB, and colors outside sRGB are clipped (see Color profiles).
  • 32-bit float grayscale files are read as 0–255, or as 0–65535 if any value is above 255. A float file stored as 0–1 shows black.
  • For the ⚠ marks in the Pairs panel, a PNG’s rotation tag is read only when it comes before the image data, which is where Pillow writes it. Checking further would mean decoding every PNG. If a PNG’s tag comes later, its row’s ⚠ mark can be wrong, but the photo itself is still shown upright and the info line’s size warning is right.
  • HEIC files are not supported.
  • RAW files are shown with a default rendering, not as edited (see Camera RAW files), and a pair of large RAW files takes several seconds to open.
  • A folder can’t be compared with a file; use two folders or two files.
  • The whole image is decoded when a pair is opened, so very large images take a moment to open.
  • Pairs are opened one at a time: moving on while a slow pair (a RAW pair) is still opening waits for it to finish before the new one starts.
  • Measuring every pair decodes every image in the background, which keeps a processor core busy for a while on a large folder of large or RAW files.
  • A pair whose sizes differ gets no numbers, even with Match sizes on.
Download Release:
⬇️ Image Compare.zip
(1 votes, average: 5.00 out of 5)

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail.