Cross-platform photo manager and lightweight DAM with RAW/JPEG non-destructive editing, EXIF/XMP metadata, AI tagging, geotagging, culling, duplicate detection, HDR, panorama, and no database.
C#
3
55 commits
updated Sep 11, 2026
A cross-platform photo manager and lightweight DAM that organises your collection by metadata, develops RAWs/JPEGs non-destructively, and stays out of the way — no local database, no cloud sync, your folders + sidecar XMP are the source of truth.

A photo library is two problems wearing one coat: finding the picture, and finishing it. Most tools pick a side — a cataloguer that cannot develop, or an editor that cannot organise — and leave the other half to a second application and a second copy of the files.
PhotoManager does both against one catalogue: dates recovered from whatever the file actually carries, culling and tagging that keep up with a real shoot, and a non-destructive development pipeline over the same images. Nothing is moved into a proprietary vault to make that work.
PhotoIssueDetector and runs only the matching stages (Zero-DCE++ → NAFNet → FBCNN → AOD-Net → auto-WB → auto-tone → CLAHE → upscale). Scores before/after with NIMA so the status bar reports the aesthetic delta.DepthBokehBlur produces realistic portrait blur with depth-distance falloff. One-click "📸 Bokeh blur" button writes IMG.bokeh.jpg next to the source.RawImageLoader, ImagePreviewLoader, RegionThumbnailExtractor, PanoramaStitchWindow, PanoramaViewerWindow) now src-over composites alpha onto white via AlphaFlattener.FlattenOntoWhite, so transparent GIFs / PNGs render correctly through the JPEG-encoding preview path. Magick.NET fallback scoped to PNGCrushCS-handled extensions only (was flattening GIF transparency against the GIF's black background).Tools → Auto-keyword scan… runs CLIP-style image+text embeddings against a 700+-word vocabulary and writes the top-K hits as flat dc:subject keywords. Library-scan flow with progress + per-file preview.pm:developSettings onto a pm:developHistory[] list (cap 20). The 🕒 Edit history button browses snapshots and rolls back any one.IMG.copyN.xmp sidecars hold alternate develop variants without duplicating pixels. Each copy surfaces as its own grid row; right-click → Promote copy to original. Open in Develop targets the copy.Tools → ✨ Memories… surfaces "on this day" / "on this trip" photos relative to the selected anchor: same-month/day across years, plus same-bookmark-area within ±N km.winget/brew/apt) when ffmpeg isn't on PATH.GPano:ProjectionType metadata. Save the current perspective view as a JPEG.Stitcher for gigapixel hand-shot panos).Travel > Italy > Rome style. Selecting a leaf writes leaf + ancestors as flat dc:subject keywords so other tools see standard keywords; the tree itself lives in app-data JSON.DateTimeOriginal proximity + filename similarity; tag each group with a pm:burstId keyword.pm:developSettings.DevelopTemplateStore preset in Settings and every newly-imported file with no embedded settings gets it stamped in automatically..cube / .3dl files into %AppData%/PhotoManager/luts/ and they appear as a "Look" picker in the develop window with an opacity slider. Round-trips via crs:LookName so 3rd-party tools see the assigned look.UserSettingsData.qa: keywords for one-click culling.OperationProgress strip below the status bar shows long ops with a Cancel button.Tools → Export KML… walks the in-memory index and writes one Placemark per geotagged photo.{date:yyyy-MM-dd}_{city}_{name}), 🕒 batch date shift (camera-clock offset).pm:developSettings round-trippm:developHistory[], cap 20) + virtual copies (IMG.copyN.xmp sibling sidecars) — multiple develop variants per source file with one-click promote-to-originalcrs:LookName round-tripdc:subject on writexmp:Pick/xmp:Reject) with P/X/U hotkeys; quality-flag scan tags blurry / over- / under-exposed photosIProgress<T> sinkyyyy/yyyyMMdd/HHmmss); duplicate handling with sequential numberingOnnxSkySegmenter, alongside the heuristic HeuristicSkyMask)HealingBrush)WriteBackQueue; pending items persist through an atomic replace)ThumbnailPreCacheService)SolarLunarCalculator, SunMoonArrowData); no map view consumes them yetDownload the application from the latest release (or a nightly-*
prerelease) and unpack it. Requires Windows and .NET; see Building to run from source.
P / X / U, star ratings and colour labels; compare side by side.yyyy/yyyyMMdd/HHmmss hierarchy, or export geotagged shots to KML.

PhotoManager helps photographers and photo enthusiasts:
yyyy/yyyyMMdd/HHmmss folder hierarchy.InputDirectory/
├── 2024/
│ ├── 20240115/
│ │ ├── 143022.jpg
│ │ ├── 143022 (2).jpg
│ │ └── 145533.png
PhotoManager/
├── PhotoManager.Core/ # Shared business logic and models
│ ├── Models/ # Data models and DTOs
│ ├── Services/ # Business logic services
│ ├── Interfaces/ # Service contracts
│ └── Enums/ # Shared enumerations
├── PhotoManager.Tests/ # Unit and integration tests
│ ├── Unit/ # Unit tests for individual components
│ └── Integration/ # End-to-end workflow tests
├── PhotoManager.UI/ # Avalonia desktop application (cross-platform)
│ ├── Controllers/ # MVC controllers
│ ├── Views/ # Avalonia AXAML windows and dialogs
│ ├── Models/ # View models
│ └── Resources/ # Localization resources
├── PhotoManager.CLI/ # Command-line interface
├── README.md # This file
├── TODO.md # Development roadmap
└── CLAUDE.md # AI assistant instructions
# Run all tests
dotnet test
# Run with coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura
# Run specific test category
dotnet test --filter Category=Unit
Settings can be configured through:
appsettings.jsonThe application follows a clean architecture pattern with separation of concerns:
PhotoManager has no local database. The truth lives in:
.xmp next to each photo) for fields the format doesn't support nativelyCaches (face embeddings, perceptual hashes, library index) are in-memory only and rebuilt on scan. Settings live in a small JSON file under %AppData%/PhotoManager/.
The system assigns reliability scores to different date sources:
Tools → Download detection models… or click 🎭 Detect subject and confirm the prompt).ffmpeg to be installed and on PATH (winget install ffmpeg / brew install ffmpeg / apt install ffmpeg). PhotoManager doesn't bundle the ffmpeg binary because of its size.# Clone the repository
git clone <repository-url>
cd PhotoManager
# Restore dependencies
dotnet restore
# Build all projects
dotnet build
# Run tests
dotnet test
dotnet run --project PhotoManager.UI
dotnet run --project PhotoManager.CLI -- --source "C:\Photos\Input"
# With options
dotnet run --project PhotoManager.CLI -- \
--source "C:\Photos\Input" \
--recursive \
--pattern "{Year}/{Date}/{Time}{Extension}" \
--dry-run
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
For issues, feature requests, or questions, please open an issue on GitHub.
If this project saves you time or money, consider supporting its development:
Licensed under LGPL-3.0-or-later — see LICENSE.
C#
100.0%
Cross-platform photo manager and lightweight DAM with RAW/JPEG non-destructive editing, EXIF/XMP metadata, AI tagging, geotagging, culling, duplicate detection, HDR, panorama, and no database.
C#
3
55 commits
updated Sep 11, 2026
A cross-platform photo manager and lightweight DAM that organises your collection by metadata, develops RAWs/JPEGs non-destructively, and stays out of the way — no local database, no cloud sync, your folders + sidecar XMP are the source of truth.

A photo library is two problems wearing one coat: finding the picture, and finishing it. Most tools pick a side — a cataloguer that cannot develop, or an editor that cannot organise — and leave the other half to a second application and a second copy of the files.
PhotoManager does both against one catalogue: dates recovered from whatever the file actually carries, culling and tagging that keep up with a real shoot, and a non-destructive development pipeline over the same images. Nothing is moved into a proprietary vault to make that work.
PhotoIssueDetector and runs only the matching stages (Zero-DCE++ → NAFNet → FBCNN → AOD-Net → auto-WB → auto-tone → CLAHE → upscale). Scores before/after with NIMA so the status bar reports the aesthetic delta.DepthBokehBlur produces realistic portrait blur with depth-distance falloff. One-click "📸 Bokeh blur" button writes IMG.bokeh.jpg next to the source.RawImageLoader, ImagePreviewLoader, RegionThumbnailExtractor, PanoramaStitchWindow, PanoramaViewerWindow) now src-over composites alpha onto white via AlphaFlattener.FlattenOntoWhite, so transparent GIFs / PNGs render correctly through the JPEG-encoding preview path. Magick.NET fallback scoped to PNGCrushCS-handled extensions only (was flattening GIF transparency against the GIF's black background).Tools → Auto-keyword scan… runs CLIP-style image+text embeddings against a 700+-word vocabulary and writes the top-K hits as flat dc:subject keywords. Library-scan flow with progress + per-file preview.pm:developSettings onto a pm:developHistory[] list (cap 20). The 🕒 Edit history button browses snapshots and rolls back any one.IMG.copyN.xmp sidecars hold alternate develop variants without duplicating pixels. Each copy surfaces as its own grid row; right-click → Promote copy to original. Open in Develop targets the copy.Tools → ✨ Memories… surfaces "on this day" / "on this trip" photos relative to the selected anchor: same-month/day across years, plus same-bookmark-area within ±N km.winget/brew/apt) when ffmpeg isn't on PATH.GPano:ProjectionType metadata. Save the current perspective view as a JPEG.Stitcher for gigapixel hand-shot panos).Travel > Italy > Rome style. Selecting a leaf writes leaf + ancestors as flat dc:subject keywords so other tools see standard keywords; the tree itself lives in app-data JSON.DateTimeOriginal proximity + filename similarity; tag each group with a pm:burstId keyword.pm:developSettings.DevelopTemplateStore preset in Settings and every newly-imported file with no embedded settings gets it stamped in automatically..cube / .3dl files into %AppData%/PhotoManager/luts/ and they appear as a "Look" picker in the develop window with an opacity slider. Round-trips via crs:LookName so 3rd-party tools see the assigned look.UserSettingsData.qa: keywords for one-click culling.OperationProgress strip below the status bar shows long ops with a Cancel button.Tools → Export KML… walks the in-memory index and writes one Placemark per geotagged photo.{date:yyyy-MM-dd}_{city}_{name}), 🕒 batch date shift (camera-clock offset).pm:developSettings round-trippm:developHistory[], cap 20) + virtual copies (IMG.copyN.xmp sibling sidecars) — multiple develop variants per source file with one-click promote-to-originalcrs:LookName round-tripdc:subject on writexmp:Pick/xmp:Reject) with P/X/U hotkeys; quality-flag scan tags blurry / over- / under-exposed photosIProgress<T> sinkyyyy/yyyyMMdd/HHmmss); duplicate handling with sequential numberingOnnxSkySegmenter, alongside the heuristic HeuristicSkyMask)HealingBrush)WriteBackQueue; pending items persist through an atomic replace)ThumbnailPreCacheService)SolarLunarCalculator, SunMoonArrowData); no map view consumes them yetDownload the application from the latest release (or a nightly-*
prerelease) and unpack it. Requires Windows and .NET; see Building to run from source.
P / X / U, star ratings and colour labels; compare side by side.yyyy/yyyyMMdd/HHmmss hierarchy, or export geotagged shots to KML.

PhotoManager helps photographers and photo enthusiasts:
yyyy/yyyyMMdd/HHmmss folder hierarchy.InputDirectory/
├── 2024/
│ ├── 20240115/
│ │ ├── 143022.jpg
│ │ ├── 143022 (2).jpg
│ │ └── 145533.png
PhotoManager/
├── PhotoManager.Core/ # Shared business logic and models
│ ├── Models/ # Data models and DTOs
│ ├── Services/ # Business logic services
│ ├── Interfaces/ # Service contracts
│ └── Enums/ # Shared enumerations
├── PhotoManager.Tests/ # Unit and integration tests
│ ├── Unit/ # Unit tests for individual components
│ └── Integration/ # End-to-end workflow tests
├── PhotoManager.UI/ # Avalonia desktop application (cross-platform)
│ ├── Controllers/ # MVC controllers
│ ├── Views/ # Avalonia AXAML windows and dialogs
│ ├── Models/ # View models
│ └── Resources/ # Localization resources
├── PhotoManager.CLI/ # Command-line interface
├── README.md # This file
├── TODO.md # Development roadmap
└── CLAUDE.md # AI assistant instructions
# Run all tests
dotnet test
# Run with coverage
dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura
# Run specific test category
dotnet test --filter Category=Unit
Settings can be configured through:
appsettings.jsonThe application follows a clean architecture pattern with separation of concerns:
PhotoManager has no local database. The truth lives in:
.xmp next to each photo) for fields the format doesn't support nativelyCaches (face embeddings, perceptual hashes, library index) are in-memory only and rebuilt on scan. Settings live in a small JSON file under %AppData%/PhotoManager/.
The system assigns reliability scores to different date sources:
Tools → Download detection models… or click 🎭 Detect subject and confirm the prompt).ffmpeg to be installed and on PATH (winget install ffmpeg / brew install ffmpeg / apt install ffmpeg). PhotoManager doesn't bundle the ffmpeg binary because of its size.# Clone the repository
git clone <repository-url>
cd PhotoManager
# Restore dependencies
dotnet restore
# Build all projects
dotnet build
# Run tests
dotnet test
dotnet run --project PhotoManager.UI
dotnet run --project PhotoManager.CLI -- --source "C:\Photos\Input"
# With options
dotnet run --project PhotoManager.CLI -- \
--source "C:\Photos\Input" \
--recursive \
--pattern "{Year}/{Date}/{Time}{Extension}" \
--dry-run
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
For issues, feature requests, or questions, please open an issue on GitHub.
If this project saves you time or money, consider supporting its development:
Licensed under LGPL-3.0-or-later — see LICENSE.
C#
100.0%