diff options
Diffstat (limited to 'src/ipa/rpi/common/ipa_base.cpp')
-rw-r--r-- | src/ipa/rpi/common/ipa_base.cpp | 431 |
1 files changed, 324 insertions, 107 deletions
diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp index f28eb36b..e0f8b7e7 100644 --- a/src/ipa/rpi/common/ipa_base.cpp +++ b/src/ipa/rpi/common/ipa_base.cpp @@ -2,7 +2,7 @@ /* * Copyright (C) 2019-2023, Raspberry Pi Ltd * - * ipa_base.cpp - Raspberry Pi IPA base class + * Raspberry Pi IPA base class */ #include "ipa_base.h" @@ -24,6 +24,7 @@ #include "controller/ccm_status.h" #include "controller/contrast_algorithm.h" #include "controller/denoise_algorithm.h" +#include "controller/hdr_algorithm.h" #include "controller/lux_status.h" #include "controller/sharpen_algorithm.h" #include "controller/statistics.h" @@ -54,9 +55,19 @@ constexpr Duration controllerMinFrameDuration = 1.0s / 30.0; /* List of controls handled by the Raspberry Pi IPA */ const ControlInfoMap::Map ipaControls{ - { &controls::AeEnable, ControlInfo(false, true) }, - { &controls::ExposureTime, ControlInfo(0, 66666) }, - { &controls::AnalogueGain, ControlInfo(1.0f, 16.0f) }, + /* \todo Move this to the Camera class */ + { &controls::AeEnable, ControlInfo(false, true, true) }, + { &controls::ExposureTimeMode, + ControlInfo(static_cast<int32_t>(controls::ExposureTimeModeAuto), + static_cast<int32_t>(controls::ExposureTimeModeManual), + static_cast<int32_t>(controls::ExposureTimeModeAuto)) }, + { &controls::ExposureTime, + ControlInfo(1, 66666, static_cast<int32_t>(defaultExposureTime.get<std::micro>())) }, + { &controls::AnalogueGainMode, + ControlInfo(static_cast<int32_t>(controls::AnalogueGainModeAuto), + static_cast<int32_t>(controls::AnalogueGainModeManual), + static_cast<int32_t>(controls::AnalogueGainModeAuto)) }, + { &controls::AnalogueGain, ControlInfo(1.0f, 16.0f, 1.0f) }, { &controls::AeMeteringMode, ControlInfo(controls::AeMeteringModeValues) }, { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) }, { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) }, @@ -67,10 +78,14 @@ const ControlInfoMap::Map ipaControls{ { &controls::AeFlickerPeriod, ControlInfo(100, 1000000) }, { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) }, { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) }, + { &controls::HdrMode, ControlInfo(controls::HdrModeValues) }, { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, - { &controls::FrameDurationLimits, ControlInfo(INT64_C(33333), INT64_C(120000)) }, - { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) } + { &controls::FrameDurationLimits, + ControlInfo(INT64_C(33333), INT64_C(120000), + static_cast<int64_t>(defaultMinFrameDuration.get<std::micro>())) }, + { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) }, + { &controls::rpi::StatsOutputEnable, ControlInfo(false, true, false) }, }; /* IPA controls handled conditionally, if the sensor is not mono */ @@ -78,6 +93,7 @@ const ControlInfoMap::Map ipaColourControls{ { &controls::AwbEnable, ControlInfo(false, true) }, { &controls::AwbMode, ControlInfo(controls::AwbModeValues) }, { &controls::ColourGains, ControlInfo(0.0f, 32.0f) }, + { &controls::ColourTemperature, ControlInfo(100, 100000) }, { &controls::Saturation, ControlInfo(0.0f, 32.0f, 1.0f) }, }; @@ -93,6 +109,13 @@ const ControlInfoMap::Map ipaAfControls{ { &controls::LensPosition, ControlInfo(0.0f, 32.0f, 1.0f) } }; +/* Platform specific controls */ +const std::map<const std::string, ControlInfoMap::Map> platformControls { + { "pisp", { + { &controls::rpi::ScalerCrops, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) } + } }, +}; + } /* namespace */ LOG_DEFINE_CATEGORY(IPARPI) @@ -100,8 +123,9 @@ LOG_DEFINE_CATEGORY(IPARPI) namespace ipa::RPi { IpaBase::IpaBase() - : controller_(), frameLengths_(FrameLengthsQueueSize, 0s), frameCount_(0), - mistrustCount_(0), lastRunTimestamp_(0), firstStart_(true), flickerState_({ 0, 0s }) + : controller_(), frameLengths_(FrameLengthsQueueSize, 0s), statsMetadataOutput_(false), + stitchSwapBuffers_(false), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0), + firstStart_(true), flickerState_({ 0, 0s }) { } @@ -123,18 +147,8 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams ¶ms, Ini return -EINVAL; } - /* - * Pass out the sensor config to the pipeline handler in order - * to setup the staggered writer class. - */ - int gainDelay, exposureDelay, vblankDelay, hblankDelay, sensorMetadata; - helper_->getDelays(exposureDelay, gainDelay, vblankDelay, hblankDelay); - sensorMetadata = helper_->sensorEmbeddedDataPresent(); - - result->sensorConfig.gainDelay = gainDelay; - result->sensorConfig.exposureDelay = exposureDelay; - result->sensorConfig.vblankDelay = vblankDelay; - result->sensorConfig.hblankDelay = hblankDelay; + /* Pass out the sensor metadata to the pipeline handler */ + int sensorMetadata = helper_->sensorEmbeddedDataPresent(); result->sensorConfig.sensorMetadata = sensorMetadata; /* Load the tuning file for this sensor. */ @@ -149,12 +163,17 @@ int32_t IpaBase::init(const IPASettings &settings, const InitParams ¶ms, Ini lensPresent_ = params.lensPresent; controller_.initialise(); + helper_->setHwConfig(controller_.getHardwareConfig()); /* Return the controls handled by the IPA */ ControlInfoMap::Map ctrlMap = ipaControls; if (lensPresent_) ctrlMap.merge(ControlInfoMap::Map(ipaAfControls)); + auto platformCtrlsIt = platformControls.find(controller_.getTarget()); + if (platformCtrlsIt != platformControls.end()) + ctrlMap.merge(ControlInfoMap::Map(platformCtrlsIt->second)); + monoSensor_ = params.sensorInfo.cfaPattern == properties::draft::ColorFilterArrangementEnum::MONO; if (!monoSensor_) ctrlMap.merge(ControlInfoMap::Map(ipaColourControls)); @@ -209,7 +228,7 @@ int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigPa /* Supply initial values for gain and exposure. */ AgcStatus agcStatus; - agcStatus.shutterTime = defaultExposureTime; + agcStatus.exposureTime = defaultExposureTime; agcStatus.analogueGain = defaultAnalogueGain; applyAGC(&agcStatus, ctrls); @@ -243,15 +262,18 @@ int32_t IpaBase::configure(const IPACameraSensorInfo &sensorInfo, const ConfigPa ControlInfoMap::Map ctrlMap = ipaControls; ctrlMap[&controls::FrameDurationLimits] = ControlInfo(static_cast<int64_t>(mode_.minFrameDuration.get<std::micro>()), - static_cast<int64_t>(mode_.maxFrameDuration.get<std::micro>())); + static_cast<int64_t>(mode_.maxFrameDuration.get<std::micro>()), + static_cast<int64_t>(defaultMinFrameDuration.get<std::micro>())); ctrlMap[&controls::AnalogueGain] = ControlInfo(static_cast<float>(mode_.minAnalogueGain), - static_cast<float>(mode_.maxAnalogueGain)); + static_cast<float>(mode_.maxAnalogueGain), + static_cast<float>(defaultAnalogueGain)); ctrlMap[&controls::ExposureTime] = - ControlInfo(static_cast<int32_t>(mode_.minShutter.get<std::micro>()), - static_cast<int32_t>(mode_.maxShutter.get<std::micro>())); + ControlInfo(static_cast<int32_t>(mode_.minExposureTime.get<std::micro>()), + static_cast<int32_t>(mode_.maxExposureTime.get<std::micro>()), + static_cast<int32_t>(defaultExposureTime.get<std::micro>())); /* Declare colour processing related controls for non-mono sensors. */ if (!monoSensor_) @@ -284,16 +306,18 @@ void IpaBase::start(const ControlList &controls, StartResult *result) /* SwitchMode may supply updated exposure/gain values to use. */ AgcStatus agcStatus; - agcStatus.shutterTime = 0.0s; + agcStatus.exposureTime = 0.0s; agcStatus.analogueGain = 0.0; metadata.get("agc.status", agcStatus); - if (agcStatus.shutterTime && agcStatus.analogueGain) { + if (agcStatus.exposureTime && agcStatus.analogueGain) { ControlList ctrls(sensorCtrls_); applyAGC(&agcStatus, ctrls); result->controls = std::move(ctrls); setCameraTimeoutValue(); } + /* Make a note of this as it tells us the HDR status of the first few frames. */ + hdrStatus_ = agcStatus.hdr; /* * Initialise frame counts, and decide how many frames must be hidden or @@ -397,11 +421,17 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms) * sensor exposure/gain changes. So fetch it from the metadata list * indexed by the IPA cookie returned, and put it in the current frame * metadata. + * + * Note if the HDR mode has changed, as things like tonemaps may need updating. */ AgcStatus agcStatus; + bool hdrChange = false; RPiController::Metadata &delayedMetadata = rpiMetadata_[params.delayContext]; - if (!delayedMetadata.get<AgcStatus>("agc.status", agcStatus)) + if (!delayedMetadata.get<AgcStatus>("agc.status", agcStatus)) { rpiMetadata.set("agc.delayed_status", agcStatus); + hdrChange = agcStatus.hdr.mode != hdrStatus_.mode; + hdrStatus_ = agcStatus.hdr; + } /* * This may overwrite the DeviceStatus using values from the sensor @@ -412,7 +442,7 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms) /* Allow a 10% margin on the comparison below. */ Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns; if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ && - delta < controllerMinFrameDuration * 0.9) { + delta < controllerMinFrameDuration * 0.9 && !hdrChange) { /* * Ensure we merge the previous frame's metadata with the current * frame. This will not overwrite exposure/gain values for the @@ -449,7 +479,7 @@ void IpaBase::prepareIsp(const PrepareParams ¶ms) reportMetadata(ipaContext); /* Ready to push the input buffer into the ISP. */ - prepareIspComplete.emit(params.buffers, false); + prepareIspComplete.emit(params.buffers, stitchSwapBuffers_); } void IpaBase::processStats(const ProcessParams ¶ms) @@ -532,6 +562,33 @@ void IpaBase::setMode(const IPACameraSensorInfo &sensorInfo) mode_.maxLineLength = sensorInfo.maxLineLength * (1.0s / sensorInfo.pixelRate); /* + * Ensure that the maximum pixel processing rate does not exceed the ISP + * hardware capabilities. If it does, try adjusting the minimum line + * length to compensate if possible. + */ + Duration minPixelTime = controller_.getHardwareConfig().minPixelProcessingTime; + Duration pixelTime = mode_.minLineLength / mode_.width; + if (minPixelTime && pixelTime < minPixelTime) { + Duration adjustedLineLength = minPixelTime * mode_.width; + if (adjustedLineLength <= mode_.maxLineLength) { + LOG(IPARPI, Info) + << "Adjusting mode minimum line length from " << mode_.minLineLength + << " to " << adjustedLineLength << " because of ISP constraints."; + mode_.minLineLength = adjustedLineLength; + } else { + LOG(IPARPI, Error) + << "Sensor minimum line length of " << pixelTime * mode_.width + << " (" << 1us / pixelTime << " MPix/s)" + << " is below the minimum allowable ISP limit of " + << adjustedLineLength + << " (" << 1us / minPixelTime << " MPix/s) "; + LOG(IPARPI, Error) + << "THIS WILL CAUSE IMAGE CORRUPTION!!! " + << "Please update the camera sensor driver to allow more horizontal blanking control."; + } + } + + /* * Set the frame length limits for the mode to ensure exposure and * framerate calculations are clipped appropriately. */ @@ -549,16 +606,26 @@ void IpaBase::setMode(const IPACameraSensorInfo &sensorInfo) mode_.sensitivity = helper_->getModeSensitivity(mode_); const ControlInfo &gainCtrl = sensorCtrls_.at(V4L2_CID_ANALOGUE_GAIN); - const ControlInfo &shutterCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE); + const ControlInfo &exposureTimeCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE); mode_.minAnalogueGain = helper_->gain(gainCtrl.min().get<int32_t>()); mode_.maxAnalogueGain = helper_->gain(gainCtrl.max().get<int32_t>()); - /* Shutter speed is calculated based on the limits of the frame durations. */ - mode_.minShutter = helper_->exposure(shutterCtrl.min().get<int32_t>(), mode_.minLineLength); - mode_.maxShutter = Duration::max(); - helper_->getBlanking(mode_.maxShutter, - mode_.minFrameDuration, mode_.maxFrameDuration); + /* + * We need to give the helper the min/max frame durations so it can calculate + * the correct exposure limits below. + */ + helper_->setCameraMode(mode_); + + /* + * Exposure time is calculated based on the limits of the frame + * durations. + */ + mode_.minExposureTime = helper_->exposure(exposureTimeCtrl.min().get<int32_t>(), + mode_.minLineLength); + mode_.maxExposureTime = Duration::max(); + helper_->getBlanking(mode_.maxExposureTime, mode_.minFrameDuration, + mode_.maxFrameDuration); } void IpaBase::setCameraTimeoutValue() @@ -643,14 +710,6 @@ static const std::map<int32_t, std::string> AwbModeTable = { { controls::AwbCustom, "custom" }, }; -static const std::map<int32_t, RPiController::DenoiseMode> DenoiseModeTable = { - { controls::draft::NoiseReductionModeOff, RPiController::DenoiseMode::Off }, - { controls::draft::NoiseReductionModeFast, RPiController::DenoiseMode::ColourFast }, - { controls::draft::NoiseReductionModeHighQuality, RPiController::DenoiseMode::ColourHighQuality }, - { controls::draft::NoiseReductionModeMinimal, RPiController::DenoiseMode::ColourOff }, - { controls::draft::NoiseReductionModeZSL, RPiController::DenoiseMode::ColourHighQuality }, -}; - static const std::map<int32_t, RPiController::AfAlgorithm::AfMode> AfModeTable = { { controls::AfModeManual, RPiController::AfAlgorithm::AfModeManual }, { controls::AfModeAuto, RPiController::AfAlgorithm::AfModeAuto }, @@ -669,9 +728,21 @@ static const std::map<int32_t, RPiController::AfAlgorithm::AfPause> AfPauseTable { controls::AfPauseResume, RPiController::AfAlgorithm::AfPauseResume }, }; +static const std::map<int32_t, std::string> HdrModeTable = { + { controls::HdrModeOff, "Off" }, + { controls::HdrModeMultiExposureUnmerged, "MultiExposureUnmerged" }, + { controls::HdrModeMultiExposure, "MultiExposure" }, + { controls::HdrModeSingleExposure, "SingleExposure" }, + { controls::HdrModeNight, "Night" }, +}; + void IpaBase::applyControls(const ControlList &controls) { + using RPiController::AgcAlgorithm; using RPiController::AfAlgorithm; + using RPiController::ContrastAlgorithm; + using RPiController::DenoiseAlgorithm; + using RPiController::HdrAlgorithm; /* Clear the return metadata buffer. */ libcameraMetadata_.clear(); @@ -693,6 +764,42 @@ void IpaBase::applyControls(const ControlList &controls) af->setMode(mode->second); } + /* + * Because some AE controls are mode-specific, handle the AE-related + * mode changes first. + */ + const auto analogueGainMode = controls.get(controls::AnalogueGainMode); + const auto exposureTimeMode = controls.get(controls::ExposureTimeMode); + + if (analogueGainMode || exposureTimeMode) { + RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( + controller_.getAlgorithm("agc")); + if (agc) { + if (analogueGainMode) { + if (*analogueGainMode == controls::AnalogueGainModeManual) + agc->disableAutoGain(); + else + agc->enableAutoGain(); + + libcameraMetadata_.set(controls::AnalogueGainMode, + *analogueGainMode); + } + + if (exposureTimeMode) { + if (*exposureTimeMode == controls::ExposureTimeModeManual) + agc->disableAutoExposure(); + else + agc->enableAutoExposure(); + + libcameraMetadata_.set(controls::ExposureTimeMode, + *exposureTimeMode); + } + } else { + LOG(IPARPI, Warning) + << "Could not set AnalogueGainMode or ExposureTimeMode - no AGC algorithm"; + } + } + /* Iterate over controls */ for (auto const &ctrl : controls) { LOG(IPARPI, Debug) << "Request ctrl: " @@ -700,23 +807,8 @@ void IpaBase::applyControls(const ControlList &controls) << " = " << ctrl.second.toString(); switch (ctrl.first) { - case controls::AE_ENABLE: { - RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set AE_ENABLE - no AGC algorithm"; - break; - } - - if (ctrl.second.get<bool>() == false) - agc->disableAuto(0); - else - agc->enableAuto(0); - - libcameraMetadata_.set(controls::AeEnable, ctrl.second.get<bool>()); - break; - } + case controls::EXPOSURE_TIME_MODE: + break; /* We already handled this one above */ case controls::EXPOSURE_TIME: { RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( @@ -727,13 +819,23 @@ void IpaBase::applyControls(const ControlList &controls) break; } + /* + * Ignore manual exposure time when the auto exposure + * algorithm is running. + */ + if (agc->autoExposureEnabled()) + break; + /* The control provides units of microseconds. */ - agc->setFixedShutter(0, ctrl.second.get<int32_t>() * 1.0us); + agc->setFixedExposureTime(0, ctrl.second.get<int32_t>() * 1.0us); libcameraMetadata_.set(controls::ExposureTime, ctrl.second.get<int32_t>()); break; } + case controls::ANALOGUE_GAIN_MODE: + break; /* We already handled this one above */ + case controls::ANALOGUE_GAIN: { RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( controller_.getAlgorithm("agc")); @@ -743,6 +845,13 @@ void IpaBase::applyControls(const ControlList &controls) break; } + /* + * Ignore manual analogue gain value when the auto gain + * algorithm is running. + */ + if (agc->autoGainEnabled()) + break; + agc->setFixedAnalogueGain(0, ctrl.second.get<float>()); libcameraMetadata_.set(controls::AnalogueGain, @@ -781,7 +890,7 @@ void IpaBase::applyControls(const ControlList &controls) int32_t idx = ctrl.second.get<int32_t>(); if (ConstraintModeTable.count(idx)) { - agc->setConstraintMode(0, ConstraintModeTable.at(idx)); + agc->setConstraintMode(ConstraintModeTable.at(idx)); libcameraMetadata_.set(controls::AeConstraintMode, idx); } else { LOG(IPARPI, Error) << "Constraint mode " << idx @@ -799,9 +908,16 @@ void IpaBase::applyControls(const ControlList &controls) break; } + /* + * Ignore AE_EXPOSURE_MODE if the shutter or the gain + * are in auto mode. + */ + if (agc->autoExposureEnabled() || agc->autoGainEnabled()) + break; + int32_t idx = ctrl.second.get<int32_t>(); if (ExposureModeTable.count(idx)) { - agc->setExposureMode(0, ExposureModeTable.at(idx)); + agc->setExposureMode(ExposureModeTable.at(idx)); libcameraMetadata_.set(controls::AeExposureMode, idx); } else { LOG(IPARPI, Error) << "Exposure mode " << idx @@ -830,6 +946,17 @@ void IpaBase::applyControls(const ControlList &controls) break; } + case controls::AE_ENABLE: { + /* + * The AeEnable control is now just a wrapper that will already have been + * converted to ExposureTimeMode and AnalogueGainMode equivalents, so there + * would be nothing to do here. Nonetheless, "handle" the control so as to + * avoid warnings from the "default:" clause of the switch statement. + */ + + break; + } + case controls::AE_FLICKER_MODE: { RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( controller_.getAlgorithm("agc")); @@ -844,12 +971,12 @@ void IpaBase::applyControls(const ControlList &controls) switch (mode) { case controls::FlickerOff: - agc->setFlickerPeriod(0, 0us); + agc->setFlickerPeriod(0us); break; case controls::FlickerManual: - agc->setFlickerPeriod(0, flickerState_.manualPeriod); + agc->setFlickerPeriod(flickerState_.manualPeriod); break; @@ -883,7 +1010,7 @@ void IpaBase::applyControls(const ControlList &controls) * first, and the period updated after, or vice versa. */ if (flickerState_.mode == controls::FlickerManual) - agc->setFlickerPeriod(0, flickerState_.manualPeriod); + agc->setFlickerPeriod(flickerState_.manualPeriod); break; } @@ -957,6 +1084,25 @@ void IpaBase::applyControls(const ControlList &controls) break; } + case controls::COLOUR_TEMPERATURE: { + /* Silently ignore this control for a mono sensor. */ + if (monoSensor_) + break; + + auto temperatureK = ctrl.second.get<int32_t>(); + RPiController::AwbAlgorithm *awb = dynamic_cast<RPiController::AwbAlgorithm *>( + controller_.getAlgorithm("awb")); + if (!awb) { + LOG(IPARPI, Warning) + << "Could not set COLOUR_TEMPERATURE - no AWB algorithm"; + break; + } + + awb->setColourTemperature(temperatureK); + /* This metadata will get reported back automatically. */ + break; + } + case controls::BRIGHTNESS: { RPiController::ContrastAlgorithm *contrast = dynamic_cast<RPiController::ContrastAlgorithm *>( controller_.getAlgorithm("contrast")); @@ -1021,6 +1167,7 @@ void IpaBase::applyControls(const ControlList &controls) break; } + case controls::rpi::SCALER_CROPS: case controls::SCALER_CROP: { /* We do nothing with this, but should avoid the warning below. */ break; @@ -1032,36 +1179,11 @@ void IpaBase::applyControls(const ControlList &controls) break; } - case controls::NOISE_REDUCTION_MODE: { - RPiController::DenoiseAlgorithm *sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>( - controller_.getAlgorithm("SDN")); - /* Some platforms may have a combined "denoise" algorithm instead. */ - if (!sdn) - sdn = dynamic_cast<RPiController::DenoiseAlgorithm *>( - controller_.getAlgorithm("denoise")); - if (!sdn) { - LOG(IPARPI, Warning) - << "Could not set NOISE_REDUCTION_MODE - no SDN algorithm"; - break; - } - - int32_t idx = ctrl.second.get<int32_t>(); - auto mode = DenoiseModeTable.find(idx); - if (mode != DenoiseModeTable.end()) { - sdn->setMode(mode->second); - - /* - * \todo If the colour denoise is not going to run due to an - * analysis image resolution or format mismatch, we should - * report the status correctly in the metadata. - */ - libcameraMetadata_.set(controls::draft::NoiseReductionMode, idx); - } else { - LOG(IPARPI, Error) << "Noise reduction mode " << idx - << " not recognised"; - } + case controls::draft::NOISE_REDUCTION_MODE: + /* Handled below in handleControls() */ + libcameraMetadata_.set(controls::draft::NoiseReductionMode, + ctrl.second.get<int32_t>()); break; - } case controls::AF_MODE: break; /* We already handled this one above */ @@ -1168,6 +1290,61 @@ void IpaBase::applyControls(const ControlList &controls) break; } + case controls::HDR_MODE: { + HdrAlgorithm *hdr = dynamic_cast<HdrAlgorithm *>(controller_.getAlgorithm("hdr")); + if (!hdr) { + LOG(IPARPI, Warning) << "No HDR algorithm available"; + break; + } + + auto mode = HdrModeTable.find(ctrl.second.get<int32_t>()); + if (mode == HdrModeTable.end()) { + LOG(IPARPI, Warning) << "Unrecognised HDR mode"; + break; + } + + AgcAlgorithm *agc = dynamic_cast<AgcAlgorithm *>(controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) << "HDR requires an AGC algorithm"; + break; + } + + if (hdr->setMode(mode->second) == 0) { + agc->setActiveChannels(hdr->getChannels()); + + /* We also disable adpative contrast enhancement if HDR is running. */ + ContrastAlgorithm *contrast = + dynamic_cast<ContrastAlgorithm *>(controller_.getAlgorithm("contrast")); + if (contrast) { + if (mode->second == "Off") + contrast->restoreCe(); + else + contrast->enableCe(false); + } + + DenoiseAlgorithm *denoise = + dynamic_cast<DenoiseAlgorithm *>(controller_.getAlgorithm("denoise")); + if (denoise) { + /* \todo - make the HDR mode say what denoise it wants? */ + if (mode->second == "Night") + denoise->setConfig("night"); + else if (mode->second == "SingleExposure") + denoise->setConfig("hdr"); + /* MultiExposure doesn't need extra extra denoise. */ + else + denoise->setConfig("normal"); + } + } else + LOG(IPARPI, Warning) + << "HDR mode " << mode->second << " not supported"; + + break; + } + + case controls::rpi::STATS_OUTPUT_ENABLE: + statsMetadataOutput_ = ctrl.second.get<bool>(); + break; + default: LOG(IPARPI, Warning) << "Ctrl " << controls::controls.at(ctrl.first)->name() @@ -1190,7 +1367,7 @@ void IpaBase::fillDeviceStatus(const ControlList &sensorControls, unsigned int i int32_t hblank = sensorControls.get(V4L2_CID_HBLANK).get<int32_t>(); deviceStatus.lineLength = helper_->hblankToLineLength(hblank); - deviceStatus.shutterSpeed = helper_->exposure(exposureLines, deviceStatus.lineLength); + deviceStatus.exposureTime = helper_->exposure(exposureLines, deviceStatus.lineLength); deviceStatus.analogueGain = helper_->gain(gainCode); deviceStatus.frameLength = mode_.height + vblank; @@ -1217,7 +1394,7 @@ void IpaBase::reportMetadata(unsigned int ipaContext) DeviceStatus *deviceStatus = rpiMetadata.getLocked<DeviceStatus>("device.status"); if (deviceStatus) { libcameraMetadata_.set(controls::ExposureTime, - deviceStatus->shutterSpeed.get<std::micro>()); + deviceStatus->exposureTime.get<std::micro>()); libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain); libcameraMetadata_.set(controls::FrameDuration, helper_->exposure(deviceStatus->frameLength, deviceStatus->lineLength).get<std::micro>()); @@ -1228,9 +1405,19 @@ void IpaBase::reportMetadata(unsigned int ipaContext) } AgcPrepareStatus *agcPrepareStatus = rpiMetadata.getLocked<AgcPrepareStatus>("agc.prepare_status"); - if (agcPrepareStatus) { - libcameraMetadata_.set(controls::AeLocked, agcPrepareStatus->locked); + if (agcPrepareStatus) libcameraMetadata_.set(controls::DigitalGain, agcPrepareStatus->digitalGain); + + RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( + controller_.getAlgorithm("agc")); + if (agc) { + if (!agc->autoExposureEnabled() && !agc->autoGainEnabled()) + libcameraMetadata_.set(controls::AeState, controls::AeStateIdle); + else if (agcPrepareStatus) + libcameraMetadata_.set(controls::AeState, + agcPrepareStatus->locked + ? controls::AeStateConverged + : controls::AeStateSearching); } LuxStatus *luxStatus = rpiMetadata.getLocked<LuxStatus>("lux.status"); @@ -1315,6 +1502,35 @@ void IpaBase::reportMetadata(unsigned int ipaContext) libcameraMetadata_.set(controls::AfPauseState, p); } + /* + * THe HDR algorithm sets the HDR channel into the agc.status at the time that those + * AGC parameters were calculated several frames ago, so it comes back to us now in + * the delayed_status. If this frame is too soon after a mode switch for the + * delayed_status to be available, we use the HDR status that came out of the + * switchMode call. + */ + const AgcStatus *agcStatus = rpiMetadata.getLocked<AgcStatus>("agc.delayed_status"); + const HdrStatus &hdrStatus = agcStatus ? agcStatus->hdr : hdrStatus_; + if (!hdrStatus.mode.empty() && hdrStatus.mode != "Off") { + int32_t hdrMode = controls::HdrModeOff; + for (auto const &[mode, name] : HdrModeTable) { + if (hdrStatus.mode == name) { + hdrMode = mode; + break; + } + } + libcameraMetadata_.set(controls::HdrMode, hdrMode); + + if (hdrStatus.channel == "short") + libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelShort); + else if (hdrStatus.channel == "long") + libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelLong); + else if (hdrStatus.channel == "medium") + libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelMedium); + else + libcameraMetadata_.set(controls::HdrChannel, controls::HdrChannelNone); + } + metadataReady.emit(libcameraMetadata_); } @@ -1339,15 +1555,16 @@ void IpaBase::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDu /* * Calculate the maximum exposure time possible for the AGC to use. - * getBlanking() will update maxShutter with the largest exposure + * getBlanking() will update maxExposureTime with the largest exposure * value possible. */ - Duration maxShutter = Duration::max(); - helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); + Duration maxExposureTime = Duration::max(); + helper_->getBlanking(maxExposureTime, minFrameDuration_, maxFrameDuration_); RPiController::AgcAlgorithm *agc = dynamic_cast<RPiController::AgcAlgorithm *>( controller_.getAlgorithm("agc")); - agc->setMaxShutter(maxShutter); + if (agc) + agc->setMaxExposureTime(maxExposureTime); } void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) @@ -1364,14 +1581,14 @@ void IpaBase::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) gainCode = std::clamp<int32_t>(gainCode, minGainCode, maxGainCode); /* getBlanking might clip exposure time to the fps limits. */ - Duration exposure = agcStatus->shutterTime; + Duration exposure = agcStatus->exposureTime; auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_, maxFrameDuration_); int32_t exposureLines = helper_->exposureLines(exposure, helper_->hblankToLineLength(hblank)); LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure - << " (Shutter lines: " << exposureLines << ", AGC requested " - << agcStatus->shutterTime << ") Gain: " + << " (Exposure lines: " << exposureLines << ", AGC requested " + << agcStatus->exposureTime << ") Gain: " << agcStatus->analogueGain << " (Gain Code: " << gainCode << ")"; |