# hquant
High quality quantization for [Pillow](https://python-pillow.org/) images.
Pillow uses Euclidean distance for color matching during quantization, which yields poor results
when using RGB due to the nonlinearity of color perception. YCbCr is a better fit for Euclidean
distance, but Pillow does not natively support quantization of such images.
This module improves the quantization by converting the images to YCbCr, and then tricking
Pillow into thinking it's an RGB image so quantization works.
## Samples
The following samples dither the original image to the classic 16 colors supported in all terminals,
using RGB (as Pillow quantize method would normally use), CIELAB and YCbCr.
| Original | RGB | CIELAB | YCbCr |
| ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| ![Original Lena](https://github.com/socram8888/hquant/blob/master/samples/lena_orig.png?raw=true) | ![Lena using RGB](https://github.com/socram8888/hquant/blob/master/samples/lena_rgb.png?raw=true) | ![Lena using CIELAB](https://github.com/socram8888/hquant/blob/master/samples/lena_lab.png?raw=true) | ![Lena using YCbCr](https://github.com/socram8888/hquant/blob/master/samples/lena_ycbcr.png?raw=true) |
| ![Original Kobold](https://github.com/socram8888/hquant/blob/master/samples/kobold_orig.png?raw=true) | ![Kobold using RGB](https://github.com/socram8888/hquant/blob/master/samples/kobold_rgb.png?raw=true) | ![Kobold using CIELAB](https://github.com/socram8888/hquant/blob/master/samples/kobold_lab.png?raw=true) | ![Kobold using YCbCr](https://github.com/socram8888/hquant/blob/master/samples/kobold_ycbcr.png?raw=true) |
## Usage example
```python
from PIL import Image
import hquant
terminal_palette = bytes([
# Primary 3-bit (8 colors). Unique representation!
0x00, 0x00, 0x00,
0x80, 0x00, 0x00,
0x00, 0x80, 0x00,
0x80, 0x80, 0x00,
0x00, 0x00, 0x80,
0x80, 0x00, 0x80,
0x00, 0x80, 0x80,
0xc0, 0xc0, 0xc0,
# Equivalent "bright" versions of original 8 colors.
0x80, 0x80, 0x80,
0xff, 0x00, 0x00,
0x00, 0xff, 0x00,
0xff, 0xff, 0x00,
0x00, 0x00, 0xff,
0xff, 0x00, 0xff,
0x00, 0xff, 0xff,
0xff, 0xff, 0xff,
])
original = Image.open('lena.png')
dithered = hquant.quantize(original, terminal_palette)
dithered.save('dithered.png')
```
Raw data
{
"_id": null,
"home_page": null,
"name": "hquant",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "image, dither, dithering, quantization, palette",
"author": null,
"author_email": "Marcos Del Sol Vives <marcos@orca.pet>",
"download_url": "https://files.pythonhosted.org/packages/3d/df/24d0c513565b6099dcd5bf9e5a01ac1877b311eda29dcde1d37747df7746/hquant-0.1.0.tar.gz",
"platform": null,
"description": "# hquant\n\nHigh quality quantization for [Pillow](https://python-pillow.org/) images.\n\nPillow uses Euclidean distance for color matching during quantization, which yields poor results\nwhen using RGB due to the nonlinearity of color perception. YCbCr is a better fit for Euclidean\ndistance, but Pillow does not natively support quantization of such images.\n\nThis module improves the quantization by converting the images to YCbCr, and then tricking\nPillow into thinking it's an RGB image so quantization works.\n\n## Samples\n\nThe following samples dither the original image to the classic 16 colors supported in all terminals,\nusing RGB (as Pillow quantize method would normally use), CIELAB and YCbCr.\n\n| Original | RGB | CIELAB | YCbCr |\n| ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |\n| ![Original Lena](https://github.com/socram8888/hquant/blob/master/samples/lena_orig.png?raw=true) | ![Lena using RGB](https://github.com/socram8888/hquant/blob/master/samples/lena_rgb.png?raw=true) | ![Lena using CIELAB](https://github.com/socram8888/hquant/blob/master/samples/lena_lab.png?raw=true) | ![Lena using YCbCr](https://github.com/socram8888/hquant/blob/master/samples/lena_ycbcr.png?raw=true) |\n| ![Original Kobold](https://github.com/socram8888/hquant/blob/master/samples/kobold_orig.png?raw=true) | ![Kobold using RGB](https://github.com/socram8888/hquant/blob/master/samples/kobold_rgb.png?raw=true) | ![Kobold using CIELAB](https://github.com/socram8888/hquant/blob/master/samples/kobold_lab.png?raw=true) | ![Kobold using YCbCr](https://github.com/socram8888/hquant/blob/master/samples/kobold_ycbcr.png?raw=true) |\n\n## Usage example\n\n```python\nfrom PIL import Image\nimport hquant\n\nterminal_palette = bytes([\n\t# Primary 3-bit (8 colors). Unique representation!\n\t0x00, 0x00, 0x00,\n\t0x80, 0x00, 0x00,\n\t0x00, 0x80, 0x00,\n\t0x80, 0x80, 0x00,\n\t0x00, 0x00, 0x80,\n\t0x80, 0x00, 0x80,\n\t0x00, 0x80, 0x80,\n\t0xc0, 0xc0, 0xc0,\n\n\t# Equivalent \"bright\" versions of original 8 colors.\n\t0x80, 0x80, 0x80,\n\t0xff, 0x00, 0x00,\n\t0x00, 0xff, 0x00,\n\t0xff, 0xff, 0x00,\n\t0x00, 0x00, 0xff,\n\t0xff, 0x00, 0xff,\n\t0x00, 0xff, 0xff,\n\t0xff, 0xff, 0xff,\n])\n\noriginal = Image.open('lena.png')\ndithered = hquant.quantize(original, terminal_palette)\ndithered.save('dithered.png')\n```\n",
"bugtrack_url": null,
"license": null,
"summary": "High quality quantization for Pillow images.",
"version": "0.1.0",
"project_urls": {
"Homepage": "https://github.com/socram8888/hquant",
"Issues": "https://github.com/socram8888/hquant/issues"
},
"split_keywords": [
"image",
" dither",
" dithering",
" quantization",
" palette"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "55d2760a4141e7acf491a783ba2fa7e49cc0c828594d8e798ec53dc6e156b168",
"md5": "45abd1fab360cb26ea38e45d1b1a6bc7",
"sha256": "ce249b4626bcf8f12e74b9f0ae2a6a61536f3b54faf855e2e8d3784401a90c7f"
},
"downloads": -1,
"filename": "hquant-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "45abd1fab360cb26ea38e45d1b1a6bc7",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 3322,
"upload_time": "2024-09-19T23:02:00",
"upload_time_iso_8601": "2024-09-19T23:02:00.772787Z",
"url": "https://files.pythonhosted.org/packages/55/d2/760a4141e7acf491a783ba2fa7e49cc0c828594d8e798ec53dc6e156b168/hquant-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "3ddf24d0c513565b6099dcd5bf9e5a01ac1877b311eda29dcde1d37747df7746",
"md5": "36fd4d150f747f1ab4a7a3fd233b93ce",
"sha256": "b7e071aa8828e46af36187f1afe405ee66a1cd346b49d14599e42f6fe5da40cc"
},
"downloads": -1,
"filename": "hquant-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "36fd4d150f747f1ab4a7a3fd233b93ce",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 347887,
"upload_time": "2024-09-19T23:02:03",
"upload_time_iso_8601": "2024-09-19T23:02:03.171211Z",
"url": "https://files.pythonhosted.org/packages/3d/df/24d0c513565b6099dcd5bf9e5a01ac1877b311eda29dcde1d37747df7746/hquant-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-19 23:02:03",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "socram8888",
"github_project": "hquant",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "hquant"
}