@@ -281,6 +281,8 @@ bool CameraCapabilities::validateManualSensorCapability()
bool CameraCapabilities::validateManualPostProcessingCapability()
{
+ camera_metadata_ro_entry_t entry;
+
const char *noMode = "Manual post processing capability unavailable: ";
if (!staticMetadata_->entryContains<uint8_t>(ANDROID_CONTROL_AWB_AVAILABLE_MODES,
@@ -307,6 +309,28 @@ bool CameraCapabilities::validateManualPostProcessingCapability()
return false;
}
+ bool found = staticMetadata_->getEntry(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES, &entry);
+ if (!found) {
+ LOG(HAL, Info) << noMode << "missing tonemapping";
+ return false;
+ }
+
+ std::set<uint8_t> tonemapModes;
+ for (unsigned int i = 0; i < entry.count; i++)
+ tonemapModes.insert(entry.data.u8[i]);
+
+ if ((!tonemapModes.count(ANDROID_TONEMAP_MODE_CONTRAST_CURVE) &&
+ !(tonemapModes.count(ANDROID_TONEMAP_MODE_GAMMA_VALUE) &&
+ tonemapModes.count(ANDROID_TONEMAP_MODE_PRESET_CURVE))) ||
+ !tonemapModes.count(ANDROID_TONEMAP_MODE_FAST) ||
+ !tonemapModes.count(ANDROID_TONEMAP_MODE_HIGH_QUALITY)) {
+ LOG(HAL, Info)
+ << noMode
+ << "tonemap modes must contain at least {contrast, fast, hq} "
+ << "or {gamma, preset, fast, hq}";
+ return false;
+ }
+
/*
* \todo return true here after we satisfy all the requirements:
* https://developer.android.com/reference/android/hardware/camera2/CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
@@ -1165,6 +1189,77 @@ int CameraCapabilities::initializeStaticMetadata()
availableResultKeys_.insert(ANDROID_EDGE_MODE);
}
+ std::vector<uint8_t> availableTonemapModes;
+ bool tonemapGammaSupported = false;
+ bool tonemapPresetSupported = false;
+ const auto tonemapModeInfo = controlsInfo.find(&controls::TonemapMode);
+ if (tonemapModeInfo != controlsInfo.end()) {
+ for (const auto &value : tonemapModeInfo->second.values()) {
+ uint8_t mode;
+ switch (value.get<int32_t>()) {
+ case controls::TonemapModeContrastCurve:
+ mode = ANDROID_TONEMAP_MODE_CONTRAST_CURVE;
+ break;
+ case controls::TonemapModeFast:
+ mode = ANDROID_TONEMAP_MODE_FAST;
+ break;
+ case controls::TonemapModeHighQuality:
+ mode = ANDROID_TONEMAP_MODE_HIGH_QUALITY;
+ break;
+ case controls::TonemapModeGammaValue:
+ mode = ANDROID_TONEMAP_MODE_GAMMA_VALUE;
+ tonemapGammaSupported = true;
+ break;
+ case controls::TonemapModePresetCurve:
+ mode = ANDROID_TONEMAP_MODE_PRESET_CURVE;
+ tonemapPresetSupported = true;
+ break;
+ default:
+ LOG(HAL, Error) << "Unknown tonemap mode";
+ continue;
+ }
+ availableTonemapModes.push_back(mode);
+ }
+ }
+
+ /* \todo Figure out how to report TonemapCurve ControlInfo */
+ const auto tonemapSizeInfo = controlsInfo.find(&controls::TonemapCurveSize);
+ if (!availableTonemapModes.empty() &&
+ (tonemapSizeInfo != controlsInfo.end())) {
+ /* Available tonemap modes */
+ staticMetadata_->addEntry(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES,
+ availableTonemapModes);
+ availableCharacteristicsKeys_.insert(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES);
+
+ /* Tonemap size */
+ int32_t tonemapMaxCurvePoints = tonemapSizeInfo->second.max().get<int32_t>();
+ staticMetadata_->addEntry(ANDROID_TONEMAP_MAX_CURVE_POINTS,
+ tonemapMaxCurvePoints);
+ availableCharacteristicsKeys_.insert(ANDROID_TONEMAP_MAX_CURVE_POINTS);
+
+ /* Tonemap mode */
+ availableRequestKeys_.insert(ANDROID_TONEMAP_MODE);
+ availableResultKeys_.insert(ANDROID_TONEMAP_MODE);
+
+ /* Tonemap curve */
+ availableRequestKeys_.insert(ANDROID_TONEMAP_CURVE_RED);
+ availableRequestKeys_.insert(ANDROID_TONEMAP_CURVE_GREEN);
+ availableRequestKeys_.insert(ANDROID_TONEMAP_CURVE_BLUE);
+ availableResultKeys_.insert(ANDROID_TONEMAP_CURVE_RED);
+ availableResultKeys_.insert(ANDROID_TONEMAP_CURVE_GREEN);
+ availableResultKeys_.insert(ANDROID_TONEMAP_CURVE_BLUE);
+
+ if (tonemapGammaSupported) {
+ availableRequestKeys_.insert(ANDROID_TONEMAP_GAMMA);
+ availableResultKeys_.insert(ANDROID_TONEMAP_GAMMA);
+ }
+
+ if (tonemapPresetSupported) {
+ availableRequestKeys_.insert(ANDROID_TONEMAP_PRESET_CURVE);
+ availableResultKeys_.insert(ANDROID_TONEMAP_PRESET_CURVE);
+ }
+ }
+
/* JPEG static metadata. */
/*
@@ -1894,6 +1989,12 @@ std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplatePreview() con
requestTemplate->addEntry(ANDROID_SHADING_MODE, shadingMode);
}
+ if (staticMetadata_->entryContains<uint8_t>(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES,
+ ANDROID_TONEMAP_MODE_FAST)) {
+ uint8_t tonemapMode = ANDROID_TONEMAP_MODE_FAST;
+ requestTemplate->addEntry(ANDROID_TONEMAP_MODE, tonemapMode);
+ }
+
return requestTemplate;
}
@@ -1922,6 +2023,12 @@ std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateStill() const
stillTemplate->appendEntry(ANDROID_NOISE_REDUCTION_MODE, noiseReduction);
}
+ if (staticMetadata_->entryContains<uint8_t>(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES,
+ ANDROID_TONEMAP_MODE_HIGH_QUALITY)) {
+ uint8_t tonemapMode = ANDROID_TONEMAP_MODE_HIGH_QUALITY;
+ stillTemplate->appendEntry(ANDROID_TONEMAP_MODE, tonemapMode);
+ }
+
return stillTemplate;
}
@@ -1945,6 +2052,12 @@ std::unique_ptr<CameraMetadata> CameraCapabilities::requestTemplateVideo() const
previewTemplate->appendEntry(ANDROID_EDGE_MODE, edgeMode);
}
+ if (staticMetadata_->entryContains<uint8_t>(ANDROID_TONEMAP_AVAILABLE_TONE_MAP_MODES,
+ ANDROID_TONEMAP_MODE_FAST)) {
+ uint8_t tonemapMode = ANDROID_TONEMAP_MODE_FAST;
+ previewTemplate->appendEntry(ANDROID_TONEMAP_MODE, tonemapMode);
+ }
+
/*
* Assume the AE_AVAILABLE_TARGET_FPS_RANGE static metadata
* has been assembled as {{min, max} {max, max}}.
@@ -1014,6 +1014,95 @@ int CameraDevice::processControls(Camera3RequestDescriptor *descriptor)
controls.set(controls::LensShadingMode, shadingMode);
}
+ if (settings.getEntry(ANDROID_TONEMAP_MODE, &entry)) {
+ const int32_t data = static_cast<int32_t>(*entry.data.u8);
+ int32_t tonemapMode;
+ switch (data) {
+ case ANDROID_TONEMAP_MODE_CONTRAST_CURVE:
+ tonemapMode = controls::TonemapModeContrastCurve;
+ break;
+ case ANDROID_TONEMAP_MODE_FAST:
+ tonemapMode = controls::TonemapModeFast;
+ break;
+ case ANDROID_TONEMAP_MODE_HIGH_QUALITY:
+ tonemapMode = controls::TonemapModeHighQuality;
+ break;
+ case ANDROID_TONEMAP_MODE_GAMMA_VALUE:
+ tonemapMode = controls::TonemapModeGammaValue;
+ break;
+ case ANDROID_TONEMAP_MODE_PRESET_CURVE:
+ tonemapMode = controls::TonemapModePresetCurve;
+ break;
+ default:
+ LOG(HAL, Error)
+ << "Unknown tonemap mode: " << data;
+ return -EINVAL;
+ }
+
+ controls.set(controls::TonemapMode, tonemapMode);
+ }
+
+ bool curveUpdated = false;
+ tonemapCurve.resize(3);
+ static std::map<camera_metadata_tag, unsigned int> curveTags = {
+ { ANDROID_TONEMAP_CURVE_RED, 0 },
+ { ANDROID_TONEMAP_CURVE_GREEN, 1 },
+ { ANDROID_TONEMAP_CURVE_BLUE, 2 },
+ };
+
+ for (const std::pair<camera_metadata_tag, unsigned int> &tag : curveTags) {
+ if (!settings.getEntry(tag.first, &entry))
+ continue;
+
+ tonemapCurve[tag.second].resize(entry.count);
+ for (unsigned int i = 0; i < entry.count; i++)
+ tonemapCurve[tag.second][i] = *(entry.data.f + i);
+
+ curveUpdated = true;
+ }
+
+ if (curveUpdated) {
+ size_t size = std::max(tonemapCurve[0].size(),
+ tonemapCurve[1].size());
+ size = std::max(size, tonemapCurve[2].size());
+ controls.set(controls::TonemapCurveSize, size);
+
+ std::vector<float> curve;
+ curve.resize(3 * size);
+ curve.insert(curve.begin(),
+ tonemapCurve[0].begin(), tonemapCurve[0].end());
+ curve.insert(curve.begin() + size,
+ tonemapCurve[1].begin(), tonemapCurve[1].end());
+ curve.insert(curve.begin() + 2 * size,
+ tonemapCurve[2].begin(), tonemapCurve[2].end());
+ controls.set(controls::TonemapCurve, curve);
+ }
+
+ if (settings.getEntry(ANDROID_TONEMAP_GAMMA, &entry)) {
+ const float data = *entry.data.f;
+ controls.set(controls::TonemapGamma, data);
+ }
+
+ if (settings.getEntry(ANDROID_TONEMAP_PRESET_CURVE, &entry)) {
+ const int32_t data = static_cast<int32_t>(*entry.data.u8);
+ int32_t presetCurve;
+ switch (data) {
+ case ANDROID_TONEMAP_PRESET_CURVE_SRGB:
+ presetCurve = controls::TonemapPresetCurveSRGB;
+ break;
+ case ANDROID_TONEMAP_PRESET_CURVE_REC709:
+ presetCurve = controls::TonemapPresetCurveREC709;
+ break;
+ default:
+ LOG(HAL, Error)
+ << "Unknown tonemap mode: " << data;
+ return -EINVAL;
+ }
+
+ controls.set(controls::TonemapPresetCurve, presetCurve);
+
+ }
+
return 0;
}
@@ -1812,6 +1901,79 @@ CameraDevice::getResultMetadata(const Camera3RequestDescriptor &descriptor) cons
}
}
+ if (metadata.contains(controls::TonemapMode)) {
+ bool valid;
+ switch (metadata.get(controls::TonemapMode)) {
+ case controls::TonemapModeContrastCurve:
+ value = ANDROID_TONEMAP_MODE_CONTRAST_CURVE;
+ valid = true;
+ break;
+ case controls::TonemapModeFast:
+ value = ANDROID_TONEMAP_MODE_FAST;
+ valid = true;
+ break;
+ case controls::TonemapModeHighQuality:
+ value = ANDROID_TONEMAP_MODE_HIGH_QUALITY;
+ valid = true;
+ break;
+ case controls::TonemapModeGammaValue:
+ value = ANDROID_TONEMAP_MODE_GAMMA_VALUE;
+ valid = true;
+ break;
+ case controls::TonemapModePresetCurve:
+ value = ANDROID_TONEMAP_MODE_PRESET_CURVE;
+ valid = true;
+ break;
+ default:
+ LOG(HAL, Error) << "Invalid tonemap mode";
+ valid = false;
+ }
+
+ /* Can be null on non-FULL */
+ if (valid)
+ resultMetadata->addEntry(ANDROID_TONEMAP_MODE, value);
+ }
+
+ if (metadata.contains(controls::TonemapCurve) &&
+ metadata.contains(controls::TonemapCurveSize)) {
+ size_t size = metadata.get(controls::TonemapCurveSize);
+
+ Span<const float> curve = metadata.get(controls::TonemapCurve);
+ Span<const float> red = curve.subspan(0, size);
+ Span<const float> green = curve.subspan(size, size);
+ Span<const float> blue = curve.subspan(2 * size, size);
+
+ resultMetadata->addEntry(ANDROID_TONEMAP_CURVE_RED, red);
+ resultMetadata->addEntry(ANDROID_TONEMAP_CURVE_GREEN, green);
+ resultMetadata->addEntry(ANDROID_TONEMAP_CURVE_BLUE, blue);
+ }
+
+ if (metadata.contains(controls::TonemapGamma)) {
+ float gamma = metadata.get(controls::TonemapGamma);
+ resultMetadata->addEntry(ANDROID_TONEMAP_GAMMA, gamma);
+ }
+
+ if (metadata.contains(controls::TonemapPresetCurve)) {
+ bool valid;
+ switch (metadata.get(controls::TonemapPresetCurve)) {
+ case controls::TonemapPresetCurveSRGB:
+ value = ANDROID_TONEMAP_PRESET_CURVE_SRGB;
+ valid = false;
+ break;
+ case controls::TonemapPresetCurveREC709:
+ value = ANDROID_TONEMAP_PRESET_CURVE_REC709;
+ valid = false;
+ break;
+ default:
+ LOG(HAL, Error) << "Invalid tonemap preset curve";
+ valid = false;
+ }
+
+ /* Can be null on non-FULL */
+ if (valid)
+ resultMetadata->addEntry(ANDROID_TONEMAP_PRESET_CURVE, value);
+ }
+
/*
* Return the result metadata pack even is not valid: get() will return
* nullptr.
@@ -140,5 +140,8 @@ private:
float lastAnalogueGain_;
float lastDigitalGain_;
+ /* Build the tonemap curve incrementally */
+ std::vector<std::vector<float>> tonemapCurve;
+
CameraMetadata lastSettings_;
};
Plumb everything related to tonemaps: - checking for manual post processing capability - static metadata: set available modes - static metadata: enable keys (if the libcamera camera supports tonemapping) - fill out template fields - converting between the android tonemap controls and libcamera tonemap controls (for request and result) Notably, for sending the tonemap in the request, we build the tonemap incrementally. That is, if the red, green, and blue parts of the curve are passed in separate requests, we "pool" them together into a single curve and pass it to the camera. As libcamera will have its own request control caching, we only send the tonemap curve to libcamera when at least one of the three curves is provided. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> --- src/android/camera_capabilities.cpp | 113 +++++++++++++++++++ src/android/camera_device.cpp | 162 ++++++++++++++++++++++++++++ src/android/camera_device.h | 3 + 3 files changed, 278 insertions(+)