Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support gray8 #170

Closed
rxrbln opened this issue Jan 24, 2022 · 19 comments
Closed

Support gray8 #170

rxrbln opened this issue Jan 24, 2022 · 19 comments

Comments

@rxrbln
Copy link

rxrbln commented Jan 24, 2022

Congratulations to the surprisingly simple and quite ok image format!

IMHO it would be nice to officially support 8-bit gray. It could be simply indicated by channel = 1 or 2 for w/ or w/o alpha and only using the 1st channel for the gray values.

@jstavats
Copy link

I also agree. The encoding will need to be different as a lot of the image space savings here are coming from not needing 3 bytes per pixel, and of course grey8 is only one byte per pixel in the first place so that wouldn't help. I've got some ideas on how to do some similar things to 8 bit grey images and may take a whack at that. If I do and come up with something half decent would it be considered as an addition to the format? (Either way it would still be useful to me)

@rxrbln
Copy link
Author

rxrbln commented Jan 24, 2022

I was more thinking of just using the quite ok encoding without any further modifications and keep stuff as simple as possible.

@oscardssmith
Copy link

The problem with this is that QOI will generally not provide compression for single channel images. The 1 channel variant would probably need tags that let you compress 2 pixels with small diffs into 1 byte.

@rxrbln
Copy link
Author

rxrbln commented Jan 24, 2022

It would still run length compress though. One could use delta encoding to compress 3 similar pixels at a time.

@jstavats
Copy link

jstavats commented Jan 27, 2022

So, I implemented a 1-byte per pixel greyscale version encoder/decoder, and I'm quite happy with both the speed and compression. It is "somewhat" faster than libpng using the intel performance primitives version of the zlib compression library with its fastest settings (which can be considerably faster than the stock zlib as it allows a special non-optimal compression type for speed). When in that mode this encoder also results in a smaller file than png. If I change the zlib compression level it can compress the data better, but then it takes considerably longer to save the image. I'll post what I'm using for the "chunks" when I clean it up a bit, they are "heavily inspired" by the colour chunks.

@jstavats
Copy link

I've changed the format on the 1 byte per pixel greyscale version i've been playing with. I did a benchmark of it here, which I'm quite happy with:

https://github.com/jstavats/qoi/blob/master/qoi-grey8-benchmark.txt

There are problems with decoded image not quite matching encoded periodically, and the comments in the file are old. But I'm getting happier with the structure and performance of it.

@bitbank2
Copy link

bitbank2 commented Feb 9, 2022

I also implemented a 1-channel modification to QOI which is still simple and similar to QOI's 3/4-channel, but compresses much better than treating 1-channel as 4. It's also quite fast. Speaking of speed, I sped up (On my MacBook M1) the decoding by 1.8x and the encoding by 2.3x while still supporting the original "intent" of the C code. You can see it in my fork here: https://github.com/bitbank2/qoi

@jstavats
Copy link

jstavats commented Feb 9, 2022

Interesting 1-channel version, what is the purpose of the QOI_OP_BADRUN8? I think its the same as my QOI_OP1_RAW which is a raw series of bytes (up to 62-ish). I've also been doing further work and tried a AVX2 version of my greyscale version (adding to the colour version found here https://github.com/MKCG/qoi/tree/simd_avx2_implementation , thanks @MKCG ). Quite fun. I removed the index as that opcode was 1 byte and thought that the byte could just be stored in the same space. How often does the QOI_OP_INDEX8 get emitted? 8 indices doesn't sound like enough options for greyscale, though it might be for screenshots/pixel art, but I'd expect those in a palettized format as they'd largely be colour. One can think of the colour QOI index colours as a sort of dynamic palette already.

I found that I'm not really getting the expected speedups over my libpng with intel performance primitives' accelerated zlib version that I was hoping for. It seems for greyscale to be only getting about ~20% faster encode than that. I also found that the libpng encode settings in the qoibench.c are the default which are not speed optimal. When I add the following lines before the png_set_IHDR call:
png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
png_set_compression_level(png, 1);
png_set_compression_strategy(png, 3);

I get a more time efficient encoding with result of (non-accelerated zlib and default 3/4 byte colour handling):

# Grand total for ../qoi_benchmark_suite/
        decode ms   encode ms   decode mpps   encode mpps   size kb    rate
libpng:       8.1        14.2         57.08         32.67       474   28.9%
stbi:         7.5        68.4         62.06          6.78       561   34.2%
qoi:          2.2         2.9        207.29        157.68       463   28.2%

same zlib and AVX2 default colour handling

# Grand total for ../qoi_benchmark_suite/
        decode ms   encode ms   decode mpps   encode mpps   size kb    rate
libpng:       7.8        13.6         59.53         34.07       474   28.9%
stbi:         7.2        59.8         64.14          7.76       561   34.2%
qoi:          2.1         1.6        223.18        292.51       463   28.2%

@bitbank2
Copy link

bitbank2 commented Feb 9, 2022

Are you running grayscale images through the original QOI benchmark code? If so, the "rate" value is quite distorted. For example, a 1000x1000 grayscale image will be promoted to 4-bytes per pixel before compression and the results will be calculated as if it's compressing a 4MB file instead of 1MB file.

To answer your question, yes, my BADRUN8 op is for runs of "uncompressible" pixels. For gradient areas, the DIFF8 op allows 2:1 compression and for areas with few unique 'colors' used, the INDEX8 op allows 2:1 compression. The combination of runs of repeating pixels along with those 2 2:1 ops allows much better compression of grayscale images than the original QOI way of doing it.

@jstavats
Copy link

jstavats commented Feb 9, 2022

When testing grey, I'm forcing the png to be loaded to be grey, using the commend line switch --forcebpp 1 I added to the version of qoibench.c here: #173

My images are the qoi_benchmark_suite ones but all are loaded in as greyscale.

I can see the index thing helping with non-photo images with abrupt intensity changes. My use case is machine vision so "real" images. The question was more how often do you get 2 pixels back to back that both hit index values.

@bitbank2
Copy link

bitbank2 commented Feb 9, 2022

My grayscale method seems to do better on some images and worse on others compared to the original QOI. I'll spend some time tweaking it and see if I can do better. It's a first pass effort after all :)
My cleanup of the general C code speeds it up in every case. The removal of the RGBA union is one reason.

I haven't done a detailed statistical dump of the ops used, so I can't really say how often the pair of index values works. I think some of the bulk is from not fleshing out the BADRUN code to look for runs of bad pixels. That was unfinished business that I need to code still.

@bitbank2
Copy link

bitbank2 commented Feb 9, 2022

Confirmed - the lack of "bad runs" > 1 was hurting my compressed size. New results for my screenshot image (now it beats PNG in size and speed):

/Users/laurencebank/Projects/qoi/images/gray_screenshot.png size: 1113x726

    decode ms   encode ms   decode mpps   encode mpps   size kb    rate

libpng: 3.3 32.9 241.69 24.59 78 10.0%
stbi: 6.2 37.7 129.83 21.45 53 6.7%
qoi: 5.4 2.2 148.34 371.29 71 9.0%

P.S. A few runs through the profiler show that the INDEX8 op is much more 'popular' than the DIFF8, but there's a pretty even distribution amongst all of the OPs I created.

@bitbank2
Copy link

bitbank2 commented Feb 10, 2022

I just did a tweak to my fork of QOI to improve compression of images with lots of repeating pixels. It improves my test images by 10% in size without affecting speed nor being detrimental to the compression of other images. This makes it incompatible with the current QOI 'standard', but at this point I assume my QOI version will become a different 'standard'.

@rxrbln
Copy link
Author

rxrbln commented Feb 10, 2022

do you have more details about the changes? One major thing I would find missing in the current standard, is x and y resolution in dpi. One of the more important meta data commonly needed, ... :-/ do you have a patch for the latest gray work?

@bitbank2
Copy link

@rxrbln I used a couple of "counts" within the RUN tag to add RUN256 and RUN1024. For images with lots of repeating pixels this saves some space. I don't have a "patch" for grayscale to work, but my fork has proper grayscale support by changing the bench tool and the QOI.h codec.

@rxrbln
Copy link
Author

rxrbln commented Feb 10, 2022

if you have a fork you can easily generate the git diff ;-) it would certainly help to move any qoi gray compression forward, ... or if you prefer write it down in a form like the standard spec sheet.

@bitbank2
Copy link

I'm still experimenting with the changes, so I don't think it makes sense to call it 'done'. I just started working on QOI this week and would like to test it on more images and add palette color image support. If you want to try my code, just clone my fork and play with it.

@bitbank2
Copy link

I've successfully added support for RGB565 (and grayscale). I deleted my fork of QOI and am starting a new format (Simple Lossless Image Codec), since my format is no longer QOI compatible. I'll share it publicly when I'm finished with it.

@bitbank2
Copy link

bitbank2 commented Mar 4, 2022

My SLIC codec is now public. Here's how it's different from QOI:

  • Supports RGB565, 8-bit grayscale and 8-bit palette images (in addition to 24/32-bit color)
  • Has a C and C++ (Arduino style) API
  • Designed to run on big or small memory CPUs. Can even run on an Arduino Uno (ATMega328 w/2K RAM)
  • No external dependencies, no need for malloc/free, no need for a file system
  • Includes optional callback functions to use it with a file system
  • Includes a command line example program which can convert BMP files into .slc and the reverse
  • Improves the compression of long runs of repeating pixels for all pixel types compared to QOI

https://github.com/bitbank2/SLIC

@phoboslab phoboslab closed this as not planned Won't fix, can't repro, duplicate, stale Aug 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants