From patchwork Fri Jul 24 14:56:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 8976 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 14170BD86F for ; Fri, 24 Jul 2020 14:56:40 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id D806D61223; Fri, 24 Jul 2020 16:56:39 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="quoIVa/X"; dkim-atps=neutral Received: from mail-wm1-x344.google.com (mail-wm1-x344.google.com [IPv6:2a00:1450:4864:20::344]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3CE60611B0 for ; Fri, 24 Jul 2020 16:56:37 +0200 (CEST) Received: by mail-wm1-x344.google.com with SMTP id f18so8543001wml.3 for ; Fri, 24 Jul 2020 07:56:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=0OcLxwAHjQXyW5pyhRAe0/bGMfnLFfe6P8GufHkoqaY=; b=quoIVa/XXI3JVTKVcDwRsh0H4unsfkY5onhw7R9bX3IvdpGGRRgEGfLx/EXA1ujtSy t+bfCrcLhfoF/prcdIY9DtzHFtF2C9uVMaFUx0P+erfNbM2WBGAOUH98d0J1p8YK3vqe qPBnN/rCG2i9DAlPS3d/eGvIbrGpo7azdHAXo0xZJtMoRs4q+BGQIxRZ56FIsWXeBvJF uG0FclKPu7da5nCWKj7ONVyywJcK3n0KNCjPwsC1kL6rm7AN80U3fAiIZ0mqds1BqhjG 1Py2TZPvWUthhDLId3OhNaRZZ2Lv584ZlhsgO7obukFKFW1FZ6wyo5wWmHfEDbOIC1qZ zZFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=0OcLxwAHjQXyW5pyhRAe0/bGMfnLFfe6P8GufHkoqaY=; b=EPzVZowsBdjBPYqn0R+5+qsMg42CPrMLyNC++/OM4z1EmLnEfgYKtl36hXsrAtZYy7 OcAN/87+SUTL9diUnCkYXJtvWikTBiQgMmiGo2qfozW8P4i2l/4PpqAtmYMglOj5J2a0 fvoYqGgYGq3x/FR0+A/39EVUep/wJ0Qn1LcX6lZrR/znw2MMsb8zq2oXSvvijxO2ZyKX PhMczGX6WSP8NFv+J8dWoQInLiWMs0Omw84D6tAZgZ8ecEhnfDp5vhZS+X+1DPy0qWXz j3JuJ+450uo5a7WxMpHl7+rpV+rA2BrogesTJSsV/n0sR7nmYnKF+1ujrjQ5ix2HqW73 tuPg== X-Gm-Message-State: AOAM5309UDl9HT7WkVogvlCd4oT09BoL00kFt7Qhr92y4Cyfd46wczZo 9HM9FQIH6kVS2BDG/tmA08eVs6BHTX0wuA== X-Google-Smtp-Source: ABdhPJyI7de/tFoSH9LipkLbsnu5zuSxRFndvPO/9C0GJ3e0gT2GxHNciAqT7DasynMuHv2pCfTLYw== X-Received: by 2002:a05:600c:2154:: with SMTP id v20mr9826065wml.185.1595602596659; Fri, 24 Jul 2020 07:56:36 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id h5sm2073156wrc.97.2020.07.24.07.56.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Jul 2020 07:56:36 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Jul 2020 15:56:16 +0100 Message-Id: <20200724145618.26304-2-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200724145618.26304-1-david.plowman@raspberrypi.com> References: <20200724145618.26304-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 1/3] libcamera: Add ColourCorrectionMatrix control X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" This control is principally for returning, in the image metadata, the CCM (Colour Correction Matrix) used by the imaging pipeline. By which we mean the 3x3 matrix that is applied to the camera RGB pixels after subtraction of black levels and white-balancing, but before any gamma transformation. Some implementations may also choose to let an application set explicit colour matrices, using this as a control. Signed-off-by: David Plowman --- src/libcamera/control_ids.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml index 988b501..3560d4a 100644 --- a/src/libcamera/control_ids.yaml +++ b/src/libcamera/control_ids.yaml @@ -262,4 +262,15 @@ controls: In this respect, it is not necessarily aimed at providing a way to implement a focus algorithm by the application, rather an indication of how in-focus a frame is. + + - ColourCorrectionMatrix: + type: float + description: | + The 3x3 matrix that converts camera RGB to sRGB within the + imaging pipeline. This should describe the matrix that is used + after pixels have been white-balanced, but before any gamma + transformation. The 3x3 matrix is stored in conventional reading + order in an array of 9 floating point values. + + size: [3x3] ... From patchwork Fri Jul 24 14:56:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 8977 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 167C9BD86F for ; Fri, 24 Jul 2020 14:56:41 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 551B26123A; Fri, 24 Jul 2020 16:56:40 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="qm2TvKpw"; dkim-atps=neutral Received: from mail-wm1-x32a.google.com (mail-wm1-x32a.google.com [IPv6:2a00:1450:4864:20::32a]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6E0B5611B0 for ; Fri, 24 Jul 2020 16:56:38 +0200 (CEST) Received: by mail-wm1-x32a.google.com with SMTP id j18so8208366wmi.3 for ; Fri, 24 Jul 2020 07:56:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=mMXsypKuztS3I5PSsSsWnIRpjlg2TPjWEKfPLJ/XywE=; b=qm2TvKpwR/PjlXPscNdAnkGKzHBgZh9p5gCpaxFPYvQow6V1+r3kncWeFiPBbVl/0X 9a8IztSM8ZQx1I8eJ5+YpjQ3+CCUVsrnNvQOrY6AwRDLfpAxQlvPCiHTAxYzvk11cO40 V1ctRqDpODHH3ULeS+klh8QV0Wc2Ifit/5jr2kVu4AxgZrBfyoiw6eBgoVOAEw9LaZZv CjW8hFclYIZPBK/CchU1FHhUOP0q420hllVWsKiwQV3/xe/ydPBamyFjRhJi9oQFXAsF YrQBfnNm/AkKSoenm8m0SoHDqC2Kic7HC0hU29D3266CZSLxrnNHQn3AENFkajzJ7s47 m5kA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mMXsypKuztS3I5PSsSsWnIRpjlg2TPjWEKfPLJ/XywE=; b=sANFG8Gq4zRoRq7VifnNpwAvrr9h7WTjiVLlW/FkxNkJgolu8hZoYd4zMQjTc/RU4O RNL4965HiE2lKVDQGOp2/oeHupbjEdjAdsbbrEQFC/LIF/qXHM6tDpLJtXhBcLIbRVqO qAQ7GuIbHFPVb4sd4pFlFFX1uBoqRqp2U43VuKmZ4tohbiwQ8U6ZyvkO3NeHC0+ZZ/ck V9PxgxgRupqfAP9pQvzMNHCyAEag94iJtmaxAxms4p4ezan5nrn+yu/5ad/s4UBkH8fs x/ZesrJBnQThUaMQM5VQRNo9Ekla+D6J1LfnP+utMcydzcpkUCIvVEmPtiArKR/IKEhw h5fA== X-Gm-Message-State: AOAM532rArF7NhVmXJ55equmxmAS9YmDVsfro2yj4qq2rkoBhUEwKGDl 975xZmFrqusT8k25hxBhzyw8dv87wvdHfg== X-Google-Smtp-Source: ABdhPJyS/afo/5LRY/Ij5x3fMIAuPoFz+ya2XNwiLRUKecUV2nEquF6+/zjeh2VfOmlD32FKa8a9Ig== X-Received: by 2002:a1c:dcd5:: with SMTP id t204mr8859825wmg.17.1595602597929; Fri, 24 Jul 2020 07:56:37 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id h5sm2073156wrc.97.2020.07.24.07.56.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Jul 2020 07:56:37 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Jul 2020 15:56:17 +0100 Message-Id: <20200724145618.26304-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200724145618.26304-1-david.plowman@raspberrypi.com> References: <20200724145618.26304-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/3] libcamera: raspberrypi: Add ColourCorrectionMatrix control X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Implements, for the Raspberry Pi platform, the returning of the CCM (Colour Correction Matrix) used by the pipeline in the libcamera metadata. Signed-off-by: David Plowman --- include/libcamera/ipa/raspberrypi.h | 1 + src/ipa/raspberrypi/raspberrypi.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/include/libcamera/ipa/raspberrypi.h b/include/libcamera/ipa/raspberrypi.h index a493776..ca62990 100644 --- a/include/libcamera/ipa/raspberrypi.h +++ b/include/libcamera/ipa/raspberrypi.h @@ -56,6 +56,7 @@ static const ControlInfoMap RPiControls = { { &controls::Contrast, ControlInfo(0.0f, 32.0f) }, { &controls::Saturation, ControlInfo(0.0f, 32.0f) }, { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, + { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) }, }; } /* namespace libcamera */ diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp index 7bd0488..3747208 100644 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ b/src/ipa/raspberrypi/raspberrypi.cpp @@ -438,6 +438,14 @@ void IPARPi::reportMetadata() int32_t focusFoM = (focusStatus->focus_measures[5] + focusStatus->focus_measures[6]) / 2; libcameraMetadata_.set(controls::FocusFoM, focusFoM); } + + CcmStatus *ccmStatus = rpiMetadata_.GetLocked("ccm.status"); + if (ccmStatus) { + float m[9]; + for (unsigned int i = 0; i < 9; i++) + m[i] = ccmStatus->matrix[i]; + libcameraMetadata_.set(controls::ColourCorrectionMatrix, m); + } } /* From patchwork Fri Jul 24 14:56:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 8978 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 6E367BD86F for ; Fri, 24 Jul 2020 14:56:42 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 3A5B261220; Fri, 24 Jul 2020 16:56:42 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="obYuQlm7"; dkim-atps=neutral Received: from mail-wr1-x42c.google.com (mail-wr1-x42c.google.com [IPv6:2a00:1450:4864:20::42c]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id DE1746123B for ; Fri, 24 Jul 2020 16:56:40 +0200 (CEST) Received: by mail-wr1-x42c.google.com with SMTP id r2so3503109wrs.8 for ; Fri, 24 Jul 2020 07:56:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=9Qul+YVzNIOpRkBcJtc0uvzNviGiy+CJSxvosrLYLoU=; b=obYuQlm7xUY9SDaN87VRbdT17itdjBkJRtZdg9wR1o87qUa1grN7afY7EpwaDe4NLo EvbVp7WQQj4YawIbM3icRzkQH8i+vGl9VsCA1X7g/vATYpK2f2bgFVy6DmOReHhzJnPY OSU/QLkwAhOo45FmIAlZoZBYPIBUDJT9+QinhNTANLQ9Z96y5M7WPInRkl2Gdu/+0TEc 9NIxhnHRtcdDxhWWNrza8XtohM/jhE6mEkQStgt+I+SFsyr6HS8+VleQuX/Pgwvum64Q j/dzdfYHM2ut4573d3N2erafNHmqvtSTGNuYR+Dvpcaad1JyvBkSKOubMRay4cN7DUfT 7tJg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=9Qul+YVzNIOpRkBcJtc0uvzNviGiy+CJSxvosrLYLoU=; b=BMFoZOOUIFRJcct5bhtKE7ZiJhiJYCu/JV+w4K71HOcon52yGox4msf51npibxmY4Q zzH/LmEzfjHk0liTiB1ss6DwCkjV08EvJYsr08Lps/TMk3OR3tjEPeXwjoVU/03sbEEM dkSjlZs6UACkT9d1riMsCJyZj9OKjmtBiZowNq2wv4Gfdby1gPKgMAaSyx7dSW3w6I2t TFrgilC0oUzKlAgyEI3b2gNOGeE80Xc+UuP6yx29/Lg7fpMgTUauukzG86jHD428JL1Q IZsTJTc5gXdGOyH8iJyKqo0/boxbbVc08eIBGLj1OT23wq+jnGcxBrdKmNvzpp0AN9LM 2yTA== X-Gm-Message-State: AOAM533Rk2AYP/X8fAEBQ+fjjQX7QMze5fc3YEbSNDmTtayEjZWtK7ap Uj4RR/j+VVjW0YX7MEV6SRgFJ0W8j0IWkg== X-Google-Smtp-Source: ABdhPJzXHGLyODKv1dKbmBP3XAFG8nITlbQfouZg9HolBq8RxqvInUfuSE9LpH7IDtdVJq3Z6afA6g== X-Received: by 2002:adf:c386:: with SMTP id p6mr8926727wrf.344.1595602600146; Fri, 24 Jul 2020 07:56:40 -0700 (PDT) Received: from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72]) by smtp.gmail.com with ESMTPSA id h5sm2073156wrc.97.2020.07.24.07.56.39 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 24 Jul 2020 07:56:39 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Fri, 24 Jul 2020 15:56:18 +0100 Message-Id: <20200724145618.26304-4-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200724145618.26304-1-david.plowman@raspberrypi.com> References: <20200724145618.26304-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 3/3] libcamera: qcam: Improve colour information in DNG files X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" This patch improves the colour information recorded in DNG files using the ColourCorrectionMatrix metadata for the image. Note that we are not supplying a full calibration using two illuminants, nonetheless the single matrix here appears to be respected by a number of tools. Signed-off-by: David Plowman Reviewed-by: Laurent Pinchart --- src/qcam/dng_writer.cpp | 138 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/src/qcam/dng_writer.cpp b/src/qcam/dng_writer.cpp index 61505d3..4f638ec 100644 --- a/src/qcam/dng_writer.cpp +++ b/src/qcam/dng_writer.cpp @@ -34,6 +34,97 @@ struct FormatInfo { unsigned int stride); }; +struct Matrix3d { + Matrix3d() + { + } + + Matrix3d(float m0, float m1, float m2, + float m3, float m4, float m5, + float m6, float m7, float m8) + { + m[0] = m0, m[1] = m1, m[2] = m2; + m[3] = m3, m[4] = m4, m[5] = m5; + m[6] = m6, m[7] = m7, m[8] = m8; + } + + Matrix3d(Span const &span) + : Matrix3d(span[0], span[1], span[2], + span[3], span[4], span[5], + span[6], span[7], span[8]) + { + } + + static Matrix3d diag(float diag0, float diag1, float diag2) + { + return Matrix3d(diag0, 0, 0, 0, diag1, 0, 0, 0, diag2); + } + + static Matrix3d identity() + { + return Matrix3d(1, 0, 0, 0, 1, 0, 0, 0, 1); + } + + Matrix3d transpose() const + { + return { m[0], m[3], m[6], m[1], m[4], m[7], m[2], m[5], m[8] }; + } + + Matrix3d cofactors() const + { + return { m[4] * m[8] - m[5] * m[7], + -(m[3] * m[8] - m[5] * m[6]), + m[3] * m[7] - m[4] * m[6], + -(m[1] * m[8] - m[2] * m[7]), + m[0] * m[8] - m[2] * m[6], + -(m[0] * m[7] - m[1] * m[6]), + m[1] * m[5] - m[2] * m[4], + -(m[0] * m[5] - m[2] * m[3]), + m[0] * m[4] - m[1] * m[3] }; + } + + Matrix3d adjugate() const + { + return cofactors().transpose(); + } + + float determinant() const + { + return m[0] * (m[4] * m[8] - m[5] * m[7]) - + m[1] * (m[3] * m[8] - m[5] * m[6]) + + m[2] * (m[3] * m[7] - m[4] * m[6]); + } + + Matrix3d inverse() const + { + return adjugate() * (1.0 / determinant()); + } + + Matrix3d operator*(Matrix3d const &other) const + { + Matrix3d result; + for (unsigned int i = 0; i < 3; i++) { + for (unsigned int j = 0; j < 3; j++) { + result.m[i * 3 + j] = + m[i * 3 + 0] * other.m[0 + j] + + m[i * 3 + 1] * other.m[3 + j] + + m[i * 3 + 2] * other.m[6 + j]; + } + } + return result; + } + + Matrix3d operator*(float f) const + { + Matrix3d result; + for (unsigned int i = 0; i < 9; i++) + result.m[i] = m[i] * f; + return result; + } + + float m[9]; +}; + void packScanlineSBGGR10P(void *output, const void *input, unsigned int width) { const uint8_t *in = static_cast(input); @@ -315,6 +406,53 @@ int DNGWriter::write(const char *filename, const Camera *camera, TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + /* + * Fill in some reasonable colour information in the DNG. We supply + * the "neutral" colour values which determine the white balance, and the + * "ColorMatrix1" which converts XYZ to (un-white-balanced) camera RGB. + * Note that this is not a "proper" colour calibration for the DNG, + * nonetheless, many tools should be able to render the colours better. + */ + float neutral[3] = { 1, 1, 1 }; + Matrix3d wbGain = Matrix3d::identity(); + /* From http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html */ + const Matrix3d rgb2xyz(0.4124564, 0.3575761, 0.1804375, + 0.2126729, 0.7151522, 0.0721750, + 0.0193339, 0.1191920, 0.9503041); + Matrix3d ccm = Matrix3d::identity(); + /* + * Pick a reasonable number eps to protect against singularities. It + * should be comfortably larger than the point at which we run into + * numerical trouble, yet smaller than any plausible gain that we might + * apply to a colour, either explicitly or as part of the colour matrix. + */ + const double eps = 1e-2; + + if (metadata.contains(controls::ColourGains)) { + Span const &colourGains = metadata.get(controls::ColourGains); + if (colourGains[0] > eps && colourGains[1] > eps) { + wbGain = Matrix3d::diag(colourGains[0], 1, colourGains[1]); + neutral[0] = 1.0 / colourGains[0]; /* red */ + neutral[2] = 1.0 / colourGains[1]; /* blue */ + } + } + if (metadata.contains(controls::ColourCorrectionMatrix)) { + Span const &coeffs = metadata.get(controls::ColourCorrectionMatrix); + Matrix3d ccm_supplied(coeffs); + if (ccm_supplied.determinant() > eps) + ccm = ccm_supplied; + } + + /* + * rgb2xyz is known to be invertible, and we've ensured above that both + * the ccm and wbGain matrices are non-singular, so the product of all + * three is guaranteed to be invertible too. + */ + Matrix3d colorMatrix1 = (rgb2xyz * ccm * wbGain).inverse(); + + TIFFSetField(tif, TIFFTAG_COLORMATRIX1, 9, colorMatrix1.m); + TIFFSetField(tif, TIFFTAG_ASSHOTNEUTRAL, 3, neutral); + /* * Reserve space for the SubIFD and ExifIFD tags, pointing to the IFD * for the raw image and EXIF data respectively. The real offsets will