Files
negative-converter/docs/ARCHITECTURE.md
Christoph K. 65b411b23d chore: initial project scaffold from architecture design
- 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>
2026-03-14 09:28:32 +01:00

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 a cv::Mat in 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::Mat uses reference counting internally, so copies are shallow
  • Moving ImageData is 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.