go-qrcode icon indicating copy to clipboard operation
go-qrcode copied to clipboard

Feature(image-compression): PNG bit depth must be 1

Open stokito opened this issue 1 year ago • 8 comments

The QR codes generated by https://github.com/skip2/go-qrcode are much smaller. I compared and the main difference is that bit depth is 24 while it's enough only 1 bit (black/white).

Also the PNG format is basically a gzip-ed BMP image. So we can also play with it's compression. Currently the default compression is used but it make sense to use maximum compression by default. It will make generation slightly slower but just a little bit. The images are often transmitted over internet and network bandwidth is more critical than CPU time. It would be nice to be able to generate BMP images so that they can be compressed freely by a developer with a compressor that best fits to their needs.

stokito avatar Aug 04 '22 21:08 stokito

I just checked the skip2/go-qrcode sources and yes, the Best PNG compression is used:

encoder := png.Encoder{CompressionLevel: png.BestCompression}

I found that I can use a custom compressor but still I have a large legacy codebase to change. Will you accept a PR with changing of the compression level?

stokito avatar Aug 04 '22 21:08 stokito

I changed code to use the best compression and for the alphanum json-like string of 641 length:

  • current 1258
  • with best 1215

So the diff is only 43 bytes. Not that much. Maybe for larger strings it will be more saving but larger strings are highly unlikely.

Looks like in fact we can do opposite and use no compression. I tried and result is 12813 bytes e.g. about 10 times more. So just a normal compression level looks a good enough.

Meanwhile the skip2/go-qrcode generated a qr code of only... 641 bytes. So maybe the main reason is the bit depth and compression may be not needed.

I created a Gist to compare https://gist.github.com/stokito/4285f23844c8c51eb4f41adcc374af26

stokito avatar Aug 04 '22 23:08 stokito

That's the weakness of this project since I didn't think about image compression in this library. I feel grateful for your suggestion, and it's a little time-consuming for me to apply compression tech in code, I'm not familiar with this technology, so you could not see the effect in time, also you can create any pull request to speed the progress. you seem to be good at this, hah~

yeqown avatar Aug 05 '22 14:08 yeqown

I got that since I used github.com/fogleman/gg to enhance the image output function, but it's using *image.RGBA which uses color.RGBA 32bit however skip2 is using image.Paletted and color.Gray16 16bit to represent colors.

  • https://github.com/fogleman/gg/blob/8febc0f526adecda6f8ae80f3869b7cd77e52984/context.go#L60
  • https://github.com/skip2/go-qrcode/blob/da1b6568686e89143e94f980a98bc2dbd5537f13/qrcode.go#L308-L310
  • https://github.com/golang/go/blob/19309779ac5e2f5a2fd3cbb34421dafb2855ac21/src/image/image.go#L1134-L1145

yeqown avatar Aug 08 '22 05:08 yeqown

Interesting. I don't know how it must be implemented properly. You see, there is also a support of built-in images which makes all more complicated

stokito avatar Aug 08 '22 08:08 stokito

The most feasible way to achieve your purpose is to implement another writer that output image in skip2's way. Actually, this is also the greatest purpose why I refactored this repository into v2. At the same time, I'm sorry about I can't change the standard writer because it's designed to support the powerful shape of QR Code, although we can hack in ImageEncoder progress to compress the output images, it's an ugly code, doesn't it?

yeqown avatar Aug 09 '22 07:08 yeqown

Well, according to a test from my gist the difference is at least twice smaller image. For me me it seems like something that is highly good thing to improve

stokito avatar Aug 09 '22 13:08 stokito

Yup, I'll try to implement another simple writer to test whether we can generate a smaller image. plz stay focused on this issue, I'll update here if I get any progress.

yeqown avatar Aug 10 '22 02:08 yeqown

Hi, thank you I tried it and results are impressive. On my data set the resulted file 1.4Kb instead of current 37kb. That means that I can now just store it in DB and return in JSON field base64 encoded. Because now I have to upload the QR code image to AWS S3.

Here I created a benchmark to comapare https://github.com/stokito/go-qrcode-libs-compare

Content length: 274
qr-yeqown-none.png: 300085
qr-yeqown-speed.png: 5459
qr-yeqown.png: 3820
qr-yeqown-best.png: 3524
qr-yeqown-current.jpeg: 37153
qr-yeqown-v2.jpg: 35978
qr-yeqown-v2.png: 3846
qr-yeqown-v2-compressed.png: 1491
qr-yeqown-v2-compressed-1px.png: 1000
qr-skip2-best-1px.png: 1003    
qr-skip2-best-1px-alphanum.png: 849

The most confusing thing is that the qr-yeqown-v2-compressed-1px.png image is exactly pixel by pixel the same as generated qr-yeqown-v2-compressed-1px.png but their size is still differs. I don't know what why they differs but this is interesting.

Thank you!

stokito avatar Sep 23 '22 19:09 stokito