cykooz_resizer is package with the optimized version of image resizing
based on Rust's crate fast_image_resize.
python3 -m pip install cykooz_resizerOr with automatically installing Pillow:
python3 -m pip install cykooz_resizer[pillow]Supported pixel types and available optimisations:
| Format | Description | SSE4.1 | AVX2 | Neon |
|---|---|---|---|---|
| U8 | One u8 component per pixel (e.g. L) |
+ | + | + |
| U8x2 | Two u8 components per pixel (e.g. LA) |
+ | + | + |
| U8x3 | Three u8 components per pixel (e.g. RGB) |
+ | + | + |
| U8x4 | Four u8 components per pixel (e.g. RGBA, RGBx, CMYK) |
+ | + | + |
| U16 | One u16 components per pixel (e.g. L16) |
+ | + | + |
| U16x2 | Two u16 components per pixel (e.g. LA16) |
+ | + | + |
| U16x3 | Three u16 components per pixel (e.g. RGB16) |
+ | + | + |
| U16x4 | Four u16 components per pixel (e.g. RGBA16, RGBx16, CMYK16) |
+ | + | + |
| I32 | One i32 component per pixel |
- | - | - |
| F32 | One f32 component per pixel |
+ | + | - |
| F32x2 | Two f32 components per pixel (e.g. LA32F) |
+ | + | - |
| F32x3 | Three f32 components per pixel (e.g. RGB32F) |
+ | + | - |
| F32x4 | Four f32 components per pixel (e.g. RGBA32F) |
+ | + | - |
Implemented resize algorithms:
- Nearest - is nearest-neighbor interpolation, replacing every pixel with the nearest pixel in the output; for upscaling this means multiple pixels of the same color will be present.
- Convolution with different filters:
- box
- bilinear
- catmull_rom
- mitchell
- gaussian
- lanczos3
- Super sampling - is resizing an image in two steps. The first step uses the "nearest" algorithm. The second step uses "convolution" with configurable filter.
from PIL import Image
from cykooz_resizer import FilterType, ResizeAlg, Resizer, ResizeOptions
resizer = Resizer()
dst_size = (255, 170)
dst_image = Image.new('RGBA', dst_size)
for i in range(1, 10):
image = Image.open('nasa_%d-4928x3279.png' % i)
resizer.resize_pil(image, dst_image)
dst_image.save('nasa_%d-255x170.png' % i)
# Resize using a bilinear filter and ignoring an alpha channel.
image = Image.open('nasa-4928x3279.png')
resizer.resize_pil(
image,
dst_image,
ResizeOptions(
resize_alg=ResizeAlg.convolution(FilterType.bilinear),
use_alpha=False,
)
)from cykooz_resizer import ImageData, PixelType, Resizer
def resize_raw(width: int, height: int, pixels: bytes):
src_image = ImageData(
width,
height,
PixelType.U8x4,
pixels,
)
resizer = Resizer()
dst_image = ImageData(255, 170, PixelType.U8x4)
# By default, Resizer multiplies and divides by alpha channel
# images with `U8x2`, `U8x4`, `U16x2` and `U16x4` pixels.
resizer.resize(src_image, dst_image)
return dst_imagefrom cykooz_resizer import Resizer, CpuExtensions
resizer = Resizer()
resizer.cpu_extensions = CpuExtensions.sse4_1
...from cykooz_resizer import Resizer, ResizeOptions, ResizerThreadPool
...
thread_pool = ResizerThreadPool(num_threads=6)
resizer = Resizer()
resizer.resize(
src_image,
dst_image,
ResizeOptions(thread_pool=thread_pool),
)
...Environment:
- CPU: AMD Ryzen 9 5950X
- RAM: DDR4 4000 MHz
- Ubuntu 24.04 (linux 6.17.0)
- Python 3.12
- Rust 1.93.0
- cykooz_resizer = "4.0" (single-threaded mode)
Other Python libraries used to compare of resizing speed:
- Pillow = "12.1.0" (https://pypi.org/project/Pillow/)
Resize algorithms:
- Nearest
- Convolution with Bilinear filter
- Convolution with Lanczos3 filter
- Source image nasa-4928x3279.png
| Package (time in ms) | nearest | bilinear | lanczos3 |
|---|---|---|---|
| Pillow | 1.04 | 103.70 | 190.19 |
| cykooz_resizer - none | 0.20 | 25.94 | 50.75 |
| cykooz_resizer - sse4_1 | 0.20 | 12.19 | 24.92 |
| cykooz_resizer - avx2 | 0.20 | 8.68 | 22.98 |
- Source image nasa-4928x3279.png has converted into grayscale image with one byte per pixel.
| Package (time in ms) | nearest | bilinear | lanczos3 |
|---|---|---|---|
| Pillow | 0.24 | 22.44 | 51.64 |
| cykooz_resizer - none | 0.17 | 5.34 | 12.41 |
| cykooz_resizer - sse4_1 | 0.17 | 2.14 | 5.87 |
| cykooz_resizer - avx2 | 0.17 | 1.90 | 4.59 |