fix: respect config film_type to force negative inversion
NegativeDetector now accepts an optional forced FilmType. When film_type != "auto" in config.ini, auto-detection is skipped and the configured type is applied directly. build_pipeline() in CliRunner maps c41→ColorNegative and bw→BWNegative accordingly. Default config changed from film_type=auto to film_type=c41 to match the project's primary use case (C-41 color negatives). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ file_extensions = arw,cr2,cr3,nef,dng,orf,rw2,raf,pef,jpg,jpeg,png,tif,tiff
|
||||
# auto – NegativeDetector analyses the histogram and orange mask
|
||||
# c41 – force C-41 colour negative processing
|
||||
# bw – force B&W negative processing
|
||||
film_type = auto
|
||||
film_type = c41
|
||||
|
||||
# Output format:
|
||||
# png16 – 16-bit PNG (lossless, archival quality)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "CliRunner.h"
|
||||
|
||||
#include "../converter/pipeline/ImageData.h"
|
||||
#include "../converter/rawloader/RawLoader.h"
|
||||
#include "../converter/preprocess/Preprocessor.h"
|
||||
#include "../converter/negative/NegativeDetector.h"
|
||||
@@ -269,8 +270,17 @@ std::vector<std::filesystem::path> CliRunner::collect_files(
|
||||
Pipeline CliRunner::build_pipeline(const AppConfig& app_cfg) {
|
||||
Pipeline pipeline;
|
||||
|
||||
// Resolve forced film type from config.
|
||||
// "auto" → Unknown (auto-detection), "c41" → ColorNegative, "bw" → BWNegative.
|
||||
FilmType forced_film = FilmType::Unknown;
|
||||
if (app_cfg.conversion.film_type == "c41") {
|
||||
forced_film = FilmType::ColorNegative;
|
||||
} else if (app_cfg.conversion.film_type == "bw") {
|
||||
forced_film = FilmType::BWNegative;
|
||||
}
|
||||
|
||||
pipeline.add_stage(std::make_unique<Preprocessor>());
|
||||
pipeline.add_stage(std::make_unique<NegativeDetector>());
|
||||
pipeline.add_stage(std::make_unique<NegativeDetector>(forced_film));
|
||||
|
||||
if (app_cfg.conversion.invert) {
|
||||
pipeline.add_stage(std::make_unique<Inverter>());
|
||||
|
||||
@@ -13,6 +13,23 @@ StageResult NegativeDetector::process(ImageData data) const {
|
||||
ErrorCode::DetectionFailed, "NegativeDetector received empty image"));
|
||||
}
|
||||
|
||||
// If the caller forced a specific film type, skip analysis entirely.
|
||||
if (forced_type_ != FilmType::Unknown) {
|
||||
data.film_type = forced_type_;
|
||||
std::cout << std::format("[Detect] Film type forced by config: {}",
|
||||
[&] {
|
||||
switch (data.film_type) {
|
||||
case FilmType::ColorNegative: return "Color Negative (C-41)";
|
||||
case FilmType::BWNegative: return "B&W Negative";
|
||||
case FilmType::ColorPositive: return "Color Positive (Slide)";
|
||||
case FilmType::BWPositive: return "B&W Positive";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}()) << std::endl;
|
||||
return data;
|
||||
}
|
||||
|
||||
// Auto-detection: histogram + orange mask analysis.
|
||||
const bool is_negative = is_negative_histogram(data.rgb);
|
||||
const bool is_bw = is_monochrome(data.rgb);
|
||||
|
||||
|
||||
@@ -13,7 +13,11 @@ namespace photoconv {
|
||||
* The detected FilmType is stored in ImageData::film_type for use
|
||||
* by the Invert and Color Correction stages.
|
||||
*
|
||||
* Detection strategy:
|
||||
* When constructed with a forced FilmType (anything other than Unknown),
|
||||
* the stage skips automatic analysis and sets that type directly.
|
||||
* This allows the config's film_type setting to override auto-detection.
|
||||
*
|
||||
* Detection strategy (auto mode):
|
||||
* 1. Compute per-channel histograms
|
||||
* 2. Analyze distribution skewness (negatives have inverted distributions)
|
||||
* 3. Check for C-41 orange mask (dominant red/orange in unexposed regions)
|
||||
@@ -27,13 +31,27 @@ public:
|
||||
/// Minimum saturation to distinguish color from B&W.
|
||||
static constexpr float kColorSaturationThreshold = 15.0f;
|
||||
|
||||
/**
|
||||
* @brief Construct in auto-detection mode.
|
||||
*/
|
||||
NegativeDetector() = default;
|
||||
|
||||
/**
|
||||
* @brief Construct with a forced film type (skips auto-detection).
|
||||
*
|
||||
* @param forced_type Film type to force. Use FilmType::Unknown for auto mode.
|
||||
*/
|
||||
explicit NegativeDetector(FilmType forced_type) : forced_type_(forced_type) {}
|
||||
|
||||
~NegativeDetector() override = default;
|
||||
|
||||
[[nodiscard]] StageResult process(ImageData data) const override;
|
||||
[[nodiscard]] std::string name() const override { return "Detect"; }
|
||||
|
||||
private:
|
||||
/// When set to anything other than Unknown, auto-detection is skipped.
|
||||
FilmType forced_type_ = FilmType::Unknown;
|
||||
|
||||
/**
|
||||
* @brief Analyze histogram to detect inverted (negative) distribution.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user