release: v1.7.0 - region-agnostic skeleton, selectable true-black, English code

Highlights:

- minimum mode: add min_true_black switch (default True = unchanged v1.6.1 behaviour).

  True  : true-black gate + CC denoise (best for black line work).

  False : no gate, keeps colored regions, distance-transform thinning

          (best for colorful posters).

- skeleton mode: replace hue-specific green-block smoothing with

  saturation-based _color_zones; no hue or side-of-image assumptions.

- Move all comments, docstrings and messages to English.

- Remove built-in default paths; examples/demo.py now requires args.

- README: document both minimum variants, resolution guidance, parameters.

- tests: 18 cases covering I/O, both modes, both minimum variants.
This commit is contained in:
dvs
2026-09-26 14:37:12 +08:00
parent b6612f1df3
commit 928253a4c9
8 changed files with 734 additions and 278 deletions
+64 -39
View File
@@ -1,7 +1,16 @@
"""
lineart-extractor 使用示例
==========================
演示三种用法: 一行函数 / 自定义配置 / 直接处理 ndarray
lineartization usage examples
=============================
Demonstrates the call styles: one-liner / custom config / raw ndarray.
Usage:
python demo.py <input_image> <output_dir>
There are no built-in default paths: both arguments are required.
Tip: extraction quality depends on the source resolution -- the higher the
resolution, the cleaner the result. For low-resolution inputs use the
``minimum`` method.
"""
import os
import sys
@@ -16,53 +25,69 @@ from lineartization import (
save_image,
)
DEMO_SRC = os.environ.get("LINEART_DEMO_SRC", "手抄报.jpg")
DEMO_OUT_DIR = os.environ.get("LINEART_DEMO_OUT", "./output")
os.makedirs(DEMO_OUT_DIR, exist_ok=True)
def demo_simple(src, out_dir):
"""Example 1: one-liner (skeleton mode)."""
print("=== Example 1: one-liner ===")
extract_lineart_file(src, os.path.join(out_dir, "simple.png"))
print(" wrote simple.png")
def demo_simple():
"""① 一行搞定"""
print("=== 示例1: 一行调用 ===")
extract_lineart_file(DEMO_SRC, os.path.join(DEMO_OUT_DIR, "simple.png"))
print(" 已生成 simple.png")
def demo_config():
"""② 自定义配置 (线宽 + 保护华表区)"""
print("=== 示例2: 自定义配置 ===")
def demo_config(src, out_dir):
"""Example 2: custom config (minimum mode + protected region)."""
print("=== Example 2: custom config ===")
cfg = LineArtConfig(
method="minimum",
denoise="strong",
line_width=2, # 统一线宽 2px
enable_green_smoothing=True, # 手抄报绿块抹平
protect_areas=[(120, 220, 940, 1050)], # 保护"华表"区域
line_width=2, # uniform 2 px stroke
protect_areas=[(120, 220, 940, 1050)], # keep this region intact
)
extract_lineart_file(DEMO_SRC, os.path.join(DEMO_OUT_DIR, "configured.png"),
extract_lineart_file(src, os.path.join(out_dir, "configured.png"),
cfg, verbose=True)
print(" 已生成 configured.png")
print(" wrote configured.png")
def demo_ndarray():
"""③ 直接处理 ndarray (可嵌入你自己的流水线)"""
print("=== 示例3: ndarray 处理 ===")
img = load_image(DEMO_SRC)
print(f" 输入尺寸: {img.shape[1]}x{img.shape[0]}")
def demo_two_variants(src, out_dir):
"""Example 3: the two minimum variants (true-black on / off)."""
print("=== Example 3: minimum variants ===")
for flag, name in ((True, "true-black"), (False, "no-true-black")):
cfg = LineArtConfig(method="minimum", min_true_black=flag)
extract_lineart_file(src, os.path.join(out_dir, f"minimum_{name}.png"), cfg)
print(f" wrote minimum_{name}.png")
def demo_ndarray(src, out_dir):
"""Example 4: work directly on an ndarray (embeddable in your pipeline)."""
print("=== Example 4: ndarray processing ===")
img = load_image(src)
print(f" input size: {img.shape[1]}x{img.shape[0]}")
lineart = extract_lineart(img, LineArtConfig(line_width=2))
black_ratio = (lineart < 128).mean() * 100
print(f" 黑占比: {black_ratio:.2f}%")
save_image(os.path.join(DEMO_OUT_DIR, "ndarray.png"), lineart)
print(" 已生成 ndarray.png")
print(f" black ratio: {black_ratio:.2f}%")
save_image(os.path.join(out_dir, "ndarray.png"), lineart)
print(" wrote ndarray.png")
def main(argv):
if len(argv) != 3:
print(__doc__.strip())
print("\nError: expected exactly 2 arguments "
"(<input_image> <output_dir>).")
return 2
src, out_dir = argv[1], argv[2]
if not os.path.exists(src):
print(f"Error: input image not found: {src}")
return 2
os.makedirs(out_dir, exist_ok=True)
demo_simple(src, out_dir)
demo_config(src, out_dir)
demo_two_variants(src, out_dir)
demo_ndarray(src, out_dir)
print("\nAll examples finished.")
return 0
if __name__ == "__main__":
if not os.path.exists(DEMO_SRC):
print(f"提示: 未找到示例图片 '{DEMO_SRC}'")
print("请设置环境变量 LINEART_DEMO_SRC 指向一张图片, 例如:")
print(" set LINEART_DEMO_SRC=D:\\pics\\手抄报.jpg")
sys.exit(0)
demo_simple()
demo_config()
demo_ndarray()
print("\n全部示例完成 ✔")
raise SystemExit(main(sys.argv))