pypindou.pattern.generate 源代码

"""
High-level image-to-pattern generation API.

The functions in this module connect image preprocessing, palette filtering,
color quantization, and :class:`pypindou.pattern.Pattern` construction.  They
are the main public entry point for applications that need a pure Python
image-to-bead-pattern workflow.

Example::

    >>> from PIL import Image
    >>> from pypindou.pattern import generate_pattern
    >>> image = Image.new("RGB", (4, 4), "white")
    >>> pattern = generate_pattern(image, width=4, height=4, max_colors=4, color_space="rgb")
    >>> pattern.board_size
    (4, 4)
"""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import Optional, Tuple, Union

from PIL import Image

from pypindou.color import ColorSpace, Palette, get_palette
from pypindou.image import (
    BackgroundMode,
    CleanupMode,
    FitMode,
    PreprocessMode,
    QuantizeMethod,
    ResampleMode,
    enhance_image,
    load_image,
    prefilter_image,
    quantize_image,
    reduce_palette_for_image,
    remove_background_by_alpha,
    resize_image,
    rgba_to_rgb_array,
)

from .model import Pattern


[文档] @dataclass(frozen=True) class PatternOptions: """ Options for :func:`generate_pattern`. :param width: Target bead-grid width. :type width: int :param height: Target bead-grid height. :type height: int :param palette: Built-in palette id or explicit palette object, defaults to ``"mard-221-alfonse-doudou"``. :type palette: Union[str, pypindou.color.Palette], optional :param fit: Image fit strategy, defaults to ``"contain"``. :type fit: pypindou.image.FitMode, optional :param background: Alpha handling strategy, defaults to ``"white"``. :type background: pypindou.image.BackgroundMode, optional :param alpha_threshold: Transparent-pixel threshold, defaults to ``16``. :type alpha_threshold: int, optional :param resample: Resize filter, defaults to ``"lanczos"``. :type resample: pypindou.image.ResampleMode, optional :param prefilter: Optional pre-resize denoising or edge-preserving filter, defaults to ``"none"``. :type prefilter: pypindou.image.PreprocessMode, optional :param prefilter_radius: Radius used by the prefilter, defaults to ``1.0``. :type prefilter_radius: float, optional :param brightness: Brightness adjustment factor, defaults to ``1.0``. :type brightness: float, optional :param contrast: Contrast adjustment factor, defaults to ``1.0``. :type contrast: float, optional :param saturation: Saturation adjustment factor, defaults to ``1.0``. :type saturation: float, optional :param sharpness: Sharpness adjustment factor, defaults to ``1.0``. :type sharpness: float, optional :param grayscale: Grayscale blend ratio, defaults to ``0.0``. :type grayscale: float, optional :param color_space: Color distance space, defaults to ``"lab"``. :type color_space: pypindou.color.ColorSpace, optional :param quantize: Quantization method, defaults to ``"nearest"``. :type quantize: pypindou.image.QuantizeMethod, optional :param dither_strength: Floyd-Steinberg diffusion strength, defaults to ``1.0``. :type dither_strength: float, optional :param max_colors: Maximum number of bead colors to use, defaults to ``None``. :type max_colors: Optional[int], optional :param cleanup: Post-quantization cleanup mode, defaults to ``"none"``. :type cleanup: pypindou.image.CleanupMode, optional :param cleanup_passes: Number of cleanup passes, defaults to ``0``. :type cleanup_passes: int, optional :param cleanup_threshold: Majority threshold for cleanup, defaults to ``5``. :type cleanup_threshold: int, optional :param min_region_size: Small connected regions below this size are merged, defaults to ``0``. :type min_region_size: int, optional :param include_codes: Optional allowed color-code list. :type include_codes: Optional[Tuple[str, ...]], optional :param exclude_codes: Optional excluded color-code list. :type exclude_codes: Optional[Tuple[str, ...]], optional :param allow_unidentified: Whether to keep colors marked unidentified, defaults to ``False``. :type allow_unidentified: bool, optional :param random_state: Random seed for palette reduction, defaults to ``0``. :type random_state: int, optional """ width: int height: int palette: Union[str, Palette] = "mard-221-alfonse-doudou" fit: FitMode = "contain" background: BackgroundMode = "white" alpha_threshold: int = 16 resample: ResampleMode = "lanczos" prefilter: PreprocessMode = "none" prefilter_radius: float = 1.0 brightness: float = 1.0 contrast: float = 1.0 saturation: float = 1.0 sharpness: float = 1.0 grayscale: float = 0.0 color_space: ColorSpace = "lab" quantize: QuantizeMethod = "nearest" dither_strength: float = 1.0 max_colors: Optional[int] = None cleanup: CleanupMode = "none" cleanup_passes: int = 0 cleanup_threshold: int = 5 min_region_size: int = 0 include_codes: Optional[Tuple[str, ...]] = None exclude_codes: Optional[Tuple[str, ...]] = None allow_unidentified: bool = False random_state: int = 0
[文档] def generate_pattern( image: Union[str, Path, Image.Image], *, width: int, height: int, palette: Union[str, Palette] = "mard-221-alfonse-doudou", fit: FitMode = "contain", background: BackgroundMode = "white", alpha_threshold: int = 16, resample: ResampleMode = "lanczos", prefilter: PreprocessMode = "none", prefilter_radius: float = 1.0, brightness: float = 1.0, contrast: float = 1.0, saturation: float = 1.0, sharpness: float = 1.0, grayscale: float = 0.0, color_space: ColorSpace = "lab", quantize: QuantizeMethod = "nearest", dither_strength: float = 1.0, max_colors: Optional[int] = None, cleanup: CleanupMode = "none", cleanup_passes: int = 0, cleanup_threshold: int = 5, min_region_size: int = 0, include_codes: Optional[Tuple[str, ...]] = None, exclude_codes: Optional[Tuple[str, ...]] = None, allow_unidentified: bool = False, random_state: int = 0, ) -> Pattern: """ Generate a fuse-bead pattern from an input image. The default path favors clean, editable regions over photographic texture: use ``max_colors`` to limit the palette, ``prefilter`` and enhancement options to simplify the source image, then optionally run ``cleanup`` and ``min_region_size`` to remove tiny color islands. Enable ``quantize="floyd-steinberg"`` when visible dithering is preferred. :param image: Source image, image path, or :class:`PIL.Image.Image`. :type image: Union[str, pathlib.Path, PIL.Image.Image] :param width: Target bead-grid width. :type width: int :param height: Target bead-grid height. :type height: int :param palette: Built-in palette id or explicit palette object, defaults to ``"mard-221-alfonse-doudou"``. :type palette: Union[str, pypindou.color.Palette], optional :param fit: Image fit strategy, defaults to ``"contain"``. :type fit: pypindou.image.FitMode, optional :param background: Alpha handling strategy, defaults to ``"white"``. :type background: pypindou.image.BackgroundMode, optional :param alpha_threshold: Transparent-pixel threshold, defaults to ``16``. :type alpha_threshold: int, optional :param resample: Resize filter, defaults to ``"lanczos"``. :type resample: pypindou.image.ResampleMode, optional :param prefilter: Optional pre-resize filter, defaults to ``"none"``. :type prefilter: pypindou.image.PreprocessMode, optional :param prefilter_radius: Radius used by the prefilter, defaults to ``1.0``. :type prefilter_radius: float, optional :param brightness: Brightness adjustment factor, defaults to ``1.0``. :type brightness: float, optional :param contrast: Contrast adjustment factor, defaults to ``1.0``. :type contrast: float, optional :param saturation: Saturation adjustment factor, defaults to ``1.0``. :type saturation: float, optional :param sharpness: Sharpness adjustment factor, defaults to ``1.0``. :type sharpness: float, optional :param grayscale: Grayscale blend ratio, defaults to ``0.0``. :type grayscale: float, optional :param color_space: Color distance space, defaults to ``"lab"``. :type color_space: pypindou.color.ColorSpace, optional :param quantize: Quantization method, defaults to ``"nearest"``. :type quantize: pypindou.image.QuantizeMethod, optional :param dither_strength: Floyd-Steinberg diffusion strength, defaults to ``1.0``. :type dither_strength: float, optional :param max_colors: Maximum number of bead colors to use, defaults to ``None``. :type max_colors: Optional[int], optional :param cleanup: Post-quantization cleanup mode, defaults to ``"none"``. :type cleanup: pypindou.image.CleanupMode, optional :param cleanup_passes: Number of cleanup passes, defaults to ``0``. :type cleanup_passes: int, optional :param cleanup_threshold: Majority threshold for cleanup, defaults to ``5``. :type cleanup_threshold: int, optional :param min_region_size: Regions smaller than this value are merged into neighboring colors, defaults to ``0``. :type min_region_size: int, optional :param include_codes: Optional allowed color-code list. :type include_codes: Optional[Tuple[str, ...]], optional :param exclude_codes: Optional excluded color-code list. :type exclude_codes: Optional[Tuple[str, ...]], optional :param allow_unidentified: Whether to keep colors marked unidentified, defaults to ``False``. :type allow_unidentified: bool, optional :param random_state: Random seed for palette reduction, defaults to ``0``. :type random_state: int, optional :return: Generated bead pattern. :rtype: pypindou.pattern.Pattern """ src = load_image(image) src = remove_background_by_alpha(src, alpha_threshold=alpha_threshold) src = enhance_image( src, brightness=brightness, contrast=contrast, saturation=saturation, sharpness=sharpness, grayscale=grayscale, ) src = prefilter_image(src, mode=prefilter, radius=prefilter_radius) resized = resize_image(src, (width, height), fit=fit, resample=resample) rgb, active = rgba_to_rgb_array(resized, background=background, alpha_threshold=alpha_threshold) base_palette = get_palette( palette, include_codes=include_codes, exclude_codes=exclude_codes, allow_unidentified=allow_unidentified, ) working_palette = reduce_palette_for_image( rgb, active, base_palette, max_colors=max_colors, color_space=color_space, random_state=random_state, ) result = quantize_image( rgb, active, working_palette, method=quantize, color_space=color_space, dither_strength=dither_strength, cleanup=cleanup, cleanup_passes=cleanup_passes, cleanup_threshold=cleanup_threshold, min_region_size=min_region_size, ) return Pattern( width=width, height=height, palette=working_palette, indices=result.indices, rgb_image=result.rgb_image, active_mask=result.active_mask, error=result.error, metadata={ "fit": fit, "background": background, "alpha_threshold": alpha_threshold, "resample": resample, "prefilter": prefilter, "prefilter_radius": prefilter_radius, "brightness": brightness, "contrast": contrast, "saturation": saturation, "sharpness": sharpness, "grayscale": grayscale, "color_space": color_space, "quantize": quantize, "dither_strength": dither_strength, "max_colors": max_colors, "cleanup": cleanup, "cleanup_passes": cleanup_passes, "cleanup_threshold": cleanup_threshold, "min_region_size": min_region_size, }, )
[文档] def generate_pattern_with_options(image: Union[str, Path, Image.Image], options: PatternOptions) -> Pattern: """ Generate a pattern from a :class:`PatternOptions` object. :param image: Source image, image path, or :class:`PIL.Image.Image`. :type image: Union[str, pathlib.Path, PIL.Image.Image] :param options: Pattern-generation options. :type options: PatternOptions :return: Generated bead pattern. :rtype: pypindou.pattern.Pattern """ return generate_pattern( image, width=options.width, height=options.height, palette=options.palette, fit=options.fit, background=options.background, alpha_threshold=options.alpha_threshold, resample=options.resample, prefilter=options.prefilter, prefilter_radius=options.prefilter_radius, brightness=options.brightness, contrast=options.contrast, saturation=options.saturation, sharpness=options.sharpness, grayscale=options.grayscale, color_space=options.color_space, quantize=options.quantize, dither_strength=options.dither_strength, max_colors=options.max_colors, cleanup=options.cleanup, cleanup_passes=options.cleanup_passes, cleanup_threshold=options.cleanup_threshold, min_region_size=options.min_region_size, include_codes=options.include_codes, exclude_codes=options.exclude_codes, allow_unidentified=options.allow_unidentified, random_state=options.random_state, )