1

How can we check whether uploaded image is compressed or not?

I want to draw an image on canvas, then compress it using canvasContext.toDataUrl(type, quality) but this compression will only be applicable to that image if it is not already compressed.

Do you have any suggestion?

IAmInPLS
  • 4,051
  • 4
  • 24
  • 57
Adhum
  • 117
  • 1
  • 12
  • I don't believe there's any general "this image is compressed" flag available. You can [read the byte stream](http://stackoverflow.com/questions/3146483/html5-file-api-read-as-text-and-binary/3146509#3146509) and interpret it, but then you have to handle all the different image formats you want to handle and understand how to tell whether they're compressed. – T.J. Crowder Apr 07 '16 at 10:25
  • You could deduct it from the uploaded size and the pixel size. A big image with a low byte count is likely compressed. – Alexander Apr 07 '16 at 10:27
  • @Alexander, An original image can be of low quality.. I doubt that could be a parameter as OP is looking for _already compressed_ images.. – Rayon Apr 07 '16 at 10:29
  • Yes but I believe there is no clear approach to this, so I wanted to add this idea. A low quality image with a low (byte) size does not need to be compressed that much. – Alexander Apr 07 '16 at 10:51
  • @Alexander how to get pixel size? – Adhum Apr 07 '16 at 11:07
  • @T.J.Crowder can you please explain it a bit how to do that? – Adhum Apr 07 '16 at 11:08
  • @Adhum: The part about reading it is in the link. The other is far too broad for SO. – T.J. Crowder Apr 07 '16 at 11:11
  • I think that every raster image your browser can read is compressed, except bmp files. The parameter you talk about is a quality one, not compression. Now to detect if you need to add a lower quality than the one your user gave to you, you'll first have to get the type of file your user gave to you, if png, it's probable that it contains transparent pixels, then it's better to keep them than converting it to an opaque jpg. You may still win if the given file was a 24bit png : the canvas will convert it to a 8bit. For jpeg, you may loose every time, since you'll first uncompress it, then apply – Kaiido Apr 07 '16 at 13:34
  • a new deterioration algorithm. So if you do win in size, its because you lost in quality. But if you do want to do it anyway, IMK, there is no other way than do the conversion, convert it back to ablob, check the size of this blob and compare it against the uploaded file's one. – Kaiido Apr 07 '16 at 13:34
  • @Kaiido _you'll first uncompress it, then apply a new deterioration algorithm_ but how can I uncompress it? – Adhum Apr 07 '16 at 14:08
  • When drawn onto the canvas, you just have the raw pixels, the image has been uncompressed. Actually, every time the browser displays it it does uncompress it first. But for JPEG, you've got the raw pixels of the already deteriored image, with all it's artifacts. If you want to keep it uncompressed, use getImageData an the array of rgba values it will give you. – Kaiido Apr 07 '16 at 14:26

1 Answers1

1

You can pretty much determine if the file is compressed by looking at the file type. If you inspect the string toDataURL() produces, you will see a mime-type defining either a PNG or JPEG file - in some cases where browsers support other file formats you can also see BMP and ICO file formats.

We know that a PNG file is always compressed as the PNG standard only support compression type 0 which is LZ77 compression (on top of line filters which affects the final compressed size).

JPEG always compresses as it uses DCT.

Compression for BMP is optional as well as for TIFF, though no browsers I know of support TIFF out of the box. It's reasonable to assume BMP and ICO files are uncompressed. They do exist in compression forms such as RLE but these are rare and can cause problems for some BMP parsers. To be absolutely sure though, you would have to parse the binary data to look in the header for compression flags.

Notice that toDataURL() always work on a raw uncompressed bitmap. It does not matter if the original image drawn to the canvas was compressed or not - the original image is always converted to a raw bitmap before drawn (actually when it's loaded).

After calling toDataURL() however, the binary image that it produces internally is converted to a Base-64 string. This means an increase of size by 33% due to how Base-64 works. On top of that: each char in JavaScript occupies 2 bytes (this is not a problem of course when in a JavaScript environment). So the length of the string is not a good indicator as it may possibly exceed the raw size (width x height x 4) in some cases (toBlob() is in any case a better alternative than toDataURL() due to its higher performance and reduced size as well as being async/non-blocking).

  • I'm not sure I totally agree with the first paragraph of this answer : "*If you inspect the string `toDataURL()`*" As said later, "*`toDataURL()` always work on a raw uncompressed bitmap*" hence when you look at the output of this method, it's already too late. You could inspect the `.result` of a `readAsDataURL()` though, but in most cases, just looking at the file extension is also a good starting point. Also, I think a word about the `quality` parameter for both jpeg and webp would have been good. Finally, one can convert a dataURI back to a blob in browsers that don't support `toBlob()`. – Kaiido Apr 08 '16 at 02:18
  • Also, it could be a good place for a link to your [png-crush](https://github.com/epistemex/png-crush) script ;-) – Kaiido Apr 08 '16 at 04:08