image-rs / image

Encoding and decoding images in Rust
Apache License 2.0
4.97k stars 618 forks source link

Support for 1-bit Grayscale #2096

Open fosskers opened 10 months ago

fosskers commented 10 months ago

Thank you for this library, my company uses it for a variety of tasks.

I would like to be able write images that are encoded in 1-bit grayscale. I have such an image on-hand which unfortunately decodes into a Luma8 underneath, effectively 8x-ing the filesize when rewritten.

I discovered this by first noticing that my large image whose dimensions I had vastly shrunk and rewritten actually had increased its file size by 50% (12mb -> 18mb with default settings). I then inspected the input and output images with ImageMagick's identify tool to discover that the input image was 1-bit grayscale while the output's is 8-bit grayscale.

Draft

Overall this should drastically reduce output image sizes for Grayscale images that don't want to use the full 8-bit channel depth.

fosskers commented 10 months ago

In the input image, we see (some fields redacted):

Geometry: 13244x71130+0+0
Channels: 2.0
Channel depth:
    Gray: 1-bit
Channel statistics:
    Pixels: 942045720
Colors: 2
Histogram:
      56641321: (0,0,0) #000000 gray(0)
     885404399: (255,255,255) #FFFFFF gray(255)
Filesize: 11.771MiB

while in the output image (processed by image) we see:

Geometry: 6904x37079+0+0
Channels: 2.0
Channel depth:
    Gray: 8-bit
Channel statistics:
    Pixels: 255993416
Colors: 256
Histogram:
  (many entries)
Filesize: 15.3349MiB

1/4 the number of pixels, yet a larger file size.

fintelia commented 10 months ago

A bunch of places in this library require that pixels are an integer number of bytes, which would break for ImageLuma1.

However, that doesn't mean we cannot support Luma1 images for limited cases. We already have ExtendedColorType::L1 to describe the color type and the ImageDecoder::original_color_type method to reveal that a conversion is being applied. We've also been wanting to switch the ImageEncoder trait to allow passing any ExtendedColorType when writing image (while still allowing individual format encoders to reject color types they do not support). At that point, we'd be able to start adding Luma1 support to individual encoders

fintelia commented 7 months ago

Update on this: as of the 0.25 release, ImageEncoder::encode now takes an ExtendedColorType. If anyone is interested in adding 1-bit grayscale support to any of our encoders, it should now be possible

fosskers commented 7 months ago

Thanks for the update!