style: unify section dividers and logger variable naming

- cli.py section dividers -> the codebase-majority `# ── Title ──` form
- logger variable unified to `logger` across identify, isobmff, c2pa,
  trustmark_detector, invisible_watermark (matching the 9-file majority)
- cosmetic only, no behavior change (719 tests / pyright / ruff green)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Victor Kuznetsov
2026-07-09 14:31:40 +03:00
parent 1a955b096a
commit 1f5d64e6c5
6 changed files with 31 additions and 47 deletions
+14 -30
View File
@@ -31,7 +31,7 @@ if TYPE_CHECKING:
from numpy.typing import NDArray
# --- plain-text output layer (replaces rich: no colors, no markup, no boxes) ---
# ── plain-text output layer (replaces rich: no colors, no markup, no boxes) ──
class _Table:
@@ -498,9 +498,7 @@ def _should_skip_invisible_scrub(force: bool, image_path: Path) -> bool:
return not has_invisible_target(image_path)
# -- Main group -------------------------------------------------------
# ── Main group ──
@click.group(invoke_without_command=True)
@click.version_option(__version__, prog_name="remove-ai-watermarks")
@click.option("-v", "--verbose", is_flag=True, help="Enable verbose logging.")
@@ -520,9 +518,7 @@ def main(ctx: click.Context, verbose: bool) -> None:
click.echo(ctx.get_help())
# -- Visible (Gemini) watermark removal -------------------------------
# ── Visible (Gemini) watermark removal ──
@main.command("visible")
@click.argument("source", type=click.Path(exists=True, path_type=Path))
@click.option(
@@ -637,9 +633,7 @@ def cmd_visible(
console.print(f" Saved: {output} ({size_kb:.0f} KB, {elapsed:.2f}s)")
# -- Universal region eraser -----------------------------------------
# ── Universal region eraser ──
def _parse_region(spec: str) -> tuple[int, int, int, int]:
"""Parse an ``x,y,w,h`` region string into a 4-int tuple."""
parts = spec.replace(" ", "").split(",")
@@ -729,9 +723,7 @@ def cmd_erase(
console.print(f" Erased {len(boxes)} region(s) -> {output} ({size_kb:.0f} KB, {elapsed:.2f}s)")
# -- Invisible watermark removal -------------------------------------
# ── Invisible watermark removal ──
@main.command("invisible")
@click.argument("source", type=click.Path(exists=True, path_type=Path))
@click.option(
@@ -867,9 +859,7 @@ def cmd_invisible(
console.print(f"\n Saved: {result_path} ({size_kb:.0f} KB, {elapsed:.1f}s)")
# -- Metadata operations ---------------------------------------------
# ── Metadata operations ──
@main.command("metadata")
@click.argument("source", type=click.Path(exists=True, path_type=Path))
@click.option("--check", is_flag=True, help="Check for AI metadata (don't modify).")
@@ -926,9 +916,7 @@ def cmd_metadata(
console.print(f" AI metadata stripped -> {out}")
# -- Provenance identification ---------------------------------------
# ── Provenance identification ──
@main.command("identify")
@click.argument("source", type=click.Path(exists=True, path_type=Path))
@click.option(
@@ -997,9 +985,7 @@ def cmd_identify(ctx: click.Context, source: Path, no_visible: bool, as_json: bo
console.print(f" - {c}")
# -- Combined "all" mode ----------------------------------------------
# ── Combined "all" mode ──
@main.command("all")
@click.argument("source", type=click.Path(exists=True, path_type=Path))
@click.option(
@@ -1102,7 +1088,7 @@ def cmd_all(
os.close(tmp_fd)
# -- Step 1: Visible watermark --------------------------------
# ── Step 1: Visible watermark ──
console.print("\n 1) Visible watermark removal")
image, alpha = image_io.read_bgr_and_alpha(source)
if image is None:
@@ -1124,7 +1110,7 @@ def cmd_all(
# Save to temp file for invisible engine input (preserve alpha if present)
image_io.write_bgr_with_alpha(tmp_path, result, alpha)
# -- Step 2: Invisible watermark ------------------------------
# ── Step 2: Invisible watermark ──
console.print("\n 2) Invisible watermark removal")
from remove_ai_watermarks.invisible_engine import is_available as invisible_available
@@ -1188,7 +1174,7 @@ def cmd_all(
)
console.print(" Invisible watermark removed")
# -- Step 3: Metadata -----------------------------------------
# ── Step 3: Metadata ──
console.print("\n 3) AI metadata stripping")
try:
from remove_ai_watermarks.metadata import remove_ai_metadata
@@ -1198,7 +1184,7 @@ def cmd_all(
except Exception as e:
console.print(f" Warning: Metadata strip failed: {e}")
# -- Write final result ----------------------------------------
# ── Write final result ──
# The invisible step (and downstream cv2.IMREAD_COLOR paths) drops alpha,
# so re-attach the original alpha plane unchanged when writing the final
# output for transparent formats.
@@ -1214,7 +1200,7 @@ def cmd_all(
if tmp_path.exists():
tmp_path.unlink()
# -- Done -----------------------------------------------------
# ── Done ──
elapsed = time.monotonic() - t0
size_kb = output.stat().st_size / 1024
console.print(f"\n Done: {output} ({size_kb:.0f} KB, {elapsed:.1f}s total)")
@@ -1238,9 +1224,7 @@ def cmd_all(
raise SystemExit(1)
# -- Batch command ----------------------------------------------------
# ── Batch command ──
def _process_batch_image(
ctx: click.Context,
img_path: Path,
+6 -6
View File
@@ -52,7 +52,7 @@ if TYPE_CHECKING:
from remove_ai_watermarks.watermark_registry import MarkDetection
log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# How much of a non-PNG container to binary-scan for the C2PA issuer.
_SCAN_BYTES = 1024 * 1024
@@ -393,7 +393,7 @@ def _visible_sparkle(image_path: Path, *, image: NDArray[Any] | None = None) ->
try:
from remove_ai_watermarks.gemini_engine import detect_sparkle_confidence
except Exception as exc: # cv2/engine assets missing
log.debug("visible-sparkle detector unavailable: %s", exc)
logger.debug("visible-sparkle detector unavailable: %s", exc)
return None
return detect_sparkle_confidence(image_path, image=image)
@@ -424,7 +424,7 @@ def _visible_text_marks(image_path: Path, *, image: NDArray[Any] | None = None)
from remove_ai_watermarks.image_io import imread
from remove_ai_watermarks.watermark_registry import get_mark
except Exception as exc: # cv2/engine assets missing
log.debug("visible-mark detectors unavailable: %s", exc)
logger.debug("visible-mark detectors unavailable: %s", exc)
return []
if image is None:
image = imread(image_path)
@@ -435,7 +435,7 @@ def _visible_text_marks(image_path: Path, *, image: NDArray[Any] | None = None)
try:
det = get_mark(key).detect(image)
except Exception as exc: # one engine failing must not break identify
log.debug("visible-mark %s detector failed: %s", key, exc)
logger.debug("visible-mark %s detector failed: %s", key, exc)
continue
if det.detected:
detections.append(det)
@@ -719,7 +719,7 @@ def identify(image_path: Path, *, check_visible: bool = True, check_invisible: b
vis_image = imread(image_path)
except Exception as exc: # cv2 missing - detectors fall back / no-op
log.debug("visible-mark decode unavailable: %s", exc)
logger.debug("visible-mark decode unavailable: %s", exc)
# ── Visible Gemini sparkle (fallback for stripped-metadata case) ─
sparkle_conf = _visible_sparkle(image_path, image=vis_image) if check_visible else None
@@ -798,6 +798,6 @@ def has_invisible_target(image_path: Path) -> bool:
try:
report = identify(image_path, check_visible=False, check_invisible=True)
except Exception: # unreadable / detector error -> do not skip the removal
log.debug("has_invisible_target: identify failed, defaulting to run", exc_info=True)
logger.debug("has_invisible_target: identify failed, defaulting to run", exc_info=True)
return True
return report.ai_from_metadata
@@ -31,7 +31,7 @@ from typing import TYPE_CHECKING, cast
if TYPE_CHECKING:
from pathlib import Path
log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Known 48-bit ``bits`` watermarks (dwtDct, no key), name -> message integer.
_BITS_48: dict[str, int] = {
@@ -96,7 +96,7 @@ def detect_invisible_watermark(image_path: Path) -> str | None:
if _bits_match(value, ref) >= _MATCH_48:
return name
except Exception as exc: # decode can fail on tiny images
log.debug("48-bit watermark decode failed for %s: %s", image_path, exc)
logger.debug("48-bit watermark decode failed for %s: %s", image_path, exc)
# 136-bit default string watermark (SD 1.x / 2.x).
try:
@@ -104,6 +104,6 @@ def detect_invisible_watermark(image_path: Path) -> str | None:
if _bytes_match_frac(raw, _SD1_STRING) >= _MATCH_SD1_FRAC:
return "Stable Diffusion 1.x / 2.x"
except Exception as exc:
log.debug("string watermark decode failed for %s: %s", image_path, exc)
logger.debug("string watermark decode failed for %s: %s", image_path, exc)
return None
+3 -3
View File
@@ -41,7 +41,7 @@ from remove_ai_watermarks.noai.constants import (
SYNTHID_C2PA_ISSUERS,
)
log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Official C2PA reader (c2pa-python, a core dependency). It is the primary,
# spec-tracking manifest parser; the hand-rolled caBX/CBOR scanner below stays as
@@ -96,7 +96,7 @@ def _read_manifest_store_impl(path_str: str) -> str | None:
try:
reader = _C2paReader.try_create(path_str)
except Exception as exc: # malformed manifest, unsupported container, etc.
log.debug("c2pa Reader could not parse %s: %s", path_str, exc)
logger.debug("c2pa Reader could not parse %s: %s", path_str, exc)
return None
if reader is None:
return None
@@ -104,7 +104,7 @@ def _read_manifest_store_impl(path_str: str) -> str | None:
with reader:
return reader.json()
except Exception as exc: # pragma: no cover - reader opened but json() failed
log.debug("c2pa Reader.json() failed on %s: %s", path_str, exc)
logger.debug("c2pa Reader.json() failed on %s: %s", path_str, exc)
return None
+2 -2
View File
@@ -33,7 +33,7 @@ from remove_ai_watermarks.metadata import (
IPTC_AI_MARKERS,
)
log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Top-level box types that may carry AI provenance. ``uuid`` boxes are checked
# against ``C2PA_UUID`` / AI-label markers before being stripped; ``jumb`` boxes
@@ -188,7 +188,7 @@ def strip_c2pa_boxes(data: bytes) -> tuple[bytes, int]:
# would silently truncate the file from the bad box to EOF -- worse than not
# stripping. If the walk did not consume the whole input, return it unchanged.
if consumed != len(data):
log.warning(
logger.warning(
"ISOBMFF box walk stopped at offset %d of %d (malformed box); "
"returning input unchanged to avoid truncation",
consumed,
@@ -28,7 +28,7 @@ from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from pathlib import Path
log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)
# Adobe ships Variant P in production (com.adobe.trustmark.P).
_MODEL_TYPE = "P"
@@ -94,10 +94,10 @@ def detect_trustmark(image_path: Path) -> str | None:
if not wm_present:
return None
if not _survives_reencode(decoder, cover, wm_schema):
log.debug("TrustMark decode for %s did not survive re-encode; treating as false positive", image_path)
logger.debug("TrustMark decode for %s did not survive re-encode; treating as false positive", image_path)
return None
except Exception as exc: # model download / decode failure / unreadable image
log.debug("TrustMark decode failed for %s: %s", image_path, exc)
logger.debug("TrustMark decode failed for %s: %s", image_path, exc)
return None
return f"Adobe TrustMark (variant {_MODEL_TYPE}, schema {wm_schema})"