magicdispel 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- magicdispel-0.1.0/CHANGELOG.md +111 -0
- magicdispel-0.1.0/LICENSE +21 -0
- magicdispel-0.1.0/MANIFEST.in +5 -0
- magicdispel-0.1.0/PKG-INFO +176 -0
- magicdispel-0.1.0/README.md +149 -0
- magicdispel-0.1.0/docs/PRIVACY.md +110 -0
- magicdispel-0.1.0/docs/RELEASE_NOTES.md +32 -0
- magicdispel-0.1.0/install.ps1 +32 -0
- magicdispel-0.1.0/install.sh +38 -0
- magicdispel-0.1.0/pyproject.toml +40 -0
- magicdispel-0.1.0/scripts/make_probes.py +135 -0
- magicdispel-0.1.0/scripts/regression.py +581 -0
- magicdispel-0.1.0/setup.cfg +4 -0
- magicdispel-0.1.0/src/magicdispel/__init__.py +3 -0
- magicdispel-0.1.0/src/magicdispel/__main__.py +3 -0
- magicdispel-0.1.0/src/magicdispel/banner.py +81 -0
- magicdispel-0.1.0/src/magicdispel/cli.py +87 -0
- magicdispel-0.1.0/src/magicdispel/core.py +89 -0
- magicdispel-0.1.0/src/magicdispel/errors.py +25 -0
- magicdispel-0.1.0/src/magicdispel/exif.py +188 -0
- magicdispel-0.1.0/src/magicdispel/exiftool.py +147 -0
- magicdispel-0.1.0/src/magicdispel/formats/__init__.py +36 -0
- magicdispel-0.1.0/src/magicdispel/formats/bmff.py +314 -0
- magicdispel-0.1.0/src/magicdispel/formats/bmp.py +42 -0
- magicdispel-0.1.0/src/magicdispel/formats/gif.py +125 -0
- magicdispel-0.1.0/src/magicdispel/formats/heif.py +624 -0
- magicdispel-0.1.0/src/magicdispel/formats/jpeg.py +350 -0
- magicdispel-0.1.0/src/magicdispel/formats/png.py +218 -0
- magicdispel-0.1.0/src/magicdispel/formats/tiff.py +281 -0
- magicdispel-0.1.0/src/magicdispel/formats/webp.py +126 -0
- magicdispel-0.1.0/src/magicdispel/icc.py +168 -0
- magicdispel-0.1.0/src/magicdispel/messages.py +69 -0
- magicdispel-0.1.0/src/magicdispel/names.py +59 -0
- magicdispel-0.1.0/src/magicdispel/pixels.py +75 -0
- magicdispel-0.1.0/src/magicdispel/xmp.py +101 -0
- magicdispel-0.1.0/src/magicdispel.egg-info/PKG-INFO +176 -0
- magicdispel-0.1.0/src/magicdispel.egg-info/SOURCES.txt +53 -0
- magicdispel-0.1.0/src/magicdispel.egg-info/dependency_links.txt +1 -0
- magicdispel-0.1.0/src/magicdispel.egg-info/entry_points.txt +2 -0
- magicdispel-0.1.0/src/magicdispel.egg-info/requires.txt +4 -0
- magicdispel-0.1.0/src/magicdispel.egg-info/top_level.txt +1 -0
- magicdispel-0.1.0/tests/test_banner.py +54 -0
- magicdispel-0.1.0/tests/test_cli.py +130 -0
- magicdispel-0.1.0/tests/test_core.py +45 -0
- magicdispel-0.1.0/tests/test_exif.py +49 -0
- magicdispel-0.1.0/tests/test_exiftool.py +23 -0
- magicdispel-0.1.0/tests/test_formats.py +193 -0
- magicdispel-0.1.0/tests/test_heif.py +265 -0
- magicdispel-0.1.0/tests/test_icc.py +248 -0
- magicdispel-0.1.0/tests/test_jpeg.py +286 -0
- magicdispel-0.1.0/tests/test_names.py +65 -0
- magicdispel-0.1.0/tests/test_pixels.py +59 -0
- magicdispel-0.1.0/tests/test_png.py +228 -0
- magicdispel-0.1.0/tests/test_tiff.py +163 -0
- magicdispel-0.1.0/tests/test_webp_gif.py +183 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-09-24)
|
|
4
|
+
|
|
5
|
+
- Command-line package for macOS, Windows and Linux.
|
|
6
|
+
- Local metadata cleaning for JPEG, PNG/APNG, HEIC/HEIF, AVIF, WebP, GIF and TIFF.
|
|
7
|
+
- Lossless BMP-to-PNG conversion and frame/page verification for GIF/APNG/TIFF.
|
|
8
|
+
- Correct handling of lossless and extended WebP format names.
|
|
9
|
+
- MPF/HDR JPEG support with per-image metadata cleaning, HDR preservation and rebuilt indexes.
|
|
10
|
+
- Recognize HEIF and AVIF sequence file types; verify animated AVIF frames and clear container dates.
|
|
11
|
+
- Handle JPEG-compressed TIFF strip aliases and verify all encoded TIFF strips/tiles directly.
|
|
12
|
+
- Image-data hash checks, original preservation and collision-safe output names.
|
|
13
|
+
- Preserve necessary HDR fields while removing recognized HEIF depth/calibration,
|
|
14
|
+
portrait/semantic masks, thumbnails and editing-only style maps with their actual bytes.
|
|
15
|
+
- Protect shared image dependencies and reject unsupported auxiliary layouts.
|
|
16
|
+
- Remove HEIF XMP toolkit strings and unused item properties.
|
|
17
|
+
- Add `--anonymous` random output filenames with collision protection.
|
|
18
|
+
- Synthetic auxiliary-graph and filename tests, run by CI on macOS, Windows and Linux.
|
|
19
|
+
The tests need only Pillow; with ExifTool installed they also check each result with it,
|
|
20
|
+
and CI runs them both ways.
|
|
21
|
+
- Explicit dependency checks and installation documentation.
|
|
22
|
+
- Clean macOS screenshots, whose display profiles carry Apple parametric curves
|
|
23
|
+
(`aarg`/`aagg`/`aabg`, kept) and display identity/setup tags (`dscm`, `mmod`,
|
|
24
|
+
`ndin`, `vcgt`, `vcgp`, removed). Previously these files were refused.
|
|
25
|
+
- One-command installers for macOS/Linux (`install.sh`) and Windows (`install.ps1`). They
|
|
26
|
+
install uv when needed, then MagicDispel, and end by showing the logo. CI runs them on
|
|
27
|
+
all three systems and cleans a screenshot with the installed command; tagging a version
|
|
28
|
+
publishes to PyPI through trusted publishing.
|
|
29
|
+
- Rewritten help screen, in English. Running `magicdispel` on its own shows the
|
|
30
|
+
MagicDispel logo in color with the credits, the tagline and how to use it; outside a
|
|
31
|
+
terminal, or with `NO_COLOR` set, it is plain text.
|
|
32
|
+
- Regression harness (`scripts/regression.py`) for local sample corpora, with macOS
|
|
33
|
+
ImageIO/ColorSync render checks and synthetic leak probes (`scripts/make_probes.py`).
|
|
34
|
+
- PNG, APNG and BMP are rebuilt by MagicDispel itself from an allowlist of the chunks
|
|
35
|
+
needed for display, instead of asking ExifTool to delete known metadata. Private and
|
|
36
|
+
unknown chunks, C2PA manifests, text, time stamps and data after the image end are
|
|
37
|
+
now removed; DPI (`pHYs`) and color chunks (`sRGB`, `gAMA`, `cHRM`, `cICP`) are kept,
|
|
38
|
+
so Retina screenshots keep their size. Compressed image data must hold exactly the
|
|
39
|
+
image, with no extra bytes. BMP-to-PNG conversion keeps the DPI. These formats no
|
|
40
|
+
longer need ExifTool, which still double-checks results when installed.
|
|
41
|
+
- JPEG is rebuilt the same way. Decoding segments and HDR data (ISO 21496-1 gain-map
|
|
42
|
+
metadata, Apple gain curves) are copied unchanged; JFIF, EXIF, XMP, the ICC profile
|
|
43
|
+
and the MPF index are written afresh with only display fields: orientation, DPI,
|
|
44
|
+
color space, Apple HDR headroom/gain and recognized gain-map XMP. Comments,
|
|
45
|
+
IPTC/Photoshop, C2PA, private segments, JFIF and EXIF thumbnails, MPF image IDs and
|
|
46
|
+
trailing data are removed. JPEG DPI is now kept. iPhone HDR JPEGs render identically
|
|
47
|
+
in macOS (SDR, HDR and gain maps).
|
|
48
|
+
- WebP and GIF are rebuilt the same way. WebP keeps its image, alpha and animation
|
|
49
|
+
chunks, a sanitized ICC profile and the orientation; XMP, unknown chunks (including
|
|
50
|
+
inside animation frames) and trailing data are removed, and files that no longer
|
|
51
|
+
need the extended header are written in the simple format. GIF keeps images, color
|
|
52
|
+
tables, frame timing, transparency, the loop count and a sanitized ICC profile;
|
|
53
|
+
comments, plain-text overlays, XMP and other application extensions are removed.
|
|
54
|
+
Reserved bits in WebP and GIF headers are cleared.
|
|
55
|
+
- HEIC/HEIF and AVIF are cleaned without ExifTool. EXIF, URI, JUMBF and non-XMP MIME
|
|
56
|
+
items are removed with their bytes (orientation lives in HEIF's irot/imir); XMP items
|
|
57
|
+
keep only recognized HDR fields; unknown item types are refused. Top-level boxes
|
|
58
|
+
other than ftyp, meta, moov and mdat (such as uuid XMP) are emptied in place or
|
|
59
|
+
dropped from the end; bytes in mdat and idat that no item or sample uses are zeroed.
|
|
60
|
+
In image sequences, creation/modification times, handler and compressor names, user
|
|
61
|
+
data and metadata boxes are cleared, and external media references are refused.
|
|
62
|
+
- TIFF is written afresh: each page keeps its image data and the tags needed to decode
|
|
63
|
+
and show it, with a sanitized ICC profile; EXIF and GPS directories, XMP, IPTC,
|
|
64
|
+
Photoshop blocks, descriptions, private tags, sub-images and free space are removed.
|
|
65
|
+
Every byte of the result is accounted for. BigTIFF and old-style JPEG are refused.
|
|
66
|
+
- **ExifTool is no longer required.** Every format is rebuilt by MagicDispel itself;
|
|
67
|
+
when ExifTool 12.73+ is installed it double-checks each result, and `--check` reports
|
|
68
|
+
whether that second check is on. The ExifTool-based cleaning pipeline is removed.
|
|
69
|
+
- HEIF items are read by one parser and removed in one pass. Damaged HEIF files are now
|
|
70
|
+
reported as damaged; boxes in the item container other than the item tables (such as
|
|
71
|
+
XML boxes) are emptied; image sequences without an item container are accepted; and
|
|
72
|
+
the check before saving also confirms that no editing image, thumbnail or item name
|
|
73
|
+
remains.
|
|
74
|
+
- Photos up to 268 megapixels are checked, including 200-megapixel phone photos; larger
|
|
75
|
+
ones are refused with a message. Previously anything over 179 megapixels stopped the
|
|
76
|
+
whole batch with a Python error. The pixel comparison now uses the decoded samples at
|
|
77
|
+
full precision (16-bit included) and hashes them in strips, never copying a whole frame:
|
|
78
|
+
a 200-megapixel photo is checked in under a second with about 0.9 GB of memory.
|
|
79
|
+
- Multi-picture JPEGs (such as iPhone HDR photos) that ExifTool has added metadata to are
|
|
80
|
+
accepted. ExifTool leaves the first image's size in the index as it was; that size is no
|
|
81
|
+
longer relied on, while MagicDispel's own index is still checked exactly.
|
|
82
|
+
- HEIC files that ExifTool's `-all=` has already processed are accepted. ExifTool leaves
|
|
83
|
+
their EXIF and XMP items in place with no data; such empty metadata items are removed
|
|
84
|
+
like any other. Previously these files were refused as damaged.
|
|
85
|
+
- Output names leave out the dates, times and timestamps that screenshots, phone cameras
|
|
86
|
+
and chat apps put in file names, which told when a picture was taken: `Screenshot
|
|
87
|
+
2026-09-23 at 15.14.15.png` becomes `Screenshot_clean.png`, `IMG_20240501_123456.jpg`
|
|
88
|
+
becomes `IMG_clean.jpg`, `mmexport1714567890123.jpg` becomes `mmexport_clean.jpg`. The
|
|
89
|
+
rest of the name stays. `--keep-name` keeps the name as it is.
|
|
90
|
+
- Hardening after an audit with crafted files and 4,000 mutated samples:
|
|
91
|
+
- HEIF item properties are now allowlisted. Only those needed to decode and show an image
|
|
92
|
+
stay, and fixed-size ones must have exactly their size. Descriptions (`udes`), creation
|
|
93
|
+
and modification times (`crtt`, `mdft`), camera parameters and unknown properties were
|
|
94
|
+
kept before and are now removed. An unknown property marked essential is refused.
|
|
95
|
+
- RAW photos built on TIFF (DNG, CR2, NEF and others) are refused with a clear message.
|
|
96
|
+
Before, a DNG was "cleaned" into a copy of its small preview.
|
|
97
|
+
- TIFF pages must hold exactly the strips or tiles their image needs, uncompressed ones at
|
|
98
|
+
exactly their size, and kept tags exactly the number of values the specification gives
|
|
99
|
+
them. Extra strips and values could carry hidden bytes.
|
|
100
|
+
- PNG palettes and transparency chunks must hold exactly what the color type allows
|
|
101
|
+
(an oversized tRNS chunk could carry hidden bytes). Images over the pixel limit are
|
|
102
|
+
refused before any data is inflated, and APNG frames must lie within the image.
|
|
103
|
+
- Any failure of Pillow's decoders now reads "cannot be decoded to check the result"
|
|
104
|
+
instead of an unexpected error (damaged AVIF files raised RuntimeError), and Pillow's
|
|
105
|
+
warnings about damaged files no longer appear in the terminal.
|
|
106
|
+
- ExifTool's second check reads each result from a pipe instead of a temporary file next to
|
|
107
|
+
the photo. On Windows, a photo in a folder with Chinese or other non-ASCII characters
|
|
108
|
+
failed the check, because Windows passes paths to ExifTool in its legacy code page.
|
|
109
|
+
- A photo that fails unexpectedly is reported as an unexpected error and no longer stops
|
|
110
|
+
the rest of a batch. A result its own format cannot read back counts as failing
|
|
111
|
+
verification.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MagicDispel contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: magicdispel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Clean common photo metadata locally while preserving image quality
|
|
5
|
+
Author: VincentC
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/v1nc3nt-continualab/magicdispel
|
|
8
|
+
Project-URL: Issues, https://github.com/v1nc3nt-continualab/magicdispel/issues
|
|
9
|
+
Project-URL: Source, https://github.com/v1nc3nt-continualab/magicdispel
|
|
10
|
+
Keywords: exif,metadata,photos,privacy,cli
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Operating System :: MacOS
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: Pillow>=11.3
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: Pillow>=11.3; extra == "test"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# MagicDispel
|
|
29
|
+
|
|
30
|
+
Remove private metadata from photos on your own computer, without touching image quality.
|
|
31
|
+
|
|
32
|
+
```console
|
|
33
|
+
magicdispel photo.jpg
|
|
34
|
+
magicdispel "photo one.heic" screenshot.png
|
|
35
|
+
magicdispel --anonymous photo.jpg
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Type `magicdispel` and a space, drag one or more photos into the terminal, and press Enter.
|
|
39
|
+
A cleaned copy named `photo_clean.jpg` appears next to each original, without the dates and
|
|
40
|
+
times that screenshots and phone cameras put in names: `Screenshot 2026-09-23 at 15.14.15.png`
|
|
41
|
+
becomes `Screenshot_clean.png`. Originals are never modified and existing files are never
|
|
42
|
+
overwritten: further copies get `_clean_1`, `_clean_2`... `--keep-name` keeps the name as it is;
|
|
43
|
+
`--anonymous` names the copy `photo_<random>.jpg` instead. It changes the name only, not what
|
|
44
|
+
the picture shows.
|
|
45
|
+
|
|
46
|
+
## How it works
|
|
47
|
+
|
|
48
|
+
MagicDispel does not hunt for known metadata to delete. It writes a new file from only the
|
|
49
|
+
parts a viewer needs to show the image, such as the compressed pixels, color profile,
|
|
50
|
+
orientation, DPI, transparency, animation timing and HDR gain maps, and leaves everything
|
|
51
|
+
else behind: location, capture time, camera and lens details, author, comments, editing
|
|
52
|
+
software, thumbnails, depth maps and portrait mattes, C2PA manifests, and private or unknown
|
|
53
|
+
data blocks, wherever a format stores them.
|
|
54
|
+
|
|
55
|
+
- Image data is copied byte for byte. Only BMP is re-encoded, losslessly, as PNG.
|
|
56
|
+
- ICC color profiles keep their color data; their dates become a fixed placeholder
|
|
57
|
+
(`2000-01-01`), their descriptions `Clean`, and device and creator fields are cleared.
|
|
58
|
+
- Every result is checked before it is saved. The format's rebuilder parses it again on its
|
|
59
|
+
own; Pillow must decode identical pixels and frames (for HEIC, which Pillow cannot decode,
|
|
60
|
+
every image item is compared byte for byte instead); and ExifTool, if installed, gives an
|
|
61
|
+
independent second reading. If any check fails, nothing is saved.
|
|
62
|
+
- Everything happens on your computer: no uploads, telemetry or network access.
|
|
63
|
+
|
|
64
|
+
**This is not an anonymity tool.** What the picture shows, a match with a copy published
|
|
65
|
+
earlier, or the account it is shared from can still identify people and places. Removing
|
|
66
|
+
depth and style data also limits later portrait, depth-of-field and style edits.
|
|
67
|
+
See [privacy and format details](https://github.com/v1nc3nt-continualab/magicdispel/blob/main/docs/PRIVACY.md).
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
One command installs MagicDispel and everything it needs. It uses
|
|
72
|
+
[uv](https://docs.astral.sh/uv/), which brings its own Python when the system has none that fits,
|
|
73
|
+
and ends by showing the MagicDispel logo.
|
|
74
|
+
|
|
75
|
+
macOS and Linux:
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
curl -LsSf https://raw.githubusercontent.com/v1nc3nt-continualab/magicdispel/main/install.sh | sh
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Windows (PowerShell):
|
|
82
|
+
|
|
83
|
+
```powershell
|
|
84
|
+
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/v1nc3nt-continualab/magicdispel/main/install.ps1 | iex"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If `magicdispel` is not found afterwards, open a new terminal window.
|
|
88
|
+
|
|
89
|
+
Already using uv or pipx (Python 3.10 or newer):
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
uv tool install magicdispel
|
|
93
|
+
pipx install magicdispel
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
To update, run the install command again, or `uv tool upgrade magicdispel`. To remove
|
|
97
|
+
MagicDispel, run `uv tool uninstall magicdispel`.
|
|
98
|
+
|
|
99
|
+
[ExifTool](https://exiftool.org/) is optional: when version 12.73 or newer is installed,
|
|
100
|
+
MagicDispel uses it to double-check every result. Install it with `brew install exiftool`,
|
|
101
|
+
`sudo apt install libimage-exiftool-perl` or `winget install --exact --id OliverBetz.ExifTool`.
|
|
102
|
+
|
|
103
|
+
## Usage
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
magicdispel --check
|
|
107
|
+
magicdispel --version
|
|
108
|
+
magicdispel photo.jpg screenshot.png portrait.heic
|
|
109
|
+
magicdispel --anonymous photo.jpg
|
|
110
|
+
magicdispel --keep-name "Screenshot 2026-09-23 at 15.14.15.png"
|
|
111
|
+
magicdispel -- "-filename-starts-with-a-dash.jpg"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Quote paths containing spaces, or drag the files into the terminal. On Windows, drag-and-drop
|
|
115
|
+
depends on the terminal; a quoted path always works. Folders are not processed recursively.
|
|
116
|
+
Run `magicdispel` on its own to see the logo and how to use it. If ExifTool is installed
|
|
117
|
+
somewhere unusual, set `MAGICDISPEL_EXIFTOOL` to its full path.
|
|
118
|
+
|
|
119
|
+
Exit codes: `0` success or help, `1` one or more files not cleaned, `2` invalid arguments,
|
|
120
|
+
`130` interrupted. A failed file does not stop the rest of a batch.
|
|
121
|
+
|
|
122
|
+
## Formats
|
|
123
|
+
|
|
124
|
+
| Format | Kept | Removed |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| JPEG | image data, JFIF density, orientation, DPI, color space, ICC profile; HDR gain-map images (MPF), Apple HDR headroom and gain-map XMP | other EXIF and XMP, IPTC/Photoshop, comments, C2PA, thumbnails, maker notes, trailing data |
|
|
127
|
+
| PNG, APNG | image data, palette, transparency, color chunks (sRGB, gAMA, cHRM, cICP, HDR), DPI, animation, ICC profile, orientation | text, time stamps, C2PA, private chunks, anything after the end |
|
|
128
|
+
| HEIC, HEIF | image items and tiles, HDR gain maps (Apple and ISO), alpha, orientation, ICC profile, HDR XMP fields | EXIF, other XMP, Apple property lists, depth and calibration, portrait and semantic mattes, style maps, thumbnails, unused data |
|
|
129
|
+
| AVIF | as HEIF, including animations; sequence times, names and user data are cleared | as HEIF |
|
|
130
|
+
| WebP | image data, alpha, animation, ICC profile, orientation | other EXIF, XMP, unknown chunks |
|
|
131
|
+
| GIF | images, palettes, frame timing, transparency, loop count, ICC profile | comments, text overlays, XMP, other extensions |
|
|
132
|
+
| TIFF | image data, decoding tags, DPI, orientation, page numbers, ICC profile | EXIF and GPS directories, XMP, IPTC, Photoshop, descriptions, private tags, sub-images |
|
|
133
|
+
| BMP | converted to lossless PNG with the same pixels, DPI and profile | everything else |
|
|
134
|
+
| RAW, video, PDF | not supported; RAW files built on TIFF (DNG, CR2, NEF...) are recognized and refused | |
|
|
135
|
+
|
|
136
|
+
Variants that cannot be rebuilt safely, such as BigTIFF, fragmented image sequences or
|
|
137
|
+
unknown HEIF item types, are refused rather than passed through. Images up to 268 megapixels
|
|
138
|
+
are checked (enough for 200-megapixel phone photos); larger ones are refused.
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
python -m venv .venv
|
|
144
|
+
# Activate the environment for your shell.
|
|
145
|
+
python -m pip install -e ".[test]" build
|
|
146
|
+
python -m unittest discover -s tests -v
|
|
147
|
+
python -m build
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The tests need only Pillow. When ExifTool is installed, they also add metadata the way other
|
|
151
|
+
programs write it and let ExifTool double-check each result; CI runs them both ways. No
|
|
152
|
+
personal photos are included.
|
|
153
|
+
|
|
154
|
+
Before and after any change to the cleaning code, run the regression harness over a folder
|
|
155
|
+
of real sample photos kept outside the repository:
|
|
156
|
+
|
|
157
|
+
```sh
|
|
158
|
+
python scripts/make_probes.py ~/magicdispel-corpus # adds synthetic leak probes
|
|
159
|
+
python scripts/regression.py ~/magicdispel-corpus # saves a run under runs/
|
|
160
|
+
python scripts/regression.py ~/magicdispel-corpus --baseline ~/magicdispel-corpus/runs/<run>.json
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
It checks that every output looks identical to its input (Pillow, and macOS ImageIO/ColorSync
|
|
164
|
+
when available), that no probe marker survives, and, against a baseline, that no sample
|
|
165
|
+
changes outcome or gains metadata. Pure refactors should also pass `--identical`, and
|
|
166
|
+
`--without-exiftool` checks the path users without ExifTool take.
|
|
167
|
+
|
|
168
|
+
`.github/workflows/test.yml` runs the tests on macOS, Windows and Linux with Python 3.10 and
|
|
169
|
+
3.13, with and without ExifTool, and the install scripts on all three. `release.yml` publishes a
|
|
170
|
+
tagged version to PyPI once those pass.
|
|
171
|
+
|
|
172
|
+
## License and attribution
|
|
173
|
+
|
|
174
|
+
MagicDispel is MIT licensed. [ExifTool](https://exiftool.org/), an optional companion, is
|
|
175
|
+
developed by Phil Harvey and distributed separately under its own license. MagicDispel is not
|
|
176
|
+
affiliated with ExifTool.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# MagicDispel
|
|
2
|
+
|
|
3
|
+
Remove private metadata from photos on your own computer, without touching image quality.
|
|
4
|
+
|
|
5
|
+
```console
|
|
6
|
+
magicdispel photo.jpg
|
|
7
|
+
magicdispel "photo one.heic" screenshot.png
|
|
8
|
+
magicdispel --anonymous photo.jpg
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Type `magicdispel` and a space, drag one or more photos into the terminal, and press Enter.
|
|
12
|
+
A cleaned copy named `photo_clean.jpg` appears next to each original, without the dates and
|
|
13
|
+
times that screenshots and phone cameras put in names: `Screenshot 2026-09-23 at 15.14.15.png`
|
|
14
|
+
becomes `Screenshot_clean.png`. Originals are never modified and existing files are never
|
|
15
|
+
overwritten: further copies get `_clean_1`, `_clean_2`... `--keep-name` keeps the name as it is;
|
|
16
|
+
`--anonymous` names the copy `photo_<random>.jpg` instead. It changes the name only, not what
|
|
17
|
+
the picture shows.
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
MagicDispel does not hunt for known metadata to delete. It writes a new file from only the
|
|
22
|
+
parts a viewer needs to show the image, such as the compressed pixels, color profile,
|
|
23
|
+
orientation, DPI, transparency, animation timing and HDR gain maps, and leaves everything
|
|
24
|
+
else behind: location, capture time, camera and lens details, author, comments, editing
|
|
25
|
+
software, thumbnails, depth maps and portrait mattes, C2PA manifests, and private or unknown
|
|
26
|
+
data blocks, wherever a format stores them.
|
|
27
|
+
|
|
28
|
+
- Image data is copied byte for byte. Only BMP is re-encoded, losslessly, as PNG.
|
|
29
|
+
- ICC color profiles keep their color data; their dates become a fixed placeholder
|
|
30
|
+
(`2000-01-01`), their descriptions `Clean`, and device and creator fields are cleared.
|
|
31
|
+
- Every result is checked before it is saved. The format's rebuilder parses it again on its
|
|
32
|
+
own; Pillow must decode identical pixels and frames (for HEIC, which Pillow cannot decode,
|
|
33
|
+
every image item is compared byte for byte instead); and ExifTool, if installed, gives an
|
|
34
|
+
independent second reading. If any check fails, nothing is saved.
|
|
35
|
+
- Everything happens on your computer: no uploads, telemetry or network access.
|
|
36
|
+
|
|
37
|
+
**This is not an anonymity tool.** What the picture shows, a match with a copy published
|
|
38
|
+
earlier, or the account it is shared from can still identify people and places. Removing
|
|
39
|
+
depth and style data also limits later portrait, depth-of-field and style edits.
|
|
40
|
+
See [privacy and format details](https://github.com/v1nc3nt-continualab/magicdispel/blob/main/docs/PRIVACY.md).
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
One command installs MagicDispel and everything it needs. It uses
|
|
45
|
+
[uv](https://docs.astral.sh/uv/), which brings its own Python when the system has none that fits,
|
|
46
|
+
and ends by showing the MagicDispel logo.
|
|
47
|
+
|
|
48
|
+
macOS and Linux:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
curl -LsSf https://raw.githubusercontent.com/v1nc3nt-continualab/magicdispel/main/install.sh | sh
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Windows (PowerShell):
|
|
55
|
+
|
|
56
|
+
```powershell
|
|
57
|
+
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/v1nc3nt-continualab/magicdispel/main/install.ps1 | iex"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
If `magicdispel` is not found afterwards, open a new terminal window.
|
|
61
|
+
|
|
62
|
+
Already using uv or pipx (Python 3.10 or newer):
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
uv tool install magicdispel
|
|
66
|
+
pipx install magicdispel
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To update, run the install command again, or `uv tool upgrade magicdispel`. To remove
|
|
70
|
+
MagicDispel, run `uv tool uninstall magicdispel`.
|
|
71
|
+
|
|
72
|
+
[ExifTool](https://exiftool.org/) is optional: when version 12.73 or newer is installed,
|
|
73
|
+
MagicDispel uses it to double-check every result. Install it with `brew install exiftool`,
|
|
74
|
+
`sudo apt install libimage-exiftool-perl` or `winget install --exact --id OliverBetz.ExifTool`.
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
magicdispel --check
|
|
80
|
+
magicdispel --version
|
|
81
|
+
magicdispel photo.jpg screenshot.png portrait.heic
|
|
82
|
+
magicdispel --anonymous photo.jpg
|
|
83
|
+
magicdispel --keep-name "Screenshot 2026-09-23 at 15.14.15.png"
|
|
84
|
+
magicdispel -- "-filename-starts-with-a-dash.jpg"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Quote paths containing spaces, or drag the files into the terminal. On Windows, drag-and-drop
|
|
88
|
+
depends on the terminal; a quoted path always works. Folders are not processed recursively.
|
|
89
|
+
Run `magicdispel` on its own to see the logo and how to use it. If ExifTool is installed
|
|
90
|
+
somewhere unusual, set `MAGICDISPEL_EXIFTOOL` to its full path.
|
|
91
|
+
|
|
92
|
+
Exit codes: `0` success or help, `1` one or more files not cleaned, `2` invalid arguments,
|
|
93
|
+
`130` interrupted. A failed file does not stop the rest of a batch.
|
|
94
|
+
|
|
95
|
+
## Formats
|
|
96
|
+
|
|
97
|
+
| Format | Kept | Removed |
|
|
98
|
+
| --- | --- | --- |
|
|
99
|
+
| JPEG | image data, JFIF density, orientation, DPI, color space, ICC profile; HDR gain-map images (MPF), Apple HDR headroom and gain-map XMP | other EXIF and XMP, IPTC/Photoshop, comments, C2PA, thumbnails, maker notes, trailing data |
|
|
100
|
+
| PNG, APNG | image data, palette, transparency, color chunks (sRGB, gAMA, cHRM, cICP, HDR), DPI, animation, ICC profile, orientation | text, time stamps, C2PA, private chunks, anything after the end |
|
|
101
|
+
| HEIC, HEIF | image items and tiles, HDR gain maps (Apple and ISO), alpha, orientation, ICC profile, HDR XMP fields | EXIF, other XMP, Apple property lists, depth and calibration, portrait and semantic mattes, style maps, thumbnails, unused data |
|
|
102
|
+
| AVIF | as HEIF, including animations; sequence times, names and user data are cleared | as HEIF |
|
|
103
|
+
| WebP | image data, alpha, animation, ICC profile, orientation | other EXIF, XMP, unknown chunks |
|
|
104
|
+
| GIF | images, palettes, frame timing, transparency, loop count, ICC profile | comments, text overlays, XMP, other extensions |
|
|
105
|
+
| TIFF | image data, decoding tags, DPI, orientation, page numbers, ICC profile | EXIF and GPS directories, XMP, IPTC, Photoshop, descriptions, private tags, sub-images |
|
|
106
|
+
| BMP | converted to lossless PNG with the same pixels, DPI and profile | everything else |
|
|
107
|
+
| RAW, video, PDF | not supported; RAW files built on TIFF (DNG, CR2, NEF...) are recognized and refused | |
|
|
108
|
+
|
|
109
|
+
Variants that cannot be rebuilt safely, such as BigTIFF, fragmented image sequences or
|
|
110
|
+
unknown HEIF item types, are refused rather than passed through. Images up to 268 megapixels
|
|
111
|
+
are checked (enough for 200-megapixel phone photos); larger ones are refused.
|
|
112
|
+
|
|
113
|
+
## Development
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
python -m venv .venv
|
|
117
|
+
# Activate the environment for your shell.
|
|
118
|
+
python -m pip install -e ".[test]" build
|
|
119
|
+
python -m unittest discover -s tests -v
|
|
120
|
+
python -m build
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The tests need only Pillow. When ExifTool is installed, they also add metadata the way other
|
|
124
|
+
programs write it and let ExifTool double-check each result; CI runs them both ways. No
|
|
125
|
+
personal photos are included.
|
|
126
|
+
|
|
127
|
+
Before and after any change to the cleaning code, run the regression harness over a folder
|
|
128
|
+
of real sample photos kept outside the repository:
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
python scripts/make_probes.py ~/magicdispel-corpus # adds synthetic leak probes
|
|
132
|
+
python scripts/regression.py ~/magicdispel-corpus # saves a run under runs/
|
|
133
|
+
python scripts/regression.py ~/magicdispel-corpus --baseline ~/magicdispel-corpus/runs/<run>.json
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
It checks that every output looks identical to its input (Pillow, and macOS ImageIO/ColorSync
|
|
137
|
+
when available), that no probe marker survives, and, against a baseline, that no sample
|
|
138
|
+
changes outcome or gains metadata. Pure refactors should also pass `--identical`, and
|
|
139
|
+
`--without-exiftool` checks the path users without ExifTool take.
|
|
140
|
+
|
|
141
|
+
`.github/workflows/test.yml` runs the tests on macOS, Windows and Linux with Python 3.10 and
|
|
142
|
+
3.13, with and without ExifTool, and the install scripts on all three. `release.yml` publishes a
|
|
143
|
+
tagged version to PyPI once those pass.
|
|
144
|
+
|
|
145
|
+
## License and attribution
|
|
146
|
+
|
|
147
|
+
MagicDispel is MIT licensed. [ExifTool](https://exiftool.org/), an optional companion, is
|
|
148
|
+
developed by Phil Harvey and distributed separately under its own license. MagicDispel is not
|
|
149
|
+
affiliated with ExifTool.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Privacy and format details
|
|
2
|
+
|
|
3
|
+
MagicDispel rebuilds each file from an allowlist: it copies only the parts a viewer needs to
|
|
4
|
+
show the image and leaves everything else behind. It does not search for known metadata to
|
|
5
|
+
delete, so metadata it has never heard of is removed too. It does not promise forensic
|
|
6
|
+
anonymization.
|
|
7
|
+
|
|
8
|
+
## What is kept, and why
|
|
9
|
+
|
|
10
|
+
- **Image data.** Compressed pixels are copied byte for byte. BMP is the only format that is
|
|
11
|
+
re-encoded, losslessly, as PNG.
|
|
12
|
+
- **Color.** ICC profiles keep their color conversion data: matrices, curves, lookup tables,
|
|
13
|
+
Apple's parametric curves in screenshot profiles, and HDR adaptive curves (with their
|
|
14
|
+
image-specific identifier cleared). Every profile is rebuilt with the fixed date
|
|
15
|
+
`2000-01-01 00:00:00` and description `Clean`; creator, maker, model, CMM, platform and profile
|
|
16
|
+
ID are cleared, and display calibration data is removed. Color declarations of the formats
|
|
17
|
+
themselves are kept: PNG sRGB, gAMA, cHRM, cICP and HDR chunks, HEIF color boxes.
|
|
18
|
+
- **Display fields.** Orientation, DPI, color space and the DCF interoperability index, written
|
|
19
|
+
into a fresh EXIF block holding nothing else. For iPhone HDR photos, Apple's HDR headroom and
|
|
20
|
+
gain, in a fresh maker note holding nothing else.
|
|
21
|
+
- **HDR.** Gain-map images (JPEG multi-picture files, HEIF auxiliary images and ISO `tmap`
|
|
22
|
+
items), ISO 21496-1 gain-map metadata, Apple gain curves, and recognized numeric gain-map XMP
|
|
23
|
+
fields.
|
|
24
|
+
- **Structure.** Transparency, animation frames, timing and loop count, TIFF pages and page
|
|
25
|
+
numbers, and the format's own headers.
|
|
26
|
+
- **The file name**, with `_clean` added and without the dates, times and timestamps that
|
|
27
|
+
screenshots, cameras and chat apps put in names: `Screenshot 2026-09-23 at 15.14.15.png`
|
|
28
|
+
becomes `Screenshot_clean.png`, `IMG_20240501_123456.jpg` becomes `IMG_clean.jpg`.
|
|
29
|
+
`--keep-name` keeps the name as it is; `--anonymous` replaces it with a random 128-bit token
|
|
30
|
+
that contains no name, time, MAC address or user ID.
|
|
31
|
+
|
|
32
|
+
## What is removed
|
|
33
|
+
|
|
34
|
+
Everything not listed above, including: EXIF capture time, camera, lens, serial numbers,
|
|
35
|
+
location and maker notes; XMP (except gain-map fields); IPTC and Photoshop blocks; comments and
|
|
36
|
+
text chunks; C2PA manifests; embedded thumbnails and previews, which may show an uncropped
|
|
37
|
+
original; HEIF depth maps, lens calibration, portrait and semantic mattes, style maps, Apple
|
|
38
|
+
property lists, item names, and item properties such as descriptions, creation times and camera
|
|
39
|
+
parameters; JPEG MPF image IDs; image-sequence creation times, handler and encoder names
|
|
40
|
+
and user data; TIFF EXIF and GPS directories, descriptions, private tags and sub-images; and
|
|
41
|
+
unknown or private data blocks and data after the end of an image.
|
|
42
|
+
|
|
43
|
+
Removing auxiliary HEIF images limits later portrait, depth-of-field and photographic-style
|
|
44
|
+
edits. Tested HEIC and HDR JPEG files render identically on macOS in SDR and HDR.
|
|
45
|
+
|
|
46
|
+
## How nothing slips through
|
|
47
|
+
|
|
48
|
+
- **Rebuild, not delete.** Each format has its own rebuilder (`src/magicdispel/formats/`). Parts
|
|
49
|
+
with a fixed size must have exactly that size, so they cannot carry extra bytes. PNG image data
|
|
50
|
+
must inflate to exactly the scanlines the header describes, with nothing after the compressed
|
|
51
|
+
stream, and its palette and transparency hold exactly what the color type allows. A TIFF page
|
|
52
|
+
holds exactly the strips or tiles its image needs, uncompressed ones at exactly their size, and
|
|
53
|
+
every kept tag has exactly the number of values the specification gives it. HEIF item
|
|
54
|
+
properties are kept only if they say how to decode and show an image. JPEG multi-picture
|
|
55
|
+
indexes are written fresh.
|
|
56
|
+
- **HEIF in place.** HEIF files are cleaned without moving any image data, so every offset
|
|
57
|
+
stays valid: the item tables are rewritten in the space they had, removed items and boxes are
|
|
58
|
+
zero-filled, bytes that no remaining item or sample uses are zeroed, and boxes at the end of
|
|
59
|
+
the file are dropped. This is why HEIC files do not shrink much.
|
|
60
|
+
- **Fail closed.** Anything that cannot be handled safely is refused, not passed through: an
|
|
61
|
+
unknown critical PNG chunk, an unknown HEIF item type or auxiliary image, a HEIF property of
|
|
62
|
+
unknown meaning that readers may not ignore, an image that depends on a removed layer, image
|
|
63
|
+
groups other than alternatives (such as the stereo pairs of spatial photos), fragmented image
|
|
64
|
+
sequences, media stored outside the file, BigTIFF, old-style JPEG in TIFF, metadata inside
|
|
65
|
+
JPEG-compressed TIFF strips, unrecognized ICC tags. RAW photos built on TIFF (DNG, CR2, NEF and
|
|
66
|
+
others) are refused too: their TIFF pages hold only a preview.
|
|
67
|
+
- **Checked before saving.** The rebuilder parses its own result independently and compares it
|
|
68
|
+
with what the original should yield: for HEIF, for instance, that every retained image item is
|
|
69
|
+
byte-identical, XMP holds only gain-map fields, no editing image, thumbnail or item name
|
|
70
|
+
remains, profiles are sanitized, and unused bytes are zero; for TIFF, that no byte of the file is unaccounted for. Pillow must then decode identical
|
|
71
|
+
pixels, frames, timing and transparency (all formats but HEIC). If ExifTool 12.73+ is
|
|
72
|
+
installed, it reads the result as a second opinion, and any warning, private field or data it
|
|
73
|
+
cannot identify stops the save. The original's hash is compared before and after, so a file
|
|
74
|
+
changed by another program during cleaning is not published.
|
|
75
|
+
|
|
76
|
+
## Out of scope
|
|
77
|
+
|
|
78
|
+
Data hidden inside the compressed image data itself, for example in JPEG scans, VP8 or HEVC
|
|
79
|
+
frames, GIF LZW data or unused palette entries, is copied along with the image. Detecting such
|
|
80
|
+
steganography is beyond this tool.
|
|
81
|
+
|
|
82
|
+
## File-system information
|
|
83
|
+
|
|
84
|
+
Outputs are new files containing only the verified bytes. Extended attributes, macOS resource
|
|
85
|
+
forks and Windows alternate data streams of the original are not copied; macOS output
|
|
86
|
+
attributes are cleared, as are Linux `user.` attributes where supported. Normal permissions and
|
|
87
|
+
creation/modification times of the new file are set by the system.
|
|
88
|
+
|
|
89
|
+
## What this cannot prevent
|
|
90
|
+
|
|
91
|
+
- Location or identity inferred from what the picture shows: faces, signs, documents,
|
|
92
|
+
scenery, reflections.
|
|
93
|
+
- Matching the image with a previously published or known original.
|
|
94
|
+
- Identification through the account or service used to share the result.
|
|
95
|
+
- Information a sharing application adds afterwards.
|
|
96
|
+
- Deliberately hidden information, steganography or sensor fingerprinting.
|
|
97
|
+
|
|
98
|
+
When anonymity matters, share a separate copy and look at what it shows.
|
|
99
|
+
|
|
100
|
+
## Validation status
|
|
101
|
+
|
|
102
|
+
On macOS, the unit tests and a local corpus of 60 real and synthetic samples pass: every output
|
|
103
|
+
renders identically in macOS ImageIO/ColorSync (pixels, sRGB and Display P3 renders, SDR, HDR,
|
|
104
|
+
gain maps, orientation and DPI), and 11 synthetic leak probes come out clean. On macOS,
|
|
105
|
+
Windows and Linux, CI runs the unit tests with Python 3.10 and 3.13, with and without ExifTool,
|
|
106
|
+
and installs MagicDispel with the install scripts. Windows and Linux are validated by those
|
|
107
|
+
synthetic tests, not by a corpus of real photos.
|
|
108
|
+
|
|
109
|
+
References: [ExifTool FAQ](https://exiftool.org/faq.html#Q32),
|
|
110
|
+
[Apple location metadata guidance](https://support.apple.com/guide/personal-safety/ips0d7a5df82/web).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
MagicDispel 0.1.0 removes private metadata from photos on your computer, without touching
|
|
2
|
+
image quality.
|
|
3
|
+
|
|
4
|
+
Install it with one command. On macOS and Linux:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
curl -LsSf https://raw.githubusercontent.com/v1nc3nt-continualab/magicdispel/main/install.sh | sh
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
On Windows, in PowerShell:
|
|
11
|
+
|
|
12
|
+
```powershell
|
|
13
|
+
powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/v1nc3nt-continualab/magicdispel/main/install.ps1 | iex"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Or, with uv or pipx: `uv tool install magicdispel` or `pipx install magicdispel`.
|
|
17
|
+
|
|
18
|
+
Then type `magicdispel` and a space, drag photos into the terminal, and press Enter. A cleaned
|
|
19
|
+
copy appears next to each original; originals are never modified and nothing is overwritten.
|
|
20
|
+
|
|
21
|
+
Supported formats: JPEG, PNG/APNG, HEIC/HEIF, AVIF, WebP, GIF, TIFF and BMP. Each file is
|
|
22
|
+
rebuilt from only what is needed to show it, so location, capture time, camera details,
|
|
23
|
+
author, comments, thumbnails, depth maps and portrait mattes, C2PA manifests and unknown data
|
|
24
|
+
blocks are left behind. Image data is copied unchanged (BMP becomes lossless PNG), and color,
|
|
25
|
+
orientation, DPI, transparency, animation and HDR gain maps are kept. The dates and times that
|
|
26
|
+
screenshots and cameras put in file names are left out of the new name. Every result is
|
|
27
|
+
checked before it is saved. ExifTool is optional; when installed, it double-checks each result.
|
|
28
|
+
|
|
29
|
+
This is not an anonymity tool: what a picture shows can still identify people and places. Read
|
|
30
|
+
the privacy details before sharing sensitive photos.
|
|
31
|
+
|
|
32
|
+
Tested on macOS, Windows and Linux with Python 3.10 and 3.13.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Install MagicDispel on Windows, from PowerShell:
|
|
2
|
+
# powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/v1nc3nt-continualab/magicdispel/main/install.ps1 | iex"
|
|
3
|
+
#
|
|
4
|
+
# It installs uv (https://docs.astral.sh/uv/) if needed, which then installs
|
|
5
|
+
# MagicDispel with a Python of its own when the system has none that fits.
|
|
6
|
+
# MAGICDISPEL_NO_MODIFY_PATH=1 leaves the user's PATH alone.
|
|
7
|
+
# MAGICDISPEL_PACKAGE installs something else, such as a local wheel to test.
|
|
8
|
+
$ErrorActionPreference = "Stop"
|
|
9
|
+
|
|
10
|
+
$package = if ($env:MAGICDISPEL_PACKAGE) { $env:MAGICDISPEL_PACKAGE } else { "magicdispel" }
|
|
11
|
+
$keepPath = $env:MAGICDISPEL_NO_MODIFY_PATH -eq "1"
|
|
12
|
+
|
|
13
|
+
$uv = (Get-Command uv -ErrorAction SilentlyContinue).Source
|
|
14
|
+
if (-not $uv) {
|
|
15
|
+
$uv = Join-Path $HOME ".local\bin\uv.exe"
|
|
16
|
+
}
|
|
17
|
+
if (-not (Test-Path $uv)) {
|
|
18
|
+
Write-Host "Installing uv, which installs MagicDispel and the Python it needs..."
|
|
19
|
+
if ($keepPath) { $env:UV_NO_MODIFY_PATH = "1" }
|
|
20
|
+
Invoke-RestMethod https://astral.sh/uv/install.ps1 | Invoke-Expression
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
Write-Host "Installing MagicDispel..."
|
|
24
|
+
& $uv tool install --upgrade $package
|
|
25
|
+
if ($LASTEXITCODE) { exit $LASTEXITCODE }
|
|
26
|
+
if (-not $keepPath) { & $uv tool update-shell *> $null }
|
|
27
|
+
|
|
28
|
+
$bin = & $uv tool dir --bin
|
|
29
|
+
& (Join-Path $bin "magicdispel.exe")
|
|
30
|
+
if (($env:PATH -split ";") -notcontains $bin) {
|
|
31
|
+
Write-Host " Open a new PowerShell window, then type magicdispel."
|
|
32
|
+
}
|