[RFC PATCH 6/6] libcamera: converter_dw100: Load and apply dewarp vertex maps

Jacopo Mondi jacopo.mondi at ideasonboard.com
Wed Jul 24 15:46:48 CEST 2024


Hi Umang

On Fri, Jul 12, 2024 at 10:59:20AM GMT, Umang Jain wrote:
> Load the dewarp vertex maps for different configurations using the
> LIBCAMERA_DEWARP_CONFIG_FILE environment variable.

The env variable needs to be documented

>
> In addition, provide a applyMappings(stream) API for converter_dw100, in
> order to apply mappings for the given stream. Plumb it into rkisp1
> pipeline handler, if the dewarper is being used.
>
> \todo The parsing of dewarp configuration file is yet to be determined.

What are you unsure about here ? The configuration file format ?

> I've used the same parsing logic as made in previous attempts:
> https://patchwork.libcamera.org/patch/17348/
>
> Signed-off-by: Umang Jain <umang.jain at ideasonboard.com>
> ---
>  .../internal/converter/converter_dw100.h      |   5 +
>  src/libcamera/converter/converter_dw100.cpp   | 127 ++++++++++++++++++
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |   4 +
>  3 files changed, 136 insertions(+)
>
> diff --git a/include/libcamera/internal/converter/converter_dw100.h b/include/libcamera/internal/converter/converter_dw100.h
> index dc41f365..a3062e84 100644
> --- a/include/libcamera/internal/converter/converter_dw100.h
> +++ b/include/libcamera/internal/converter/converter_dw100.h
> @@ -19,6 +19,11 @@ class ConverterDW100 : public V4L2M2MConverter
>  {
>  public:
>  	ConverterDW100(std::shared_ptr<MediaDevice> media);
> +
> +	int applyMappings(const Stream *stream);

Doesn't this 'override' the one declared in the V4L2M2MConverter base
class ?

> +
> +private:
> +	int loadDewarpMaps();
>  };
>
>  } /* namespace libcamera */
> diff --git a/src/libcamera/converter/converter_dw100.cpp b/src/libcamera/converter/converter_dw100.cpp
> index 3061fc71..1a641779 100644
> --- a/src/libcamera/converter/converter_dw100.cpp
> +++ b/src/libcamera/converter/converter_dw100.cpp
> @@ -7,12 +7,17 @@
>
>  #include "libcamera/internal/converter/converter_dw100.h"
>
> +#include <linux/dw100.h>
> +
> +#include <libcamera/base/file.h>
>  #include <libcamera/base/log.h>
>
> +#include <libcamera/stream.h>
>  #include <libcamera/geometry.h>
>
>  #include "libcamera/internal/media_device.h"
>  #include "libcamera/internal/v4l2_videodevice.h"
> +#include "libcamera/internal/yaml_parser.h"
>
>  namespace libcamera {
>
> @@ -32,6 +37,128 @@ LOG_DECLARE_CATEGORY(Converter)
>  ConverterDW100::ConverterDW100(std::shared_ptr<MediaDevice> media)
>  	: V4L2M2MConverter(media.get(), Feature::Crop)
>  {
> +	loadDewarpMaps();
> +}
> +
> +int ConverterDW100::loadDewarpMaps()

Return value is useless if only called at construction time

> +{
> +	int ret;
> +
> +	char const *configFromEnv = utils::secure_getenv("LIBCAMERA_DEWARP_CONFIG_FILE");
> +	if (!configFromEnv || *configFromEnv == '\0')
> +		return 0;

Is the dewarper usable without a config file ?

Should we have an example of a mapping file somewhere or it is not
needed ?

> +
> +	LOG(Converter, Info) << "Parsing dewarp configuration file " << configFromEnv;
> +
> +	std::string filename = std::string(configFromEnv);
> +	File file(filename);
> +
> +	if (!file.open(File::OpenModeFlag::ReadOnly)) {
> +		ret = file.error();
> +		LOG(Converter, Error) << "Failed to open configuration file "
> +				      << filename << ": " << strerror(-ret);
> +		return ret;
> +	}
> +
> +	std::unique_ptr<libcamera::YamlObject> data = YamlParser::parse(file);
> +	if (!data)
> +		return -EINVAL;
> +
> +	if (!data->contains("mappings")) {
> +		LOG(Converter, Error) << "Vertex mapping key missing";
> +		return -EINVAL;
> +	}
> +
> +	const YamlObject &mappings = (*data)["mappings"];
> +	if (!mappings.isList() || mappings.size() == 0) {
> +		LOG(Converter, Error) << "Invalid mappings entry";
> +		return -EINVAL;
> +	}
> +
> +	LOG(Converter, Debug) << "Parsing " << mappings.size() << " mappings";
> +	mappings_.clear();
> +	mappings_.reserve(mappings.size());
> +
> +	for (std::size_t i = 0; i < mappings.size(); i++) {

Does
        for (const YamlObject &mapping : mappings)

work ?

> +		const YamlObject &mapping = mappings[i];
> +		if (!mapping.isDictionary()) {
> +			LOG(Converter, Error) << "Mapping is not a dictionary";
> +			return -EINVAL;
> +		}
> +
> +		if (!mapping.contains("input-resolution")) {
> +			LOG(Converter, Error) << "Input resolution missing";
> +			return -EINVAL;
> +		}
> +
> +		if (!mapping.contains("output-resolution")) {
> +			LOG(Converter, Error) << "Output resolution missing";
> +			return -EINVAL;
> +		}
> +
> +		if (!mapping.contains("mapping")) {
> +			LOG(Converter, Error) << "Mapping table missing";
> +			return -EINVAL;
> +		}
> +
> +		const YamlObject &input_res = mapping["input-resolution"];
> +		if (!input_res.isList() || input_res.size() != 2) {
> +			LOG(Converter, Error) << "Incorrect input resolution";
> +			return -EINVAL;
> +		}
> +
> +		const YamlObject &output_res = mapping["output-resolution"];
> +		if (!output_res.isList() || output_res.size() != 2) {
> +			LOG(Converter, Error) << "Incorrect output resolution";
> +			return -EINVAL;
> +		}
> +
> +		const YamlObject &map = mapping["mapping"];
> +		if (!map.isList() || map.size() == 0) {
> +			LOG(Converter, Error) << "Incorrect mapping entries";
> +			return -EINVAL;
> +		}
> +
> +		Size input(input_res[0].get<uint32_t>(0), input_res[1].get<uint32_t>(0));
> +		Size output(output_res[0].get<uint32_t>(0), output_res[1].get<uint32_t>(0));
> +		const auto &mapVector = map.getList<uint32_t>().value_or(std::vector<uint32_t>{});
> +
> +		LOG(Converter, Debug)
> +			<< "Input/Output mapping resolution " << input << " -> " << output;
> +
> +		mappings_.emplace_back(Mapping(input, output, mapVector));
> +	}
> +
> +	return mappings.size();
> +}
> +
> +/*
> + * \brief Apply
> + * \todo this is just a wrapper, trying to test waters
> + * \param[in] media The media device implementing the converter
> + */
> +int ConverterDW100::applyMappings(const Stream *stream)
> +{
> +	const StreamConfiguration &config = stream->configuration();
> +	ControlList ctrls;
> +	int ret = 0;
> +
> +	for (const auto &map : mappings_) {
> +		/* Currently DW100's input and output configuration are same. */
> +		if (map.inputSize() == config.size &&
> +		    map.outputSize() == config.size) {
> +			auto value = Span<const int32_t>(reinterpret_cast<const int32_t *>(map.mapping()), map.size());
> +
> +			ControlValue c(value);
> +			ctrls.set(V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP, c);
> +			ret = applyControls(stream, ctrls);
> +
> +			LOG(Converter, Debug) << "Dewarp mapping applied for " << config.toString();
> +			break;
> +		}
> +	}
> +
> +       return ret;
>  }
>
>  } /* namespace libcamera */
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 881e10e1..f102b364 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -1019,6 +1019,10 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
>  				LOG(RkISP1, Error) << "Failed to start dewarper";
>  				return ret;
>  			}
> +
> +			ret = dewarper_->applyMappings(&data->mainPathStream_);
> +			if (ret)
> +				LOG(RkISP1, Warning) << "Dewarper mapping couldn't be applied";
>  		}
>  	}
>
> --
> 2.45.0
>


More information about the libcamera-devel mailing list