[libcamera-devel] [PATCH 4/5] qcam: dng_writer: Populate DNG tags from metadata

Niklas Söderlund niklas.soderlund at ragnatech.se
Sun May 3 03:18:56 CEST 2020


Hi Laurent,

Thanks for your work.

On 2020-05-03 01:57:03 +0300, Laurent Pinchart wrote:
> Populate the DNG black level, ISO speed rating and exposure time from
> metadata. The ISO speed rating and exposure time are standardized as
> EXIF tags, not TIFF tags, and require a separate EXIF IFD.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

Reviewed-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>

> ---
>  src/qcam/dng_writer.cpp | 71 +++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 68 insertions(+), 3 deletions(-)
> 
> diff --git a/src/qcam/dng_writer.cpp b/src/qcam/dng_writer.cpp
> index e55985ba86e7..d04a8e161218 100644
> --- a/src/qcam/dng_writer.cpp
> +++ b/src/qcam/dng_writer.cpp
> @@ -7,11 +7,14 @@
>  
>  #include "dng_writer.h"
>  
> +#include <algorithm>
>  #include <iostream>
>  #include <map>
>  
>  #include <tiffio.h>
>  
> +#include <libcamera/control_ids.h>
> +
>  using namespace libcamera;
>  
>  enum CFAPatternColour : uint8_t {
> @@ -153,6 +156,7 @@ int DNGWriter::write(const char *filename, const Camera *camera,
>  	uint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8];
>  
>  	toff_t rawIFDOffset = 0;
> +	toff_t exifIFDOffset = 0;
>  
>  	/*
>  	 * Start with a thumbnail in IFD 0 for compatibility with TIFF baseline
> @@ -185,10 +189,12 @@ int DNGWriter::write(const char *filename, const Camera *camera,
>  	TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
>  
>  	/*
> -	 * Reserve space for the SubIFD tag, pointing to the IFD for the raw
> -	 * image. The real offset will be set later.
> +	 * Reserve space for the SubIFD and ExifIFD tags, pointing to the IFD
> +	 * for the raw image and EXIF data respectively. The real offsets will
> +	 * be set later.
>  	 */
>  	TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset);
> +	TIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset);
>  
>  	/* Write the thumbnail. */
>  	const uint8_t *row = static_cast<const uint8_t *>(data);
> @@ -230,7 +236,47 @@ int DNGWriter::write(const char *filename, const Camera *camera,
>  	TIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, cfaPlaneColor);
>  	TIFFSetField(tif, TIFFTAG_CFALAYOUT, 1);
>  
> -	/* \todo Add more EXIF fields to output. */
> +	const uint16_t blackLevelRepeatDim[] = { 2, 2 };
> +	float blackLevel[] = { 0.0f, 0.0f, 0.0f, 0.0f };
> +	uint32_t whiteLevel = (1 << info->bitsPerSample) - 1;
> +
> +	if (metadata.contains(controls::SensorBlackLevels)) {
> +		Span<const int32_t> levels = metadata.get(controls::SensorBlackLevels);
> +
> +		/*
> +		 * The black levels control is specified in R, Gr, Gb, B order.
> +		 * Map it to the TIFF tag that is specified in CFA pattern
> +		 * order.
> +		 */
> +		unsigned int green = (info->pattern[0] == CFAPatternRed ||
> +				      info->pattern[1] == CFAPatternRed)
> +				   ? 0 : 1;
> +
> +		for (unsigned int i = 0; i < 4; ++i) {
> +			unsigned int level;
> +
> +			switch (info->pattern[i]) {
> +			case CFAPatternRed:
> +				level = levels[0];
> +				break;
> +			case CFAPatternGreen:
> +				level = levels[green + 1];
> +				green = (green + 1) % 2;
> +				break;
> +			case CFAPatternBlue:
> +			default:
> +				level = levels[3];
> +				break;
> +			}
> +
> +			/* Map the 16-bit value to the bits per sample range. */
> +			blackLevel[i] = level >> (16 - info->bitsPerSample);
> +		}
> +	}
> +
> +	TIFFSetField(tif, TIFFTAG_BLACKLEVELREPEATDIM, &blackLevelRepeatDim);
> +	TIFFSetField(tif, TIFFTAG_BLACKLEVEL, 4, &blackLevel);
> +	TIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &whiteLevel);
>  
>  	/* Write RAW content. */
>  	row = static_cast<const uint8_t *>(data);
> @@ -252,9 +298,28 @@ int DNGWriter::write(const char *filename, const Camera *camera,
>  	rawIFDOffset = TIFFCurrentDirOffset(tif);
>  	TIFFWriteDirectory(tif);
>  
> +	/* Create a new IFD for the EXIF data and fill it. */
> +	TIFFCreateEXIFDirectory(tif);
> +
> +	if (metadata.contains(controls::AnalogueGain)) {
> +		float gain = metadata.get(controls::AnalogueGain);
> +		uint16_t iso = std::min(std::max(gain * 100, 0.0f), 65535.0f);
> +		TIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, &iso);
> +	}
> +
> +	if (metadata.contains(controls::ExposureTime)) {
> +		float exposureTime = metadata.get(controls::ExposureTime) / 1e6;
> +		TIFFSetField(tif, EXIFTAG_EXPOSURETIME, exposureTime);
> +	}
> +
> +	TIFFCheckpointDirectory(tif);
> +	exifIFDOffset = TIFFCurrentDirOffset(tif);
> +	TIFFWriteDirectory(tif);
> +
>  	/* Update the IFD offsets and close the file. */
>  	TIFFSetDirectory(tif, 0);
>  	TIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset);
> +	TIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset);
>  	TIFFWriteDirectory(tif);
>  
>  	TIFFClose(tif);
> -- 
> Regards,
> 
> Laurent Pinchart
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

-- 
Regards,
Niklas Söderlund


More information about the libcamera-devel mailing list