#177 Some JPEG-compressed color TIFFs are loaded as mirror image

open
Hervé Drolon
None
5
2014-08-18
2011-06-21
No

The JPEG-compressed color TIFF that can be downloaded from the link below is opened as a mirror image by FreeImage. I am using the most recent precompiled win32 FreeImage dll distribution, version 3.15.0.0 - build date 23 January 2011.

The sample tiff can be downloaded here: http://support.decos.nl/berend/DOC019.zip
It is unfortunately not possible for me to attach the sample to the Artifact report due to the 256k file limit.

Many thanks in advance for your help with this problem,
With kind regards,

Berend Engelbrecht

Discussion

  • Hervé Drolon
    Hervé Drolon
    2011-06-25

    Hi,

    This image is a TIFF-JPEG image using an old and outdated JPEG 6.0 encoding.
    The "Orientation" Exif tag indicates "8", meaning that the orientation is "left side, bottom".
    However, this is wrong, because the tag should indicate "4", meaning that the orientation is "bottom, left side"
    According to the Exif specification, the orientation tag is such that :
    8 = The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom.
    4 = The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side.
    So, even if you apply an automatic rotation according to the Exif Orientation tag, the result will be wrong.
    The software that produced this image uses a wrong interpretation of the Exif specification.

    Hervé

     
  • Dear Herv,

    My problem with your answer is that I also get other files with orientation 8 that work properly (vlak and white tiffs from the same and other scanners) and that other viewers (photoshop, MODI) load the example file correctly. How can I distinguish that an outdated software is used and that I should treat 8 as if it is 4? For instance, is your claim that I should treat 8 as 4 always for JPEG 6 encoded color tiffs?

    I cannot influence the software that produces the scan, because it is the firmware of a scanner and the machine is not owned by me.

     
  • Hervé Drolon
    Hervé Drolon
    2011-06-25

    About :
    "is your claim that I should treat 8 as 4 always for JPEG 6 encoded color tiffs"
    The answer is "NO !!!"
    First, TIFF-JPEG images are loaded "as-is" by FreeImage, i.e. without automatic Exif rotation.
    => This is up to you to rotate the image according to the Exif orientation tag, if this tag is available.
    Next, this specific DOC019 image uses a wrong interpretation of the Exif orientation tag, so that a function (written by you) using the Exif orientation tag would fail with this image.
    That's all I wanted to say ...
    There's no bug inside FreeImage here, there's only a problem with a specific image producer (here it is "Xerox WorkCentre 7435").

    Hervé

     
  • Thanks for your help. I am now thinking to go the route of introducing a configurable correction for certain orientations of this scanner type, that will certainly help. The only thing I still find strange is that software that ignores the orientation tag like MODI shows the image correctly.

     
  • Hervé Drolon
    Hervé Drolon
    2011-06-25

    I tried to find equivalent problems with other software, but without success ...
    I think that the original problem comes from the JPEG 6.0 encoding (this format was officially abandoned on 17-Mar-95).
    Exif data coming from old JPEG-6.0-In-TIFF images probably do not conform to the standard Exif specification but rather to a maker-specific specification.
    Thus, you cannot trust the "Orientation" tag using the standard specification : you will have to use a maker-specific definition !
    When the TIFF-Exif "Compression" tag indicates "6" (i.e. 6.0 old JPEG compression mechanism), then you could use a specific processing mechanism, chosen according to the image producers.

    About Microsoft products, I don't know how they handle TIFF-JPEG 6.0 images (they do it the right way with my own test image set).

     
  • Dear Hervé,

    I believe there is a bug in LibTIFF for any jpeg-compressed color tiff having orientation 8, independent of the compression type. Please consider my explanation and suggested fix for tiff_getimage.c below. Please let me know if you agree with my findings, the bugfix and if I should report the bug at the LibTIFF bugtracker. I have compared the code to LibTIFF 4.0.0 beta 7, and the same code is still present.

    To prove that setorientation malfunctions, I have temporarilty added the following TEST code to function TIFFVGetFieldDefaulted in tif_aux.c:

    case TIFFTAG_ORIENTATION:
    

    td->td_orientation = 8; // Berend 20110630 TEST
    va_arg(ap, uint16 ) = td->td_orientation;
    return (1);

    With this extra line of code, all tiff images with compression 6 and compression 7 will be loaded rotated 90 degrees AND mirrored by the original LibTIFF code, although orientation 8 should only cause a rotation.

    EXPLANATION OF PROBLEM

    PluginTIFF.Load calls TIFFReadRGBAImage for a JPEG-compressed color tiff. TIFFReadRGBAImage calls TIFFReadRGBAImageOriented for the default orientation ORIENTATION_BOTLEFT. If no orientation tag is present, the orientation in the tiff defaults to ORIENTATION_TOPLEFT, which is the native scanline order for a Tiff.

    During processing, function setorientation in tif_getimage.c is called, with the detected orientation for the tiff in img->orientation and ORIENTATION_BOTLEFT (4) in img->req_orientation. This function is supposed to determine whether a horizontal or vertical flip (or both) is needed between the two orientations.

    For the default orientation 1, setorientation returns FLIP_VERICALLY. That is correct, so without orientation tag, the image looks fine. However, if the input value is ORIENTATION_LEFTBOT (8), setorientation returns 0, indicating that no flip is required. I believe that is a bug. Please consider this web page showing visual samples of exif rotations:
    http://sylvana.net/jpegcrop/exif_orientation.html

    If you compare 8 and 4, you'll see that a rotation and a flip (either horizontal or vertical) is needed to correctly transform between the two orientations. I don't think this depends on the compression type, but is a bug for all color tiff images processed by TIFFReadRGBAImage, if they happen to have rotation 8.

    Function setorientation assumes that the pairs (TOPLEFT,LEFTTOP), (TOPRIGHT,RIGHTTOP), (BOTRIGHT,RIGHTBOT) and (BOTLEFT,LEFTBOT) require the same transformation. That is not correct. If you look at the exif_orientation.html web page, you'll see that these pairs are in fact pairs of mirror images, and thus cannot possibly have the same flip transformation requirement.

    SUGGESTED FIX

    My suggested fix is the following replacement code for setorientation:

    /
    * Return value will have FLIP_VERTICALLY and/or FLIP_HORIZONTALLY bits set
    * if these transformations are needed to transform from img->orientation to
    * img->req_orientation.
    /
    static int
    setorientation(TIFFRGBAImage* img)
    {
    const uint16 flipbits[8] = {0,1,3,2,2,3,1,0};
    uint16 flip_orient = flipbits[img->orientation - 1];
    uint16 flip_req_or = flipbits[img->req_orientation - 1];
    return (flip_orient && !flip_req_or) || (!flip_orient && flip_req_or);

    // TL TR BR BL LT RT RB LB <- Source orientation
    // flipbits 0 1 3 2 2 3 1 0
    // + - - - - - - - -
    // TL 0 | 0 1 3 2 2 3 1 0
    // TR 1 | 1 0 2 3 3 2 0 1
    // BR 3 | 3 2 0 1 1 0 2 3
    // BL 2 | 2 3 1 0 0 1 3 2
    // LT 2 | 2 3 1 0 0 1 3 2
    // RT 3 | 3 2 0 1 1 0 2 3
    // RB 1 | 1 0 2 3 3 2 0 1
    // LB 0 | 0 1 3 2 2 3 1 0 |
    // - return values
    // ^ Target orientation
    }

    The new code uses matrix transformation logic, that makes it much more compact and I believe also more robust than before. The only weakness is that I only have one single test image with a rotation tag. It could well be that for some rotations I specified a horizontal flip where it should be vertical, or vice-versa. Please supply me with test images if you find any that are rotated incorrectly.

     
  • I noticed a bug in my source code: by mistake I used normal boolean instead of bitwise logic blush. Also I reversed the flip horizontal and vertical settings.

    The code should be this:

    const uint16 flipbits[8] = {0,1,3,2,2,0,1,3};
    uint16 flip_orient = flipbits[img->orientation - 1];
    uint16 flip_req_or = flipbits[img->req_orientation - 1];
    return (flip_orient & ~flip_req_or) | (~flip_orient & flip_req_or);

     
  • I agree. LibTIFF fixes should be incorporated by the LibTIFF team, not as a fork in FreeImage. I am glad that they now have bugzilla, the old system with the mailing list made it difficult for outsiders to contribute code.