144 lines
4.1 KiB
Python
144 lines
4.1 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
import cv2
|
|
import numpy as np
|
|
|
|
from process_characters import (
|
|
IMAGE_SUFFIXES,
|
|
binarize_foreground,
|
|
clean_mask,
|
|
matrix_map,
|
|
mask_to_display,
|
|
read_image,
|
|
write_image,
|
|
)
|
|
|
|
|
|
def matrix_to_text(matrix: np.ndarray) -> str:
|
|
return "\n".join(
|
|
"".join("1" if value > 0 else "0" for value in row) for row in matrix
|
|
) + "\n"
|
|
|
|
|
|
def iter_image_paths(input_dir: Path) -> list[Path]:
|
|
return sorted(
|
|
path
|
|
for path in input_dir.iterdir()
|
|
if path.is_file() and path.suffix.lower() in IMAGE_SUFFIXES
|
|
)
|
|
|
|
|
|
def process_image(
|
|
image_path: Path,
|
|
cleaned_dir: Path,
|
|
matrix_dir: Path,
|
|
grid_size: int = 17,
|
|
cell_samples: int = 32,
|
|
open_kernel_size: int = 1,
|
|
close_kernel_size: int = 0,
|
|
median_size: int = 1,
|
|
min_component_area: int = 8,
|
|
triangle_threshold: float = 0.18,
|
|
) -> None:
|
|
original = read_image(image_path)
|
|
gray = cv2.cvtColor(original, cv2.COLOR_BGR2GRAY)
|
|
binary = binarize_foreground(gray)
|
|
cleaned = clean_mask(
|
|
binary,
|
|
open_kernel_size=open_kernel_size,
|
|
close_kernel_size=close_kernel_size,
|
|
median_size=median_size,
|
|
min_component_area=min_component_area,
|
|
grid_size=grid_size,
|
|
)
|
|
mapping = matrix_map(
|
|
cleaned,
|
|
grid_size=grid_size,
|
|
triangle_threshold=triangle_threshold,
|
|
cell_samples=cell_samples,
|
|
)
|
|
|
|
stem = image_path.stem
|
|
write_image(cleaned_dir / f"{stem}_cleaned.png", mask_to_display(cleaned))
|
|
(matrix_dir / f"{stem}_matrix.txt").write_text(
|
|
matrix_to_text(mapping.matrix),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
|
|
def process_directory(
|
|
input_dir: Path,
|
|
cleaned_dir: Path,
|
|
matrix_dir: Path,
|
|
grid_size: int = 17,
|
|
cell_samples: int = 32,
|
|
open_kernel_size: int = 1,
|
|
close_kernel_size: int = 0,
|
|
median_size: int = 1,
|
|
min_component_area: int = 8,
|
|
triangle_threshold: float = 0.18,
|
|
) -> int:
|
|
cleaned_dir.mkdir(parents=True, exist_ok=True)
|
|
matrix_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
image_paths = iter_image_paths(input_dir)
|
|
if not image_paths:
|
|
raise SystemExit(f"No images found in {input_dir}")
|
|
|
|
for image_path in image_paths:
|
|
process_image(
|
|
image_path=image_path,
|
|
cleaned_dir=cleaned_dir,
|
|
matrix_dir=matrix_dir,
|
|
grid_size=grid_size,
|
|
cell_samples=cell_samples,
|
|
open_kernel_size=open_kernel_size,
|
|
close_kernel_size=close_kernel_size,
|
|
median_size=median_size,
|
|
min_component_area=min_component_area,
|
|
triangle_threshold=triangle_threshold,
|
|
)
|
|
print(f"processed: {image_path.name}")
|
|
|
|
return len(image_paths)
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
parser = argparse.ArgumentParser(
|
|
description="Clean seal-script images and export 17x17 matrix text files."
|
|
)
|
|
parser.add_argument("--input-dir", type=Path, required=True)
|
|
parser.add_argument("--cleaned-dir", type=Path, required=True)
|
|
parser.add_argument("--matrix-dir", type=Path, required=True)
|
|
parser.add_argument("--grid-size", type=int, default=17)
|
|
parser.add_argument("--cell-samples", type=int, default=32)
|
|
parser.add_argument("--open-kernel-size", type=int, default=1)
|
|
parser.add_argument("--close-kernel-size", type=int, default=0)
|
|
parser.add_argument("--median-size", type=int, default=1)
|
|
parser.add_argument("--min-component-area", type=int, default=8)
|
|
parser.add_argument("--triangle-threshold", type=float, default=0.18)
|
|
return parser
|
|
|
|
|
|
def main() -> None:
|
|
args = build_parser().parse_args()
|
|
process_directory(
|
|
input_dir=args.input_dir,
|
|
cleaned_dir=args.cleaned_dir,
|
|
matrix_dir=args.matrix_dir,
|
|
grid_size=args.grid_size,
|
|
cell_samples=args.cell_samples,
|
|
open_kernel_size=args.open_kernel_size,
|
|
close_kernel_size=args.close_kernel_size,
|
|
median_size=args.median_size,
|
|
min_component_area=args.min_component_area,
|
|
triangle_threshold=args.triangle_threshold,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|