mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-24 06:22:29 +02:00
feat(auto): content-adaptive --auto quality mode, Phase 1
Add `auto_config.plan(image_path) -> AutoConfig`, the first step of the invisible/all pipeline: it inspects the input image (before the diffusion model loads) and picks the quality modes so the run adapts to content. Quality-priority routing -- ControlNet (text/face-structure preservation) is the default, skipped for plain SDXL only on a clearly structure-less image; GFPGAN face restore when a face is present; a mild sharpen + grain polish when a smoothing pass ran. Exposed as `--auto` on `all`/`invisible` (`_apply_auto`; explicit flags override via click's parameter source). Not wired into batch (its engine is cached per-mode). Detection is cv2-only and torch-free (~100 MB peak RSS, a few ms): OpenCV YuNet (`cv2.FaceDetectorYN`, MIT, 232 KB model bundled in assets/) for faces, a Canny edge-density + MSER heuristic for text/structure (a rough Phase-1 placeholder; DBNet via cv2.dnn is the planned upgrade). ZERO new pip deps. Designed to run wherever the pipeline runs -- the raiw.cc Modal GPU worker -- never on the 512 MB web host. Real-ESRGAN-via-Spandrel upscaling (a new `esrgan` extra) and an adaptive Laplacian-variance polish are deferred to later phases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ea59bdc3e2
commit
9bd2c17cc4
@@ -159,6 +159,48 @@ _unsharp_option = click.option(
|
||||
"--unsharp", type=float, default=0.0, help="Unsharp-mask sharpening strength (0 = off, typical: 0.3-0.8)."
|
||||
)
|
||||
|
||||
_auto_option = click.option(
|
||||
"--auto",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Auto-pick quality modes (pipeline, face restore, sharpen/grain) from image content. "
|
||||
"Explicit flags override. EXPERIMENTAL.",
|
||||
)
|
||||
|
||||
|
||||
def _apply_auto(
|
||||
ctx: click.Context,
|
||||
source: Path,
|
||||
pipeline: str,
|
||||
restore_faces: bool,
|
||||
unsharp: float,
|
||||
humanize: float,
|
||||
) -> tuple[str, bool, float, float]:
|
||||
"""Resolve ``--auto``: plan modes from the image, overriding only the flags the
|
||||
user left at their default (an explicit flag always wins). Returns the resolved
|
||||
``(pipeline, restore_faces, unsharp, humanize)`` and prints the chosen plan.
|
||||
"""
|
||||
from remove_ai_watermarks import auto_config
|
||||
|
||||
cfg = auto_config.plan(source)
|
||||
if cfg is None:
|
||||
console.print(" Auto: could not read image; using defaults")
|
||||
return pipeline, restore_faces, unsharp, humanize
|
||||
|
||||
def _is_default(name: str) -> bool:
|
||||
return ctx.get_parameter_source(name) == click.core.ParameterSource.DEFAULT
|
||||
|
||||
if _is_default("pipeline"):
|
||||
pipeline = cfg.pipeline
|
||||
if _is_default("restore_faces"):
|
||||
restore_faces = cfg.restore_faces
|
||||
if _is_default("unsharp"):
|
||||
unsharp = cfg.unsharp
|
||||
if _is_default("humanize"):
|
||||
humanize = cfg.humanize
|
||||
console.print(f" Auto: {cfg.reason}")
|
||||
return pipeline, restore_faces, unsharp, humanize
|
||||
|
||||
|
||||
def _restore_faces_options(f: Any) -> Any:
|
||||
"""Attach the shared GFPGAN face-restoration flags to an invisible-pipeline command."""
|
||||
@@ -507,6 +549,7 @@ def cmd_erase(
|
||||
@_restore_faces_options
|
||||
@_min_resolution_option
|
||||
@_unsharp_option
|
||||
@_auto_option
|
||||
@click.pass_context
|
||||
def cmd_invisible(
|
||||
ctx: click.Context,
|
||||
@@ -525,6 +568,7 @@ def cmd_invisible(
|
||||
controlnet_scale: float,
|
||||
restore_faces: bool,
|
||||
restore_faces_weight: float,
|
||||
auto: bool,
|
||||
) -> None:
|
||||
"""Remove invisible AI watermarks (SynthID, StableSignature, TreeRing).
|
||||
|
||||
@@ -542,6 +586,10 @@ def cmd_invisible(
|
||||
from remove_ai_watermarks.invisible_engine import InvisibleEngine
|
||||
|
||||
source = _validate_image(source)
|
||||
if auto:
|
||||
pipeline, restore_faces, unsharp, humanize = _apply_auto(
|
||||
ctx, source, pipeline, restore_faces, unsharp, humanize
|
||||
)
|
||||
if output is None:
|
||||
output = source.with_stem(source.stem + "_clean")
|
||||
|
||||
@@ -758,6 +806,7 @@ def cmd_identify(ctx: click.Context, source: Path, no_visible: bool, as_json: bo
|
||||
@_restore_faces_options
|
||||
@_min_resolution_option
|
||||
@_unsharp_option
|
||||
@_auto_option
|
||||
@click.pass_context
|
||||
def cmd_all(
|
||||
ctx: click.Context,
|
||||
@@ -779,6 +828,7 @@ def cmd_all(
|
||||
controlnet_scale: float,
|
||||
restore_faces: bool,
|
||||
restore_faces_weight: float,
|
||||
auto: bool,
|
||||
) -> None:
|
||||
"""Remove ALL watermarks: visible + invisible + metadata.
|
||||
|
||||
@@ -793,6 +843,10 @@ def cmd_all(
|
||||
|
||||
_banner()
|
||||
source = _validate_image(source)
|
||||
if auto:
|
||||
pipeline, restore_faces, unsharp, humanize = _apply_auto(
|
||||
ctx, source, pipeline, restore_faces, unsharp, humanize
|
||||
)
|
||||
|
||||
if output is None:
|
||||
output = source.with_stem(source.stem + "_clean")
|
||||
|
||||
Reference in New Issue
Block a user