Home (All Topics) → Technology → Software → AI-Created Software
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.
A desktop tool for blending an after image into a before image, weighted by a grayscale mask and scaled by a single slider. White areas of the mask take the after image, black areas keep the before image, and grays mix the two. At 0% the result is the pure before image; at 100% it is the full mask-weighted blend.
It is built for comparing and dialing in edits: retouching, relighting, compositing tests, or any case where you want an edit applied only in some places and only by some amount.
python3-tk)Tested with Python 3.12 and Pillow 12.
The app is a single script, so there is nothing else to install. If a file named image-blender-with-mask.ico sits next to the script, it is used as the window icon.
Every option is named and takes a value, written --name VALUE or --name=VALUE, in any order.--help lists them with their defaults. Script launchers that read the options (such as Scriptary) offer --selftest’s TRUE and FALSE as buttons.
| Option | What it does |
|---|---|
--selftest TRUE |
Check the image and mask functions (mask adjustments, blending in both modes, 16-bit masks, EXIF orientation, the camera data kept on export, project files) with images made on the spot, print one line per check and which optional features are installed, and exit with 0 when all pass or 1 when one fails. No window is opened and none of your files is touched. |
--working-folder-path FOLDER |
The folder for the settings file and temporary files, created if needed. Blank or not given: %APPDATA%\image-blender-with-mask on Windows, ~/Library/Application Support/image-blender-with-mask on macOS, ~/.config/image-blender-with-mask elsewhere. |
A mistake on the command line, such as an unknown option or a folder that can’t be created, is reported with “Use –help for usage.” and exit status 2. Started without a console (pythonw.exe), the message is shown in a message box instead.
If you don’t have a mask, choose All white in the Mask switch to apply the after image uniformly, or use Mask → Generate mask from difference.
The after image and mask are resampled to the before image’s size, so they don’t need to match it exactly. If the after image has a different aspect ratio, it is stretched to fit and a warning appears in the slot strip. Photos are turned upright according to their camera orientation.
Supported formats are PNG, JPEG, TIFF, WebP and BMP.
With tkinterdnd2 installed, you can drop files in two ways:
before, after, mask or matte go to the matching slot; the rest fill the remaining empty slots in order. If every slot is already full, you’re asked which slot to replace.Dropping a project file opens that project.
The View switch sets the base scale:
| Mode | Shows |
|---|---|
| Fit | The whole image, scaled to the window |
| 1:1 before | The before image at actual size |
| 1:1 after | The after image at actual size |
| 2× | The smaller of the two images at double size |
The mouse wheel and the + and − keys zoom from any mode, keeping the point under the cursor in place.
0 returns to Fit. Drag with the mouse to pan.
In wipe view, drag the divider to move it; drag anywhere else to pan.
The Mask switch chooses between the Loaded mask, All white and All black. The overlay color and opacity are set in Mask tools and never affect saved output.
Mask tools offers:
| Setting | Effect |
|---|---|
| Channel | Which channel of the mask file is used |
| Invert | Swaps where the after image is applied |
| Black / white point | Stretches the mask’s tonal range |
| Threshold | Turns the mask into a hard-edged selection at a chosen level |
| Feather | Blurs the mask edge by the given radius in pixels |
The Mask tools window opens where you last closed it. While you drag a Mask tools slider, the preview updates at screen resolution. The full-resolution mask is rebuilt in the background a moment after you stop. Save mask (also Mask → Export processed mask) writes the adjusted mask at its own resolution as a 16-bit PNG or TIFF.
Every mask change, including loading a new mask or generating one, can be undone with Undo (at the right end of the view controls) or Ctrl+Z, and redone with Redo, Ctrl+Shift+Z or Ctrl+Y. The buttons are grayed out when there is nothing to undo or redo. A continuous slider drag counts as one step.
The last 100 changes are kept, until you close the app.
The bar below the slider shows the values under the cursor: image coordinates, before and after RGB, the mask value and the blend weight it produces, and the resulting RGB. These are computed from the full-resolution sources, whatever the zoom level.
Turn on Watch files to reload any source that changes on disk, such as a mask you are painting in another editor.
Sources are checked once a second. A file that disappears or can’t be read is reported in the slot strip, and the app retries on the next check.
Ctrl+R reloads all sources on demand. The slider, zoom and mask settings are kept.
Save project asks for a folder and creates a sub-folder inside it named after the before image. That sub-folder contains:
<name>.ibwmproj, plain JSON)generated_mask.png, if the mask was generated rather than loaded from a fileresult.png, the rendered blend at the before image’s sizeHover over Save project or Load project for a summary of the files each writes or expects.
Projects restore the images, the slider value, the zoom, all mask settings, the mask override, the overlay settings and the linear-light option. Recently opened projects are listed under File → Recent projects.
Use portable export when you want to move or share a project. Linked projects stay small but depend on the original files staying where they are. On systems where symbolic links aren’t allowed, hard links are used instead.
The title bar shows • while there are unsaved changes, and the app offers to save before you close it or open another project.
File → Export image (Ctrl+E) saves the blend at the before image’s size, the after image’s size, or the current zoomed-out view size.
| Format | Notes |
|---|---|
| PNG | Lossless |
| JPEG | Adjustable quality (95 to start with), without chroma subsampling |
| TIFF | Lossless, LZW-compressed |
| WebP | Adjustable quality (95 to start with) |
| BMP | Uncompressed; no color profile |
The file extension you type decides the format, so result.jpg is always saved as a JPEG. Every export is written to a temporary file beside the target first and then put in its place, so a failed export never leaves a half-written file over an earlier one. The before image’s ICC color profile is embedded in every format except BMP, and its camera data (EXIF) in PNG, JPEG and WebP, with the orientation reset since the result is already upright.
File → Export slider sweep (Ctrl+Shift+E) renders a series of slider values between a start and end percentage. It can export:
sweep_000.png, sweep_001.png… files in a folder. If the folder already contains frames from an earlier sweep, you’re offered the chance to delete them first.Easing controls how the slider values are spaced across the frames: linear, ease in, ease out, or ease in-out.
Ping-pong plays the sweep forward and then back.
A progress bar tracks rendering, and Cancel stops the export. Animations hold every frame in memory while they are encoded, so you’re warned first if that would need more than about 2 GB.
On macOS, use Cmd in place of Ctrl.
| Keys | Action |
|---|---|
| Ctrl+1 / Ctrl+2 / Ctrl+3 | Load before / after / mask |
| Ctrl+O | Load project |
| Ctrl+S | Save project |
| Ctrl+E | Export image |
| Ctrl+Shift+E | Export slider sweep |
| Ctrl+R | Reload sources |
| Ctrl+Z | Undo mask change |
| Ctrl+Shift+Z or Ctrl+Y | Redo mask change |
| Left / Right | Slider −1% / +1% |
| Shift+Left / Shift+Right | Slider −10% / +10% |
| Home / End | Slider to 0% / 100% |
| + / − | Zoom in / out |
| 0 | Fit to window |
| Hold B | Show the pure before image |
| Hold M | Show the mask on its own |
| O | Toggle the mask overlay |
| W | Toggle the wipe comparison |
| I | Invert the mask |
Single-key shortcuts act in the main window only. No shortcut acts while you are typing in a field (there, Ctrl+Z undoes your typing) or while a dialog such as Export is open. The Ctrl shortcuts work the same with Caps Lock on.
Every color in the interface is a neutral gray, with the same red, green and blue values, so nothing next to the images tints how you see them. The only colors on screen come from the images themselves and the mask overlay. A chosen option is shown filled in light gray, and anything that needs your attention, such as a missing source file, is shown in brighter text with a ⚠ sign.
Everything is drawn to match the display scaling set in Windows or Linux, not only the text: buttons, spacing, sliders, badges and the wipe divider. At 150% they are all half as large again as at 100%. On Windows the app tells the system it handles scaling itself, so text is drawn sharply at the screen’s own resolution instead of being stretched. On macOS, sizes follow the system’s points.
When the window is narrow, the file buttons use shorter labels; the View switch and the mask summary stay on the first row of view controls while the Mask, Overlay and Mask tools controls move to a second, and Undo and Redo stay at the right end of the first; and the key hint under the slider is shortened, its full text in a tooltip. At the smallest window size (700 × 600 at 100% scaling) nothing is cut off.
Window size and position, where Mask tools and the other popup windows were last closed, recent projects, and preferences such as portable export and file watching are stored in image-blender-with-mask.ini in the working folder (see Command line), and saved when the app closes.
If a saved window position is no longer on a monitor (on Windows, each monitor is checked), the window opens at its saved size in a default position instead; a popup window opens centered over the main window.
Earlier versions kept this file next to the script. The first time the app starts without a settings file in its working folder, it copies the window, preferences and recent projects from the old file; the old file is left as it was, and can be deleted.--settings-file-path is gone: use --working-folder-path instead.
If the settings can’t be read (for example, after editing the file by hand), the app starts with the defaults; a value it doesn’t understand falls back to its default. If the working folder can’t be created, the app runs without saving settings and says so on the console. If the settings file can’t be written, the app says so in a message box and carries on.
Drag and drop doesn’t work. Install tkinterdnd2 with pip install tkinterdnd2 and restart the app. Without it, the drop hint is not shown and files must be opened from the menus or buttons.
“Tkinter is not available”. Your Python was built without Tk; the app says so and exits. On Debian or Ubuntu run sudo apt install python3-tk; on other systems, use the python.org installer or your package manager’s Tk package.--help and --selftest TRUE work without it.
My settings are gone after updating. Settings now live in the working folder (see Settings file). They are copied from the old file next to the script only when the working folder has no settings yet; if you started the new version with --working-folder-path pointing somewhere else first, copy the values across by hand.
The taskbar shows the app as Python, or a pinned shortcut opens a second button. On Windows the app now has its own taskbar ID, so a shortcut pinned by an earlier version belongs to Python. Unpin it, start the app, and pin its own button again.
A linked project shows missing files. The original source files were moved or deleted. Restore them, or re-save the project with portable export turned on while the files are still available.
The window opens smaller than you left it, once, after updating. On Windows with display scaling, versions before the app became DPI-aware saved the window size in scaled-down units. Resize or maximize the window once and the new size is remembered.
The mask looks inverted or too faint. Check the source channel and the invert setting in Mask tools, and try the black and white points. Holding M shows exactly the mask that is being applied.
The tests use pytest and drive the real window on images they make themselves; they don’t touch your files or settings.
pip install pytest
python -m pytest # from the app's folder
python image-blender-with-mask.py --selftest TRUE # the quick checks, without a window
They need a display: without one (on Linux), they start Xvfb and openbox themselves if installed. To test at 150% display scaling, start Xvfb :98 -screen 0 3000x1800x24 -dpi 144 and run DISPLAY=:98 python -m pytest.
They cover blending (the slider, the mask, a 16-bit mask kept at full precision), a file that fails to open, export (quality, chroma subsampling, the color profile and camera data), projects (a round trip, a damaged project file left alone, and the Save and Load project tooltips naming the files), settings (saved and restored, a hand-edited file, the old settings file taken over, a failed save reported), popup windows reopening where they were closed, the command line, the neutral-gray styles, controls fitting at the smallest window, the switches with real clicks, Ctrl shortcuts while typing and with Caps Lock on, the Undo and Redo buttons, tooltips (one for a whole file cell, above a control at the bottom of the window, none when blank), closing without leftover jobs, and the icon. The 24 tests took about 9 to 10 seconds, and all passed at 100% and at 150% display scaling, with Python 3.12, Pillow 12.3, NumPy 2.5 and pytest 9.1 under Linux.


Leave a Reply