[{"id":4728,"web_url":"https://patchwork.libcamera.org/comment/4728/","msgid":"<20200503011856.GD3012491@oden.dyn.berto.se>","date":"2020-05-03T01:18:56","subject":"Re: [libcamera-devel] [PATCH 4/5] qcam: dng_writer: Populate DNG\n\ttags from metadata","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nThanks for your work.\n\nOn 2020-05-03 01:57:03 +0300, Laurent Pinchart wrote:\n> Populate the DNG black level, ISO speed rating and exposure time from\n> metadata. The ISO speed rating and exposure time are standardized as\n> EXIF tags, not TIFF tags, and require a separate EXIF IFD.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nReviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\n> ---\n>  src/qcam/dng_writer.cpp | 71 +++++++++++++++++++++++++++++++++++++++--\n>  1 file changed, 68 insertions(+), 3 deletions(-)\n> \n> diff --git a/src/qcam/dng_writer.cpp b/src/qcam/dng_writer.cpp\n> index e55985ba86e7..d04a8e161218 100644\n> --- a/src/qcam/dng_writer.cpp\n> +++ b/src/qcam/dng_writer.cpp\n> @@ -7,11 +7,14 @@\n>  \n>  #include \"dng_writer.h\"\n>  \n> +#include <algorithm>\n>  #include <iostream>\n>  #include <map>\n>  \n>  #include <tiffio.h>\n>  \n> +#include <libcamera/control_ids.h>\n> +\n>  using namespace libcamera;\n>  \n>  enum CFAPatternColour : uint8_t {\n> @@ -153,6 +156,7 @@ int DNGWriter::write(const char *filename, const Camera *camera,\n>  \tuint8_t scanline[(config.size.width * info->bitsPerSample + 7) / 8];\n>  \n>  \ttoff_t rawIFDOffset = 0;\n> +\ttoff_t exifIFDOffset = 0;\n>  \n>  \t/*\n>  \t * Start with a thumbnail in IFD 0 for compatibility with TIFF baseline\n> @@ -185,10 +189,12 @@ int DNGWriter::write(const char *filename, const Camera *camera,\n>  \tTIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);\n>  \n>  \t/*\n> -\t * Reserve space for the SubIFD tag, pointing to the IFD for the raw\n> -\t * image. The real offset will be set later.\n> +\t * Reserve space for the SubIFD and ExifIFD tags, pointing to the IFD\n> +\t * for the raw image and EXIF data respectively. The real offsets will\n> +\t * be set later.\n>  \t */\n>  \tTIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset);\n> +\tTIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset);\n>  \n>  \t/* Write the thumbnail. */\n>  \tconst uint8_t *row = static_cast<const uint8_t *>(data);\n> @@ -230,7 +236,47 @@ int DNGWriter::write(const char *filename, const Camera *camera,\n>  \tTIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, cfaPlaneColor);\n>  \tTIFFSetField(tif, TIFFTAG_CFALAYOUT, 1);\n>  \n> -\t/* \\todo Add more EXIF fields to output. */\n> +\tconst uint16_t blackLevelRepeatDim[] = { 2, 2 };\n> +\tfloat blackLevel[] = { 0.0f, 0.0f, 0.0f, 0.0f };\n> +\tuint32_t whiteLevel = (1 << info->bitsPerSample) - 1;\n> +\n> +\tif (metadata.contains(controls::SensorBlackLevels)) {\n> +\t\tSpan<const int32_t> levels = metadata.get(controls::SensorBlackLevels);\n> +\n> +\t\t/*\n> +\t\t * The black levels control is specified in R, Gr, Gb, B order.\n> +\t\t * Map it to the TIFF tag that is specified in CFA pattern\n> +\t\t * order.\n> +\t\t */\n> +\t\tunsigned int green = (info->pattern[0] == CFAPatternRed ||\n> +\t\t\t\t      info->pattern[1] == CFAPatternRed)\n> +\t\t\t\t   ? 0 : 1;\n> +\n> +\t\tfor (unsigned int i = 0; i < 4; ++i) {\n> +\t\t\tunsigned int level;\n> +\n> +\t\t\tswitch (info->pattern[i]) {\n> +\t\t\tcase CFAPatternRed:\n> +\t\t\t\tlevel = levels[0];\n> +\t\t\t\tbreak;\n> +\t\t\tcase CFAPatternGreen:\n> +\t\t\t\tlevel = levels[green + 1];\n> +\t\t\t\tgreen = (green + 1) % 2;\n> +\t\t\t\tbreak;\n> +\t\t\tcase CFAPatternBlue:\n> +\t\t\tdefault:\n> +\t\t\t\tlevel = levels[3];\n> +\t\t\t\tbreak;\n> +\t\t\t}\n> +\n> +\t\t\t/* Map the 16-bit value to the bits per sample range. */\n> +\t\t\tblackLevel[i] = level >> (16 - info->bitsPerSample);\n> +\t\t}\n> +\t}\n> +\n> +\tTIFFSetField(tif, TIFFTAG_BLACKLEVELREPEATDIM, &blackLevelRepeatDim);\n> +\tTIFFSetField(tif, TIFFTAG_BLACKLEVEL, 4, &blackLevel);\n> +\tTIFFSetField(tif, TIFFTAG_WHITELEVEL, 1, &whiteLevel);\n>  \n>  \t/* Write RAW content. */\n>  \trow = static_cast<const uint8_t *>(data);\n> @@ -252,9 +298,28 @@ int DNGWriter::write(const char *filename, const Camera *camera,\n>  \trawIFDOffset = TIFFCurrentDirOffset(tif);\n>  \tTIFFWriteDirectory(tif);\n>  \n> +\t/* Create a new IFD for the EXIF data and fill it. */\n> +\tTIFFCreateEXIFDirectory(tif);\n> +\n> +\tif (metadata.contains(controls::AnalogueGain)) {\n> +\t\tfloat gain = metadata.get(controls::AnalogueGain);\n> +\t\tuint16_t iso = std::min(std::max(gain * 100, 0.0f), 65535.0f);\n> +\t\tTIFFSetField(tif, EXIFTAG_ISOSPEEDRATINGS, 1, &iso);\n> +\t}\n> +\n> +\tif (metadata.contains(controls::ExposureTime)) {\n> +\t\tfloat exposureTime = metadata.get(controls::ExposureTime) / 1e6;\n> +\t\tTIFFSetField(tif, EXIFTAG_EXPOSURETIME, exposureTime);\n> +\t}\n> +\n> +\tTIFFCheckpointDirectory(tif);\n> +\texifIFDOffset = TIFFCurrentDirOffset(tif);\n> +\tTIFFWriteDirectory(tif);\n> +\n>  \t/* Update the IFD offsets and close the file. */\n>  \tTIFFSetDirectory(tif, 0);\n>  \tTIFFSetField(tif, TIFFTAG_SUBIFD, 1, &rawIFDOffset);\n> +\tTIFFSetField(tif, TIFFTAG_EXIFIFD, exifIFDOffset);\n>  \tTIFFWriteDirectory(tif);\n>  \n>  \tTIFFClose(tif);\n> -- \n> Regards,\n> \n> Laurent Pinchart\n> \n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-lf1-x141.google.com (mail-lf1-x141.google.com\n\t[IPv6:2a00:1450:4864:20::141])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id ADD3C603F0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSun,  3 May 2020 03:18:57 +0200 (CEST)","by mail-lf1-x141.google.com with SMTP id a4so311973lfh.12\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 02 May 2020 18:18:57 -0700 (PDT)","from localhost (h-209-203.A463.priv.bahnhof.se. [155.4.209.203])\n\tby smtp.gmail.com with ESMTPSA id\n\tz16sm5467546lfq.18.2020.05.02.18.18.56\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSat, 02 May 2020 18:18:56 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=ragnatech-se.20150623.gappssmtp.com\n\theader.i=@ragnatech-se.20150623.gappssmtp.com header.b=\"IcQ8pYEg\"; \n\tdkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to;\n\tbh=AWK2HtoTsEnmCd+uhOEb6l4VjcX2VebHV8ds3qjN2TE=;\n\tb=IcQ8pYEgI95FC0Y6WisuWCZUTj/2aA2bZLhm8Z2tO6VR6v/dTK+luAxO0SbDa756lK\n\tKFiVOD7PNwhbWGpqk35lpOdmrKAIskQCg+jt8fov11VJszflXw6gN7kAe3bex7z/Oh+8\n\theClJGSD0VL4owHI/9XGWAYd3DuLF0U8GDvswCgid9PhwyYpKoZzhxoM1i51Wgnd5wb/\n\tLEp3h9BJwbvj7USersCebYiSTB3CKiR49YDhAvre+BL+I2f7qI5DONBcSzqFifalxhfF\n\twTNxSao1UXz+VOPbdztogtZWbLZ0hH/soPw20quw8qo/0+4gXaQWfiMuorePA8MqAX5D\n\tyHRw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to;\n\tbh=AWK2HtoTsEnmCd+uhOEb6l4VjcX2VebHV8ds3qjN2TE=;\n\tb=qyPyH/dEgMDN8u4uaWWZfbtZ0sE4A55TrHoHNhNt5RDAYPuMIQimY9FZMPoB+S2UjB\n\tLEvNraz3U3MKvCsNwW5pXsQ/k9why0tgtTC4Awj56PpDMh3NZIBFU54uSiZ7JMXvz2xA\n\tvmDraFmLGgJUQDsxFlmWOgU74hoJ5b3DxX1Vnxqa42Q5LWo2BJETup5AvtFTOwjTQlok\n\tYI6uE6ofDJ2l764JZkuqNHBftl41lXA54pmSAC3lp1lOmEHlPY0Ey+T3xqmH6AlquDr7\n\tqCjuTEZ2u0RYBojoYzD41ZKdn/CR0bKJwQ+wzEyOeqh7QgqAhs9b230/8D/YUlXUQnL+\n\tuU/A==","X-Gm-Message-State":"AGi0PubedGlSiATK0wZb4SY1+p8Xo29QkG2xNYIkiLADqmDYMZHjYN3P\n\tmFRvgLHk+q9uLhNLH7au7D35Lg==","X-Google-Smtp-Source":"APiQypKxReyWEAydCN/kpiulhtM6ePVrzpfTRsh1LeDFLOWi02+6bg2qGK2uxsELivu4qp0OxS+tUg==","X-Received":"by 2002:ac2:5482:: with SMTP id t2mr6074473lfk.202.1588468737007;\n\tSat, 02 May 2020 18:18:57 -0700 (PDT)","Date":"Sun, 3 May 2020 03:18:56 +0200","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20200503011856.GD3012491@oden.dyn.berto.se>","References":"<20200502225704.2911-1-laurent.pinchart@ideasonboard.com>\n\t<20200502225704.2911-5-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20200502225704.2911-5-laurent.pinchart@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH 4/5] qcam: dng_writer: Populate DNG\n\ttags from metadata","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sun, 03 May 2020 01:18:57 -0000"}}]