Skip to main content

A simpler wrapper around qoi (https://github.com/phoboslab/qoi)

Project description

PyPI version fury.io

QOI

A simple Python wrapper around qoi, the "Quite OK Image" image format. It's

  • Lossless with comparable compression to PNG, but fast! It encodes 10x faster and decodes around 5x faster than PNG in OpenCV or PIL.
  • You can make it lossy with a simple trick, and then you can get similar compression to JPEG but a whole lot faster. (These number vary a lot depending on how "lossy" you make JPEG or QOI). That's cool.
  • Multi-threaded - no GIL hold-ups here.

Install

pip install qoi

Example

import numpy as np
import qoi

# Get your image as a numpy array (OpenCV, Pillow, etc. but here we just create a bunch of noise)
rgb = np.random.randint(low=0, high=255, size=(224, 244, 3)).astype(np.uint8)

# Write it:
_ = qoi.write("/tmp/img.qoi", rgb)

# Read it and check it matches (it should, as we're lossless)
rgb_read = qoi.read("/tmp/img.qoi")
assert np.array_equal(rgb, rgb_read)

# Likewise for encode/decode to/from bytes:
bites = qoi.encode(rgb)
rgb_decoded = qoi.decode(bites)
assert np.array_equal(rgb, rgb_decoded)

# Benchmarking
from qoi.benchmark import benchmark
benchmark()  # Check out the arguments if you're interested

If you want to really max out your CPU:

from concurrent.futures import ThreadPoolExecutor, wait
import numpy as np
import qoi

RGB = np.random.randint(low=0, high=255, size=(224, 244, 3)).astype(np.uint8)

def worker():
    bites = bytearray(qoi.encode(RGB))
    img_decoded = qoi.decode(bites)

print("Go watch your CPU utilization ...")
with ThreadPoolExecutor(8) as pool:
    futures = [pool.submit(worker) for _ in range(10000)]
    wait(futures)

(Single-threaded) Benchmarks

If we consider lossless, then we're generally comparing with PNG. Yup, there are others, but they're not as common. Benchmarks:

Test image Method Format Input (kb) Encode (ms) Encode (kb) Decode (ms) SSIM
all black ('best' case) PIL png 6075.0 37.75 6.0 16.04 1.00
all black ('best' case) opencv png 6075.0 23.82 7.7 17.93 1.00
all black ('best' case) qoi qoi 6075.0 4.13 32.7 2.67 1.00
koi photo PIL png 6075.0 849.07 2821.5 85.46 1.00
koi photo opencv png 6075.0 95.24 3121.5 44.34 1.00
koi photo qoi qoi 6075.0 28.37 3489.0 17.19 1.00
random noise (worst case) PIL png 6075.0 300.37 6084.5 46.30 1.00
random noise (worst case) opencv png 6075.0 63.72 6086.9 14.01 1.00
random noise (worst case) qoi qoi 6075.0 16.16 8096.1 7.67 1.00

So qoi isn't far off PNG in terms of compression, but 4x-20x faster to encode and 1.5x-6x faster to decode.

NB:

  1. There's additional overhead here with PIL images being converted back to an array as the return type, to be consistent. In some sense, this isn't fair, as PIL will be faster if you're dealing with PIL images. On the other hand, if your common use case involves arrays (e.g. for computer vision) then it's reasonable.
  2. Produced with python src/qoi/benchmark.py --implementations=qoi,opencv,pil --formats=png,qoi on an i7-9750H. Not going to the point of optimised OpenCV/PIL (e.g. SIMD, or pillow-simd) as the results are clear enough for this 'normal' scenario. If you want to dig further, go for it! You can easily run these tests yourself.

If we consider lossy compression, again, JPEG is usually what we're comparing with. Normally, it'd be unfair to compare QOI with JPEG as QOI is lossless, however we can do a slight trick to make QOI lossy - downscale the image, then encode it, then upsample it by the same amount after decoding. You can see we've implemented that below with a downscaling to 40% and JPEG quality of 80 (which results in them having the same visual compression i.e. SSIM). So, results (only on koi photo as the rest are less meaningful/fair for lossy):

Test image Method Format Input (kb) Encode (ms) Encode (kb) Decode (ms) SSIM
koi photo PIL jpg @ 80 6075.0 47.67 275.2 24.01 0.94
koi photo opencv jpg @ 80 6075.0 24.03 275.3 19.58 0.94
koi photo qoi qoi 6075.0 23.17 3489.0 12.94 1.00
koi photo qoi-lossy-0.40x0.40 qoi 6075.0 4.38 667.5 2.96 0.94

Here we see that lossless qoi is losing out considerably in compression, as expected for lossy vs lossless. Also, qoi is only 1x-2x faster of encoding, and 1.5x-2x faster for decoding. However, it's important to note that this varies a lot depending on the jpeg quality specified - here it's 80 but the default for OpenCV is actually 95 which is 3x worse compression and a bit slower.

However, that's still lossy vs lossless! If you look at qoi-lossy-0.40x0.40 where we downscale as above, you can see that it can perform really well. The compression ratio is now only 3x that of JPEG (and 5x better than lossless QOI, and also the same as the default OpenCV JPEG encoding at a quality of 95), but it's so fast - 5x-10x faster encoding, and 7x-8x faster decoding.

Anyway, there are definitely use cases where qoi may still make sense over JPEG. Even lossless QOI can be worth it if size isn't an issue, as it's a bit faster. But if you use the "lossy" QOI, you're getting "comparable" (depending on JPEG quality) compression but much faster.

NB:

  1. See above re additional PIL overhead.
  2. Produced with python src/qoi/benchmark.py --images=koi --implementations=qoi,qoi-lossy,opencv,pil --formats=jpg,qoi --qoi-lossy-scale=0.4 --jpeg-quality=0.8 on an i7-9750H. Not going to the point of optimised OpenCV/PIL (e.g. SIMD, or pillow-simd, libjpeg-turbo, different JPEG qualities, etc.) as the results are clear enough for this 'normal' scenario. If you want to dig further, go for it! You can easily run these tests yourself.

Developing

git clone --recursive https://github.com/kodonnell/qoi/
USE_CYTHON=1 pip install -e .[dev]
pytest .

We use cibuildwheel to build all the wheels, which runs in a Github action. If you want to check this succeeds locally, you can try (untested):

cibuildwheel --platform linux .

Finally, when you're happy, submit a PR.

Publishing

When you're on main on your local, git tag vX.X.X then git push origin vX.X.X. This pushes the tag which triggers the full GitHub Action and:

  • Builds source distribution and wheels (for various platforms)
  • Pushes to PyPI
  • Creates a new release with the appropriate artifacts attached.

TODO

  • Make benchmark.py a CLI in setup.py
  • Create a qoi CLI
  • Benchmarks - add real images, and also compare performance with QOI to see overhead of python wrapper.
  • setuptools_scm_git_archive?
  • Code completion?

Discussion

Wrap or rewrite?

For now, this is just a simple wrapper. We'll leave the original project to do all the hard work on performance etc., and also maintaining (or not) compatibility or adding new features etc. We make no claims to do any more than that - we're basically just porting that C functionality to (C)Python.

On the name

For now, let's rock with qoi because

  • We're already in python, and the py in pyqoi seems redundant. For what it's worth, 3 < 5.
  • pyqoi seems like a good name for a python-only version of QOI (useful for pypy etc.), which this isn't.
  • qoi is generally new so let's not overthink it for now. We can always rename later if needed.

What's up with ./src?

See here and here. I didn't read all of it, but yeh, import qoi is annoying when there's also a folder called qoi.

USE_CYTHON=1?

See here. Fair point.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qoi-0.2.0.tar.gz (3.3 MB view details)

Uploaded Source

Built Distributions

qoi-0.2.0-cp310-cp310-win_amd64.whl (84.7 kB view details)

Uploaded CPython 3.10 Windows x86-64

qoi-0.2.0-cp310-cp310-win32.whl (71.1 kB view details)

Uploaded CPython 3.10 Windows x86

qoi-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (431.3 kB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

qoi-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (420.2 kB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ i686 manylinux: glibc 2.5+ i686

qoi-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl (87.2 kB view details)

Uploaded CPython 3.10 macOS 10.9+ x86-64

qoi-0.2.0-cp39-cp39-win_amd64.whl (85.7 kB view details)

Uploaded CPython 3.9 Windows x86-64

qoi-0.2.0-cp39-cp39-win32.whl (71.6 kB view details)

Uploaded CPython 3.9 Windows x86

qoi-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (438.1 kB view details)

Uploaded CPython 3.9 manylinux: glibc 2.17+ x86-64

qoi-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (425.5 kB view details)

Uploaded CPython 3.9 manylinux: glibc 2.17+ i686 manylinux: glibc 2.5+ i686

qoi-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl (87.5 kB view details)

Uploaded CPython 3.9 macOS 10.9+ x86-64

qoi-0.2.0-cp38-cp38-win_amd64.whl (85.6 kB view details)

Uploaded CPython 3.8 Windows x86-64

qoi-0.2.0-cp38-cp38-win32.whl (71.6 kB view details)

Uploaded CPython 3.8 Windows x86

qoi-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (440.1 kB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ x86-64

qoi-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (428.7 kB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ i686 manylinux: glibc 2.5+ i686

qoi-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl (84.8 kB view details)

Uploaded CPython 3.8 macOS 10.9+ x86-64

File details

Details for the file qoi-0.2.0.tar.gz.

File metadata

  • Download URL: qoi-0.2.0.tar.gz
  • Upload date:
  • Size: 3.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for qoi-0.2.0.tar.gz
Algorithm Hash digest
SHA256 605859264517cac44769a0e34102831e0e6d1033a1ca8b74b7047e07035ca54d
MD5 aa87b1dc73fd5baf773fb7575a3ec4ad
BLAKE2b-256 2059cd036658c331ebdaa4bd137def81c28a31fc04b41eebe0bb69afd211eaf0

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp310-cp310-win_amd64.whl.

File metadata

  • Download URL: qoi-0.2.0-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 84.7 kB
  • Tags: CPython 3.10, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for qoi-0.2.0-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 0ac355d272103f07d24bd7ae7e44096d274b4ec93eb05f724002677c97dae15c
MD5 dd9a938b8a18133376855de8f23a3cfc
BLAKE2b-256 61e966cca4c9d505087e4019de43560cc4e6b0b66ab12eb94c4bdee8948945f1

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp310-cp310-win32.whl.

File metadata

  • Download URL: qoi-0.2.0-cp310-cp310-win32.whl
  • Upload date:
  • Size: 71.1 kB
  • Tags: CPython 3.10, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for qoi-0.2.0-cp310-cp310-win32.whl
Algorithm Hash digest
SHA256 3a63d1ac5f4249a46c72ad08b4e6405dda9949b9a8588dd26988b837f85ae25d
MD5 26e1a4df61e0a3fa86f68890933ee14a
BLAKE2b-256 d6895b306fb3a4ae17bdd01b0c8d94bb3b89145beb53af9224415db88f99c712

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 e754995e4a6368b72c2e73bd74e5495f64f7cdc8b2cb442e957ea5f941db8e87
MD5 163142b7bbdd72986592c97035f1643a
BLAKE2b-256 25c9228112fd0cdeb2c6192ce3f0380809fafd2ae4bd5c685a722263b532a8ad

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 c680a355c4c8ed1385bea78582328c600afb07045e770e1d9ccde7a3378bd28c
MD5 d968c9b7cb73e96a86da974fc1bc005d
BLAKE2b-256 1176bb33e6080f31bdcb787983f6e427e6ccafb83a14282790c27ef84697d295

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 84f353e0a2be969cdd0de431d48fc4430f4457969a53ba0091b5871134dc3d7f
MD5 d0a52179301d0d32d3c44927539195a6
BLAKE2b-256 a7ae5b65b4680a68a56f9ea2e1950e9895015e595b95a86640dc2e353fcf29d6

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp39-cp39-win_amd64.whl.

File metadata

  • Download URL: qoi-0.2.0-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 85.7 kB
  • Tags: CPython 3.9, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for qoi-0.2.0-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 77e92bb19706a81ef11026c5f7deb8f5a0d9795c42815913c5463623b600bb6c
MD5 ba39e14f54584166feb3fdc725a6d179
BLAKE2b-256 83231e28a1530b3dd2cbc75ec14837bbd541691e77e84df37161eb071ff1c94d

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp39-cp39-win32.whl.

File metadata

  • Download URL: qoi-0.2.0-cp39-cp39-win32.whl
  • Upload date:
  • Size: 71.6 kB
  • Tags: CPython 3.9, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for qoi-0.2.0-cp39-cp39-win32.whl
Algorithm Hash digest
SHA256 cd649fb66913d74452eeeb82abee247a5cc852d8b8eed3334192c719d9978a41
MD5 e5bdfe28823589212cf6ca8766b17fc0
BLAKE2b-256 bdb800c998c939f638ab8c42737d423d8fa7a71a040d7192cdf6bf10d0f49b99

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 042bde603197f610544a2833bc5a092134b26964013de4ee5e061b9cee6fe752
MD5 1465ec283c3f4b0f13829d27c6d5f871
BLAKE2b-256 e70ad06de1980447b1da7923144058d052eeda17d7949ee4aeb36dd8a7f4bdb7

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 0c1826a7cf9e2379d81fed8cc63805cbc5550c946ebb617bfd0555b0aa38e816
MD5 1fad0de7ff85f02d230dade7c8ae9da8
BLAKE2b-256 bcd361184e051ce124737035820453a6522d4c1fece33c61a35d3f1fe9bc9709

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 9ad8ba4445509db00917e8af6f532c4fe4848e5194e172a052397f9336cc9f83
MD5 fc18429a1c6596f9bb339142e54b1222
BLAKE2b-256 130b63e5752be317705e79c5f0dadae70555cce716b1bcf7ff1eaf4eb7654594

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp38-cp38-win_amd64.whl.

File metadata

  • Download URL: qoi-0.2.0-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 85.6 kB
  • Tags: CPython 3.8, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for qoi-0.2.0-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 c19e79df390ed48fecaf203a7f909e65f01f8d2109e2742d3b01e2a73ac0c1f9
MD5 0779e2e7fc1d4660f3a838ad3c17f7f1
BLAKE2b-256 ebc4a77d208204e360e83c3a74fd0b3dace0daa38904e71efb3d6666882ee625

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp38-cp38-win32.whl.

File metadata

  • Download URL: qoi-0.2.0-cp38-cp38-win32.whl
  • Upload date:
  • Size: 71.6 kB
  • Tags: CPython 3.8, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.9.13

File hashes

Hashes for qoi-0.2.0-cp38-cp38-win32.whl
Algorithm Hash digest
SHA256 4a3bcb063aba8a58cfa8f698dcba281eab1fbdf0ec6d96bdb945d0b40130d949
MD5 117399c243448c8457c13bdf88341f8e
BLAKE2b-256 a6ea7f10ac96e591490a662a4b3b10069f9dac53df43aae78fb4e688f3f0696e

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 6690f4fe327dd05be98f71b7145219b589771e284453f0f35f173537b7d95570
MD5 b4011cbcab6cb13685330ac46eb11ae7
BLAKE2b-256 c0d792228ba96e370595c2763eb47134c5e0e18cc1f94ca5268b2de7b54a9bba

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 fad7c0bb2fa4550c21065669e42b3a229191f3858aa56413ea3de69e0686fe98
MD5 78a4fbe937c00bee5ad0da55ec0063a6
BLAKE2b-256 224343402a51305ecab05968ec420e4acc1c9067b982d39bbccfa90fa1080662

See more details on using hashes here.

File details

Details for the file qoi-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl.

File metadata

File hashes

Hashes for qoi-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 7e8a34a787ea6e39583b62e73782c59911caded071b840cf0898860c6646a416
MD5 254f3ca213c6b99bcbd9267d44c52a6d
BLAKE2b-256 20ad3660f12d3634f00ecd174b6d69bb068880946d270d6d2b477453313d2837

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page