[libcamera-devel,v5,8/9] ipa: rkisp1: Add support of Filter control
diff mbox series

Message ID 20220727222149.30627-9-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • ipa: rkisp1: Add GSL, LSC, DPCC, Filter and CProc algorithms
Related show

Commit Message

Laurent Pinchart July 27, 2022, 10:21 p.m. UTC
From: Florian Sylvestre <fsylvestre@baylibre.com>

Denoise and Sharpness filters will be applied by RkISP1 during the
demosaicing step. The denoise filter is responsible for removing noise from
the image, while the sharpness filter will enhance its acutance.

Add filter algorithm with denoise and sharpness values based on user controls.

Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 src/ipa/rkisp1/algorithms/filter.cpp     | 201 +++++++++++++++++++++++
 src/ipa/rkisp1/algorithms/filter.h       |  30 ++++
 src/ipa/rkisp1/algorithms/meson.build    |   1 +
 src/ipa/rkisp1/data/ov5640.yaml          |   1 +
 src/ipa/rkisp1/ipa_context.cpp           |  14 ++
 src/ipa/rkisp1/ipa_context.h             |   6 +
 src/libcamera/pipeline/rkisp1/rkisp1.cpp |   8 +
 7 files changed, 261 insertions(+)
 create mode 100644 src/ipa/rkisp1/algorithms/filter.cpp
 create mode 100644 src/ipa/rkisp1/algorithms/filter.h

Comments

Jacopo Mondi July 28, 2022, 7:51 a.m. UTC | #1
Hi Laurent

On Thu, Jul 28, 2022 at 01:21:48AM +0300, Laurent Pinchart via libcamera-devel wrote:
> From: Florian Sylvestre <fsylvestre@baylibre.com>
>
> Denoise and Sharpness filters will be applied by RkISP1 during the
> demosaicing step. The denoise filter is responsible for removing noise from
> the image, while the sharpness filter will enhance its acutance.
>
> Add filter algorithm with denoise and sharpness values based on user controls.
>
> Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  src/ipa/rkisp1/algorithms/filter.cpp     | 201 +++++++++++++++++++++++
>  src/ipa/rkisp1/algorithms/filter.h       |  30 ++++
>  src/ipa/rkisp1/algorithms/meson.build    |   1 +
>  src/ipa/rkisp1/data/ov5640.yaml          |   1 +
>  src/ipa/rkisp1/ipa_context.cpp           |  14 ++
>  src/ipa/rkisp1/ipa_context.h             |   6 +
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp |   8 +
>  7 files changed, 261 insertions(+)
>  create mode 100644 src/ipa/rkisp1/algorithms/filter.cpp
>  create mode 100644 src/ipa/rkisp1/algorithms/filter.h
>
> diff --git a/src/ipa/rkisp1/algorithms/filter.cpp b/src/ipa/rkisp1/algorithms/filter.cpp
> new file mode 100644
> index 000000000000..8ca10fd1ee9d
> --- /dev/null
> +++ b/src/ipa/rkisp1/algorithms/filter.cpp
> @@ -0,0 +1,201 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021-2022, Ideas On Board
> + *
> + * filter.cpp - RkISP1 Filter control
> + */
> +
> +#include "filter.h"
> +
> +#include <cmath>
> +
> +#include <libcamera/base/log.h>
> +
> +#include <libcamera/control_ids.h>
> +
> +/**
> + * \file filter.h
> + */
> +
> +namespace libcamera {
> +
> +namespace ipa::rkisp1::algorithms {
> +
> +/**
> + * \class Filter
> + * \brief RkISP1 Filter control
> + *
> + * Denoise and Sharpness filters will be applied by RkISP1 during the
> + * demosaicing step. The denoise filter is responsible for removing noise from
> + * the image, while the sharpness filter will enhance its acutance.
> + *
> + * \todo In current version the denoise and sharpness control is based on user
> + * controls. In a future version it should be controlled automatically by the
> + * algorithm.
> + */
> +
> +LOG_DEFINE_CATEGORY(RkISP1Filter)
> +
> +static constexpr uint32_t kFiltLumWeightDefault = 0x00022040;
> +static constexpr uint32_t kFiltModeDefault = 0x000004f2;
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::queueRequest
> + */
> +void Filter::queueRequest(IPAContext &context,
> +			  [[maybe_unused]] const uint32_t frame,
> +			  const ControlList &controls)
> +{
> +	auto &filter = context.frameContext.filter;
> +
> +	const auto &sharpness = controls.get(controls::Sharpness);
> +	if (sharpness) {
> +		filter.sharpness = std::round(std::clamp(*sharpness, 0.0f, 10.0f));
> +		filter.updateParams = true;
> +
> +		LOG(RkISP1Filter, Debug) << "Set sharpness to " << *sharpness;
> +	}
> +
> +	const auto &denoise = controls.get(controls::draft::NoiseReductionMode);
> +	if (denoise) {
> +		LOG(RkISP1Filter, Debug) << "Set denoise to " << *denoise;
> +
> +		switch (*denoise) {
> +		case controls::draft::NoiseReductionModeOff:
> +			filter.denoise = 0;
> +			filter.updateParams = true;
> +			break;
> +		case controls::draft::NoiseReductionModeMinimal:
> +			filter.denoise = 1;
> +			filter.updateParams = true;
> +			break;
> +		case controls::draft::NoiseReductionModeHighQuality:
> +		case controls::draft::NoiseReductionModeFast:
> +			filter.denoise = 3;
> +			filter.updateParams = true;
> +			break;
> +		default:
> +			LOG(RkISP1Filter, Error)
> +				<< "Unsupported denoise value "
> +				<< *denoise;
> +		}
> +	}
> +}
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::prepare
> + */
> +void Filter::prepare(IPAContext &context, rkisp1_params_cfg *params)
> +{
> +	auto &filter = context.frameContext.filter;
> +
> +	/* Check if the algorithm configuration has been updated. */
> +	if (!filter.updateParams)
> +		return;
> +
> +	filter.updateParams = false;
> +
> +	static constexpr uint16_t filt_fac_sh0[] = {
> +		0x04, 0x07, 0x0a, 0x0c, 0x10, 0x14, 0x1a, 0x1e, 0x24, 0x2a, 0x30
> +	};
> +
> +	static constexpr uint16_t filt_fac_sh1[] = {
> +		0x04, 0x08, 0x0c, 0x10, 0x16, 0x1b, 0x20, 0x26, 0x2c, 0x30, 0x3f
> +	};
> +
> +	static constexpr uint16_t filt_fac_mid[] = {
> +		0x04, 0x06, 0x08, 0x0a, 0x0c, 0x10, 0x13, 0x17, 0x1d, 0x22, 0x28
> +	};
> +
> +	static constexpr uint16_t filt_fac_bl0[] = {
> +		0x02, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x10, 0x15, 0x1a, 0x24
> +	};
> +
> +	static constexpr uint16_t filt_fac_bl1[] = {
> +		0x00, 0x00, 0x00, 0x02, 0x04, 0x04, 0x06, 0x08, 0x0d, 0x14, 0x20
> +	};
> +
> +	static constexpr uint16_t filt_thresh_sh0[] = {
> +		0, 18, 26, 36, 41, 75, 90, 120, 170, 250, 1023
> +	};
> +
> +	static constexpr uint16_t filt_thresh_sh1[] = {
> +		0, 33, 44, 51, 67, 100, 120, 150, 200, 300, 1023
> +	};
> +
> +	static constexpr uint16_t filt_thresh_bl0[] = {
> +		0, 8, 13, 23, 26, 50, 60, 80, 140, 180, 1023
> +	};
> +
> +	static constexpr uint16_t filt_thresh_bl1[] = {
> +		0, 2, 5, 10, 15, 20, 26, 51, 100, 150, 1023
> +	};
> +
> +	static constexpr uint16_t stage1_select[] = {
> +		6, 6, 4, 4, 3, 3, 2, 2, 2, 1, 0
> +	};
> +
> +	static constexpr uint16_t filt_chr_v_mode[] = {
> +		1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
> +	};
> +
> +	static constexpr uint16_t filt_chr_h_mode[] = {
> +		0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
> +	};
> +
> +	uint8_t denoise = filter.denoise;
> +	uint8_t sharpness = filter.sharpness;
> +	auto &flt_config = params->others.flt_config;
> +
> +	flt_config.fac_sh0 = filt_fac_sh0[sharpness];
> +	flt_config.fac_sh1 = filt_fac_sh1[sharpness];
> +	flt_config.fac_mid = filt_fac_mid[sharpness];
> +	flt_config.fac_bl0 = filt_fac_bl0[sharpness];
> +	flt_config.fac_bl1 = filt_fac_bl1[sharpness];
> +
> +	flt_config.lum_weight = kFiltLumWeightDefault;
> +	flt_config.mode = kFiltModeDefault;
> +	flt_config.thresh_sh0 = filt_thresh_sh0[denoise];
> +	flt_config.thresh_sh1 = filt_thresh_sh1[denoise];
> +	flt_config.thresh_bl0 = filt_thresh_bl0[denoise];
> +	flt_config.thresh_bl1 = filt_thresh_bl1[denoise];
> +	flt_config.grn_stage1 = stage1_select[denoise];
> +	flt_config.chr_v_mode = filt_chr_v_mode[denoise];
> +	flt_config.chr_h_mode = filt_chr_h_mode[denoise];
> +
> +	/*
> +	 * Combined high denoising and high sharpening requires some
> +	 * adjustments to the configuration of the filters. A first stage
> +	 * filter with a lower strength must be selected, and the blur factors
> +	 * must be decreased.
> +	 */
> +	if (denoise == 9) {
> +		if (sharpness > 3)
> +			flt_config.grn_stage1 = 2;
> +	} else if (denoise == 10) {
> +		if (sharpness > 5)
> +			flt_config.grn_stage1 = 2;
> +		else if (sharpness > 3)
> +			flt_config.grn_stage1 = 1;
> +	}
> +
> +	if (denoise > 7) {
> +		if (sharpness > 7) {
> +			flt_config.fac_bl0 /= 2;
> +			flt_config.fac_bl1 /= 4;
> +		} else if (sharpness > 4) {
> +			flt_config.fac_bl0 = flt_config.fac_bl0 * 3 / 4;
> +			flt_config.fac_bl1 /= 2;
> +		}
> +	}
> +
> +	params->module_en_update |= RKISP1_CIF_ISP_MODULE_FLT;
> +	params->module_ens |= RKISP1_CIF_ISP_MODULE_FLT;
> +	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_FLT;
> +}
> +
> +REGISTER_IPA_ALGORITHM(Filter, "Filter")
> +
> +} /* namespace ipa::rkisp1::algorithms */
> +
> +} /* namespace libcamera */
> diff --git a/src/ipa/rkisp1/algorithms/filter.h b/src/ipa/rkisp1/algorithms/filter.h
> new file mode 100644
> index 000000000000..9eb170eb7da1
> --- /dev/null
> +++ b/src/ipa/rkisp1/algorithms/filter.h
> @@ -0,0 +1,30 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021-2022, Ideas On Board
> + *
> + * filter.h - RkISP1 Filter control
> + */
> +
> +#pragma once
> +
> +#include <sys/types.h>
> +
> +#include "algorithm.h"
> +
> +namespace libcamera {
> +
> +namespace ipa::rkisp1::algorithms {
> +
> +class Filter : public Algorithm
> +{
> +public:
> +	Filter() = default;
> +	~Filter() = default;
> +
> +	void queueRequest(IPAContext &context, const uint32_t frame,
> +			  const ControlList &controls) override;
> +	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
> +};
> +
> +} /* namespace ipa::rkisp1::algorithms */
> +} /* namespace libcamera */
> diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
> index 870074939d6a..dcd24fe07d23 100644
> --- a/src/ipa/rkisp1/algorithms/meson.build
> +++ b/src/ipa/rkisp1/algorithms/meson.build
> @@ -5,6 +5,7 @@ rkisp1_ipa_algorithms = files([
>      'awb.cpp',
>      'blc.cpp',
>      'dpcc.cpp',
> +    'filter.cpp',
>      'gsl.cpp',
>      'lsc.cpp',
>  ])
> diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
> index 2315ec437f77..99529481f593 100644
> --- a/src/ipa/rkisp1/data/ov5640.yaml
> +++ b/src/ipa/rkisp1/data/ov5640.yaml
> @@ -155,4 +155,5 @@ algorithms:
>            rnd-offsets:
>              green: 2
>              red-blue: 2
> +  - Filter:
>  ...
> diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> index 30bb87a967bd..4b1171865f1f 100644
> --- a/src/ipa/rkisp1/ipa_context.cpp
> +++ b/src/ipa/rkisp1/ipa_context.cpp
> @@ -136,6 +136,20 @@ namespace libcamera::ipa::rkisp1 {
>   * \brief Estimated color temperature
>   */
>
> +/**
> + * \var IPAFrameContext::filter
> + * \brief Context for the Filter algorithm
> + *
> + * \struct IPAFrameContext::filter.denoise
> + * \brief Denoising level
> + *
> + * \var IPAFrameContext::filter.sharpness
> + * \brief Sharpness level
> + *
> + * \var IPAFrameContext::filter.updateParams
> + * \brief Indicates if ISP parameters need to be updated
> + */
> +
>  /**
>   * \var IPAFrameContext::sensor
>   * \brief Effective sensor values
> diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
> index 3bfb262c8eb3..3b2f6af1276f 100644
> --- a/src/ipa/rkisp1/ipa_context.h
> +++ b/src/ipa/rkisp1/ipa_context.h
> @@ -57,6 +57,12 @@ struct IPAFrameContext {
>  		double temperatureK;
>  	} awb;
>
> +	struct {
> +		uint8_t denoise;
> +		uint8_t sharpness;
> +		bool updateParams;
> +	} filter;
> +
>  	struct {
>  		uint32_t exposure;
>  		double gain;
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 99eecd444f95..4e000d3122fb 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -968,6 +968,14 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
>  						   hasSelfPath_ ? &selfPath_ : nullptr);
>
>  	ControlInfoMap::Map ctrls;
> +	ctrls.emplace(std::piecewise_construct,
> +		      std::forward_as_tuple(&controls::Sharpness),
> +		      std::forward_as_tuple(0.0f, 10.0f, 1.0f));
> +
> +	ctrls.emplace(std::piecewise_construct,
> +		      std::forward_as_tuple(&controls::draft::NoiseReductionMode),
> +		      std::forward_as_tuple(controls::draft::NoiseReductionModeValues));
> +

Shouldn't these be registered by the IPA ?

I see below AeEnable is also registered here and it should be moved
there as well, so I guess it will have to be done on top ?

>  	ctrls.emplace(std::piecewise_construct,
>  		      std::forward_as_tuple(&controls::AeEnable),
>  		      std::forward_as_tuple(false, true));
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart July 28, 2022, 10:29 a.m. UTC | #2
Hi Jacopo,

On Thu, Jul 28, 2022 at 09:51:49AM +0200, Jacopo Mondi wrote:
> On Thu, Jul 28, 2022 at 01:21:48AM +0300, Laurent Pinchart via libcamera-devel wrote:
> > From: Florian Sylvestre <fsylvestre@baylibre.com>
> >
> > Denoise and Sharpness filters will be applied by RkISP1 during the
> > demosaicing step. The denoise filter is responsible for removing noise from
> > the image, while the sharpness filter will enhance its acutance.
> >
> > Add filter algorithm with denoise and sharpness values based on user controls.
> >
> > Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > ---
> >  src/ipa/rkisp1/algorithms/filter.cpp     | 201 +++++++++++++++++++++++
> >  src/ipa/rkisp1/algorithms/filter.h       |  30 ++++
> >  src/ipa/rkisp1/algorithms/meson.build    |   1 +
> >  src/ipa/rkisp1/data/ov5640.yaml          |   1 +
> >  src/ipa/rkisp1/ipa_context.cpp           |  14 ++
> >  src/ipa/rkisp1/ipa_context.h             |   6 +
> >  src/libcamera/pipeline/rkisp1/rkisp1.cpp |   8 +
> >  7 files changed, 261 insertions(+)
> >  create mode 100644 src/ipa/rkisp1/algorithms/filter.cpp
> >  create mode 100644 src/ipa/rkisp1/algorithms/filter.h
> >
> > diff --git a/src/ipa/rkisp1/algorithms/filter.cpp b/src/ipa/rkisp1/algorithms/filter.cpp
> > new file mode 100644
> > index 000000000000..8ca10fd1ee9d
> > --- /dev/null
> > +++ b/src/ipa/rkisp1/algorithms/filter.cpp
> > @@ -0,0 +1,201 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2021-2022, Ideas On Board
> > + *
> > + * filter.cpp - RkISP1 Filter control
> > + */
> > +
> > +#include "filter.h"
> > +
> > +#include <cmath>
> > +
> > +#include <libcamera/base/log.h>
> > +
> > +#include <libcamera/control_ids.h>
> > +
> > +/**
> > + * \file filter.h
> > + */
> > +
> > +namespace libcamera {
> > +
> > +namespace ipa::rkisp1::algorithms {
> > +
> > +/**
> > + * \class Filter
> > + * \brief RkISP1 Filter control
> > + *
> > + * Denoise and Sharpness filters will be applied by RkISP1 during the
> > + * demosaicing step. The denoise filter is responsible for removing noise from
> > + * the image, while the sharpness filter will enhance its acutance.
> > + *
> > + * \todo In current version the denoise and sharpness control is based on user
> > + * controls. In a future version it should be controlled automatically by the
> > + * algorithm.
> > + */
> > +
> > +LOG_DEFINE_CATEGORY(RkISP1Filter)
> > +
> > +static constexpr uint32_t kFiltLumWeightDefault = 0x00022040;
> > +static constexpr uint32_t kFiltModeDefault = 0x000004f2;
> > +
> > +/**
> > + * \copydoc libcamera::ipa::Algorithm::queueRequest
> > + */
> > +void Filter::queueRequest(IPAContext &context,
> > +			  [[maybe_unused]] const uint32_t frame,
> > +			  const ControlList &controls)
> > +{
> > +	auto &filter = context.frameContext.filter;
> > +
> > +	const auto &sharpness = controls.get(controls::Sharpness);
> > +	if (sharpness) {
> > +		filter.sharpness = std::round(std::clamp(*sharpness, 0.0f, 10.0f));
> > +		filter.updateParams = true;
> > +
> > +		LOG(RkISP1Filter, Debug) << "Set sharpness to " << *sharpness;
> > +	}
> > +
> > +	const auto &denoise = controls.get(controls::draft::NoiseReductionMode);
> > +	if (denoise) {
> > +		LOG(RkISP1Filter, Debug) << "Set denoise to " << *denoise;
> > +
> > +		switch (*denoise) {
> > +		case controls::draft::NoiseReductionModeOff:
> > +			filter.denoise = 0;
> > +			filter.updateParams = true;
> > +			break;
> > +		case controls::draft::NoiseReductionModeMinimal:
> > +			filter.denoise = 1;
> > +			filter.updateParams = true;
> > +			break;
> > +		case controls::draft::NoiseReductionModeHighQuality:
> > +		case controls::draft::NoiseReductionModeFast:
> > +			filter.denoise = 3;
> > +			filter.updateParams = true;
> > +			break;
> > +		default:
> > +			LOG(RkISP1Filter, Error)
> > +				<< "Unsupported denoise value "
> > +				<< *denoise;
> > +		}
> > +	}
> > +}
> > +
> > +/**
> > + * \copydoc libcamera::ipa::Algorithm::prepare
> > + */
> > +void Filter::prepare(IPAContext &context, rkisp1_params_cfg *params)
> > +{
> > +	auto &filter = context.frameContext.filter;
> > +
> > +	/* Check if the algorithm configuration has been updated. */
> > +	if (!filter.updateParams)
> > +		return;
> > +
> > +	filter.updateParams = false;
> > +
> > +	static constexpr uint16_t filt_fac_sh0[] = {
> > +		0x04, 0x07, 0x0a, 0x0c, 0x10, 0x14, 0x1a, 0x1e, 0x24, 0x2a, 0x30
> > +	};
> > +
> > +	static constexpr uint16_t filt_fac_sh1[] = {
> > +		0x04, 0x08, 0x0c, 0x10, 0x16, 0x1b, 0x20, 0x26, 0x2c, 0x30, 0x3f
> > +	};
> > +
> > +	static constexpr uint16_t filt_fac_mid[] = {
> > +		0x04, 0x06, 0x08, 0x0a, 0x0c, 0x10, 0x13, 0x17, 0x1d, 0x22, 0x28
> > +	};
> > +
> > +	static constexpr uint16_t filt_fac_bl0[] = {
> > +		0x02, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x10, 0x15, 0x1a, 0x24
> > +	};
> > +
> > +	static constexpr uint16_t filt_fac_bl1[] = {
> > +		0x00, 0x00, 0x00, 0x02, 0x04, 0x04, 0x06, 0x08, 0x0d, 0x14, 0x20
> > +	};
> > +
> > +	static constexpr uint16_t filt_thresh_sh0[] = {
> > +		0, 18, 26, 36, 41, 75, 90, 120, 170, 250, 1023
> > +	};
> > +
> > +	static constexpr uint16_t filt_thresh_sh1[] = {
> > +		0, 33, 44, 51, 67, 100, 120, 150, 200, 300, 1023
> > +	};
> > +
> > +	static constexpr uint16_t filt_thresh_bl0[] = {
> > +		0, 8, 13, 23, 26, 50, 60, 80, 140, 180, 1023
> > +	};
> > +
> > +	static constexpr uint16_t filt_thresh_bl1[] = {
> > +		0, 2, 5, 10, 15, 20, 26, 51, 100, 150, 1023
> > +	};
> > +
> > +	static constexpr uint16_t stage1_select[] = {
> > +		6, 6, 4, 4, 3, 3, 2, 2, 2, 1, 0
> > +	};
> > +
> > +	static constexpr uint16_t filt_chr_v_mode[] = {
> > +		1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
> > +	};
> > +
> > +	static constexpr uint16_t filt_chr_h_mode[] = {
> > +		0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
> > +	};
> > +
> > +	uint8_t denoise = filter.denoise;
> > +	uint8_t sharpness = filter.sharpness;
> > +	auto &flt_config = params->others.flt_config;
> > +
> > +	flt_config.fac_sh0 = filt_fac_sh0[sharpness];
> > +	flt_config.fac_sh1 = filt_fac_sh1[sharpness];
> > +	flt_config.fac_mid = filt_fac_mid[sharpness];
> > +	flt_config.fac_bl0 = filt_fac_bl0[sharpness];
> > +	flt_config.fac_bl1 = filt_fac_bl1[sharpness];
> > +
> > +	flt_config.lum_weight = kFiltLumWeightDefault;
> > +	flt_config.mode = kFiltModeDefault;
> > +	flt_config.thresh_sh0 = filt_thresh_sh0[denoise];
> > +	flt_config.thresh_sh1 = filt_thresh_sh1[denoise];
> > +	flt_config.thresh_bl0 = filt_thresh_bl0[denoise];
> > +	flt_config.thresh_bl1 = filt_thresh_bl1[denoise];
> > +	flt_config.grn_stage1 = stage1_select[denoise];
> > +	flt_config.chr_v_mode = filt_chr_v_mode[denoise];
> > +	flt_config.chr_h_mode = filt_chr_h_mode[denoise];
> > +
> > +	/*
> > +	 * Combined high denoising and high sharpening requires some
> > +	 * adjustments to the configuration of the filters. A first stage
> > +	 * filter with a lower strength must be selected, and the blur factors
> > +	 * must be decreased.
> > +	 */
> > +	if (denoise == 9) {
> > +		if (sharpness > 3)
> > +			flt_config.grn_stage1 = 2;
> > +	} else if (denoise == 10) {
> > +		if (sharpness > 5)
> > +			flt_config.grn_stage1 = 2;
> > +		else if (sharpness > 3)
> > +			flt_config.grn_stage1 = 1;
> > +	}
> > +
> > +	if (denoise > 7) {
> > +		if (sharpness > 7) {
> > +			flt_config.fac_bl0 /= 2;
> > +			flt_config.fac_bl1 /= 4;
> > +		} else if (sharpness > 4) {
> > +			flt_config.fac_bl0 = flt_config.fac_bl0 * 3 / 4;
> > +			flt_config.fac_bl1 /= 2;
> > +		}
> > +	}
> > +
> > +	params->module_en_update |= RKISP1_CIF_ISP_MODULE_FLT;
> > +	params->module_ens |= RKISP1_CIF_ISP_MODULE_FLT;
> > +	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_FLT;
> > +}
> > +
> > +REGISTER_IPA_ALGORITHM(Filter, "Filter")
> > +
> > +} /* namespace ipa::rkisp1::algorithms */
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/ipa/rkisp1/algorithms/filter.h b/src/ipa/rkisp1/algorithms/filter.h
> > new file mode 100644
> > index 000000000000..9eb170eb7da1
> > --- /dev/null
> > +++ b/src/ipa/rkisp1/algorithms/filter.h
> > @@ -0,0 +1,30 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2021-2022, Ideas On Board
> > + *
> > + * filter.h - RkISP1 Filter control
> > + */
> > +
> > +#pragma once
> > +
> > +#include <sys/types.h>
> > +
> > +#include "algorithm.h"
> > +
> > +namespace libcamera {
> > +
> > +namespace ipa::rkisp1::algorithms {
> > +
> > +class Filter : public Algorithm
> > +{
> > +public:
> > +	Filter() = default;
> > +	~Filter() = default;
> > +
> > +	void queueRequest(IPAContext &context, const uint32_t frame,
> > +			  const ControlList &controls) override;
> > +	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
> > +};
> > +
> > +} /* namespace ipa::rkisp1::algorithms */
> > +} /* namespace libcamera */
> > diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
> > index 870074939d6a..dcd24fe07d23 100644
> > --- a/src/ipa/rkisp1/algorithms/meson.build
> > +++ b/src/ipa/rkisp1/algorithms/meson.build
> > @@ -5,6 +5,7 @@ rkisp1_ipa_algorithms = files([
> >      'awb.cpp',
> >      'blc.cpp',
> >      'dpcc.cpp',
> > +    'filter.cpp',
> >      'gsl.cpp',
> >      'lsc.cpp',
> >  ])
> > diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
> > index 2315ec437f77..99529481f593 100644
> > --- a/src/ipa/rkisp1/data/ov5640.yaml
> > +++ b/src/ipa/rkisp1/data/ov5640.yaml
> > @@ -155,4 +155,5 @@ algorithms:
> >            rnd-offsets:
> >              green: 2
> >              red-blue: 2
> > +  - Filter:
> >  ...
> > diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
> > index 30bb87a967bd..4b1171865f1f 100644
> > --- a/src/ipa/rkisp1/ipa_context.cpp
> > +++ b/src/ipa/rkisp1/ipa_context.cpp
> > @@ -136,6 +136,20 @@ namespace libcamera::ipa::rkisp1 {
> >   * \brief Estimated color temperature
> >   */
> >
> > +/**
> > + * \var IPAFrameContext::filter
> > + * \brief Context for the Filter algorithm
> > + *
> > + * \struct IPAFrameContext::filter.denoise
> > + * \brief Denoising level
> > + *
> > + * \var IPAFrameContext::filter.sharpness
> > + * \brief Sharpness level
> > + *
> > + * \var IPAFrameContext::filter.updateParams
> > + * \brief Indicates if ISP parameters need to be updated
> > + */
> > +
> >  /**
> >   * \var IPAFrameContext::sensor
> >   * \brief Effective sensor values
> > diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
> > index 3bfb262c8eb3..3b2f6af1276f 100644
> > --- a/src/ipa/rkisp1/ipa_context.h
> > +++ b/src/ipa/rkisp1/ipa_context.h
> > @@ -57,6 +57,12 @@ struct IPAFrameContext {
> >  		double temperatureK;
> >  	} awb;
> >
> > +	struct {
> > +		uint8_t denoise;
> > +		uint8_t sharpness;
> > +		bool updateParams;
> > +	} filter;
> > +
> >  	struct {
> >  		uint32_t exposure;
> >  		double gain;
> > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > index 99eecd444f95..4e000d3122fb 100644
> > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > @@ -968,6 +968,14 @@ int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
> >  						   hasSelfPath_ ? &selfPath_ : nullptr);
> >
> >  	ControlInfoMap::Map ctrls;
> > +	ctrls.emplace(std::piecewise_construct,
> > +		      std::forward_as_tuple(&controls::Sharpness),
> > +		      std::forward_as_tuple(0.0f, 10.0f, 1.0f));
> > +
> > +	ctrls.emplace(std::piecewise_construct,
> > +		      std::forward_as_tuple(&controls::draft::NoiseReductionMode),
> > +		      std::forward_as_tuple(controls::draft::NoiseReductionModeValues));
> > +
> 
> Shouldn't these be registered by the IPA ?
> 
> I see below AeEnable is also registered here and it should be moved
> there as well, so I guess it will have to be done on top ?

Yes, they both should move to the IPA. I've told Florian I would handle
this on top indeed, to avoid delaying the implementation of the
algorithms. I'll get to it shortly.

> >  	ctrls.emplace(std::piecewise_construct,
> >  		      std::forward_as_tuple(&controls::AeEnable),
> >  		      std::forward_as_tuple(false, true));

Patch
diff mbox series

diff --git a/src/ipa/rkisp1/algorithms/filter.cpp b/src/ipa/rkisp1/algorithms/filter.cpp
new file mode 100644
index 000000000000..8ca10fd1ee9d
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/filter.cpp
@@ -0,0 +1,201 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021-2022, Ideas On Board
+ *
+ * filter.cpp - RkISP1 Filter control
+ */
+
+#include "filter.h"
+
+#include <cmath>
+
+#include <libcamera/base/log.h>
+
+#include <libcamera/control_ids.h>
+
+/**
+ * \file filter.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+/**
+ * \class Filter
+ * \brief RkISP1 Filter control
+ *
+ * Denoise and Sharpness filters will be applied by RkISP1 during the
+ * demosaicing step. The denoise filter is responsible for removing noise from
+ * the image, while the sharpness filter will enhance its acutance.
+ *
+ * \todo In current version the denoise and sharpness control is based on user
+ * controls. In a future version it should be controlled automatically by the
+ * algorithm.
+ */
+
+LOG_DEFINE_CATEGORY(RkISP1Filter)
+
+static constexpr uint32_t kFiltLumWeightDefault = 0x00022040;
+static constexpr uint32_t kFiltModeDefault = 0x000004f2;
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::queueRequest
+ */
+void Filter::queueRequest(IPAContext &context,
+			  [[maybe_unused]] const uint32_t frame,
+			  const ControlList &controls)
+{
+	auto &filter = context.frameContext.filter;
+
+	const auto &sharpness = controls.get(controls::Sharpness);
+	if (sharpness) {
+		filter.sharpness = std::round(std::clamp(*sharpness, 0.0f, 10.0f));
+		filter.updateParams = true;
+
+		LOG(RkISP1Filter, Debug) << "Set sharpness to " << *sharpness;
+	}
+
+	const auto &denoise = controls.get(controls::draft::NoiseReductionMode);
+	if (denoise) {
+		LOG(RkISP1Filter, Debug) << "Set denoise to " << *denoise;
+
+		switch (*denoise) {
+		case controls::draft::NoiseReductionModeOff:
+			filter.denoise = 0;
+			filter.updateParams = true;
+			break;
+		case controls::draft::NoiseReductionModeMinimal:
+			filter.denoise = 1;
+			filter.updateParams = true;
+			break;
+		case controls::draft::NoiseReductionModeHighQuality:
+		case controls::draft::NoiseReductionModeFast:
+			filter.denoise = 3;
+			filter.updateParams = true;
+			break;
+		default:
+			LOG(RkISP1Filter, Error)
+				<< "Unsupported denoise value "
+				<< *denoise;
+		}
+	}
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void Filter::prepare(IPAContext &context, rkisp1_params_cfg *params)
+{
+	auto &filter = context.frameContext.filter;
+
+	/* Check if the algorithm configuration has been updated. */
+	if (!filter.updateParams)
+		return;
+
+	filter.updateParams = false;
+
+	static constexpr uint16_t filt_fac_sh0[] = {
+		0x04, 0x07, 0x0a, 0x0c, 0x10, 0x14, 0x1a, 0x1e, 0x24, 0x2a, 0x30
+	};
+
+	static constexpr uint16_t filt_fac_sh1[] = {
+		0x04, 0x08, 0x0c, 0x10, 0x16, 0x1b, 0x20, 0x26, 0x2c, 0x30, 0x3f
+	};
+
+	static constexpr uint16_t filt_fac_mid[] = {
+		0x04, 0x06, 0x08, 0x0a, 0x0c, 0x10, 0x13, 0x17, 0x1d, 0x22, 0x28
+	};
+
+	static constexpr uint16_t filt_fac_bl0[] = {
+		0x02, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x10, 0x15, 0x1a, 0x24
+	};
+
+	static constexpr uint16_t filt_fac_bl1[] = {
+		0x00, 0x00, 0x00, 0x02, 0x04, 0x04, 0x06, 0x08, 0x0d, 0x14, 0x20
+	};
+
+	static constexpr uint16_t filt_thresh_sh0[] = {
+		0, 18, 26, 36, 41, 75, 90, 120, 170, 250, 1023
+	};
+
+	static constexpr uint16_t filt_thresh_sh1[] = {
+		0, 33, 44, 51, 67, 100, 120, 150, 200, 300, 1023
+	};
+
+	static constexpr uint16_t filt_thresh_bl0[] = {
+		0, 8, 13, 23, 26, 50, 60, 80, 140, 180, 1023
+	};
+
+	static constexpr uint16_t filt_thresh_bl1[] = {
+		0, 2, 5, 10, 15, 20, 26, 51, 100, 150, 1023
+	};
+
+	static constexpr uint16_t stage1_select[] = {
+		6, 6, 4, 4, 3, 3, 2, 2, 2, 1, 0
+	};
+
+	static constexpr uint16_t filt_chr_v_mode[] = {
+		1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
+	};
+
+	static constexpr uint16_t filt_chr_h_mode[] = {
+		0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
+	};
+
+	uint8_t denoise = filter.denoise;
+	uint8_t sharpness = filter.sharpness;
+	auto &flt_config = params->others.flt_config;
+
+	flt_config.fac_sh0 = filt_fac_sh0[sharpness];
+	flt_config.fac_sh1 = filt_fac_sh1[sharpness];
+	flt_config.fac_mid = filt_fac_mid[sharpness];
+	flt_config.fac_bl0 = filt_fac_bl0[sharpness];
+	flt_config.fac_bl1 = filt_fac_bl1[sharpness];
+
+	flt_config.lum_weight = kFiltLumWeightDefault;
+	flt_config.mode = kFiltModeDefault;
+	flt_config.thresh_sh0 = filt_thresh_sh0[denoise];
+	flt_config.thresh_sh1 = filt_thresh_sh1[denoise];
+	flt_config.thresh_bl0 = filt_thresh_bl0[denoise];
+	flt_config.thresh_bl1 = filt_thresh_bl1[denoise];
+	flt_config.grn_stage1 = stage1_select[denoise];
+	flt_config.chr_v_mode = filt_chr_v_mode[denoise];
+	flt_config.chr_h_mode = filt_chr_h_mode[denoise];
+
+	/*
+	 * Combined high denoising and high sharpening requires some
+	 * adjustments to the configuration of the filters. A first stage
+	 * filter with a lower strength must be selected, and the blur factors
+	 * must be decreased.
+	 */
+	if (denoise == 9) {
+		if (sharpness > 3)
+			flt_config.grn_stage1 = 2;
+	} else if (denoise == 10) {
+		if (sharpness > 5)
+			flt_config.grn_stage1 = 2;
+		else if (sharpness > 3)
+			flt_config.grn_stage1 = 1;
+	}
+
+	if (denoise > 7) {
+		if (sharpness > 7) {
+			flt_config.fac_bl0 /= 2;
+			flt_config.fac_bl1 /= 4;
+		} else if (sharpness > 4) {
+			flt_config.fac_bl0 = flt_config.fac_bl0 * 3 / 4;
+			flt_config.fac_bl1 /= 2;
+		}
+	}
+
+	params->module_en_update |= RKISP1_CIF_ISP_MODULE_FLT;
+	params->module_ens |= RKISP1_CIF_ISP_MODULE_FLT;
+	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_FLT;
+}
+
+REGISTER_IPA_ALGORITHM(Filter, "Filter")
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/filter.h b/src/ipa/rkisp1/algorithms/filter.h
new file mode 100644
index 000000000000..9eb170eb7da1
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/filter.h
@@ -0,0 +1,30 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021-2022, Ideas On Board
+ *
+ * filter.h - RkISP1 Filter control
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+class Filter : public Algorithm
+{
+public:
+	Filter() = default;
+	~Filter() = default;
+
+	void queueRequest(IPAContext &context, const uint32_t frame,
+			  const ControlList &controls) override;
+	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
+};
+
+} /* namespace ipa::rkisp1::algorithms */
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
index 870074939d6a..dcd24fe07d23 100644
--- a/src/ipa/rkisp1/algorithms/meson.build
+++ b/src/ipa/rkisp1/algorithms/meson.build
@@ -5,6 +5,7 @@  rkisp1_ipa_algorithms = files([
     'awb.cpp',
     'blc.cpp',
     'dpcc.cpp',
+    'filter.cpp',
     'gsl.cpp',
     'lsc.cpp',
 ])
diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
index 2315ec437f77..99529481f593 100644
--- a/src/ipa/rkisp1/data/ov5640.yaml
+++ b/src/ipa/rkisp1/data/ov5640.yaml
@@ -155,4 +155,5 @@  algorithms:
           rnd-offsets:
             green: 2
             red-blue: 2
+  - Filter:
 ...
diff --git a/src/ipa/rkisp1/ipa_context.cpp b/src/ipa/rkisp1/ipa_context.cpp
index 30bb87a967bd..4b1171865f1f 100644
--- a/src/ipa/rkisp1/ipa_context.cpp
+++ b/src/ipa/rkisp1/ipa_context.cpp
@@ -136,6 +136,20 @@  namespace libcamera::ipa::rkisp1 {
  * \brief Estimated color temperature
  */
 
+/**
+ * \var IPAFrameContext::filter
+ * \brief Context for the Filter algorithm
+ *
+ * \struct IPAFrameContext::filter.denoise
+ * \brief Denoising level
+ *
+ * \var IPAFrameContext::filter.sharpness
+ * \brief Sharpness level
+ *
+ * \var IPAFrameContext::filter.updateParams
+ * \brief Indicates if ISP parameters need to be updated
+ */
+
 /**
  * \var IPAFrameContext::sensor
  * \brief Effective sensor values
diff --git a/src/ipa/rkisp1/ipa_context.h b/src/ipa/rkisp1/ipa_context.h
index 3bfb262c8eb3..3b2f6af1276f 100644
--- a/src/ipa/rkisp1/ipa_context.h
+++ b/src/ipa/rkisp1/ipa_context.h
@@ -57,6 +57,12 @@  struct IPAFrameContext {
 		double temperatureK;
 	} awb;
 
+	struct {
+		uint8_t denoise;
+		uint8_t sharpness;
+		bool updateParams;
+	} filter;
+
 	struct {
 		uint32_t exposure;
 		double gain;
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 99eecd444f95..4e000d3122fb 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -968,6 +968,14 @@  int PipelineHandlerRkISP1::createCamera(MediaEntity *sensor)
 						   hasSelfPath_ ? &selfPath_ : nullptr);
 
 	ControlInfoMap::Map ctrls;
+	ctrls.emplace(std::piecewise_construct,
+		      std::forward_as_tuple(&controls::Sharpness),
+		      std::forward_as_tuple(0.0f, 10.0f, 1.0f));
+
+	ctrls.emplace(std::piecewise_construct,
+		      std::forward_as_tuple(&controls::draft::NoiseReductionMode),
+		      std::forward_as_tuple(controls::draft::NoiseReductionModeValues));
+
 	ctrls.emplace(std::piecewise_construct,
 		      std::forward_as_tuple(&controls::AeEnable),
 		      std::forward_as_tuple(false, true));