144 lines
4.4 KiB
Python
144 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
import cv2
|
|
|
|
import clean_characters
|
|
import vectorize_characters
|
|
|
|
|
|
def process_image(
|
|
image_path: Path,
|
|
image_output_dir: Path,
|
|
svg_output_dir: Path,
|
|
min_component_area: int | None,
|
|
max_hole_area: int | None,
|
|
max_isolated_speck_area: int | None,
|
|
clean_smooth: bool,
|
|
vector_smooth_iterations: int | None,
|
|
epsilon_ratio: float | None,
|
|
) -> None:
|
|
original = clean_characters.read_image(image_path)
|
|
gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
|
|
binary = clean_characters.binarize_foreground(gray)
|
|
(
|
|
adaptive_min_area,
|
|
adaptive_max_hole_area,
|
|
adaptive_max_isolated_speck_area,
|
|
) = clean_characters.adaptive_clean_params(binary)
|
|
|
|
cleaned = clean_characters.clean_mask(
|
|
binary,
|
|
min_component_area=(
|
|
adaptive_min_area if min_component_area is None else min_component_area
|
|
),
|
|
max_hole_area=(
|
|
adaptive_max_hole_area if max_hole_area is None else max_hole_area
|
|
),
|
|
max_isolated_speck_area=(
|
|
adaptive_max_isolated_speck_area
|
|
if max_isolated_speck_area is None
|
|
else max_isolated_speck_area
|
|
),
|
|
smooth=clean_smooth,
|
|
)
|
|
|
|
auto_smooth_iterations, auto_epsilon_ratio = vectorize_characters.vector_params(
|
|
cleaned
|
|
)
|
|
smoothed = vectorize_characters.smooth_mask(
|
|
cleaned,
|
|
iterations=(
|
|
auto_smooth_iterations
|
|
if vector_smooth_iterations is None
|
|
else vector_smooth_iterations
|
|
),
|
|
)
|
|
ratio = auto_epsilon_ratio if epsilon_ratio is None else epsilon_ratio
|
|
|
|
stem = image_path.stem
|
|
clean_characters.write_image(
|
|
image_output_dir / f"{stem}_cleaned_smoothed.png",
|
|
clean_characters.mask_to_display(smoothed),
|
|
)
|
|
svg = vectorize_characters.mask_to_svg(
|
|
smoothed,
|
|
title=stem,
|
|
epsilon_ratio=ratio,
|
|
)
|
|
(svg_output_dir / f"{stem}.svg").write_text(svg, encoding="utf-8")
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(
|
|
description="Clean, smooth, and vectorize complex seal-script characters."
|
|
)
|
|
parser.add_argument(
|
|
"--input-dir",
|
|
type=Path,
|
|
default=Path("PreProcessing_Common/original_characters"),
|
|
)
|
|
parser.add_argument(
|
|
"--image-output-dir",
|
|
type=Path,
|
|
default=Path("PreProcessing_Common/processed_characters"),
|
|
help="Output folder for clean+smoothed PNG files.",
|
|
)
|
|
parser.add_argument(
|
|
"--svg-output-dir",
|
|
type=Path,
|
|
default=Path("PreProcessing_Common/vector_characters"),
|
|
help="Output folder for SVG vector files.",
|
|
)
|
|
parser.add_argument("--min-component-area", type=int, default=None)
|
|
parser.add_argument("--max-hole-area", type=int, default=None)
|
|
parser.add_argument("--max-isolated-speck-area", type=int, default=None)
|
|
parser.add_argument(
|
|
"--no-clean-smooth",
|
|
action="store_true",
|
|
help="Skip the light 3x3 smoothing inside the clean stage.",
|
|
)
|
|
parser.add_argument(
|
|
"--vector-smooth-iterations",
|
|
type=int,
|
|
default=None,
|
|
help="Override automatic vector smoothing. Dense characters default to less smoothing.",
|
|
)
|
|
parser.add_argument(
|
|
"--epsilon-ratio",
|
|
type=float,
|
|
default=None,
|
|
help="Override contour simplification ratio. Smaller values keep more detail.",
|
|
)
|
|
return parser
|
|
|
|
|
|
def main() -> None:
|
|
args = build_parser().parse_args()
|
|
args.image_output_dir.mkdir(parents=True, exist_ok=True)
|
|
args.svg_output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
image_paths = clean_characters.iter_images(args.input_dir)
|
|
if not image_paths:
|
|
raise SystemExit(f"No images found in {args.input_dir}")
|
|
|
|
for image_path in image_paths:
|
|
process_image(
|
|
image_path=image_path,
|
|
image_output_dir=args.image_output_dir,
|
|
svg_output_dir=args.svg_output_dir,
|
|
min_component_area=args.min_component_area,
|
|
max_hole_area=args.max_hole_area,
|
|
max_isolated_speck_area=args.max_isolated_speck_area,
|
|
clean_smooth=not args.no_clean_smooth,
|
|
vector_smooth_iterations=args.vector_smooth_iterations,
|
|
epsilon_ratio=args.epsilon_ratio,
|
|
)
|
|
print(f"processed: {image_path.name}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|