utils: raspberrypi: ctt: dng_load_image: Work with DNG files from Picamera2

The DNG specification is based on the TIFF file format and recommends
storing the raw image data in a SubIFD and the Exif tags in an Exif IFD.
Other options are allowed, even if not recommended, such as storing both
the raw image data and the Exif data in IFD0, as done by the TIFF/EP
specification.

libcamera-apps use pyexiv2 to produce DNG files, following the DNG
recommendation, while applications based on picamera2 use PiDNG, which
adopts the TIFF/EP structure. Why it does so is not currently clear (see
https://github.com/schoolpost/PiDNG/issues/65 for discussions on this
topic), but as files based on the DNG and TIFF/EP variants exist in the
wild, both need to be supported by ctt.

Add code to identify which tags are being used, and then load the
metadata from the correct tags.

Signed-off-by: William Vinnicombe <william.vinnicombe@raspberrypi.com>
Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
William Vinnicombe 2022-08-03 09:25:39 +01:00 committed by Laurent Pinchart
parent 79b4c1c51e
commit 23b6965a93

View file

@ -301,17 +301,35 @@ def dng_load_image(Cam, im_str):
metadata.read()
Img.ver = 100 # random value
"""
The DNG and TIFF/EP specifications use different IFDs to store the raw
image data and the Exif tags. DNG stores them in a SubIFD and in an Exif
IFD respectively (named "SubImage1" and "Photo" by pyexiv2), while
TIFF/EP stores them both in IFD0 (name "Image"). Both are used in "DNG"
files, with libcamera-apps following the DNG recommendation and
applications based on picamera2 following TIFF/EP.
This code detects which tags are being used, and therefore extracts the
correct values.
"""
try:
Img.w = metadata['Exif.SubImage1.ImageWidth'].value
subimage = "SubImage1"
photo = "Photo"
except KeyError:
Img.w = metadata['Exif.Image.ImageWidth'].value
subimage = "Image"
photo = "Image"
Img.pad = 0
Img.h = metadata['Exif.SubImage1.ImageLength'].value
white = metadata['Exif.SubImage1.WhiteLevel'].value
Img.h = metadata[f'Exif.{subimage}.ImageLength'].value
white = metadata[f'Exif.{subimage}.WhiteLevel'].value
Img.sigbits = int(white).bit_length()
Img.fmt = (Img.sigbits - 4) // 2
Img.exposure = int(metadata['Exif.Photo.ExposureTime'].value*1000000)
Img.againQ8 = metadata['Exif.Photo.ISOSpeedRatings'].value*256/100
Img.exposure = int(metadata[f'Exif.{photo}.ExposureTime'].value * 1000000)
Img.againQ8 = metadata[f'Exif.{photo}.ISOSpeedRatings'].value * 256 / 100
Img.againQ8_norm = Img.againQ8 / 256
Img.camName = metadata['Exif.Image.Model'].value
Img.blacklevel = int(metadata['Exif.SubImage1.BlackLevel'].value[0])
Img.blacklevel = int(metadata[f'Exif.{subimage}.BlackLevel'].value[0])
Img.blacklevel_16 = Img.blacklevel << (16 - Img.sigbits)
bayer_case = {
'0 1 1 2': (0, (0, 1, 2, 3)),
@ -319,7 +337,7 @@ def dng_load_image(Cam, im_str):
'2 1 1 0': (2, (3, 2, 1, 0)),
'1 0 2 1': (3, (1, 0, 3, 2))
}
cfa_pattern = metadata['Exif.SubImage1.CFAPattern'].value
cfa_pattern = metadata[f'Exif.{subimage}.CFAPattern'].value
Img.pattern = bayer_case[cfa_pattern][0]
Img.order = bayer_case[cfa_pattern][1]