2008.09.23 23:34 "[Tiff] TIFF Image Quality Question", by Rick Cone, Secure Payment Systems

2008.10.01 03:03 "[Tiff] Image Orientation tag and rotating images", by Richard Nolde

Concerning rotating images in tiffcrop:

I've been working on the orientation tag issue for images rotated tiffcrop and I get two distinctly different results with a with

variety of viewers. Below are the outputs from tiffinfo on the
image and original

three versions rotated by 90, 180, and 270 degrees clockwise. The test document has three pages but I am only showing the results for the first page in each case.

My first question is whether I have made the correct adjustments to the orientation tag for rotations of 90, 180, and 270 degrees clockwise. Photoshop 7 on Winders displays the files as I would expect to see them, which is the same as before I added the code to change the orientation field in the output file. The Gimp 2.4.5 on Win2K and Fedora 8 display them as I would expect as well. The same is true for Qfaxreader. However, DIMIN Viewer n5 and 11 View on Winders and GThumb 2.7.10 on Fedora 8 seem to be adding an additional mirroring operation to the displayed image, eg. rotating by 90 degrees clockwise puts the top of the original image on the right of the new image, but now the left and right sides of the original, albeit rotated image to top and bottom positions, are reversed so that I am seeing the text from the back side as though the top and bottom of the rotated image are now reversed. GQView 2.0.4 and Mirage 0.9.3 on Fedora 8 display R180 the same as the original and R270 the same as R90, both of which are rotated 90 and then mirrored vertically. Quickscan 1.0 Level3 and Quickscan 4.0 by Captiva Software on Winders display all the images in the original orientation.

My second question is whether I should be updating the orientation tag in addition to transforming the data or is that the cause of the problem. Based on earlier posts to this list, it looks like an either/or proposition but not both. I could offer the option to modify the data or the tag or both as my current code does for inverting the photometric interpretation tag for bilevel and grayscale images. My concept is allow users to "fix" incorrectly stored images as well as to modify the data when that is the desired result, but since this tag is not always supported according to the TIFF 6.0 spec, I want the majority of Linux viewers to be able to display the images correctly. It is only since I added the new code to modify the orientation tag, that I have seen the problem. Previously, all images were written out with the orientation tag copied from the input file even though the data are being reoriented and for all the images I have, this was ORIENTATION_TOPLEFT. If I rotate the image 90 degrees in Photoshop 7 on Winders, the orientation tag is no longer reported by tiffinfo. Quickscan Pro rotates the image data but does not update the orientation tag.

tiffinfo /tmp/test.tiff

TIFF Directory at offset 0x2738 (10040)
    Subfile Type: multi-page document (2 = 0x2)
    Image Width: 2552 Image Length: 4200
    Resolution: 300, 300 pixels/inch
    Bits/Sample: 1
    Compression Scheme: CCITT Group 4
    Photometric Interpretation: min-is-white
    Thresholding: bilevel art scan
    FillOrder: lsb-to-msb
    Orientation: row 0 top, col 0 lhs
    Samples/Pixel: 1
    Rows/Strip: 4200
    Min Sample Value: 0
    Max Sample Value: 1
    Planar Configuration: single image plane

  Page Number: 0-3
  Make: RICOH
  Group 4 Options: (0 = 0x0)

tiffinfo /tmp/testR90.tiff

TIFF Directory at offset 0x30ce (12494)
    Subfile Type: multi-page document (2 = 0x2)
    Image Width: 4200 Image Length: 2552
    Resolution: 300, 300 pixels/inch
    Bits/Sample: 1
    Compression Scheme: CCITT Group 4
    Photometric Interpretation: min-is-white
    Thresholding: bilevel art scan
    FillOrder: lsb-to-msb
    Orientation: row 0 lhs, col 0 bottom
    Samples/Pixel: 1
    Rows/Strip: 2552
    Min Sample Value: 0
    Max Sample Value: 1
    Planar Configuration: single image plane

  Page Number: 0-3
  Make: RICOH
  Group 4 Options: (0 = 0x0)

tiffinfo /tmp/testR180.tiff

TIFF Directory at offset 0x270e (9998)
    Subfile Type: multi-page document (2 = 0x2)
    Image Width: 2552 Image Length: 4200
    Resolution: 300, 300 pixels/inch
    Bits/Sample: 1
    Compression Scheme: CCITT Group 4
    Photometric Interpretation: min-is-white
    Thresholding: bilevel art scan
    FillOrder: lsb-to-msb
    Orientation: row 0 bottom, col 0 rhs
    Samples/Pixel: 1
    Rows/Strip: 4200
    Min Sample Value: 0
    Max Sample Value: 1
    Planar Configuration: single image plane

  Page Number: 0-3
  Make: RICOH
  Group 4 Options: (0 = 0x0)

tiffinfo /tmp/testR270.tiff

TIFF Directory at offset 0x34b0 (13488)
    Subfile Type: multi-page document (2 = 0x2)
    Image Width: 4200 Image Length: 2552
    Resolution: 300, 300 pixels/inch
    Bits/Sample: 1
    Compression Scheme: CCITT Group 4
    Photometric Interpretation: min-is-white
    Thresholding: bilevel art scan
    FillOrder: lsb-to-msb
    Orientation: row 0 rhs, col 0 top
    Samples/Pixel: 1
    Rows/Strip: 2552
    Min Sample Value: 0
    Max Sample Value: 1
    Planar Configuration: single image plane

  Page Number: 0-3
  Make: RICOH
  Group 4 Options: (0 = 0x0)

Here is the piece of code that I am testing to assign the new orientation value. Rotation is the clockwise value being applied to the image data itself and image->orientation is initially the value read from the source image and it gets reset to the new value below before being written back to the output file. If it turns out that I should not update the orientation tag when reordering the image pixels, I can make this code conditional based on the users request to update the tag OR to update only the tag.

  switch (rotation)
    {
    case 90:
      switch (image->orientation)
        {

        case ORIENTATION_TOPLEFT:     /* 1, row 0 top, col 0 lhs */
             image->orientation += 2;
        case ORIENTATION_TOPRIGHT:    /* 2, row 0 top, col 0 rhs */
             image->orientation += 2;
        case ORIENTATION_BOTRIGHT:    /* 3, row 0 bottom, col 0 rhs */
             image->orientation += 2;
        case ORIENTATION_BOTLEFT:     /* 4, row 0 bottom, col 0 lhs */
             image->orientation += 1;
             break;
        case ORIENTATION_RIGHTTOP:    /* 6, row 0 rhs, col 0 top */
        case ORIENTATION_LEFTBOT:     /* 8, row 0 lhs, col 0 bottom */
             image->orientation -= 2;
        case ORIENTATION_LEFTTOP:     /* 5, row 0 lhs, col 0 top */
        case ORIENTATION_RIGHTBOT:    /* 7, row 0 rhs, col 0 bottom */
             image->orientation -= 3;
             break;
        default: image->orientation = ORIENTATION_TOPLEFT;
        }
      break;
    case 180:
      switch (image->orientation)
        {
        case ORIENTATION_TOPLEFT:     /* 1, row 0 top, col 0 lhs */
        case ORIENTATION_TOPRIGHT:    /* 2, row 0 top, col 0 rhs */
        case ORIENTATION_LEFTTOP:     /* 5, row 0 lhs, col 0 top */
        case ORIENTATION_RIGHTTOP:    /* 6, row 0 rhs, col 0 top */
             image->orientation += 2;
             break;
        case ORIENTATION_BOTRIGHT:    /* 3, row 0 bottom, col 0 rhs */
        case ORIENTATION_BOTLEFT:     /* 4, row 0 bottom, col 0 lhs */
        case ORIENTATION_RIGHTBOT:    /* 7, row 0 rhs, col 0 bottom */
        case ORIENTATION_LEFTBOT:     /* 8, row 0 lhs, col 0 bottom */
             image->orientation -= 2;
             break;
        default: image->orientation = ORIENTATION_TOPLEFT;
        }
      break;
    case 270:
      switch (image->orientation)
        {
        case ORIENTATION_TOPLEFT:     /* 1, row 0 top, col 0 lhs */
        case ORIENTATION_BOTRIGHT:    /* 3, row 0 bottom, col 0 rhs */
             image->orientation += 2;
        case ORIENTATION_TOPRIGHT:    /* 2, row 0 top, col 0 rhs */
        case ORIENTATION_BOTLEFT:     /* 4, row 0 bottom, col 0 lhs */
             image->orientation += 3;
             break;
        case ORIENTATION_LEFTBOT:     /* 8, row 0 lhs, col 0 bottom */
             image->orientation -= 2;
        case ORIENTATION_RIGHTBOT:    /* 7, row 0 rhs, col 0 bottom */
             image->orientation -= 2;
        case ORIENTATION_RIGHTTOP:    /* 6, row 0 rhs, col 0 top */
             image->orientation -= 2;
        case ORIENTATION_LEFTTOP:     /* 5, row 0 lhs, col 0 top */
             image->orientation -= 1;

             break;
        default: image->orientation = ORIENTATION_TOPLEFT;
    }
      break;
    default: TIFFError("rotateImage", "Invalid rotation angle %d",
rotation);
              return (-1);
    }

Richard Nolde

Tiffcrop author