summaryrefslogtreecommitdiff
path: root/src/qcam
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart@ideasonboard.com>2020-05-02 21:45:56 +0300
committerLaurent Pinchart <laurent.pinchart@ideasonboard.com>2020-05-03 04:32:49 +0300
commit319fc87ef1432f3da8f9cb52a0d2e00d0626e69b (patch)
tree2c98951772d9a5f90a5eade409ca5612490a97e9 /src/qcam
parentd1561e3c36ead5591f65e2eba8ce3bf5ce584fca (diff)
qcam: dng_writer: Populate DNG tags from metadata
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@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
Diffstat (limited to 'src/qcam')
-rw-r--r--src/qcam/dng_writer.cpp71
1 files changed, 68 insertions, 3 deletions
diff --git a/src/qcam/dng_writer.cpp b/src/qcam/dng_writer.cpp
index e55985ba..d04a8e16 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);