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.

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

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?
  • Investigate a simple 'lossy' compression with QOI - halve the image size and compress, and on decode, just upscale. It'll likely be very visually similar, but also much smaller, but should compare with JPEG.

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.1.2.tar.gz (3.3 MB view details)

Uploaded Source

Built Distributions

qoi-0.1.2-cp310-cp310-win_amd64.whl (83.6 kB view details)

Uploaded CPython 3.10 Windows x86-64

qoi-0.1.2-cp310-cp310-win32.whl (70.3 kB view details)

Uploaded CPython 3.10 Windows x86

qoi-0.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (427.4 kB view details)

Uploaded CPython 3.10 manylinux: glibc 2.17+ x86-64

qoi-0.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (415.1 kB view details)

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

qoi-0.1.2-cp310-cp310-macosx_10_9_x86_64.whl (86.4 kB view details)

Uploaded CPython 3.10 macOS 10.9+ x86-64

qoi-0.1.2-cp39-cp39-win_amd64.whl (84.6 kB view details)

Uploaded CPython 3.9 Windows x86-64

qoi-0.1.2-cp39-cp39-win32.whl (70.8 kB view details)

Uploaded CPython 3.9 Windows x86

qoi-0.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (432.5 kB view details)

Uploaded CPython 3.9 manylinux: glibc 2.17+ x86-64

qoi-0.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (420.9 kB view details)

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

qoi-0.1.2-cp39-cp39-macosx_10_9_x86_64.whl (86.7 kB view details)

Uploaded CPython 3.9 macOS 10.9+ x86-64

qoi-0.1.2-cp38-cp38-win_amd64.whl (84.5 kB view details)

Uploaded CPython 3.8 Windows x86-64

qoi-0.1.2-cp38-cp38-win32.whl (70.8 kB view details)

Uploaded CPython 3.8 Windows x86

qoi-0.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (434.5 kB view details)

Uploaded CPython 3.8 manylinux: glibc 2.17+ x86-64

qoi-0.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl (423.4 kB view details)

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

qoi-0.1.2-cp38-cp38-macosx_10_9_x86_64.whl (83.8 kB view details)

Uploaded CPython 3.8 macOS 10.9+ x86-64

File details

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

File metadata

  • Download URL: qoi-0.1.2.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.1.2.tar.gz
Algorithm Hash digest
SHA256 45d081b6af04f3d851ce2f54c56c1bf355204ac48f14b8e79e6fae94fd46cc21
MD5 b6cde5f03deec3f88f7705cb9d114b4d
BLAKE2b-256 da5d84424c2cb2e66bede76da8cb6bdc060ac3caf61d79e5e1da4f5a9b22408b

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qoi-0.1.2-cp310-cp310-win_amd64.whl
  • Upload date:
  • Size: 83.6 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.1.2-cp310-cp310-win_amd64.whl
Algorithm Hash digest
SHA256 616173b1443da47755c741ab97d2f2413206804ded8be9eeca6ce16e9aeb757b
MD5 c4c0d088e3507688d9b20a100000d333
BLAKE2b-256 30460d74e51662b5831d6cbb7a400b486481701f93aeb34c183054c09772db55

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qoi-0.1.2-cp310-cp310-win32.whl
  • Upload date:
  • Size: 70.3 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.1.2-cp310-cp310-win32.whl
Algorithm Hash digest
SHA256 17c480df06de40fcb327ef195a55227508106630d175e23e028fd5efe49b1c07
MD5 b24445f45646c7f1592444d67e2c39ed
BLAKE2b-256 700c8a7aac088f8c4664fcf9eba7d8f32bb06706e9e9ed980aa8a21dc6d5dc18

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 3b9aebbc7f4ff803b811def8e6af7778e20e9db95da45c193f624c3ad99aa05a
MD5 31ca5b723b7365677559bf11a500e033
BLAKE2b-256 f514e13451a2452d6cabc38d6fb84b7d3942890b9e4c2d0a0848c31de933efb5

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 00bf41cfdb2a80e81df2998d66fa9f53ad7fc32bdec2d7dadb5a0ce56bd86fa5
MD5 b1e8064374597c08f08bf020375aa877
BLAKE2b-256 2355be2e99e8f085dd86aa125b2265be412f7326d80467e5efedfd975f8dcecb

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp310-cp310-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 ef08f882172df80b29a666d94261faa1e09a3c7ace4fd26bc18207f52a8350d9
MD5 7b242e8eac97fe2432b8ac01eb624265
BLAKE2b-256 644ff9f20f39ed6d23286cc9ba758abeb5f7709420816cec5bbb3a7dcfbc0d7c

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qoi-0.1.2-cp39-cp39-win_amd64.whl
  • Upload date:
  • Size: 84.6 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.1.2-cp39-cp39-win_amd64.whl
Algorithm Hash digest
SHA256 10a0ac7eb2decdb5c26d4d9159c58a5a40ad0c5db394f8c2bf1baac7c5f8a993
MD5 b99346dbb2c731821a02292c58cf81ab
BLAKE2b-256 e8c2badf37b87a1ce2d23df79dadfefbf566951ceeb9d4367b0a84d6d07c6708

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qoi-0.1.2-cp39-cp39-win32.whl
  • Upload date:
  • Size: 70.8 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.1.2-cp39-cp39-win32.whl
Algorithm Hash digest
SHA256 2c31f69cec30e0521efc487c9a8737f386c6943f58b814b5a2fed3544b457289
MD5 c3346bef951158fecda3f54772e3eaa3
BLAKE2b-256 9560b0a57819c1d585299464157ffc4e798a6eb776cadae82becd7778c18ee06

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 8dd815e9164ac34d3fd3e4a9f743db220aeab695af4c0a1b0252166cbcd94b06
MD5 789c5757294f5f390240770dbdcb03e4
BLAKE2b-256 17d8ff37b75a3c644d477c213859a8d0a598e3190757e17393b221960001a6af

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 f9178d5aeb8f87db0f8933ad118a122cf1a7a8141bc57458e32dc989ee10b2db
MD5 eaa3d5e3412b9881189e14ecceb2b585
BLAKE2b-256 c2e5f28b5315c2f3a2a468d7e48451b87001efda67ccbc9ec9c81dbe7c8ee5b6

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp39-cp39-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 88d24977205f693699db89201bd072654f89f6ea08250cc5bcb21238f3647bfc
MD5 2d7aa6946a257772787764087caa1605
BLAKE2b-256 c88b08c4cc4b04b529dc287dc0c209c4c371bade8395c163dae937f4f4f84536

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qoi-0.1.2-cp38-cp38-win_amd64.whl
  • Upload date:
  • Size: 84.5 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.1.2-cp38-cp38-win_amd64.whl
Algorithm Hash digest
SHA256 a602f3742fd08be9c9a52f4b9559b82135bfbb511a73d55d047cbd47e7670025
MD5 177a53370274d6e57b128eee32bd7a14
BLAKE2b-256 0b10e57368c7d29da8a097bed5aeae959d753bec5ed8df5ff026cfb18879ed42

See more details on using hashes here.

File details

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

File metadata

  • Download URL: qoi-0.1.2-cp38-cp38-win32.whl
  • Upload date:
  • Size: 70.8 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.1.2-cp38-cp38-win32.whl
Algorithm Hash digest
SHA256 8c0980b055601c3e52baf4b06f58833c7f879187ac4a3adc3bdb3d450f2359fb
MD5 86b047ff1611628978e6e7884ba52b9d
BLAKE2b-256 6f78c816f129cf52d8909594998d614b055df1eba0e87f746c9f4088439b9059

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f1a17302d1e2536adcfd5104ca40d1f526f6d68944f137d74406b3288c19d9ff
MD5 47fd1c8652da08516f98729d770c86c6
BLAKE2b-256 bcc4ba1676e028ed997b94e9ba3c1e095a89d9d83685112b7d20b30cd34a3f31

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl
Algorithm Hash digest
SHA256 adb3f310c3adbcbcb10d7038302327d43eab6ca3dba24f24fa3babf4a69de4b4
MD5 b43ad93c7409b22cfd280ad35a386b9d
BLAKE2b-256 77eb131a22c29f9f16f094789388abd6364adfaa93828749dfd52ba9ca4e74aa

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for qoi-0.1.2-cp38-cp38-macosx_10_9_x86_64.whl
Algorithm Hash digest
SHA256 d01308a2abd4e464ca438d827dac22489b2c2a4cae74e41f1a6d98587e324bbe
MD5 60be317627cd017d8fa4389ab47487c4
BLAKE2b-256 b733d542eb1e8fbd82f5e7d20d4042dc9b9db0012e0e6dbc714b738d34973372

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