Search code examples
javaandroidimageimage-processingjpeg

Create JPEG thumb image with general fixed header


I want to create preview thumb for my photos like Facebook's preview photo. My plan:

  • Sender: generate a scaled thumb (with 30px max dimension) from original photo, strip out all fixed header to send.
  • Receiver: From the "minified" byte array, append with the fixed header (hardcode in the client code). Then convert it to Bitmap to display.

Finally I come up with the solution base on Q42.ImagePreview.

I split these parts as fixed header:

  • Start Of Image (0xFFD8)
  • App0 (start with 0xFFE0)
  • Define Quantization Table(s)
  • Define Huffman Table(s)

The dynamic parts are:

  • Start Of Frame (start with 0xFFC0): because it contains the width/height bytes.
  • Start Of Scan (start with 0xFFDA).
  • Compressed image data.
  • End Of Image (0xFFD9)

But it only works on 1 of my devices & not works on others.

So how to generate a fixed, general & standard JPEG header that can use on both Android & iOS devices?

Thank you.


More detail:

Generate minified data flow:

  • Create a scaled bitmap from original image (max dimension 30px, keep aspect ratio) using BitmapFactory & Matrix

  • Compress scaled bitmap with quality 64 using Bitmap#compress() and store in byte[] thumbData.

  • Sub-array the thumbData above from 0xFFDA to the end. (SOS, image data & EOI) and store in byte[] body.

  • Prepend with the 4 bytes that repsent width & height to body, convert to Base64 string & send.

In the device that working fine, the size of thumbData is longer than others devices that not work. And the different is in Huffman Table(s), SOS & image data parts, see this: Diff check between 2 image photos


Solution

  • I'm afraid you cannot do that using built-in method of each platform. The problem is in the compressing phase.

    There are a number of variables in JPEG compression, including the type and breakdown of scans, samples, DHT selection, and DQT selection. If any of those are different in the encoder you use, you are going to get different output. It's the nature of the beast.

    For example: The Define Huffman Table (DHT) define how the "image data" (after SoS segment) was compressed. And you use fixed Huffman tables only for decoding, that’s what caused the problem.


    So you may have some options to choose:

    • Send the full quality image (without compressing) after scaled down to max dimension 30px as preview thumb photo.
    • Write your own compression algorithm or use a cross-platform library.
    • Upload entire original image to your server to process & send back the "minified data" to Android/iOS.

    Telegram has preview photo too, and their approach is similar to you. But they transfer entire original image (in byte array) to server, create a thumb photo, strip out "fixed header" and send back to the receivers the "minified data".

    When receive on mobile, they decode the "minified data" to bitmap, by appending it with "fixed header" (Bitmaps.java#L111) & update image size in SoF segment. See ImageLoader.java#L750.