libcamera: software_isp: Add focus control

Signed-off-by: Vasiliy Doylov <nekocwd@mainlining.org>
This commit is contained in:
Vasiliy Doylov 2025-03-17 04:24:56 +03:00
parent 9f2edfa764
commit 37aa6edbf3
Signed by: NekoCWD
GPG key ID: B7BE22D44474A582
10 changed files with 169 additions and 12 deletions

View file

@ -86,11 +86,11 @@ public:
Signal<FrameBuffer *> outputBufferReady; Signal<FrameBuffer *> outputBufferReady;
Signal<uint32_t, uint32_t> ispStatsReady; Signal<uint32_t, uint32_t> ispStatsReady;
Signal<uint32_t, const ControlList &> metadataReady; Signal<uint32_t, const ControlList &> metadataReady;
Signal<const ControlList &> setSensorControls; Signal<const ControlList &, const ControlList &> setSensorControls;
private: private:
void saveIspParams(); void saveIspParams();
void setSensorCtrls(const ControlList &sensorControls); void setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls);
void statsReady(uint32_t frame, uint32_t bufferId); void statsReady(uint32_t frame, uint32_t bufferId);
void inputReady(FrameBuffer *input); void inputReady(FrameBuffer *input);
void outputReady(FrameBuffer *output); void outputReady(FrameBuffer *output);

View file

@ -10,6 +10,7 @@ import "include/libcamera/ipa/core.mojom";
struct IPAConfigInfo { struct IPAConfigInfo {
libcamera.ControlInfoMap sensorControls; libcamera.ControlInfoMap sensorControls;
libcamera.ControlInfoMap lensControls;
}; };
interface IPASoftInterface { interface IPASoftInterface {
@ -32,7 +33,7 @@ interface IPASoftInterface {
}; };
interface IPASoftEventInterface { interface IPASoftEventInterface {
setSensorControls(libcamera.ControlList sensorControls); setSensorControls(libcamera.ControlList sensorControls, libcamera.ControlList lensControls);
setIspParams(); setIspParams();
metadataReady(uint32 frame, libcamera.ControlList metadata); metadataReady(uint32 frame, libcamera.ControlList metadata);
}; };

View file

@ -0,0 +1,71 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2025 Vasiliy Doylov <nekodevelopper@gmail.com>
*
* Auto focus
*/
#include "af.h"
#include <stdint.h>
#include <libcamera/base/log.h>
#include "control_ids.h"
namespace libcamera {
LOG_DEFINE_CATEGORY(IPASoftAutoFocus)
namespace ipa::soft::algorithms {
Af::Af()
{
}
int Af::init(IPAContext &context,
[[maybe_unused]] const YamlObject &tuningData)
{
context.ctrlMap[&controls::LensPosition] = ControlInfo(0.0f, 100.0f, 50.0f);
return 0;
}
int Af::configure(IPAContext &context,
[[maybe_unused]] const IPAConfigInfo &configInfo)
{
context.activeState.knobs.focus_pos = std::optional<double>();
return 0;
}
void Af::queueRequest([[maybe_unused]] typename Module::Context &context,
[[maybe_unused]] const uint32_t frame,
[[maybe_unused]] typename Module::FrameContext &frameContext,
const ControlList &controls)
{
const auto &focus_pos = controls.get(controls::LensPosition);
if (focus_pos.has_value()) {
context.activeState.knobs.focus_pos = focus_pos;
LOG(IPASoftAutoFocus, Debug) << "Setting focus position to " << focus_pos.value();
}
}
void Af::updateFocus([[maybe_unused]] IPAContext &context, [[maybe_unused]] IPAFrameContext &frameContext, [[maybe_unused]] double exposureMSV)
{
frameContext.lens.focus_pos = context.activeState.knobs.focus_pos.value_or(50.0) / 100.0 * (context.configuration.focus.focus_max - context.configuration.focus.focus_min);
}
void Af::process([[maybe_unused]] IPAContext &context,
[[maybe_unused]] const uint32_t frame,
[[maybe_unused]] IPAFrameContext &frameContext,
[[maybe_unused]] const SwIspStats *stats,
[[maybe_unused]] ControlList &metadata)
{
updateFocus(context, frameContext, 0);
}
REGISTER_IPA_ALGORITHM(Af, "Af")
} /* namespace ipa::soft::algorithms */
} /* namespace libcamera */

View file

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2025 Vasiliy Doylov <nekodevelopper@gmail.com>
*
* Auto focus
*/
#pragma once
#include "algorithm.h"
namespace libcamera {
namespace ipa::soft::algorithms {
class Af : public Algorithm
{
public:
Af();
~Af() = default;
int init(IPAContext &context, const YamlObject &tuningData) override;
int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
void queueRequest(typename Module::Context &context,
const uint32_t frame,
typename Module::FrameContext &frameContext,
const ControlList &controls)
override;
void process(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
const SwIspStats *stats,
ControlList &metadata) override;
private:
void updateFocus(IPAContext &context, IPAFrameContext &frameContext, double focus);
};
} /* namespace ipa::soft::algorithms */
} /* namespace libcamera */

View file

@ -6,4 +6,5 @@ soft_simple_ipa_algorithms = files([
'blc.cpp', 'blc.cpp',
'ccm.cpp', 'ccm.cpp',
'lut.cpp', 'lut.cpp',
'af.cpp',
]) ])

View file

@ -16,4 +16,5 @@ algorithms:
0, 0, 1] 0, 0, 1]
- Lut: - Lut:
- Agc: - Agc:
- Af:
... ...

View file

@ -34,6 +34,9 @@ struct IPASessionConfiguration {
struct { struct {
std::optional<uint8_t> level; std::optional<uint8_t> level;
} black; } black;
struct {
int32_t focus_min, focus_max;
} focus;
}; };
struct IPAActiveState { struct IPAActiveState {
@ -68,6 +71,8 @@ struct IPAActiveState {
std::optional<double> brightness; std::optional<double> brightness;
/* 0..1 range, 1 = normal */ /* 0..1 range, 1 = normal */
std::optional<bool> ae_enabled; std::optional<bool> ae_enabled;
/* 0..100 range, 50.0 = normal */
std::optional<double> focus_pos;
} knobs; } knobs;
}; };
@ -81,6 +86,10 @@ struct IPAFrameContext : public FrameContext {
double gain; double gain;
} sensor; } sensor;
struct {
int32_t focus_pos;
} lens;
struct { struct {
double red; double red;
double blue; double blue;

View file

@ -77,6 +77,7 @@ private:
SwIspStats *stats_; SwIspStats *stats_;
std::unique_ptr<CameraSensorHelper> camHelper_; std::unique_ptr<CameraSensorHelper> camHelper_;
ControlInfoMap sensorInfoMap_; ControlInfoMap sensorInfoMap_;
ControlInfoMap lensInfoMap_;
/* Local parameter storage */ /* Local parameter storage */
struct IPAContext context_; struct IPAContext context_;
@ -196,6 +197,7 @@ int IPASoftSimple::init(const IPASettings &settings,
int IPASoftSimple::configure(const IPAConfigInfo &configInfo) int IPASoftSimple::configure(const IPAConfigInfo &configInfo)
{ {
sensorInfoMap_ = configInfo.sensorControls; sensorInfoMap_ = configInfo.sensorControls;
lensInfoMap_ = configInfo.lensControls;
const ControlInfo &exposureInfo = sensorInfoMap_.find(V4L2_CID_EXPOSURE)->second; const ControlInfo &exposureInfo = sensorInfoMap_.find(V4L2_CID_EXPOSURE)->second;
const ControlInfo &gainInfo = sensorInfoMap_.find(V4L2_CID_ANALOGUE_GAIN)->second; const ControlInfo &gainInfo = sensorInfoMap_.find(V4L2_CID_ANALOGUE_GAIN)->second;
@ -205,6 +207,17 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo)
context_.activeState = {}; context_.activeState = {};
context_.frameContexts.clear(); context_.frameContexts.clear();
if (lensInfoMap_.empty()) {
LOG(IPASoft, Warning) << "No camera leans found! Focus control disabled.";
context_.configuration.focus.focus_min = 0;
context_.configuration.focus.focus_max = 0;
} else {
const ControlInfo &lensInfo = lensInfoMap_.find(V4L2_CID_FOCUS_ABSOLUTE)->second;
context_.configuration.focus.focus_min = lensInfo.min().get<int32_t>();
context_.configuration.focus.focus_max = lensInfo.max().get<int32_t>();
LOG(IPASoft, Warning) << "Camera leans found! Focus: " << context_.configuration.focus.focus_min << "-" << context_.configuration.focus.focus_max;
}
context_.configuration.agc.lineDuration = context_.configuration.agc.lineDuration =
context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate; context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate;
context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>(); context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>();
@ -327,7 +340,10 @@ void IPASoftSimple::processStats(const uint32_t frame,
ctrls.set(V4L2_CID_ANALOGUE_GAIN, ctrls.set(V4L2_CID_ANALOGUE_GAIN,
static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(againNew) : againNew)); static_cast<int32_t>(camHelper_ ? camHelper_->gainCode(againNew) : againNew));
setSensorControls.emit(ctrls); ControlList lens_ctrls(lensInfoMap_);
lens_ctrls.set(V4L2_CID_FOCUS_ABSOLUTE, frameContext.lens.focus_pos);
setSensorControls.emit(ctrls, lens_ctrls);
} }
std::string IPASoftSimple::logPrefix() const std::string IPASoftSimple::logPrefix() const

View file

@ -30,6 +30,7 @@
#include <libcamera/stream.h> #include <libcamera/stream.h>
#include "libcamera/internal/camera.h" #include "libcamera/internal/camera.h"
#include "libcamera/internal/camera_lens.h"
#include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/camera_sensor.h"
#include "libcamera/internal/camera_sensor_properties.h" #include "libcamera/internal/camera_sensor_properties.h"
#include "libcamera/internal/converter.h" #include "libcamera/internal/converter.h"
@ -41,6 +42,8 @@
#include "libcamera/internal/v4l2_subdevice.h" #include "libcamera/internal/v4l2_subdevice.h"
#include "libcamera/internal/v4l2_videodevice.h" #include "libcamera/internal/v4l2_videodevice.h"
#include "libcamera/controls.h"
namespace libcamera { namespace libcamera {
LOG_DEFINE_CATEGORY(SimplePipeline) LOG_DEFINE_CATEGORY(SimplePipeline)
@ -356,7 +359,7 @@ private:
void ispStatsReady(uint32_t frame, uint32_t bufferId); void ispStatsReady(uint32_t frame, uint32_t bufferId);
void metadataReady(uint32_t frame, const ControlList &metadata); void metadataReady(uint32_t frame, const ControlList &metadata);
void setSensorControls(const ControlList &sensorControls); void setSensorControls(const ControlList &sensorControls, const ControlList &lensControls);
}; };
class SimpleCameraConfiguration : public CameraConfiguration class SimpleCameraConfiguration : public CameraConfiguration
@ -1002,7 +1005,7 @@ void SimpleCameraData::metadataReady(uint32_t frame, const ControlList &metadata
tryCompleteRequest(info->request); tryCompleteRequest(info->request);
} }
void SimpleCameraData::setSensorControls(const ControlList &sensorControls) void SimpleCameraData::setSensorControls(const ControlList &sensorControls, const ControlList &lensControls)
{ {
delayedCtrls_->push(sensorControls); delayedCtrls_->push(sensorControls);
/* /*
@ -1013,10 +1016,21 @@ void SimpleCameraData::setSensorControls(const ControlList &sensorControls)
* but it also bypasses delayedCtrls_, creating AGC regulation issues. * but it also bypasses delayedCtrls_, creating AGC regulation issues.
* Both problems should be fixed. * Both problems should be fixed.
*/ */
if (!frameStartEmitter_) { if (frameStartEmitter_)
return;
ControlList ctrls(sensorControls); ControlList ctrls(sensorControls);
sensor_->setControls(&ctrls); sensor_->setControls(&ctrls);
}
CameraLens *focusLens = sensor_->focusLens();
if (!focusLens)
return;
if (!lensControls.contains(V4L2_CID_FOCUS_ABSOLUTE))
return;
const ControlValue &focusValue = lensControls.get(V4L2_CID_FOCUS_ABSOLUTE);
focusLens->setFocusPosition(focusValue.get<int32_t>());
} }
/* Retrieve all source pads connected to a sink pad through active routes. */ /* Retrieve all source pads connected to a sink pad through active routes. */
@ -1406,6 +1420,10 @@ int SimplePipelineHandler::configure(Camera *camera, CameraConfiguration *c)
} else { } else {
ipa::soft::IPAConfigInfo configInfo; ipa::soft::IPAConfigInfo configInfo;
configInfo.sensorControls = data->sensor_->controls(); configInfo.sensorControls = data->sensor_->controls();
if (data->sensor_->focusLens() != nullptr)
configInfo.lensControls = data->sensor_->focusLens()->controls();
else
configInfo.lensControls = ControlInfoMap();
return data->swIsp_->configure(inputCfg, outputCfgs, configInfo); return data->swIsp_->configure(inputCfg, outputCfgs, configInfo);
} }
} }

View file

@ -420,9 +420,9 @@ void SoftwareIsp::saveIspParams()
debayerParams_ = *sharedParams_; debayerParams_ = *sharedParams_;
} }
void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls) void SoftwareIsp::setSensorCtrls(const ControlList &sensorControls, const ControlList &lensControls)
{ {
setSensorControls.emit(sensorControls); setSensorControls.emit(sensorControls, lensControls);
} }
void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId) void SoftwareIsp::statsReady(uint32_t frame, uint32_t bufferId)