[libcamera-devel] [PATCH 3/3] utils: raspberrypi: ctt: dng_load_image: Work with DNG files from Picamera2

Laurent Pinchart laurent.pinchart at ideasonboard.com
Thu Jul 7 09:57:01 CEST 2022


Hi David,

On Thu, Jul 07, 2022 at 08:35:39AM +0100, David Plowman wrote:
> On Wed, 6 Jul 2022 at 19:32, Laurent Pinchart wrote:
> > On Wed, Jul 06, 2022 at 11:18:36AM +0100, David Plowman via libcamera-devel wrote:
> > > From: William Vinnicombe <william.vinnicombe at raspberrypi.com>
> > >
> > > The exif tags are different between raw files from libcamera-apps and
> > > from Picamera2, causing issues loading data.
> > >
> > > 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 at raspberrypi.com>
> > > ---
> > >  utils/raspberrypi/ctt/ctt_image_load.py | 26 ++++++++++++++++++-------
> > >  1 file changed, 19 insertions(+), 7 deletions(-)
> > >
> > > diff --git a/utils/raspberrypi/ctt/ctt_image_load.py b/utils/raspberrypi/ctt/ctt_image_load.py
> > > index 934db123..29c17581 100644
> > > --- a/utils/raspberrypi/ctt/ctt_image_load.py
> > > +++ b/utils/raspberrypi/ctt/ctt_image_load.py
> > > @@ -301,17 +301,29 @@ def dng_load_image(Cam, im_str):
> > >          metadata.read()
> > >
> > >          Img.ver = 100  # random value
> > > -        Img.w = metadata['Exif.SubImage1.ImageWidth'].value
> > > +        """
> > > +        libcamera-apps create a separate Exif.Subimage1 for the picture
> > > +        picamera2 stores everything under Exif.Image
> >
> > Is this valid according to the DNG specification ? Or is it that
> > picamera2 produces TIFF/EP files instead of DNG ? Is there a reason not
> > to use DNG in all cases ?
> 
> Actually DNG and EXIF are all really flavours of TIFF. I would say

Note that I mentioned TIFF/EP, which is also a different flavour of
TIFF. Those formats are an utter mess :-)

> that Picamera2, which uses the PiDNG library, does it properly. In C++
> we use libtiff which, as far as I can tell, doesn't really support
> "modern" flavours of DNG-style TIFF. For example, you can't include
> exposure times in the main image which PiDNG can.

Where is the "modern" flavour of "DNG-style TIFF" documented ? I'm
looking at the DNG v1.4.0.0. specification, which states, in the subIFDs
trees section,

    DNG recommends the use of SubIFD trees, as described in the TIFF-EP
    specification. SubIFD chains are not supported.

    The highest-resolution and quality IFD should use NewSubFileType
    equal to 0. Reduced resolution (or quality) thumbnails or previews,
    if any, should use NewSubFileType equal to 1 (for a primary preview)
    or 10001.H (for an alternate preview).

    DNG recommends, but does not require, that the first IFD contain a
    low-resolution thumbnail, as described in the TIFF-EP specification.

and in the metadata section,

    Additional metadata may be embedded in DNG in the following ways:

    • Using TIFF-EP or EXIF metadata tags
    • Using the IPTC metadata tag (33723)
    • Using the XMP metadata tag (700)

    Note that TIFF-EP and EXIF use nearly the same metadata tag set, but
    TIFF-EP stores the tags in IFD 0, while EXIF store the tags in a
    separate IFD. Either location is allowed by DNG, but the EXIF
    location is preferred.

While the DNG specification seems to allow storing the EXIF tags in
IFD0, as well as storing the main image there as well, that isn't
recommended. I'm thus curious to know what you mean by "modern
flavours".

> But they are all DNGs, and DarkTable, RawTherapee and dcraw support
> all of them. Just there are some annoying implementation details...
> 
> > > +        this code detects which one is 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 +331,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]
> > >

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list