# unfake
Improve AI-generated pixel art through scale detection, color quantization, and smart downscaling. Features Rust acceleration for critical operations, achieving a 10-20% speedup over the original JavaScript implementation.
Based on the excellent work by:
- **Eugeniy Smirnov** ([jenissimo/unfake.js](https://github.com/jenissimo/unfake.js)) - Original JavaScript implementation
- **Igor Bezkrovnyi** ([ibezkrovnyi/image-quantization](https://github.com/ibezkrovnyi/image-quantization)) - Image quantization algorithms
## Examples
Click each image to view the original, processed result, and results of two different naïve fixed-size-nearest-neighbor methods.
<img width="4156" height="1054" alt="comparison_grid" src="https://github.com/user-attachments/assets/f48d09a6-991e-4c51-b881-f75ddfc45e86" />
<img width="4140" height="1054" alt="comparison_grid" src="https://github.com/user-attachments/assets/4b17c7c3-cb0e-48d5-b47b-9daaa44e7999" />
<img width="4128" height="1038" alt="comparison_grid" src="https://github.com/user-attachments/assets/2bdee15e-ee00-462b-bc1a-456e8f156377" />
<img width="7212" height="2328" alt="comparison_grid" src="https://github.com/user-attachments/assets/9f37e1f7-68aa-4173-90d7-1fbfbcad5895" />
<img width="7196" height="2334" alt="comparison_grid" src="https://github.com/user-attachments/assets/65afdd0d-6f80-4997-bb6f-5b63e2fa83bc" />
Images taken from examples for AI pixel art models like [Pixel Art XL](https://huggingface.co/nerijs/pixel-art-xl), [FLUX.1-Dev Pixel LoRA](https://huggingface.co/UmeAiRT/FLUX.1-dev-LoRA-Modern_Pixel_art), and [FLUX.1-Kontext Pixel LoRA](https://huggingface.co/Shakker-Labs/FLUX.1-Kontext-dev-LoRA-Pixel-Style?image-viewer=EE86A1D8F1D252D65E9E06A3AAA2F5EF79A47E8A).
## Features
- **Automatic Scale Detection**: Detects the inherent scale of pixel art using both runs-based and edge-aware methods
- **Advanced Color Quantization**: Wu color quantization algorithm with Rust acceleration
- **Smart Downscaling**: Multiple methods including dominant color, median, mode, and content-adaptive
- **Image Cleanup**: Alpha binarization, morphological operations, and jaggy edge removal
- **Grid Snapping**: Automatic alignment to pixel grid for clean results
- **Flexible API**: Both synchronous and asynchronous interfaces
- **Fast**: Process a 1-megapixel image in as fast as half a second.
### Upcoming
- Vectorization
## Installation
### From PyPI (recommended)
```bash
pip install unfake
```
### From Source
```bash
# Clone the repository
git clone https://github.com/yourusername/unfake.git
cd unfake
# Install with pip (includes Rust compilation)
pip install .
# Or for development
pip install -e .
```
### Requirements
- Python 3.8+
- Rust toolchain (for building from source)
- OpenCV Python bindings
- Pillow
- NumPy
## Usage
### Command Line
```bash
# Basic usage with auto-detection
unfake input.png
# Specify output file
unfake input.png -o output.png
# Control color palette size
unfake input.png -c 16 # Maximum 16 colors
unfake input.png --auto-colors # Auto-detect optimal color count
# Force specific scale
unfake input.png --scale 4 # Force 4x downscaling
# Choose downscaling method
unfake input.png -m dominant # Dominant color (default, best for pixel art)
unfake input.png -m median # Median color
unfake input.png -m content-adaptive # High quality but slower
# Enable cleanup operations
unfake input.png --cleanup morph,jaggy # Morphological + jaggy edge cleanup
# Use fixed color palette
unfake input.png --palette palette.txt # File with hex colors, one per line
# Adjust processing parameters
unfake input.png --alpha-threshold 200 # Higher threshold for alpha binarization
unfake input.png --threshold 0.1 # Dominant color threshold (0.0-1.0)
unfake input.png --no-snap # Disable grid snapping
# Verbose output
unfake input.png -v # Show detailed processing info
```
### Python API
```python
import unfake
# Basic processing with defaults
result = unfake.process_image_sync(
"input.png",
max_colors=32, # Maximum colors in output
detect_method="auto", # Scale detection: "auto", "runs", "edge"
downscale_method="dominant", # Method: "dominant", "median", "mode", "mean", "content-adaptive"
cleanup={"morph": False, "jaggy": False},
snap_grid=True # Align to pixel grid
)
# Access results
processed_image = result['image'] # PIL Image
palette = result['palette'] # List of hex colors
manifest = result['manifest'] # Processing metadata
# Auto-detect optimal colors
result = unfake.process_image_sync(
"input.png",
max_colors=None, # Auto-detect
auto_color_detect=True
)
# Use fixed palette
fixed_colors = ['#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff']
result = unfake.process_image_sync(
"input.png",
fixed_palette=fixed_colors
)
```
#### Asynchronous API
```python
import asyncio
import unfake
async def process_image_async():
result = await unfake.process_image(
"input.png",
max_colors=16,
detect_method="runs",
downscale_method="median",
cleanup={"morph": True, "jaggy": False},
snap_grid=True
)
result["image"].save("output.png")
asyncio.run(process_image_async())
```
### Processing Options
#### Scale Detection Methods
- **`auto`** (default): Tries runs-based first, falls back to edge-aware
- **`runs`**: Analyzes color run lengths (fast, works well for clean pixel art)
- **`edge`**: Uses edge detection (slower but handles anti-aliased images)
#### Downscaling Methods
- **`dominant`** (default): Uses most frequent color in each block (best for pixel art)
- **`median`**: Median color value (good for photos)
- **`mode`**: Most common color (similar to dominant)
- **`mean`**: Average color (can create new colors)
- **`content-adaptive`**: Advanced algorithm based on [Kopf & Lischinski 2011](https://johanneskopf.de/publications/downscaling/)
#### Cleanup Options
- **`morph`**: Morphological operations to remove noise
- **`jaggy`**: Removes isolated diagonal pixels
## Performance
Example processing times for a 1024x1024 image on a high-end Intel desktop CPU using defaults:
- Pure Python: ~71 seconds
- With Rust Acceleration: ~700 milliseconds (about 100x speedup!)
## Algorithm Details
### Scale Detection
The tool uses two methods to detect the inherent scale of pixel art:
1. **Runs-based**: Analyzes horizontal and vertical color runs to find the GCD
2. **Edge-aware**: Uses Sobel edge detection to find regular grid patterns
### Color Quantization
Implements the Wu color quantization algorithm (1992) which:
- Builds a 3D color histogram
- Recursively subdivides color space
- Minimizes variance within each partition
- Produces high-quality palettes
### Downscaling
The dominant color method:
- Divides image into scale×scale blocks
- Finds most frequent color in each block
- Falls back to mean if no color is dominant
- Preserves original palette colors
## Credits
This Python/Rust implementation is based on:
- **[unfake.js](https://github.com/jenissimo/unfake.js)** by Eugeniy Smirnov - The original JavaScript implementation that inspired this project
- **[image-quantization](https://github.com/ibezkrovnyi/image-quantization)** by Igor Bezkrovnyi - TypeScript implementation of various color quantization algorithms
Additional references:
- Wu, Xiaolin. "Efficient Statistical Computations for Optimal Color Quantization" (1992)
- Kopf, Johannes and Dani Lischinski. "Depixelizing Pixel Art" (2011)
## License
MIT License
Raw data
{
"_id": null,
"home_page": null,
"name": "unfake",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "pixel-art, image-processing, quantization, ai-art, optimization, rust-python",
"author": null,
"author_email": "Benjamin Paine <painebenjamin@gmail.com>",
"download_url": null,
"platform": null,
"description": "# unfake\n\nImprove AI-generated pixel art through scale detection, color quantization, and smart downscaling. Features Rust acceleration for critical operations, achieving a 10-20% speedup over the original JavaScript implementation.\n\nBased on the excellent work by:\n- **Eugeniy Smirnov** ([jenissimo/unfake.js](https://github.com/jenissimo/unfake.js)) - Original JavaScript implementation\n- **Igor Bezkrovnyi** ([ibezkrovnyi/image-quantization](https://github.com/ibezkrovnyi/image-quantization)) - Image quantization algorithms\n\n## Examples\nClick each image to view the original, processed result, and results of two different na\u00efve fixed-size-nearest-neighbor methods.\n<img width=\"4156\" height=\"1054\" alt=\"comparison_grid\" src=\"https://github.com/user-attachments/assets/f48d09a6-991e-4c51-b881-f75ddfc45e86\" />\n<img width=\"4140\" height=\"1054\" alt=\"comparison_grid\" src=\"https://github.com/user-attachments/assets/4b17c7c3-cb0e-48d5-b47b-9daaa44e7999\" />\n<img width=\"4128\" height=\"1038\" alt=\"comparison_grid\" src=\"https://github.com/user-attachments/assets/2bdee15e-ee00-462b-bc1a-456e8f156377\" />\n<img width=\"7212\" height=\"2328\" alt=\"comparison_grid\" src=\"https://github.com/user-attachments/assets/9f37e1f7-68aa-4173-90d7-1fbfbcad5895\" />\n<img width=\"7196\" height=\"2334\" alt=\"comparison_grid\" src=\"https://github.com/user-attachments/assets/65afdd0d-6f80-4997-bb6f-5b63e2fa83bc\" />\n\nImages taken from examples for AI pixel art models like [Pixel Art XL](https://huggingface.co/nerijs/pixel-art-xl), [FLUX.1-Dev Pixel LoRA](https://huggingface.co/UmeAiRT/FLUX.1-dev-LoRA-Modern_Pixel_art), and [FLUX.1-Kontext Pixel LoRA](https://huggingface.co/Shakker-Labs/FLUX.1-Kontext-dev-LoRA-Pixel-Style?image-viewer=EE86A1D8F1D252D65E9E06A3AAA2F5EF79A47E8A).\n\n## Features\n\n- **Automatic Scale Detection**: Detects the inherent scale of pixel art using both runs-based and edge-aware methods\n- **Advanced Color Quantization**: Wu color quantization algorithm with Rust acceleration\n- **Smart Downscaling**: Multiple methods including dominant color, median, mode, and content-adaptive\n- **Image Cleanup**: Alpha binarization, morphological operations, and jaggy edge removal\n- **Grid Snapping**: Automatic alignment to pixel grid for clean results\n- **Flexible API**: Both synchronous and asynchronous interfaces\n- **Fast**: Process a 1-megapixel image in as fast as half a second.\n\n### Upcoming\n\n- Vectorization\n\n## Installation\n\n### From PyPI (recommended)\n\n```bash\npip install unfake\n```\n\n### From Source\n\n```bash\n# Clone the repository\ngit clone https://github.com/yourusername/unfake.git\ncd unfake\n\n# Install with pip (includes Rust compilation)\npip install .\n\n# Or for development\npip install -e .\n```\n\n### Requirements\n\n- Python 3.8+\n- Rust toolchain (for building from source)\n- OpenCV Python bindings\n- Pillow\n- NumPy\n\n## Usage\n\n### Command Line\n\n```bash\n# Basic usage with auto-detection\nunfake input.png\n\n# Specify output file\nunfake input.png -o output.png\n\n# Control color palette size\nunfake input.png -c 16 # Maximum 16 colors\nunfake input.png --auto-colors # Auto-detect optimal color count\n\n# Force specific scale\nunfake input.png --scale 4 # Force 4x downscaling\n\n# Choose downscaling method\nunfake input.png -m dominant # Dominant color (default, best for pixel art)\nunfake input.png -m median # Median color\nunfake input.png -m content-adaptive # High quality but slower\n\n# Enable cleanup operations\nunfake input.png --cleanup morph,jaggy # Morphological + jaggy edge cleanup\n\n# Use fixed color palette\nunfake input.png --palette palette.txt # File with hex colors, one per line\n\n# Adjust processing parameters\nunfake input.png --alpha-threshold 200 # Higher threshold for alpha binarization\nunfake input.png --threshold 0.1 # Dominant color threshold (0.0-1.0)\nunfake input.png --no-snap # Disable grid snapping\n\n# Verbose output\nunfake input.png -v # Show detailed processing info\n```\n\n### Python API\n\n```python\nimport unfake\n\n# Basic processing with defaults\nresult = unfake.process_image_sync(\n \"input.png\",\n max_colors=32, # Maximum colors in output\n detect_method=\"auto\", # Scale detection: \"auto\", \"runs\", \"edge\"\n downscale_method=\"dominant\", # Method: \"dominant\", \"median\", \"mode\", \"mean\", \"content-adaptive\"\n cleanup={\"morph\": False, \"jaggy\": False},\n snap_grid=True # Align to pixel grid\n)\n\n# Access results\nprocessed_image = result['image'] # PIL Image\npalette = result['palette'] # List of hex colors\nmanifest = result['manifest'] # Processing metadata\n\n# Auto-detect optimal colors\nresult = unfake.process_image_sync(\n \"input.png\",\n max_colors=None, # Auto-detect\n auto_color_detect=True\n)\n\n# Use fixed palette\nfixed_colors = ['#000000', '#ffffff', '#ff0000', '#00ff00', '#0000ff']\nresult = unfake.process_image_sync(\n \"input.png\",\n fixed_palette=fixed_colors\n)\n```\n\n#### Asynchronous API\n\n```python\nimport asyncio\nimport unfake\n\nasync def process_image_async():\n result = await unfake.process_image(\n \"input.png\",\n max_colors=16,\n detect_method=\"runs\",\n downscale_method=\"median\",\n cleanup={\"morph\": True, \"jaggy\": False},\n snap_grid=True\n )\n result[\"image\"].save(\"output.png\")\n\nasyncio.run(process_image_async())\n```\n\n### Processing Options\n\n#### Scale Detection Methods\n- **`auto`** (default): Tries runs-based first, falls back to edge-aware\n- **`runs`**: Analyzes color run lengths (fast, works well for clean pixel art)\n- **`edge`**: Uses edge detection (slower but handles anti-aliased images)\n\n#### Downscaling Methods\n- **`dominant`** (default): Uses most frequent color in each block (best for pixel art)\n- **`median`**: Median color value (good for photos)\n- **`mode`**: Most common color (similar to dominant)\n- **`mean`**: Average color (can create new colors)\n- **`content-adaptive`**: Advanced algorithm based on [Kopf & Lischinski 2011](https://johanneskopf.de/publications/downscaling/)\n\n#### Cleanup Options\n- **`morph`**: Morphological operations to remove noise\n- **`jaggy`**: Removes isolated diagonal pixels\n\n## Performance\nExample processing times for a 1024x1024 image on a high-end Intel desktop CPU using defaults:\n- Pure Python: ~71 seconds\n- With Rust Acceleration: ~700 milliseconds (about 100x speedup!)\n\n## Algorithm Details\n\n### Scale Detection\nThe tool uses two methods to detect the inherent scale of pixel art:\n\n1. **Runs-based**: Analyzes horizontal and vertical color runs to find the GCD\n2. **Edge-aware**: Uses Sobel edge detection to find regular grid patterns\n\n### Color Quantization\nImplements the Wu color quantization algorithm (1992) which:\n- Builds a 3D color histogram\n- Recursively subdivides color space\n- Minimizes variance within each partition\n- Produces high-quality palettes\n\n### Downscaling\nThe dominant color method:\n- Divides image into scale\u00d7scale blocks\n- Finds most frequent color in each block\n- Falls back to mean if no color is dominant\n- Preserves original palette colors\n\n## Credits\n\nThis Python/Rust implementation is based on:\n\n- **[unfake.js](https://github.com/jenissimo/unfake.js)** by Eugeniy Smirnov - The original JavaScript implementation that inspired this project\n- **[image-quantization](https://github.com/ibezkrovnyi/image-quantization)** by Igor Bezkrovnyi - TypeScript implementation of various color quantization algorithms\n\nAdditional references:\n- Wu, Xiaolin. \"Efficient Statistical Computations for Optimal Color Quantization\" (1992)\n- Kopf, Johannes and Dani Lischinski. \"Depixelizing Pixel Art\" (2011)\n\n## License\n\nMIT License \n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "High-performance tool for improving AI-generated pixel art",
"version": "1.0.1",
"project_urls": {
"Bug Tracker": "https://github.com/painebenjamin/unfake.py/issues",
"Homepage": "https://github.com/painebenjamin/unfake.py",
"Repository": "https://github.com/painebenjamin/unfake.py"
},
"split_keywords": [
"pixel-art",
" image-processing",
" quantization",
" ai-art",
" optimization",
" rust-python"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "1466ef784e88a56ed5c23906f7bb36ab140645d1db5725e15c400226c75c439b",
"md5": "6e8fda69752bdeef7d0dfe1a527af0dd",
"sha256": "1df61a549c201183f260fccd79e3145ac55b042fc872c1be94d195608799de46"
},
"downloads": -1,
"filename": "unfake-1.0.1-cp38-abi3-macosx_11_0_arm64.whl",
"has_sig": false,
"md5_digest": "6e8fda69752bdeef7d0dfe1a527af0dd",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 306248,
"upload_time": "2025-07-26T21:35:36",
"upload_time_iso_8601": "2025-07-26T21:35:36.358401Z",
"url": "https://files.pythonhosted.org/packages/14/66/ef784e88a56ed5c23906f7bb36ab140645d1db5725e15c400226c75c439b/unfake-1.0.1-cp38-abi3-macosx_11_0_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "ce5c3c59408ebefa8130d915d97f015202df83da5f4d58563845a87c3a6148da",
"md5": "4248f15be021d6c93e43c0c10bbf6362",
"sha256": "82658acfa3cb1526c1746c68faa60e81b518fe535e40d2fd8c926e087b7bebe8"
},
"downloads": -1,
"filename": "unfake-1.0.1-cp38-abi3-manylinux_2_34_x86_64.whl",
"has_sig": false,
"md5_digest": "4248f15be021d6c93e43c0c10bbf6362",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 350694,
"upload_time": "2025-07-26T21:35:37",
"upload_time_iso_8601": "2025-07-26T21:35:37.877361Z",
"url": "https://files.pythonhosted.org/packages/ce/5c/3c59408ebefa8130d915d97f015202df83da5f4d58563845a87c3a6148da/unfake-1.0.1-cp38-abi3-manylinux_2_34_x86_64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "b13fbd8d2acff4790292de5db040a9142650641bba543db9219725d6644e8cc6",
"md5": "f651ebac4bbd721c40173aeeb61d0982",
"sha256": "13a695b90712ae97673bcde0081151d8649ac165595269955c1a5a311da45d9f"
},
"downloads": -1,
"filename": "unfake-1.0.1-cp38-abi3-win_amd64.whl",
"has_sig": false,
"md5_digest": "f651ebac4bbd721c40173aeeb61d0982",
"packagetype": "bdist_wheel",
"python_version": "cp38",
"requires_python": ">=3.8",
"size": 268933,
"upload_time": "2025-07-26T21:35:38",
"upload_time_iso_8601": "2025-07-26T21:35:38.884771Z",
"url": "https://files.pythonhosted.org/packages/b1/3f/bd8d2acff4790292de5db040a9142650641bba543db9219725d6644e8cc6/unfake-1.0.1-cp38-abi3-win_amd64.whl",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-26 21:35:36",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "painebenjamin",
"github_project": "unfake.py",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "unfake"
}