[libcamera-devel,3/5] ipa: rkisp1: Add support of Lens Shading Correction control
diff mbox series

Message ID 20220622151918.451635-4-fsylvestre@baylibre.com
State Accepted
Headers show
Series
  • Add GSL, LSC and DPCC tuning support for rkisp1
Related show

Commit Message

Florian Sylvestre June 22, 2022, 3:19 p.m. UTC
The Lens Shading Correction algorithm applies a correction on the pixels based
on values defined in the YAML tuning file.

Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>
---
 src/ipa/rkisp1/algorithms/lsc.cpp     | 171 ++++++++++++++++++++++++++
 src/ipa/rkisp1/algorithms/lsc.h       |  44 +++++++
 src/ipa/rkisp1/algorithms/meson.build |   1 +
 src/ipa/rkisp1/data/ov5640.yaml       |  81 ++++++++++++
 src/ipa/rkisp1/rkisp1.cpp             |   1 +
 5 files changed, 298 insertions(+)
 create mode 100644 src/ipa/rkisp1/algorithms/lsc.cpp
 create mode 100644 src/ipa/rkisp1/algorithms/lsc.h

Comments

Nicolas Dufresne via libcamera-devel July 12, 2022, 11:12 a.m. UTC | #1
Hi Florian,

On Wed, Jun 22, 2022 at 05:19:16PM +0200, Florian Sylvestre via libcamera-devel wrote:
> The Lens Shading Correction algorithm applies a correction on the pixels based
> on values defined in the YAML tuning file.
> 
> Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>

Same comments as the patch on GSL.


Paul

> ---
>  src/ipa/rkisp1/algorithms/lsc.cpp     | 171 ++++++++++++++++++++++++++
>  src/ipa/rkisp1/algorithms/lsc.h       |  44 +++++++
>  src/ipa/rkisp1/algorithms/meson.build |   1 +
>  src/ipa/rkisp1/data/ov5640.yaml       |  81 ++++++++++++
>  src/ipa/rkisp1/rkisp1.cpp             |   1 +
>  5 files changed, 298 insertions(+)
>  create mode 100644 src/ipa/rkisp1/algorithms/lsc.cpp
>  create mode 100644 src/ipa/rkisp1/algorithms/lsc.h
> 
> diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
> new file mode 100644
> index 00000000..f68243a1
> --- /dev/null
> +++ b/src/ipa/rkisp1/algorithms/lsc.cpp
> @@ -0,0 +1,171 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021-2022, Ideas On Board
> + *
> + * lsc.cpp - RkISP1 Lens Shading Correction control
> + */
> +
> +#include "lsc.h"
> +
> +#include <libcamera/base/log.h>
> +
> +#include "libcamera/internal/yaml_parser.h"
> +#include "linux/rkisp1-config.h"
> +
> +/**
> + * \file lsc.h
> + */
> +
> +namespace libcamera {
> +
> +namespace ipa::rkisp1::algorithms {
> +
> +/**
> + * \class LensShadingCorrection
> + * \brief RkISP1 Lens Shading Correction control
> + *
> + * Due to the optical characteristics of the lens, the light intensity received
> + * by the sensor is not uniform.
> + *
> + * The Lens Shading Correction algorithm applies a correction on the pixels for
> + * each component based on measurement done during the camera tuning process.
> + */
> +
> +LOG_DEFINE_CATEGORY(RkISP1Lsc)
> +
> +LensShadingCorrection::LensShadingCorrection()
> +	: tuningParameters_(false)
> +{
> +}
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::init
> + */
> +int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,
> +				const YamlObject &tuningData)
> +{
> +	xSizeTbl_ = tuningData["x-size"].getList<uint16_t>();
> +	if (xSizeTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'x-size'"
> +			<< "in tuning file (list size:"
> +			<< xSizeTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> +		return -EINVAL;
> +	}
> +
> +	ySizeTbl_ = tuningData["y-size"].getList<uint16_t>();
> +	if (ySizeTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'y-size'"
> +			<< "in tuning file (list size:"
> +			<< ySizeTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> +		return -EINVAL;
> +	}
> +
> +	xGradTbl_ = tuningData["x-grad"].getList<uint16_t>();
> +	if (xGradTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'x-grad'"
> +			<< "in tuning file (list size:"
> +			<< xGradTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> +		return -EINVAL;
> +	}
> +
> +	yGradTbl_ = tuningData["y-grad"].getList<uint16_t>();
> +	if (yGradTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'y-grad'"
> +			<< "in tuning file (list size:"
> +			<< yGradTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> +		return -EINVAL;
> +	}
> +
> +	rDataTbl_ = tuningData["r"].getList<uint16_t>();
> +	if (rDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'r'"
> +			<< "in tuning file (list size:"
> +			<< rDataTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> +		return -EINVAL;
> +	}
> +
> +	grDataTbl_ = tuningData["gr"].getList<uint16_t>();
> +	if (grDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'gr'"
> +			<< "in tuning file (list size:"
> +			<< grDataTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> +		return -EINVAL;
> +	}
> +
> +	gbDataTbl_ = tuningData["gb"].getList<uint16_t>();
> +	if (gbDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'gb'"
> +			<< "in tuning file (list size:"
> +			<< gbDataTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> +		return -EINVAL;
> +	}
> +
> +	bDataTbl_ = tuningData["b"].getList<uint16_t>();
> +	if (bDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> +		LOG(RkISP1Lsc, Error)
> +			<< "Issue while parsing 'b'"
> +			<< "in tuning file (list size:"
> +			<< bDataTbl_.size() << "/"
> +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> +		return -EINVAL;
> +	}
> +
> +	tuningParameters_ = true;
> +
> +	return 0;
> +}
> +
> +/**
> + * \copydoc libcamera::ipa::Algorithm::prepare
> + */
> +void LensShadingCorrection::prepare(IPAContext &context,
> +				    rkisp1_params_cfg *params)
> +{
> +	if (context.frameContext.frameCount > 0)
> +		return;
> +
> +	if (!tuningParameters_)
> +		return;
> +
> +	std::copy(xSizeTbl_.begin(), xSizeTbl_.end(),
> +		  params->others.lsc_config.x_size_tbl);
> +	std::copy(ySizeTbl_.begin(), ySizeTbl_.end(),
> +		  params->others.lsc_config.y_size_tbl);
> +	std::copy(xGradTbl_.begin(), xGradTbl_.end(),
> +		  params->others.lsc_config.x_grad_tbl);
> +	std::copy(yGradTbl_.begin(), yGradTbl_.end(),
> +		  params->others.lsc_config.y_grad_tbl);
> +
> +	std::copy(rDataTbl_.begin(), rDataTbl_.end(),
> +		  &params->others.lsc_config.r_data_tbl[0][0]);
> +	std::copy(grDataTbl_.begin(), grDataTbl_.end(),
> +		  &params->others.lsc_config.gr_data_tbl[0][0]);
> +	std::copy(gbDataTbl_.begin(), gbDataTbl_.end(),
> +		  &params->others.lsc_config.gb_data_tbl[0][0]);
> +	std::copy(bDataTbl_.begin(), bDataTbl_.end(),
> +		  &params->others.lsc_config.b_data_tbl[0][0]);
> +
> +	params->module_en_update |= RKISP1_CIF_ISP_MODULE_LSC;
> +	params->module_ens |= RKISP1_CIF_ISP_MODULE_LSC;
> +	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_LSC;
> +}
> +
> +REGISTER_IPA_ALGORITHM(LensShadingCorrection)
> +
> +} /* namespace ipa::rkisp1::algorithms */
> +
> +} /* namespace libcamera */
> diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h
> new file mode 100644
> index 00000000..7f620ffa
> --- /dev/null
> +++ b/src/ipa/rkisp1/algorithms/lsc.h
> @@ -0,0 +1,44 @@
> +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> +/*
> + * Copyright (C) 2021-2022, Ideas On Board
> + *
> + * gsl.h - RkISP1 Lens Shading Correction control
> + */
> +
> +#pragma once
> +
> +#include <linux/rkisp1-config.h>
> +
> +#include "algorithm.h"
> +
> +namespace libcamera {
> +
> +struct IPACameraSensorInfo;
> +
> +namespace ipa::rkisp1::algorithms {
> +
> +class LensShadingCorrection : public Algorithm
> +{
> +public:
> +	LensShadingCorrection();
> +	~LensShadingCorrection() = default;
> +
> +	int init(IPAContext &context, const YamlObject &tuningData) override;
> +	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
> +
> +private:
> +	bool tuningParameters_;
> +
> +	std::vector<uint16_t> rDataTbl_;
> +	std::vector<uint16_t> grDataTbl_;
> +	std::vector<uint16_t> gbDataTbl_;
> +	std::vector<uint16_t> bDataTbl_;
> +
> +	std::vector<uint16_t> xGradTbl_;
> +	std::vector<uint16_t> yGradTbl_;
> +	std::vector<uint16_t> xSizeTbl_;
> +	std::vector<uint16_t> ySizeTbl_;
> +};
> +
> +} /* namespace ipa::rkisp1::algorithms */
> +} /* namespace libcamera */
> diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
> index 0597c353..64e11dce 100644
> --- a/src/ipa/rkisp1/algorithms/meson.build
> +++ b/src/ipa/rkisp1/algorithms/meson.build
> @@ -5,4 +5,5 @@ rkisp1_ipa_algorithms = files([
>      'awb.cpp',
>      'blc.cpp',
>      'gsl.cpp',
> +    'lsc.cpp',
>  ])
> diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
> index 6cb84ed9..154ed3b5 100644
> --- a/src/ipa/rkisp1/data/ov5640.yaml
> +++ b/src/ipa/rkisp1/data/ov5640.yaml
> @@ -16,4 +16,85 @@ algorithms:
>          red:   [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
>          green: [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
>          blue:  [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
> +  - LensShadingCorrection:
> +      x-size: [ 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096 ]
> +      y-size: [ 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096 ]
> +      x-grad: [ 0, 0, 0, 0, 0, 0, 0, 0 ]
> +      y-grad: [ 0, 0, 0, 0, 0, 0, 0, 0 ]
> +      r:  [
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +          ]
> +      gr: [
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +          ]
> +      gb: [
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +          ]
> +      b:  [
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> +          ]
>  ...
> diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
> index 622a0d61..996edc0a 100644
> --- a/src/ipa/rkisp1/rkisp1.cpp
> +++ b/src/ipa/rkisp1/rkisp1.cpp
> @@ -32,6 +32,7 @@
>  #include "algorithms/awb.h"
>  #include "algorithms/blc.h"
>  #include "algorithms/gsl.h"
> +#include "algorithms/lsc.h"
>  #include "libipa/camera_sensor_helper.h"
>  
>  #include "ipa_context.h"
> -- 
> 2.34.1
>
Laurent Pinchart July 13, 2022, 12:27 a.m. UTC | #2
Hello,

On Tue, Jul 12, 2022 at 08:12:50PM +0900, Paul Elder via libcamera-devel wrote:
> On Wed, Jun 22, 2022 at 05:19:16PM +0200, Florian Sylvestre via libcamera-devel wrote:
> > The Lens Shading Correction algorithm applies a correction on the pixels based
> > on values defined in the YAML tuning file.
> > 
> > Signed-off-by: Florian Sylvestre <fsylvestre@baylibre.com>
> 
> Same comments as the patch on GSL.

Ditto, plus a few comments below.

> > ---
> >  src/ipa/rkisp1/algorithms/lsc.cpp     | 171 ++++++++++++++++++++++++++
> >  src/ipa/rkisp1/algorithms/lsc.h       |  44 +++++++
> >  src/ipa/rkisp1/algorithms/meson.build |   1 +
> >  src/ipa/rkisp1/data/ov5640.yaml       |  81 ++++++++++++
> >  src/ipa/rkisp1/rkisp1.cpp             |   1 +
> >  5 files changed, 298 insertions(+)
> >  create mode 100644 src/ipa/rkisp1/algorithms/lsc.cpp
> >  create mode 100644 src/ipa/rkisp1/algorithms/lsc.h
> > 
> > diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
> > new file mode 100644
> > index 00000000..f68243a1
> > --- /dev/null
> > +++ b/src/ipa/rkisp1/algorithms/lsc.cpp
> > @@ -0,0 +1,171 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2021-2022, Ideas On Board
> > + *
> > + * lsc.cpp - RkISP1 Lens Shading Correction control
> > + */
> > +
> > +#include "lsc.h"
> > +
> > +#include <libcamera/base/log.h>
> > +
> > +#include "libcamera/internal/yaml_parser.h"
> > +#include "linux/rkisp1-config.h"
> > +
> > +/**
> > + * \file lsc.h
> > + */
> > +
> > +namespace libcamera {
> > +
> > +namespace ipa::rkisp1::algorithms {
> > +
> > +/**
> > + * \class LensShadingCorrection
> > + * \brief RkISP1 Lens Shading Correction control
> > + *
> > + * Due to the optical characteristics of the lens, the light intensity received
> > + * by the sensor is not uniform.
> > + *
> > + * The Lens Shading Correction algorithm applies a correction on the pixels for
> > + * each component based on measurement done during the camera tuning process.
> > + */
> > +
> > +LOG_DEFINE_CATEGORY(RkISP1Lsc)
> > +
> > +LensShadingCorrection::LensShadingCorrection()
> > +	: tuningParameters_(false)
> > +{
> > +}
> > +
> > +/**
> > + * \copydoc libcamera::ipa::Algorithm::init
> > + */
> > +int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,
> > +				const YamlObject &tuningData)
> > +{
> > +	xSizeTbl_ = tuningData["x-size"].getList<uint16_t>();
> > +	if (xSizeTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'x-size'"
> > +			<< "in tuning file (list size:"
> > +			<< xSizeTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> > +		return -EINVAL;
> > +	}
> > +
> > +	ySizeTbl_ = tuningData["y-size"].getList<uint16_t>();
> > +	if (ySizeTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'y-size'"
> > +			<< "in tuning file (list size:"
> > +			<< ySizeTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> > +		return -EINVAL;
> > +	}
> > +
> > +	xGradTbl_ = tuningData["x-grad"].getList<uint16_t>();
> > +	if (xGradTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'x-grad'"
> > +			<< "in tuning file (list size:"
> > +			<< xGradTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> > +		return -EINVAL;
> > +	}

The gradients are directly derived from the sector sizes and should thus
be computed in the LensShadingCorrection::prepare() function instead of
loaded from the tuning data. Please see below

> > +
> > +	yGradTbl_ = tuningData["y-grad"].getList<uint16_t>();
> > +	if (yGradTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'y-grad'"
> > +			<< "in tuning file (list size:"
> > +			<< yGradTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
> > +		return -EINVAL;
> > +	}
> > +
> > +	rDataTbl_ = tuningData["r"].getList<uint16_t>();
> > +	if (rDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'r'"
> > +			<< "in tuning file (list size:"
> > +			<< rDataTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> > +		return -EINVAL;
> > +	}
> > +
> > +	grDataTbl_ = tuningData["gr"].getList<uint16_t>();
> > +	if (grDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'gr'"
> > +			<< "in tuning file (list size:"
> > +			<< grDataTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> > +		return -EINVAL;
> > +	}
> > +
> > +	gbDataTbl_ = tuningData["gb"].getList<uint16_t>();
> > +	if (gbDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'gb'"
> > +			<< "in tuning file (list size:"
> > +			<< gbDataTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> > +		return -EINVAL;
> > +	}
> > +
> > +	bDataTbl_ = tuningData["b"].getList<uint16_t>();
> > +	if (bDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
> > +		LOG(RkISP1Lsc, Error)
> > +			<< "Issue while parsing 'b'"
> > +			<< "in tuning file (list size:"
> > +			<< bDataTbl_.size() << "/"
> > +			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
> > +		return -EINVAL;
> > +	}
> > +
> > +	tuningParameters_ = true;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * \copydoc libcamera::ipa::Algorithm::prepare
> > + */
> > +void LensShadingCorrection::prepare(IPAContext &context,
> > +				    rkisp1_params_cfg *params)
> > +{
> > +	if (context.frameContext.frameCount > 0)
> > +		return;
> > +
> > +	if (!tuningParameters_)
> > +		return;
> > +
> > +	std::copy(xSizeTbl_.begin(), xSizeTbl_.end(),
> > +		  params->others.lsc_config.x_size_tbl);
> > +	std::copy(ySizeTbl_.begin(), ySizeTbl_.end(),
> > +		  params->others.lsc_config.y_size_tbl);
> > +	std::copy(xGradTbl_.begin(), xGradTbl_.end(),
> > +		  params->others.lsc_config.x_grad_tbl);
> > +	std::copy(yGradTbl_.begin(), yGradTbl_.end(),
> > +		  params->others.lsc_config.y_grad_tbl);

The gradient value must be equal to

	grad = round(32768 / size)

in both the X and Y directions.

> > +
> > +	std::copy(rDataTbl_.begin(), rDataTbl_.end(),
> > +		  &params->others.lsc_config.r_data_tbl[0][0]);
> > +	std::copy(grDataTbl_.begin(), grDataTbl_.end(),
> > +		  &params->others.lsc_config.gr_data_tbl[0][0]);
> > +	std::copy(gbDataTbl_.begin(), gbDataTbl_.end(),
> > +		  &params->others.lsc_config.gb_data_tbl[0][0]);
> > +	std::copy(bDataTbl_.begin(), bDataTbl_.end(),
> > +		  &params->others.lsc_config.b_data_tbl[0][0]);
> > +
> > +	params->module_en_update |= RKISP1_CIF_ISP_MODULE_LSC;
> > +	params->module_ens |= RKISP1_CIF_ISP_MODULE_LSC;
> > +	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_LSC;
> > +}
> > +
> > +REGISTER_IPA_ALGORITHM(LensShadingCorrection)
> > +
> > +} /* namespace ipa::rkisp1::algorithms */
> > +
> > +} /* namespace libcamera */
> > diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h
> > new file mode 100644
> > index 00000000..7f620ffa
> > --- /dev/null
> > +++ b/src/ipa/rkisp1/algorithms/lsc.h
> > @@ -0,0 +1,44 @@
> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */
> > +/*
> > + * Copyright (C) 2021-2022, Ideas On Board
> > + *
> > + * gsl.h - RkISP1 Lens Shading Correction control
> > + */
> > +
> > +#pragma once
> > +
> > +#include <linux/rkisp1-config.h>
> > +
> > +#include "algorithm.h"
> > +
> > +namespace libcamera {
> > +
> > +struct IPACameraSensorInfo;
> > +
> > +namespace ipa::rkisp1::algorithms {
> > +
> > +class LensShadingCorrection : public Algorithm
> > +{
> > +public:
> > +	LensShadingCorrection();
> > +	~LensShadingCorrection() = default;
> > +
> > +	int init(IPAContext &context, const YamlObject &tuningData) override;
> > +	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
> > +
> > +private:
> > +	bool tuningParameters_;
> > +
> > +	std::vector<uint16_t> rDataTbl_;
> > +	std::vector<uint16_t> grDataTbl_;
> > +	std::vector<uint16_t> gbDataTbl_;
> > +	std::vector<uint16_t> bDataTbl_;
> > +
> > +	std::vector<uint16_t> xGradTbl_;
> > +	std::vector<uint16_t> yGradTbl_;
> > +	std::vector<uint16_t> xSizeTbl_;
> > +	std::vector<uint16_t> ySizeTbl_;

I would drop all the Tbl_ suffixes.

> > +};
> > +
> > +} /* namespace ipa::rkisp1::algorithms */
> > +} /* namespace libcamera */
> > diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
> > index 0597c353..64e11dce 100644
> > --- a/src/ipa/rkisp1/algorithms/meson.build
> > +++ b/src/ipa/rkisp1/algorithms/meson.build
> > @@ -5,4 +5,5 @@ rkisp1_ipa_algorithms = files([
> >      'awb.cpp',
> >      'blc.cpp',
> >      'gsl.cpp',
> > +    'lsc.cpp',
> >  ])
> > diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
> > index 6cb84ed9..154ed3b5 100644
> > --- a/src/ipa/rkisp1/data/ov5640.yaml
> > +++ b/src/ipa/rkisp1/data/ov5640.yaml
> > @@ -16,4 +16,85 @@ algorithms:
> >          red:   [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
> >          green: [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
> >          blue:  [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
> > +  - LensShadingCorrection:
> > +      x-size: [ 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096 ]
> > +      y-size: [ 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096 ]

I'm afraid this won't work. The sizes express how the image is divided
in a grid of 16x16 blocks (as the grid mus be symmetrical around the
frame centre, they're specified as 8 values instead of 16). The values
must thus be computed dynamically based on the frame size, with the sum
of all the sizes horizontally and vertically being exactly equal to half
of the image width and height respectively.

You could store relative sizes in the configuration file, for instance
as floating point numbers, with the constraint that the sum must be
equal to 0.5 and then multiply these values by the image width when
configuring the ISP. Alternatively, you can also use fixed-by values.

For this configuration file, I would start with giving all the cells an
identical size.

> > +      x-grad: [ 0, 0, 0, 0, 0, 0, 0, 0 ]
> > +      y-grad: [ 0, 0, 0, 0, 0, 0, 0, 0 ]
> > +      r:  [
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

The values surprise me, as they're expressed on 12 bits with 2 bits of
integral part and 10 bits of fractional part, with valid values in the
range [1, 3.999]. I would thus expect all values here to be set to 1024
(we could also store floating-point values, but that would increase the
processing time during initialization, I don't think it would be
useful).

> > +          ]
> > +      gr: [
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +          ]
> > +      gb: [
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +          ]
> > +      b:  [
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
> > +          ]
> >  ...
> > diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
> > index 622a0d61..996edc0a 100644
> > --- a/src/ipa/rkisp1/rkisp1.cpp
> > +++ b/src/ipa/rkisp1/rkisp1.cpp
> > @@ -32,6 +32,7 @@
> >  #include "algorithms/awb.h"
> >  #include "algorithms/blc.h"
> >  #include "algorithms/gsl.h"
> > +#include "algorithms/lsc.h"
> >  #include "libipa/camera_sensor_helper.h"
> >  
> >  #include "ipa_context.h"

Patch
diff mbox series

diff --git a/src/ipa/rkisp1/algorithms/lsc.cpp b/src/ipa/rkisp1/algorithms/lsc.cpp
new file mode 100644
index 00000000..f68243a1
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/lsc.cpp
@@ -0,0 +1,171 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021-2022, Ideas On Board
+ *
+ * lsc.cpp - RkISP1 Lens Shading Correction control
+ */
+
+#include "lsc.h"
+
+#include <libcamera/base/log.h>
+
+#include "libcamera/internal/yaml_parser.h"
+#include "linux/rkisp1-config.h"
+
+/**
+ * \file lsc.h
+ */
+
+namespace libcamera {
+
+namespace ipa::rkisp1::algorithms {
+
+/**
+ * \class LensShadingCorrection
+ * \brief RkISP1 Lens Shading Correction control
+ *
+ * Due to the optical characteristics of the lens, the light intensity received
+ * by the sensor is not uniform.
+ *
+ * The Lens Shading Correction algorithm applies a correction on the pixels for
+ * each component based on measurement done during the camera tuning process.
+ */
+
+LOG_DEFINE_CATEGORY(RkISP1Lsc)
+
+LensShadingCorrection::LensShadingCorrection()
+	: tuningParameters_(false)
+{
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::init
+ */
+int LensShadingCorrection::init([[maybe_unused]] IPAContext &context,
+				const YamlObject &tuningData)
+{
+	xSizeTbl_ = tuningData["x-size"].getList<uint16_t>();
+	if (xSizeTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'x-size'"
+			<< "in tuning file (list size:"
+			<< xSizeTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
+		return -EINVAL;
+	}
+
+	ySizeTbl_ = tuningData["y-size"].getList<uint16_t>();
+	if (ySizeTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'y-size'"
+			<< "in tuning file (list size:"
+			<< ySizeTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
+		return -EINVAL;
+	}
+
+	xGradTbl_ = tuningData["x-grad"].getList<uint16_t>();
+	if (xGradTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'x-grad'"
+			<< "in tuning file (list size:"
+			<< xGradTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
+		return -EINVAL;
+	}
+
+	yGradTbl_ = tuningData["y-grad"].getList<uint16_t>();
+	if (yGradTbl_.size() != RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'y-grad'"
+			<< "in tuning file (list size:"
+			<< yGradTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SECTORS_TBL_SIZE << ")";
+		return -EINVAL;
+	}
+
+	rDataTbl_ = tuningData["r"].getList<uint16_t>();
+	if (rDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'r'"
+			<< "in tuning file (list size:"
+			<< rDataTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
+		return -EINVAL;
+	}
+
+	grDataTbl_ = tuningData["gr"].getList<uint16_t>();
+	if (grDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'gr'"
+			<< "in tuning file (list size:"
+			<< grDataTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
+		return -EINVAL;
+	}
+
+	gbDataTbl_ = tuningData["gb"].getList<uint16_t>();
+	if (gbDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'gb'"
+			<< "in tuning file (list size:"
+			<< gbDataTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
+		return -EINVAL;
+	}
+
+	bDataTbl_ = tuningData["b"].getList<uint16_t>();
+	if (bDataTbl_.size() != RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX) {
+		LOG(RkISP1Lsc, Error)
+			<< "Issue while parsing 'b'"
+			<< "in tuning file (list size:"
+			<< bDataTbl_.size() << "/"
+			<< RKISP1_CIF_ISP_LSC_SAMPLES_MAX * RKISP1_CIF_ISP_LSC_SAMPLES_MAX << ")";
+		return -EINVAL;
+	}
+
+	tuningParameters_ = true;
+
+	return 0;
+}
+
+/**
+ * \copydoc libcamera::ipa::Algorithm::prepare
+ */
+void LensShadingCorrection::prepare(IPAContext &context,
+				    rkisp1_params_cfg *params)
+{
+	if (context.frameContext.frameCount > 0)
+		return;
+
+	if (!tuningParameters_)
+		return;
+
+	std::copy(xSizeTbl_.begin(), xSizeTbl_.end(),
+		  params->others.lsc_config.x_size_tbl);
+	std::copy(ySizeTbl_.begin(), ySizeTbl_.end(),
+		  params->others.lsc_config.y_size_tbl);
+	std::copy(xGradTbl_.begin(), xGradTbl_.end(),
+		  params->others.lsc_config.x_grad_tbl);
+	std::copy(yGradTbl_.begin(), yGradTbl_.end(),
+		  params->others.lsc_config.y_grad_tbl);
+
+	std::copy(rDataTbl_.begin(), rDataTbl_.end(),
+		  &params->others.lsc_config.r_data_tbl[0][0]);
+	std::copy(grDataTbl_.begin(), grDataTbl_.end(),
+		  &params->others.lsc_config.gr_data_tbl[0][0]);
+	std::copy(gbDataTbl_.begin(), gbDataTbl_.end(),
+		  &params->others.lsc_config.gb_data_tbl[0][0]);
+	std::copy(bDataTbl_.begin(), bDataTbl_.end(),
+		  &params->others.lsc_config.b_data_tbl[0][0]);
+
+	params->module_en_update |= RKISP1_CIF_ISP_MODULE_LSC;
+	params->module_ens |= RKISP1_CIF_ISP_MODULE_LSC;
+	params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_LSC;
+}
+
+REGISTER_IPA_ALGORITHM(LensShadingCorrection)
+
+} /* namespace ipa::rkisp1::algorithms */
+
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/lsc.h b/src/ipa/rkisp1/algorithms/lsc.h
new file mode 100644
index 00000000..7f620ffa
--- /dev/null
+++ b/src/ipa/rkisp1/algorithms/lsc.h
@@ -0,0 +1,44 @@ 
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Copyright (C) 2021-2022, Ideas On Board
+ *
+ * gsl.h - RkISP1 Lens Shading Correction control
+ */
+
+#pragma once
+
+#include <linux/rkisp1-config.h>
+
+#include "algorithm.h"
+
+namespace libcamera {
+
+struct IPACameraSensorInfo;
+
+namespace ipa::rkisp1::algorithms {
+
+class LensShadingCorrection : public Algorithm
+{
+public:
+	LensShadingCorrection();
+	~LensShadingCorrection() = default;
+
+	int init(IPAContext &context, const YamlObject &tuningData) override;
+	void prepare(IPAContext &context, rkisp1_params_cfg *params) override;
+
+private:
+	bool tuningParameters_;
+
+	std::vector<uint16_t> rDataTbl_;
+	std::vector<uint16_t> grDataTbl_;
+	std::vector<uint16_t> gbDataTbl_;
+	std::vector<uint16_t> bDataTbl_;
+
+	std::vector<uint16_t> xGradTbl_;
+	std::vector<uint16_t> yGradTbl_;
+	std::vector<uint16_t> xSizeTbl_;
+	std::vector<uint16_t> ySizeTbl_;
+};
+
+} /* namespace ipa::rkisp1::algorithms */
+} /* namespace libcamera */
diff --git a/src/ipa/rkisp1/algorithms/meson.build b/src/ipa/rkisp1/algorithms/meson.build
index 0597c353..64e11dce 100644
--- a/src/ipa/rkisp1/algorithms/meson.build
+++ b/src/ipa/rkisp1/algorithms/meson.build
@@ -5,4 +5,5 @@  rkisp1_ipa_algorithms = files([
     'awb.cpp',
     'blc.cpp',
     'gsl.cpp',
+    'lsc.cpp',
 ])
diff --git a/src/ipa/rkisp1/data/ov5640.yaml b/src/ipa/rkisp1/data/ov5640.yaml
index 6cb84ed9..154ed3b5 100644
--- a/src/ipa/rkisp1/data/ov5640.yaml
+++ b/src/ipa/rkisp1/data/ov5640.yaml
@@ -16,4 +16,85 @@  algorithms:
         red:   [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
         green: [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
         blue:  [ 0, 256, 512, 768, 1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096 ]
+  - LensShadingCorrection:
+      x-size: [ 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096 ]
+      y-size: [ 512, 1024, 1536, 2048, 2560, 3072, 3584, 4096 ]
+      x-grad: [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+      y-grad: [ 0, 0, 0, 0, 0, 0, 0, 0 ]
+      r:  [
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+          ]
+      gr: [
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+          ]
+      gb: [
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+          ]
+      b:  [
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+          ]
 ...
diff --git a/src/ipa/rkisp1/rkisp1.cpp b/src/ipa/rkisp1/rkisp1.cpp
index 622a0d61..996edc0a 100644
--- a/src/ipa/rkisp1/rkisp1.cpp
+++ b/src/ipa/rkisp1/rkisp1.cpp
@@ -32,6 +32,7 @@ 
 #include "algorithms/awb.h"
 #include "algorithms/blc.h"
 #include "algorithms/gsl.h"
+#include "algorithms/lsc.h"
 #include "libipa/camera_sensor_helper.h"
 
 #include "ipa_context.h"