尚未检测到更新。GitHub 发布版本后会显示在这里。
Native macOS app that predicts keyword tags for photos with Core ML and saves them to the image metadata or XMP sidecars.
翻译准备中
取自仓库 README
正在加载完整 README。
查看完整 README ↗Image Tagger predicts an image's keywords using a deep learning model based on ResNet. It helps photographers automate the image tagging process. 📸
This is a native macOS port of the Java/Kotlin ImageTagger, written in SwiftUI, with the tagging model running on Core ML.
Website: dnutiu.github.io/ImageTaggerMac · Support · Privacy policy
imagetagger command tags the images in a folder from the Terminal and saves the tags to the files. See
Command line.Tags are added to the keywords stored in the file, the field Lightroom, Capture One, Photo Mechanic, Bridge, Spotlight and Windows show as keywords or tags. Existing keywords are kept, and tags that are already there aren't added again.
dc:subject. If the file also has legacy IPTC IIM keywords, they're updated too.IMG_1234.xmp for IMG_1234.CR2, which Lightroom, Bridge, Capture One
and Photo Mechanic read. An existing sidecar keeps its other metadata, including the develop settings of photo
editors, and a sidecar that can't be read is never replaced. Lightroom reads the metadata of DNG files from the DNG
itself, so it doesn't see sidecars of DNG files.ImageTagger-Info.plist declares the xmp extension as a related item type (NSIsRelatedItemType).The app from the GitHub releases includes the imagetagger
command, which tags images and saves their tags to the files, like Save Tags to All Images in the app. The Mac App
Store version doesn't include it, because every executable in a Mac App Store app has to be sandboxed, and a sandboxed
command couldn't read the images in the Terminal's current folder.
To install it, choose ImageTagger › Install Command Line Tool… and run the command it shows in the Terminal. It
links the command into /usr/local/bin:
sudo mkdir -p /usr/local/bin
sudo ln -sf /Applications/ImageTagger.app/Contents/Helpers/imagetagger /usr/local/bin/imagetagger
Then run it in a folder of photos:
cd ~/Pictures/Trip
imagetagger # Tag the images in the folder and its subfolders, and save the tags
imagetagger --dry-run # Only print the tags
imagetagger --format hashtags IMG_1234.jpg Edits/
IMG_1234.jpg: lake, nature, night, no people, outdoors, reflection, sky, water
IMG_1235.CR2: architecture, building exterior, built structure, history, tower
Saved the tags of 2 of 2 images.
<path>: <tags>, in the order of the files. Errors and the summary are printed to
the standard error.The Core ML model's weights (116 MB) are stored with Git LFS, because GitHub doesn't allow files over 100 MB in a repository. Install Git LFS before cloning, so the model is downloaded with the code:
brew install git-lfs
git lfs install
git clone https://github.com/dnutiu/ImageTaggerMac.git
If you cloned without Git LFS, run git lfs pull in the repository to download the model.
Open ImageTagger.xcodeproj and run the ImageTagger scheme, or build from the command line:
xcodebuild -project ImageTagger.xcodeproj -scheme ImageTagger build
xcodebuild -project ImageTagger.xcodeproj -scheme ImageTagger test
ImageTagger/Resources/TagPrediction.mlpackage is converted from the ONNX model of the Java version:
Download the AIModels archive from the
ImageTagger releases page. It contains prediction.onnx and
prediction_categories.txt.
Convert the model:
python3 -m venv .venv
.venv/bin/pip install -r Scripts/requirements.txt
.venv/bin/python Scripts/convert_model.py \
--onnx path/to/prediction.onnx \
--labels path/to/prediction_categories.txt \
--output ImageTagger/Resources/TagPrediction.mlpackage
The conversion wraps the network with ImageNet normalization and a sigmoid output, embeds the 1708 tag names, and stores the weights as 16-bit floats. Its predictions match the original 32-bit ONNX model. 8-bit weights would halve the size, but they changed the predicted tags.
Scripts/release.sh archives the app, signs it with the team's Developer ID certificate, has Apple notarize it, and
creates build/release/ImageTagger-<version>.dmg. It uses the Apple Account signed in to Xcode, so it doesn't need a
notarization password. Before finishing, it checks that the app in the DMG opens like an app downloaded from the
internet.
Scripts/release.sh # Create the DMG
Scripts/release.sh --publish release-notes.md # Also publish it as the v<version> GitHub release
Scripts/release.sh --app path/to/ImageTagger.app # Create the DMG from an app that's already notarized
The version is the app's MARKETING_VERSION. Notarization usually takes a few minutes, but it can take much longer,
so the script waits up to 3 hours (set NOTARIZATION_TIMEOUT_MINUTES to change it).
Archive the app in Xcode with Product › Archive, and upload it from the Organizer with Distribute App › App Store
Connect. Release builds leave out the imagetagger command, which isn't sandboxed, so the archive can be uploaded as
is. Scripts/release.sh builds with EMBEDS_COMMAND_LINE_TOOL=YES to include it in the DMG, and the
Install Command Line Tool… menu item is only shown when the app includes it.
The App Store screenshots are in AppStore/Screenshots. Each one is a frame in AppStore/screenshots.html that
combines a screenshot of the app with a headline. After changing the frames or the app screenshots in docs, render
them again with Google Chrome:
Scripts/app_store_screenshots.sh # 1440×900
SCALE=2 Scripts/app_store_screenshots.sh # 2880×1800, for app screenshots at least twice as large
The CI workflow builds the app and runs the unit tests for every push to main and every pull request. The model is
cached between runs, so it isn't downloaded from Git LFS every time.
The Release workflow runs Scripts/release.sh --publish when a version tag is pushed. The tag has to match the
app's version, and the message of an annotated tag becomes the release notes:
git tag -a v1.1 -m "What's new in this release"
git push origin v1.1
On GitHub the script can't use an Xcode account, so it signs and notarizes with an App Store Connect API key instead. Create a team key with the Admin role in App Store Connect › Users and Access › Integrations, and add it to the repository's secrets:
gh secret set APP_STORE_CONNECT_KEY < AuthKey_<key ID>.p8
gh secret set APP_STORE_CONNECT_KEY_ID --body <key ID>
gh secret set APP_STORE_CONNECT_ISSUER_ID --body <issuer ID>
| Path | Purpose |
|---|---|
ImageTagger/Model | Tag prediction with Vision and Core ML, image loading and metadata reading with ImageIO, tag formats. |
ImageTagger/Session | The observable session: the list of images, the analysis queue, cancellation and actions. |
ImageTagger/Views | The SwiftUI sidebar, image preview and inspector. |
ImageTagger/ImageTaggerCommands.swift | The menu bar commands. |
ImageTaggerCLI | The imagetagger command. It's embedded in the app at Contents/Helpers/imagetagger, and compiles the files in ImageTagger/Model. |
ImageTagger-Info.plist | Declares the image and folder types the app can open, XMP sidecars as related items, and that the app doesn't use encryption (ITSAppUsesNonExemptEncryption). It's merged with the generated Info.plist. |
Scripts | Converts the ONNX model to Core ML, builds notarized releases, and renders the App Store screenshots. |
AppStore | The App Store screenshots, and the page they're rendered from. |
.github | The CI and Release workflows. |
docs | The website, published with GitHub Pages from this folder on main: the landing page, the support page (the App Store support URL) and the privacy policy. It also holds the README's screenshots. |
#nopeople instead of #no people.Licensed under the GNU General Public License v3.