1

I am trying to update the embedded JFIF thumbnail in a JPEG File using Python.

This is the (slightly hackish) method that is supposed to achieve this:

def set_thumbnail(self, data):
    # Data of the updated thumbnail
    data = bytearray(data)
    # Get offset of the old thumbnail data
    offset = (self._exif_start +
              self._unpack('I', self._get_tag_offset(0x201)+8))
    # Get size of the old thumbnail
    old_size = self._unpack('I', self._get_tag_offset(0x202)+8)
    try:
        # Strip everything between the JFIF APP1 and the quant table
        jfif_start = data.index('\xff\xe0')
        quant_start = data.index('\xff\xdb')
        stripped_data = data[0:jfif_start] + data[quant_start:]
    except ValueError:
        stripped_data = data
    # Writes the new length to the buffer
    self._pack('I', self._get_tag_offset(0x202)+8, len(stripped_data))
    # Writes the new data to the image buffer
    self._buf[offset:offset+old_size] = stripped_data

This function works fine when I re-write the old thumbnail, i.e. the size of the thumbnail data does not change. However, once I apply some transformation (e.g. crop or rotate) to it and store it again, the resulting file seems to be no longer valid.

I uploaded both an original image and one with an updated thumbnail for better comparison.

The error I get from e.g. identify is the following:

identify.im6: Invalid JPEG file structure: two SOI markers `/tmp/thumb_rotated.jpg' @ error/jpeg.c/JPEGErrorHandler/316.

On diffing the two images, the value in the 0x202 size tag matches the size of the embedded thumbnail data, and the file is also accordingly larger.

Paul R
  • 208,748
  • 37
  • 389
  • 560
jbaiter
  • 6,913
  • 4
  • 30
  • 40

2 Answers2

1

This is the structure of your image file. Notice there is a second SOI marker right after your APP1 marker.

{ Start Of Image }

{ APP0 Marker Length: 16 Version: 1.1 Density Unit: (pixels per inch) X Density: 72 Y Density: 72 Thumbnail Width: 0 Thumbnail Height: 0 }

{ APP1 Marker Length: 7678 Exif }

{ Start Of Image }

{ Define Quantization Table Length: 67 Table Index: 0 Table Precision: 0 Table Values: 8 6 6 7 6 5 8 7 7 7 9 9 8 10 12 20 13 12 11 11 12 25 18 19 15 20 29 26 31 30 29 26 28 28 32 36 46 39 32 34 44 35 28 28 40 55 41 44 48 49 52 52 52 31 39 57 61 56 50 60 46 51 52 50 }

{ Define Quantization Table Length: 67 Table Index: 1 Table Precision: 0 Table Values: 9 9 9 12 11 12 24 13 13 24 50 33 28 33 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 }

{ Start Of Frame Type: Baseline (Huffman) Length: 17 Precision: 8 Height: 360 Width: 480 Component Count: 3 Component 1 Horizontal Frequency: 2 Vertical Frequency: 2 Quantization Table: 0 Component 2 Horizontal Frequency: 1 Vertical Frequency: 1 Quantization Table: 1 Component 3 Horizontal Frequency: 1 Vertical Frequency: 1 Quantization Table: 1 }

{ Define Huffman Table Length: 28 Table Index 0 Table Class: DC Code Counts: 0 1 5 1 1 1 0 0 0 0 0 0 0 0 0 0 Code Values: 3 1 2 4 5 6 0 7 8 }

{ Define Huffman Table Length: 65 Table Index 0 Table Class: AC Code Counts: 0 2 1 3 2 4 4 4 4 4 5 3 3 4 3 0 Code Values: 1 2 3 0 4 11 12 21 5 31 41 51 13 22 61 71 6 32 81 91 14 42 a1 b1 23 52 c1 d1 7 15 33 62 e1 43 72 f0 24 92 f1 16 34 53 82 25 83 c2 }

{ Define Huffman Table Length: 26 Table Index 1 Table Class: DC Code Counts: 0 3 1 1 1 1 0 0 0 0 0 0 0 0 0 0 Code Values: 0 1 2 3 4 5 6 }

{ Define Huffman Table Length: 45 Table Index 1 Table Class: AC Code Counts: 0 2 2 1 4 1 4 2 2 2 1 5 0 0 0 0 Code Values: 0 1 2 11 3 4 12 21 31 41 13 22 51 61 32 71 5 14 23 91 f0 33 42 81 a1 b1 }

{ Start Of Scan Length: 12 Component ID: 1 AC Entropy Table: 0 DC Entropy Table: 0 Component ID: 2 AC Entropy Table: 1 DC Entropy Table: 1 Component ID: 3 AC Entropy Table: 1 DC Entropy Table: 1 Spectral Selection Start: 0 Spectral Selection End: 63 Sucessive Approximation High: 0 Sucessive Approximation Low: 0 }

{ End Of Image }

user3344003
  • 20,574
  • 3
  • 26
  • 62
  • The second SOI marker is the SOI of the thumbnail image, which is also present in the original image, so that's not the problem. What software did you use to generate that listing? – jbaiter May 08 '14 at 16:04
1

I just found the answer:

The problem was that while I updated the size of the thumbnail in the TIFF tag, I did not update the APP1 length. Once that was updated as well, the image loaded correctly.

jbaiter
  • 6,913
  • 4
  • 30
  • 40