RawCull AI is a native macOS photo review and culling application for Sony ARW RAW files. It is built for Apple Silicon and combines fast embedded-preview loading with focus-point extraction, sharpness analysis, visual similarity, burst grouping, ratings, and selective export.
The application is written in Swift 6 with SwiftUI. Image parsing, analysis, shared culling models, JSON encoding, and rsync execution are separated into focused Swift packages so that RawCull primarily owns application state, workflow, caching, persistence, and presentation.
- macOS 27 or newer
- Apple Silicon Mac
- Xcode 27 or newer for development
- Swift 6 language mode
RawCull's AI-assisted culling runs locally on Apple Silicon. Photos are not uploaded to an external inference service. Both are trained neural networks. The precise difference is primarily what they produce and what they were trained to do.
| CLIP: vision-language encoder | SAM 3: vision-language segmentation | |
|---|---|---|
| Question answered | “How well does this text match this image?” | “Where are the pixels belonging to this concept?” |
| Inputs | Image or text | Image plus text/visual prompt |
| Output | One fixed-length vector per image or text | Masks, boxes, presence and confidence scores |
| Spatial information | Mostly compresses the whole image into one vector | Preserves detailed spatial information |
| Training objective | Match related image-caption vectors | Detect and segment prompted objects |
| RawCull use | Search and similarity ranking | Isolate the subject for detailed analysis |
Their simplified pipelines are:
CLIP
Image ── image encoder ──► vector ─┐
├─► similarity score
Text ── text encoder ──► vector ─┘
SAM 3
Image ── image encoder ─────────────┐
├─► detector + mask decoder ─► masks and boxes
Prompt ── text/visual encoder ──────┘
CLIP compresses an entire image into a global semantic summary. It might determine that an image closely matches “a bird in flight,” but it does not identify precisely which pixels form the bird.
SAM 3 retains spatial image features. Given the prompt “bird,” it finds relevant instances and produces pixel masks around them. SAM 3 therefore contains encoders too, but its complete system includes detection and segmentation components.
Both require neural-network inference initially, but caching changes how frequently they run:
- CLIP image encoding runs once per photograph. Later searches reuse the cached image vectors and only run the text-query path.
- Comparing cached CLIP vectors is ordinary mathematical computation, not another neural-network pass.
- SAM 3 normally runs for each image and prompt, but RawCull can cache the resulting mask.
- Reusing a cached SAM 3 mask also avoids another neural-network run.
The shortest distinction is:
CLIP determines what an image is related to; SAM 3 determines where that thing is in the image.
- CLIP: vision-language encoder. It converts images and text into comparable vectors for search, similarity, and classification.
- SAM 3: vision-language segmentation model. It uses an image plus text or visual prompts to locate objects and produce masks and boxes.
-
Vision feature-print similarity is built into macOS and works without an additional model download.
-
CLIP similarity requires a validated PhotoAIKit-compatible CLIP Core AI model bundle. If it is missing or invalid, RawCull safely falls back to Vision feature prints.
-
Deep Review requires a validated PhotoAIKit-compatible SAM 3 Core AI model bundle. The feature remains unavailable when the model is missing or invalid.
-
CLIP and SAM 3 model bundles must contain
metadata.json, the selected.aimodelor.aimodelcasset, and any resources declared by the model manifest. -
Models are not bundled with RawCull, and the in-app SAM 3 download is not yet implemented. Install the resources manually, then open Settings > AI and select Check Again to validate them. The standard non-sandboxed locations are:
~/Library/Application Support/RawCull/Models/CLIP/ ~/Library/Application Support/RawCull/Models/SAM3/Sandboxed builds resolve Application Support inside the app container; Settings > AI displays the exact expected path.
-
The first use of a portable Core AI model can take longer while macOS specializes the model for the current Mac.
See AI model distribution and installation for model format, validation, and installation details.
- Similarity and burst grouping: CLIP image embeddings, or Vision feature prints as the fallback, measure visual similarity and help group neighboring frames into bursts for comparison.
- Sharpness and subject evidence: PhotoAnalysisKit combines sharpness, saliency, classification, focus-mask, and camera AF-point evidence to rank burst candidates and explain cautions.
- Deep Review: SAM 3 isolates the subject, evaluates detail inside the mask, checks whether the AF point falls within the subject, and recommends a winner with confidence and supporting reasons. Choosing Mark Winner & Close saves the winner, gives it a three-star rating, and marks the burst reviewed.
- Local caching: Embeddings, masks, scores, and burst decisions are cached locally so compatible results can be reused in later sessions.
To use CLIP, enable Use CLIP for similarity under Settings > AI after the model is validated. To use SAM 3, analyze a catalog into burst groups, select Deep Review on a burst, choose the review target, and run the review.
Not yet avaliable for download, compile your own version until macOS 27 and Xcode 27 are released.
- Discover and scan supported RAW files in a selected catalog.
- Read EXIF metadata, dimensions, camera/lens information, ISO, and aperture.
- Extract normalized camera AF points from Sony MakerNotes.
- Display cached thumbnails, embedded full-size JPEG previews, or developed RAW previews.
- Render AF-point overlays and GPU-generated focus masks.
- Score image sharpness using full-frame, salient-subject, and AF-region evidence.
- Apply photo-type presets and fast, balanced, or high-precision scoring.
- Generate validated CLIP image embeddings for similarity ranking and burst grouping, with targeted retry/provider recovery and safe per-image exclusion.
- Compare burst candidates with sharpness, similarity, and caution details.
- Tag, reject, or assign star ratings to selected images.
- Persist ratings, sharpness results, saliency labels, burst decisions, and cache signatures.
- Export embedded or developed JPEG files.
- Copy tagged or rated RAW files with streaming rsync progress.
- Monitor thumbnail-cache usage and macOS memory-pressure events.
flowchart LR
Catalog["RAW catalog"] --> Parser["RawParserKit"]
Parser --> Adapter["RawCull decoding adapters"]
Adapter --> Domain["RawCullCore models"]
Adapter --> Analysis["PhotoAnalysisKit"]
Adapter --> PhotoAI["PhotoAIKit"]
Models["CLIP / SAM 3 Core AI models"] --> PhotoAI
Analysis --> Sharpness["Sharpness, focus mask, saliency"]
PhotoAI --> Similarity["CLIP embeddings / Vision fallback"]
PhotoAI --> Masks["SAM 3 segmentation / mask storage"]
Domain --> Bursts["RawCullCore burst grouping"]
Similarity --> Bursts
Sharpness --> Ranking["RawCull ranking and review policy"]
Bursts --> Ranking
Masks --> DeepReview["Deep Review subject-detail evidence"]
Sharpness --> DeepReview
Domain --> ViewModels["@Observable view models"]
Ranking --> ViewModels
DeepReview --> ViewModels
ViewModels --> UI["SwiftUI views"]
ViewModels --> Cache["RAM and disk caches"]
ViewModels --> Persistence["JSON and burst persistence"]
ViewModels --> Copy["rsync copy workflow"]
RawCull keeps application-specific policy outside the packages:
- RAW source selection and decoding size
- security-scoped folder access
- settings and user preferences
- progress and cancellation presentation
- cache locations and file identity
- ratings, tagging, burst decisions, and culling workflow
The imported packages own reusable parsing, sharpness analysis, AI inference, model validation, similarity artifacts, segmentation, mask storage, domain, serialization, and process-execution concerns.
All package requirements are pinned to exact versions or revisions in the Xcode
project and are recorded in Package.resolved.
| Package | Pinned requirement | Responsibility | Main APIs used by RawCull |
|---|---|---|---|
| PhotoAIKit | revision ef4ce1a |
AI contracts, validated Core AI model resources, CLIP and SAM 3 inference, Vision similarity fallback, segmentation workflows, and subject-mask storage | CoreAICLIPProvider, CoreAISAM3Provider, VisionFeaturePrintBackend, SimilarityArtifactIndexer, SegmentationService, SubjectMaskSelector, SubjectMaskMemoryStore, SubjectMaskDiskStore |
| PhotoAnalysisKit | 1.2.0 | Sharpness scoring, focus masks, Vision saliency/classification, calibration, batch analysis, and cache identity | PhotoAnalyzer.analyzeBatch, PhotoAnalyzer.calibrate, PhotoAnalyzer.focusMask, PhotoAnalyzer.analyzeWithFocusMask, PhotoAnalyzer.sharpnessDescriptor, SharpnessPreset, SharpnessQuality |
| RawParserKit | 1.2.6 | RAW discovery, metadata parsing, embedded JPEG extraction, previews, and manufacturer MakerNote parsing | RawFormatRegistry, RawImageLoader.metadata, thumbnailCGImage, thumbnail, previewImage, SonyMakerNoteParser, NikonMakerNoteParser, SupportedFileType |
| RawCullCore | 1.1.0 | Shared file, catalog, EXIF, burst-grouping, ranking, and review-state value types | RawCullFileItem, RawCullSourceCatalog, ExifMetadata, BurstGroupingConfig, BurstGroupingEngine.group, BurstAnalysisResult, BurstCandidateScore, BurstReviewState |
| RsyncArguments | 1.0.0 | Type-safe construction of rsync parameters and synchronization arguments | Parameters, BasicRsyncParameters, OptionalRsyncParameters, SSHParameters, PathConfiguration, RsyncParametersSynchronize.argumentsForSynchronize, computedArguments |
| RsyncProcessStreaming | 1.0.0 | Starts and cancels rsync processes and streams file/progress output | ProcessHandlers, RsyncProcess, executeProcess, cancel |
| ParseRsyncOutput | 1.0.0 | Parses rsync summary output into counts and formatted transfer statistics | ParseRsyncOutput, getstats, numbersonly, and the formatted file/size properties |
| DecodeEncodeGeneric | 1.0.0 | Generic Codable helpers for persistent JSON data | DecodeGeneric.decodeArray, EncodeGeneric.encode |
PhotoAnalysisKit owns the complete reusable focus and sharpness pipeline:
- RawCull selects an embedded preview or a Core Image demosaiced RAW image.
RawCullPhotoAnalysisAdaptersupplies asynchronousPhotoAnalysisInputproviders.PhotoAnalyzer.analyzeBatchperforms bounded concurrent analysis and reports completion progress.SharpnessScoringModelpublishes scores, saliency summaries, focus breakdowns, and estimated time to the UI.PhotoAnalyzer.calibratederives a visual focus threshold from a catalog or burst.PhotoAnalyzer.focusMaskandanalyzeWithFocusMaskgenerate the focus overlay and its supporting evidence.
Per-image ISO, aperture, and normalized AF position are passed through PhotoAnalysisInput. Photo-type and quality choices map to package-owned SharpnessPreset and SharpnessQuality values.
Persistent sharpness results use PhotoAnalyzer.sharpnessDescriptor(for:). RawCull layers the scoring source, decoded image size, source-file size, and modification date around that descriptor. This invalidates stale scores when either the package algorithm or the input file changes.
PhotoAnalysisKit deliberately does not know about FileItem, RAW formats, security-scoped URLs, application settings, cache directories, or ratings.
PhotoAIKit is RawCull's reusable AI runtime boundary. RawCull imports six of its
products: PhotoAIContracts, PhotoAIWorkflows, PhotoAIStorage,
CoreAICLIPBackend, CoreAISAM3Backend, and VisionFeaturePrintBackend.
The package provides the model-backed and fallback services used by the AI features:
CoreAICLIPProvidercreates normalized CLIP image embeddings and cosine distances.VisionFeaturePrintBackendcreates and compares Codable Vision feature prints.CoreAISAM3Providerperforms in-process subject segmentation with a validated SAM 3 Core AI model.SegmentationServiceandSubjectMaskSelectoracquire and select masks, whilePhotoAIStoragesupplies memory and disk mask stores.- A persisted setting selects CLIP when its validated model is available. Non-finite output is retried once, then retried with a replacement provider; unresolved images are excluded from automatic burst analysis.
- Adjacent distances are passed to
BurstGroupingEngine.groupfrom RawCullCore.
RawCullAIIntegration validates model bundles, selects CLIP or the Vision
fallback, constructs SAM 3 mask services, and injects narrow services into the
application models. RawCull retains ownership of RAW decoding, model locations,
settings, subject-detail scoring, recommendation policy, ratings, and review
state.
RawParserKit is the boundary between camera RAW files and RawCull's application models.
RawParserKitImageLoader adapts package results to RawCull:
RawImageLoader.metadata(for:)becomes RawCullCoreExifMetadata.RawImageLoader.thumbnailCGImagefeeds thumbnail caching, sharpness scoring, and feature-print generation.RawImageLoader.thumbnailsupplies AppKit thumbnail images.RawImageLoader.previewImagesupplies embedded full-size previews.- MakerNote focus coordinates are converted to normalized
CGPointvalues.
RawFormatRegistry is used for supported-file discovery. The diagnostic tools also call the Sony and Nikon MakerNote parsers directly to report embedded JPEG locations and focus metadata.
RawCullCore contains application-neutral domain types shared across RawCull workflows. RawCull uses aliases for its central models:
typealias FileItem = RawCullFileItem
typealias ARWSourceCatalog = RawCullSourceCatalog
typealias ExifMetadata = RawCullCore.ExifMetadataThe package also owns the burst-grouping contracts and algorithm. RawCull provides ordered files and adjacent visual distances, then stores and presents the resulting groups, candidate scores, confidence, cautions, and review state.
The RAW copy workflow is divided into three package responsibilities:
RsyncArgumentsbuilds the base rsync argument list.- RawCull adds a NUL-separated
--files-fromlist containing the selected tagged or rated filenames and appends security-scoped source/destination paths. RsyncProcessStreamingexecutes/usr/bin/rsync, streams progress, and supports cancellation.ParseRsyncOutputconverts the final output into file counts, transferred sizes, created/deleted counts, and display-ready statistics.
RawCull persists its saved catalog records as Codable JSON. DecodeGeneric loads the stored array, while EncodeGeneric creates the encoded data written atomically to Application Support.
| Framework | Main use |
|---|---|
SwiftUI |
Application scenes, navigation, grids, comparison views, settings, overlays, and controls |
Observation |
@Observable view models and application state |
AppKit |
NSImage, macOS windows, panels, pasteboard, and image bridging |
Foundation |
URLs, file management, Codable, tasks, dates, collections, and persistence |
CoreGraphics |
CGImage, normalized AF coordinates, image sizes, and drawing |
CoreImage |
Optional CIRAWFilter demosaicing for developed-RAW previews and high-precision scoring |
ImageIO |
JPEG properties, orientation, image-source diagnostics, and cache encoding/decoding |
CryptoKit |
Stable MD5-derived disk-cache keys |
Dispatch |
macOS memory-pressure monitoring |
OSLog and os |
Structured logging and lock-backed cache diagnostics |
UniformTypeIdentifiers |
RAW/JPEG file selection and export types |
Vision and Metal sharpness analysis are encapsulated by PhotoAnalysisKit. Core AI inference, AI-side Vision feature prints, and subject-mask storage are encapsulated by PhotoAIKit rather than implemented directly in RawCull.
- The user selects a security-scoped catalog.
ScanFilesidentifies supported files through RawParserKit.- Metadata and AF information are read concurrently.
- RawCullCore
FileItemvalues are created and published to the main actor. - Ratings and persisted sharpness results are restored when their file and analysis signatures still match.
RawCull uses a two-tier thumbnail cache:
SharedMemoryCacheprovides the RAM layer throughNSCache.DiskCacheManagerstores JPEG thumbnails below~/Library/Caches/no.blogspot.RawCull/Thumbnails/.- A cache miss is decoded through RawParserKit.
Full-size embedded and developed previews use a separate disk cache. Memory pressure is monitored with DispatchSourceMemoryPressure, allowing RawCull to reduce cache pressure while keeping diagnostics available in the Memory Console.
- RawCull creates package batch requests for the selected files.
- PhotoAnalysisKit invokes RawCull's decoding providers with bounded concurrency and analyzes the resulting inputs.
- The package runs saliency, classification, Gaussian blur, Metal Laplacian analysis, regional scoring, and failure classification.
- RawCull stores the scalar score, subject summary, and detailed breakdown.
- Focus-mask views request a package-rendered mask using existing evidence when possible.
- RawParserKit supplies 512-pixel thumbnails.
- PhotoAIKit creates CLIP image embeddings. RawCull validates each artifact, performs targeted recovery for non-finite output, and excludes unresolved images; Vision remains the catalog backend when CLIP is unavailable.
- RawCull calculates adjacent distances and passes them to RawCullCore.
- RawCullCore groups the ordered images into bursts.
- RawCull ranks candidates using sharpness, similarity, and review rules.
- Burst artifacts and decisions are cached for later sessions.
Ratings, tags, saliency labels, sharpness signatures, and manual burst winners are stored in:
~/Library/Application Support/RawCull/savedfiles.json
Settings are stored separately in:
~/Library/Application Support/RawCull/settings.json
When copying selected RAW files, RawCull creates a temporary --files-from list, starts rsync with streaming handlers, updates progress, parses the final statistics, and releases both security-scoped folders during cleanup.
- View models are
@Observable,final, and@MainActor. - Background concerns use actor-per-responsibility isolation.
- Package boundary values and providers conform to
Sendable. - Dynamic parallel work uses structured task groups with bounded concurrency.
- Long-running scans, analysis, extraction, grouping, and copy operations support cooperative cancellation.
- Results are committed to observable state only after successful completion.
- Superseded similarity and grouping generations cannot publish stale results.
Important actors include:
| Actor | Responsibility |
|---|---|
SharedMemoryCache |
RAM thumbnails, grid-cache admission, memory-pressure handling, and diagnostics |
DiskCacheManager |
Thumbnail JPEG persistence |
FullSizeJPGDiskCache |
Embedded and developed full-size preview persistence |
ScanFiles |
Catalog scanning, metadata extraction, and AF-point collection |
ScanAndCreateThumbnails |
Bounded thumbnail preloading |
ExtractAndSaveJPGs |
Batch JPEG extraction |
BurstAnalysisCache |
Burst groups, embeddings, sharpness results, signatures, and review-state snapshots |
WriteSavedFilesJSON |
Atomic persistence of culling records |
RawCull/
├── Actors/ Background scanning, caching, extraction, and persistence
├── Main/ App entry point and shared type aliases
├── Model/
│ ├── Cache/ Cache configuration and diagnostics
│ ├── Diagnostics/ RAW and ImageIO diagnostics
│ ├── Handlers/ App and streaming callbacks
│ ├── JSON/ Codable persistence models
│ ├── ParametersRsync/ RAW copy configuration and execution
│ └── ViewModels/ MainActor application and workflow state
├── Views/ SwiftUI catalog, grid, comparison, settings, and zoom UI
└── Assets.xcassets
RawCullTests/
├── TEST_ARCHITECTURE.md
└── Swift Testing suites
Debug build without notarization:
make debugRelease archive, signing, notarization, stapling, and DMG generation:
make buildClean generated build output:
make cleanThe Xcode scheme builds for Apple Silicon:
xcodebuild \
-project RawCull.xcodeproj \
-scheme RawCull \
-destination 'platform=OS X,arch=arm64'Tests use Apple's Swift Testing framework. Fast package-integration and critical smoke coverage:
make test-smokeFull suite with Thread Sanitizer:
make test-fullPerformance and extreme-concurrency coverage:
make test-performanceThe test suites cover package integration, sharpness and focus metrics, structured cancellation, latest-run-wins behavior, memory-cache counters, security-scoped access, disk caches, burst persistence, RAW parsing adapters, and copy startup/cleanup.