image-rs / image

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

Allow operations like resize to return an image with the same pixel but a diferent subpixel #1484

Open JAicewizard opened 3 years ago

JAicewizard commented 3 years ago

I would like to be able to resize an image with pixel type Rgb<u8>down to a smaller size, and have the resize function return an image with pixel type Rgb<u16>

This can be useful for downscaling an image to a smaller size, while retaining as much colour accuracy as possible. I need to process an image in arbitrary amount of blocks, but need to retain as detailed colour data as possible. 256 possible colours per channel is not enough.

This could be a generic thing, so scaling from u8->u16 should compile to a single shift per subpixel, although I am not sure how to implement this.

My current workaround is to scale to 9x the target size, and downscale the last part in 2 loops but this requires 1 extra allocation for the image, and costs time for the second downscale.

fintelia commented 3 years ago

This seems reasonable to me. The actual code in the imageops module already converts to floating point and back so there probably doesn't need to be many changes. The hardest part might actually just be getting the generics to work out so that users that don't care about converting subpixels don't get confused.

JAicewizard commented 3 years ago

May I ask why it converts to floats? Any range beyond the target subpixel is useless, and you risk rounding errors with floats.

The hardest part might actually just be getting the generics to work out so that users that don't care about converting subpixels don't get confused.

That was my concern as well. Its possible to add a new function and just make resize wrap that function where the target subpixel defaults to the input subpixel. I am not very experienced with rust generics but I hope you figure out an elegant solution.

fintelia commented 3 years ago

May I ask why it converts to floats? Any range beyond the target subpixel is useless, and you risk rounding errors with floats.

You need a little bit extra range for doing interpolation, and the code is generic over subpixel type (including the floating point types) so I assume that was the easiest design. It isn't optimal but it works well enough.