qoi的简单包装器(https://github.com/phoboslab/qoi)
项目描述
QOI
QOI是一个简单的Python包装器,围绕“相当好的图像”格式qoi(https://github.com/phoboslab/qoi)。它是
- 无损的,压缩效果与PNG相当,但速度快!它比PNG在OpenCV或PIL中编码快10倍,解码快约5倍。
- 您可以通过一个简单的技巧使其有损,然后您可以获得与JPEG类似的压缩效果,但速度要快得多。(这些数字取决于您如何使JPEG或QOI“有损”)。这很酷。
- 多线程 - 没有GIL挂起。
安装
pip install qoi
示例
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). Note: HWC ordering
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
如果您想真正发挥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)
(单线程) 基准测试
如果我们考虑无损,那么我们通常与PNG进行比较。是的,还有其他一些,但它们不太常见。基准测试
测试图像 | 方法 | 格式 | 输入 (kb) | 编码 (ms) | 编码 (kb) | 解码 (ms) | SSIM |
---|---|---|---|---|---|---|---|
全黑 ('最佳'情况) | PIL | png | 6075.0 | 37.75 | 6.0 | 16.04 | 1.00 |
全黑 ('最佳'情况) | opencv | png | 6075.0 | 23.82 | 7.7 | 17.93 | 1.00 |
全黑 ('最佳'情况) | 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 |
随机噪声 (最差情况) | PIL | png | 6075.0 | 300.37 | 6084.5 | 46.30 | 1.00 |
随机噪声 (最差情况) | opencv | png | 6075.0 | 63.72 | 6086.9 | 14.01 | 1.00 |
随机噪声 (最差情况) | qoi | qoi | 6075.0 | 16.16 | 8096.1 | 7.67 | 1.00 |
因此,在压缩方面,qoi与PNG不相上下,但编码速度快4-20倍,解码速度快1.5-6倍。
注意
- 这里有一些额外的开销,因为PIL图像被转换回数组作为返回类型,以保持一致。从某种意义上说,这并不公平,因为如果您处理的是PIL图像,PIL将更快。另一方面,如果您的常见用例涉及数组(例如,用于计算机视觉),则这是合理的。
- 使用
python src/qoi/benchmark.py --implementations=qoi,opencv,pil --formats=png,qoi
在 i7-9750H 上生成。由于结果对于这种“普通”场景已经足够清晰,所以没有进行 OpenCV/PIL 优化(例如 SIMD 或pillow-simd
)。如果你想要深入了解,请随意!你可以轻松地自己运行这些测试。
如果我们考虑有损压缩,那么通常我们会将 JPEG 作为比较对象。通常情况下,将 QOI 与 JPEG 比较是不公平的,因为 QOI 是无损的。然而,我们可以进行一个小技巧,使 QOI 变得有损——将图像缩小,然后编码,然后在解码后按相同的比例放大。你可以看到我们在下面实现了这个技巧,将图像缩小到 40%,并使用 80 的 JPEG 质量度(这使得它们的视觉压缩效果相同,即 SSIM)。所以,结果(仅针对 koi photo
,因为其余的对于有损压缩来说不那么有意义/公平)
测试图像 | 方法 | 格式 | 输入 (kb) | 编码 (ms) | 编码 (kb) | 解码 (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 |
在这里,我们看到无损的 qoi
在压缩方面损失很大,正如预期的那样,因为这是有损与无损的对比。此外,qoi
的编码速度仅比编码速度快 1x-2x,解码速度快 1.5x-2x。然而,需要注意的是,这取决于指定的 JPEG 质量值——这里为 80,但 OpenCV 的默认值实际上是 95,这会导致压缩效果差 3 倍,并且速度略慢。
然而,这仍然是有损与无损的对比!如果你看 qoi-lossy-0.40x0.40
,其中我们将图像按上述方式缩小,你可以看到它的性能非常好。压缩比现在是 JPEG 的 3 倍(比无损 QOI 好出 5 倍,也与质量为 95 的默认 OpenCV JPEG 编码相同),但它的速度非常快——编码速度快 5x-10x,解码速度快 7x-8x。
当然,有些情况下使用 qoi
可能仍然比 JPEG 更有意义。即使是无损 QOI,如果大小不是问题,它也值得使用,因为它稍微快一点。但是,如果你使用“有损”QOI,你将获得“相当”(取决于 JPEG 质量值)的压缩效果,但速度要快得多。
注意
- 关于 PIL 的额外开销,请参见上文。
- 使用
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
在 i7-9750H 上生成。由于结果对于这种“普通”场景已经足够清晰,所以没有进行 OpenCV/PIL 优化(例如 SIMD 或pillow-simd
、libjpeg-turbo
、不同的 JPEG 质量值等)。如果你想要深入了解,请随意!你可以轻松地自己运行这些测试。
开发中
git clone --recursive https://github.com/kodonnell/qoi/
USE_CYTHON=1 pip install -e .[dev]
pytest .
我们使用 cibuildwheel
来构建所有轮子,它在 Github 动作中运行。如果你想检查本地是否成功,你可以尝试(未测试)
cibuildwheel --platform linux .
最后,当你满意时,提交一个 PR。
发布
当你在本地的 main
上时,使用 git tag vX.X.X
然后使用 git push origin vX.X.X
。这将推送标签,触发完整的 GitHub Action 和
- 构建源分布和轮子(针对各种平台)
- 推送到 PyPI
- 创建一个带有适当附件的新版本。
待办事项
- 将
benchmark.py
作为 CLI 在 setup.py 中实现 - 创建一个
qoi
CLI - 基准测试 - 添加真实图像,并与 QOI 进行性能比较,以查看 Python 包装器的开销。
setuptools_scm_git_archive
?- 代码补全?
讨论
包装还是重写?
目前,这只是一个简单的包装器。我们将让原始项目去做所有关于性能等困难的工作,以及维护(或不)兼容性或添加新功能等。我们不做任何进一步的声明——我们基本上只是将 C 功能性移植到(C)Python 中。
关于命名
让我们现在就使用 qoi
吧,因为
- 我们已经在 Python 中,而
py
在pyqoi
中似乎多余。就其价值而言,3 < 5
。 pyqoi
似乎是一个好的名字,用于 QOI 的纯 Python 版本(对于 pypy 等非常有用),但这不是本项目的目标。qoi
通常很新,所以我们现在不必过分思考它。如果需要,我们总是可以在以后重命名。
关于 ./src
的什么问题?
请参阅此处和此处。我没有全部阅读,但确实如此,当存在名为qoi
的文件夹时,import qoi
很烦人。
USE_CYTHON=1
?
请参阅此处。很有道理。
项目详情
qoi-0.7.0.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | f010831702b776c83a06b2560fba455df06988fa7563406704e70838526ca0d5 |
|
MD5 | f0500160501bce01f9672ebe30260f00 |
|
BLAKE2b-256 | 1c0e5ea8ff9c6c855982e55c833f6ed56efcf479326544ce9d18d3646ee815d7 |