diff mbox

[FFmpeg-devel,v2] avcodec/tiff: add limited support for ReferenceBlackWhite and YCbCrCoefficients tags

Message ID 9c8a962b-e36f-2ab9-8608-6c98f45288ba@gmail.com
State New
Headers show

Commit Message

Skakov Pavel Oct. 4, 2019, 6:29 p.m. UTC
>> Add support for properly handling PC/TV ranges and Rec601/Rec709 color spaces.
> Can't this be implemented without using floats?

AVRationals are not a good idea as those values are unsigned and often use full unsigned range.
Here is slightly different approach: range check for fractional rationals and exact check for integer expected values.
Warning text is probably better left with floats as they provide the most meaningful values for end user.
diff mbox

Patch

From 14c712eb2a7c3b92f83c6edd0115d6eefccac476 Mon Sep 17 00:00:00 2001
From: Pavel Skakov <pavelsx@gmail.com>
Date: Fri, 4 Oct 2019 21:12:19 +0300
Subject: [PATCH] avcodec/tiff: add limited support for ReferenceBlackWhite and
 YCbCrCoefficients tags

Signed-off-by: Pavel Skakov <pavelsx@gmail.com>
---
 libavcodec/tiff.c              | 73 ++++++++++++++++++++++++++++++++++++++++--
 tests/ref/fate/exif-image-tiff |  2 +-
 2 files changed, 72 insertions(+), 3 deletions(-)

diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c
index 8b39ca1ebc..069792b4c3 100644
--- a/libavcodec/tiff.c
+++ b/libavcodec/tiff.c
@@ -1216,6 +1216,13 @@  static void set_sar(TiffContext *s, unsigned tag, unsigned num, unsigned den)
     }
 }
 
+static int r_near(unsigned n1, unsigned d1, unsigned n2, unsigned d2)
+{
+    uint64_t v1 = (uint64_t)n1*d2;
+    uint64_t v2 = (uint64_t)n2*d1;
+    return v2 - d1 < v1 && v1 < v2 + d1;
+}
+
 static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
 {
     unsigned tag, type, count, off, value = 0, value2 = 1; // value2 is a denominator so init. to 1
@@ -1441,12 +1448,16 @@  static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
         break;
     case TIFF_PHOTOMETRIC:
         switch (value) {
+        case TIFF_PHOTOMETRIC_YCBCR:
+            s->avctx->colorspace = AVCOL_SPC_BT470BG;
+            // fallthrough
+        case TIFF_PHOTOMETRIC_RGB:
+            s->avctx->color_range = AVCOL_RANGE_JPEG;
+            // fallthrough
         case TIFF_PHOTOMETRIC_WHITE_IS_ZERO:
         case TIFF_PHOTOMETRIC_BLACK_IS_ZERO:
-        case TIFF_PHOTOMETRIC_RGB:
         case TIFF_PHOTOMETRIC_PALETTE:
         case TIFF_PHOTOMETRIC_SEPARATED:
-        case TIFF_PHOTOMETRIC_YCBCR:
         case TIFF_PHOTOMETRIC_CFA:
         case TIFF_PHOTOMETRIC_LINEAR_RAW: // Used by DNG images
             s->photometric = value;
@@ -1519,6 +1530,64 @@  static int tiff_decode_tag(TiffContext *s, AVFrame *frame)
             }
         }
         break;
+    case TIFF_YCBCR_COEFFICIENTS:
+        if (s->photometric == TIFF_PHOTOMETRIC_YCBCR)
+            if (count != 3 || type != TIFF_RATIONAL) {
+                av_log(s->avctx, AV_LOG_ERROR, "YCbCrCoefficients are invalid\n");
+                return AVERROR_INVALIDDATA;
+            } else {
+                unsigned c[6];
+                for (i = 0; i < 6; i++)
+                    c[i] = ff_tget(&s->gb, TIFF_LONG, s->le);
+                if (r_near(c[0], c[1], 2125, 10000) && r_near(c[2], c[3], 7154, 10000) && r_near(c[4], c[5], 721, 10000))
+                    s->avctx->colorspace = AVCOL_SPC_BT709;
+                else if (!r_near(c[0], c[1], 299, 1000) || !r_near(c[2], c[3], 587, 1000) || !r_near(c[4], c[5], 114, 1000)) {
+                    av_log(s->avctx, AV_LOG_WARNING, "Unrecognized YCbCrCoefficients values: %.4f %.4f %.4f\n", (float)c[0]/c[1], (float)c[2]/c[3], (float)c[4]/c[5]);
+                    s->avctx->colorspace = AVCOL_SPC_UNSPECIFIED;
+                }
+            }
+        break;
+    case TIFF_REFERENCE_BW:
+        if (s->photometric == TIFF_PHOTOMETRIC_YCBCR || s->photometric == TIFF_PHOTOMETRIC_RGB)
+            if (count != 6 || type != TIFF_RATIONAL) {
+                av_log(s->avctx, AV_LOG_ERROR, "ReferenceBlackWhite is invalid\n");
+                return AVERROR_INVALIDDATA;
+            } else {
+                unsigned bpp = s->bpp/s->bppcount;
+                uint64_t mul = 1 << (bpp - 8);
+                uint64_t max_val = (1 << bpp) - 1;
+                uint64_t mid_val = 1 << (bpp - 1);
+                unsigned c[12];
+                for (i = 0; i < 12; i++)
+                    c[i] = ff_tget(&s->gb, TIFF_LONG, s->le);
+                if (s->photometric == TIFF_PHOTOMETRIC_YCBCR) {
+                    if (!c[0] && c[2] == max_val*c[3] && c[4] == mid_val*c[5] && c[6] == max_val*c[7] && c[8] == mid_val*c[9] && c[10] == max_val*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_JPEG;
+                    // NOTE: TIFF 6.0 specification has an example where it mistakenly shows TV range c[0] as 15
+                    else if (c[0] ==  16*mul*c[1] && c[ 2] == 235*mul*c[3] &&
+                             c[4] == 128*mul*c[5] && c[ 6] == 240*mul*c[7] &&
+                             c[8] == 128*mul*c[9] && c[10] == 240*mul*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_MPEG;
+                    else {
+                        av_log(s->avctx, AV_LOG_WARNING, "Unrecognized ReferenceBlackWhite values: [%g;%g] [%g;%g] [%g;%g]\n",
+                            (float)c[0]/c[1], (float)c[2]/c[3], (float)c[4]/c[5], (float)c[6]/c[7], (float)c[8]/c[9], (float)c[10]/c[11]);
+                        s->avctx->color_range = AVCOL_RANGE_UNSPECIFIED;
+                    }
+                } else {
+                    if (!c[0] && c[2] == max_val*c[3] && !c[4] && c[6] == max_val*c[7] && !c[8] && c[10] == max_val*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_JPEG;
+                    else if (c[0] == 16*mul*c[1] && c[ 2] == 235*mul*c[3] &&
+                             c[4] == 16*mul*c[5] && c[ 6] == 235*mul*c[7] &&
+                             c[8] == 16*mul*c[9] && c[10] == 235*mul*c[11])
+                        s->avctx->color_range = AVCOL_RANGE_MPEG;
+                    else {
+                        av_log(s->avctx, AV_LOG_WARNING, "Unrecognized ReferenceBlackWhite values: [%g;%g] [%g;%g] [%g;%g]\n",
+                            (float)c[0]/c[1], (float)c[2]/c[3], (float)c[4]/c[5], (float)c[6]/c[7], (float)c[8]/c[9], (float)c[10]/c[11]);
+                        s->avctx->color_range = AVCOL_RANGE_UNSPECIFIED;
+                    }
+                }
+            }
+        break;
     case TIFF_T4OPTIONS:
         if (s->compr == TIFF_G3)
             s->fax_opts = value;
diff --git a/tests/ref/fate/exif-image-tiff b/tests/ref/fate/exif-image-tiff
index 51580601e1..ebf2f38d6e 100644
--- a/tests/ref/fate/exif-image-tiff
+++ b/tests/ref/fate/exif-image-tiff
@@ -22,7 +22,7 @@  display_picture_number=0
 interlaced_frame=0
 top_field_first=0
 repeat_pict=0
-color_range=unknown
+color_range=pc
 color_space=unknown
 color_primaries=unknown
 color_transfer=unknown
-- 
2.13.2.windows.1