Last active
May 9, 2025 09:18
-
-
Save XCanG/b21f2c80d64f0df012ebe143c9c46224 to your computer and use it in GitHub Desktop.
Pixelate image. Use `pixelate.py --help` to get help message. Install required libraries `pip install opencv-python numpy typer`. General usage with default params: `pixelate.py img.png`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from typing import Annotated | |
| from pathlib import Path | |
| import cv2 | |
| import numpy as np | |
| import typer | |
| app = typer.Typer(no_args_is_help=True) | |
| @app.command() | |
| def main( | |
| image: Annotated[Path, typer.Argument(help="Path to image")], | |
| quantization_level: Annotated[int, typer.Option("--quantization-level", "-q", help="Number of levels of quantization, 0 - disabled", rich_help_panel="Params")] = 16, | |
| pixel_size: Annotated[int, typer.Option("--pixel-size", "-p", help="Pixel size, 1 or 0 - disabled", rich_help_panel="Params")] = 10, | |
| gamma: Annotated[float, typer.Option("--gamma", "-g", help="Adjust gamma, 1.0 - disabled, 0 - auto", rich_help_panel="Params")] = 0.0, | |
| preview: Annotated[bool, typer.Option(help="Show preview of pixelated image", rich_help_panel="Debug")] = True, | |
| output_dir: Annotated[Path | None, typer.Option("--output-dir", "-d", help="Specify output directory, default - input image directory", rich_help_panel="File")] = None, | |
| output_name: Annotated[ | |
| str | None, | |
| typer.Option("--output-name", "-n", help="Specify output file name, default - input image name + passed parameters", rich_help_panel="File"), | |
| ] = None, | |
| ) -> cv2.typing.MatLike: | |
| """ | |
| # Convert image to pixelated image | |
| By default posterize image to 16 levels of quantization (improve sharpness before pixelation), | |
| pixelate to 10x10 pixel size and adjust gamma based of size of pixel (as it become dimmed) | |
| """ | |
| im: cv2.typing.MatLike = cv2.imread(str(image)) | |
| im2: cv2.typing.MatLike | |
| im3: cv2.typing.MatLike | |
| im4: cv2.typing.MatLike | |
| imf: cv2.typing.MatLike | |
| if 255 > quantization_level > 0: | |
| indices = np.arange(0, 256) # List of all colors | |
| divider = np.linspace(0, 255, quantization_level + 1)[1] # we get a divider | |
| quantiz = np.intp(np.linspace(0, 255, quantization_level)) # we get quantization colors | |
| color_levels = np.clip(np.intp(indices / divider), 0, quantization_level - 1) # color levels 0, 1, 2... | |
| palette = quantiz[color_levels] # type: ignore[palette] # Creating the palette | |
| im2 = palette[im] # Applying palette on image | |
| im2 = cv2.convertScaleAbs(im2) # Converting image back to uint8 | |
| else: | |
| im2 = im | |
| if pixel_size >= 2: | |
| h, w, _ = im2.shape | |
| sw, sh = max(round(w / pixel_size), 1), max(round(h / pixel_size), 1) | |
| im3 = cv2.resize(im2, (sw, sh), interpolation=cv2.INTER_AREA) # Downscale | |
| im4 = cv2.resize(im3, (sw * pixel_size, sh * pixel_size), interpolation=cv2.INTER_NEAREST) # Upscale | |
| else: | |
| im4 = im2 | |
| if gamma != 1.0: # Adjust gamma | |
| # build a lookup table mapping the pixel values [0, 255] to their adjusted gamma values | |
| if gamma <= 0.0: | |
| gamma = 0.4 / pixel_size + 0.6 # Approximate influence on gamma by pixel_size | |
| inv_gamma = 1.0 / gamma | |
| table = np.array([((i / 255.0) ** inv_gamma) * 255 for i in np.arange(0, 256)]).astype("uint8") | |
| imf = cv2.LUT(im4, table) # apply gamma correction using the lookup table | |
| else: | |
| imf = im4 | |
| if preview: | |
| # cv2.namedWindow("preview", cv2.WINDOW_NORMAL) # | cv2.WINDOW_KEEPRATIO | |
| cv2.imshow("preview", imf) | |
| cv2.waitKey(5_000) | |
| cv2.destroyAllWindows() | |
| file_name = output_name or f"{image.stem}.q{quantization_level}.px{pixel_size}.g{gamma:.2f}{image.suffix}" | |
| new_file = output_dir / file_name if output_dir else image.with_name(file_name) | |
| print(f"Saving pixelated file to: {new_file}") | |
| if new_file.suffix in (".jpg", ".jpeg"): | |
| cv2.imwrite(str(new_file), imf, [int(cv2.IMWRITE_JPEG_QUALITY), 90]) # Set JPEG quality to 90% | |
| else: | |
| cv2.imwrite(str(new_file), imf) | |
| return imf | |
| if __name__ == "__main__": | |
| app() |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Pixelate image
Use
pixelate.py --helpto get help message.Install required libraries:
General usage with default params:
Advanced usage: