- Add CLAUDE.md with project overview, tech stack, build commands, architecture description, coding standards, and sample images section - Add full directory structure: src/, docs/, tests/, import/ - Add CMakeLists.txt with C++20, OpenCV/LibRaw/Qt6 dependencies, converter_core static lib, optional GUI, and GTest tests - Add architecture documentation: ARCHITECTURE.md, PIPELINE.md, MODULES.md - Add source skeletons for all pipeline stages: RawLoader, Preprocessor, NegativeDetector, Inverter, ColorCorrector, CropProcessor, OutputWriter, Pipeline, MainWindow, CliRunner, main.cpp - Add initial test stubs for pipeline and rawloader - Add sample ARW files in import/ for integration testing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.3 KiB
Architecture
Overview
Photo-converter is a C++20 cross-platform desktop application that converts digitized analog film negatives (35mm, 120mm) into digital positives. It supports RAW camera formats (CR2, NEF, ARW, DNG, etc.) and standard image formats (JPG, PNG, TIFF), producing high-quality 16-bit output with automatic film type detection, color correction, and cropping.
The architecture follows Clean Architecture principles: the core processing logic has zero GUI dependencies and can run in both interactive (Qt GUI) and batch (CLI) modes.
Component Diagram
+------------------------------------------------------------------+
| Application Layer |
| |
| +------------------+ +------------------+ |
| | MainWindow | | CliRunner | |
| | (Qt GUI) | | (Batch CLI) | |
| +--------+---------+ +--------+---------+ |
| | | |
+------------|------------------------------|----------------------+
| |
v v
+------------------------------------------------------------------+
| Pipeline Orchestration |
| |
| +----------------------------------------------------------+ |
| | Pipeline | |
| | Owns: vector<unique_ptr<PipelineStage>> | |
| | Executes stages in order, propagates errors | |
| +----------------------------------------------------------+ |
| |
+------------------------------------------------------------------+
|
v
+------------------------------------------------------------------+
| Pipeline Stages |
| |
| +----------+ +---------+ +--------+ +--------+ +---------+ |
| |RawLoader |->|Preproc. |->|Detect |->|Invert |->|Color | |
| |(Loader) | | | | | | | |Corrector| |
| +----------+ +---------+ +--------+ +--------+ +---------+ |
| | |
| +-----------v-----+ |
| | CropProcessor | |
| | (Post-Process) | |
| +-----------+-----+ |
| | |
| +-----------v-----+ |
| | OutputWriter | |
| +-----------------+ |
+------------------------------------------------------------------+
|
v
+------------------------------------------------------------------+
| Core Data Types |
| |
| ImageData RawMetadata FilmType Error ErrorCode |
| |
+------------------------------------------------------------------+
|
v
+------------------------------------------------------------------+
| External Libraries |
| |
| OpenCV 4.10+ LibRaw 0.21+ Qt 6.8 LTS |
| (imgproc, imgcodecs) (RAW demosaicing) (Widgets, GUI) |
| |
+------------------------------------------------------------------+
Layer Responsibilities
Application Layer
Contains the two entry points (GUI and CLI). This layer is thin -- it constructs the pipeline, feeds it input, and presents results. No image processing logic lives here.
- MainWindow (Qt): File dialogs, preview, progress bar. Depends on Qt 6.8 Widgets.
- CliRunner: Command-line argument parsing and batch loop. Zero Qt dependency.
Pipeline Orchestration
The Pipeline class owns an ordered sequence of PipelineStage objects and executes them sequentially. It implements the Chain of Responsibility pattern: each stage either transforms the data and passes it forward, or returns an error that stops the chain.
Pipeline Stages
Each stage implements the PipelineStage interface (process(ImageData) -> StageResult). Stages are stateless or hold only configuration. This enables the Strategy pattern -- stages can be swapped, reordered, or extended without modifying the Pipeline.
Core Data Types
ImageData: The carrier struct flowing through all stages. Always contains acv::Matin CV_16UC3 format.Error/ErrorCode: Structured error with source location for diagnostics.StageResult = std::expected<ImageData, Error>: The universal return type.
Design Decisions
1. std::expected over Exceptions
Decision: All error handling uses std::expected<T, Error>, never exceptions for control flow.
Rationale:
- Explicit error paths in function signatures (self-documenting)
- No hidden control flow jumps
- Zero-cost when no error occurs
- Forces callers to handle errors (compiler warnings with
[[nodiscard]]) - Consistent with modern C++ idioms (C++23)
2. 16-bit Pipeline Throughout
Decision: All internal processing uses CV_16UC3 (16-bit per channel, 3-channel BGR).
Rationale:
- RAW files contain 12-14 bit data; 8-bit would lose dynamic range
- Inversion, color correction, and levels adjustments need headroom
- Conversion to 8-bit happens only at the final output stage
- No information loss in intermediate stages
3. LibRaw RAII Guard
Decision: LibRaw::recycle() is guaranteed via an RAII guard class (LibRawGuard).
Rationale:
- LibRaw requires explicit cleanup; forgetting recycle() leaks memory
- RAII ensures cleanup on all paths (success, error, exception)
- The guard is a private implementation detail of RawLoader
4. Core Library Separation
Decision: All processing code is in the converter_core static library, which has no Qt dependency.
Rationale:
- Enables CLI-only builds without Qt
- Makes the core testable without GUI framework
- Clean dependency graph: GUI depends on core, never vice versa
5. Value Semantics for ImageData
Decision: ImageData is passed by value (moved) through the pipeline.
Rationale:
cv::Matuses reference counting internally, so copies are shallow- Moving
ImageDatais cheap (Mat header + a few scalars) - Avoids shared mutable state between stages
- Each stage owns its input; previous stages cannot interfere
Directory Structure
photo-converter/
+-- CMakeLists.txt Root build configuration
+-- CLAUDE.md AI agent instructions
+-- docs/
| +-- ARCHITECTURE.md This file
| +-- PIPELINE.md Pipeline stage documentation
| +-- MODULES.md Module catalog
+-- import/ Sample test images (ARW)
+-- output/ Default output directory
+-- src/
| +-- main.cpp Entry point (GUI/CLI dispatch)
| +-- converter/
| | +-- pipeline/
| | | +-- Pipeline.h/.cpp Pipeline orchestrator
| | | +-- PipelineStage.h Stage interface (abstract)
| | | +-- ImageData.h Core data structure
| | | +-- Error.h Error types
| | +-- rawloader/
| | | +-- RawLoader.h/.cpp RAW + standard image loading
| | +-- preprocess/
| | | +-- Preprocessor.h/.cpp Bit depth validation, deskew
| | +-- negative/
| | | +-- NegativeDetector.h/.cpp Film type classification
| | +-- invert/
| | | +-- Inverter.h/.cpp Negative-to-positive inversion
| | +-- color/
| | | +-- ColorCorrector.h/.cpp WB, C-41 correction
| | +-- crop/
| | | +-- CropProcessor.h/.cpp Auto-crop, levels, sharpen
| | +-- output/
| | +-- OutputWriter.h/.cpp File output (PNG/TIFF/JPEG)
| +-- gui/
| | +-- MainWindow.h/.cpp Qt GUI main window
| +-- cli/
| +-- CliRunner.h/.cpp CLI batch runner
+-- tests/
+-- CMakeLists.txt Test build configuration
+-- test_pipeline.cpp Pipeline and stage unit tests
+-- test_rawloader.cpp RawLoader integration tests
Cross-Platform Build
The project builds on Linux, Windows, and macOS. Platform-specific dependency management:
| Platform | Package Manager | Command |
|---|---|---|
| Linux | apt | sudo apt install libopencv-dev libraw-dev libqt6widgets6 |
| Windows | vcpkg | vcpkg install opencv libraw qt6-base |
| macOS | Homebrew | brew install opencv libraw qt@6 |
The CMake build system handles all three platforms with appropriate find_package/pkg_check_modules calls. The BUILD_GUI option allows CLI-only builds when Qt is unavailable.