mirror of
https://github.com/wiltodelta/remove-ai-watermarks.git
synced 2026-08-07 06:28:36 +02:00
fix: address whole-project code review (visible all/batch, engine consolidation, I/O)
Nine findings from a high-effort project-wide review, fixed and verified (571 passed, ruff/pyright clean): Correctness: - all/batch now remove Doubao/Jimeng/Samsung visible text marks: the visible step routes through the registry (new cli._remove_visible_auto) instead of a hardcoded GeminiEngine, so they no longer leave the wordmark intact. - batch always reads the original source (dropped the out_path-reuse that re-processed already-cleaned outputs on a re-run). - img2img_runner only retries the diffusion call on the deprecated-callback TypeError; any other TypeError now propagates instead of double-running. - gemini detect/remove and the reverse-alpha engines normalize channels via a new image_io.to_bgr, fixing a grayscale/BGRA crash in the FP-gate path. - _png_late_metadata advances its cursor by the clamped length, so a malformed chunk length no longer aborts the late AI-label scan. Cleanup / efficiency: - Consolidate the ~90%-identical Doubao/Jimeng/Samsung engines into a shared config-driven _text_mark_engine.TextMarkEngine base; each engine is now a thin subclass (TextMarkConfig + test shims). Behavior is byte-exact (the three engine test suites pass unchanged). Registry adapters collapse to one _text_mark(...) row each. Gemini stays a separate engine. - scan_head is memoized per (path, size, mtime), so identify() reads the file head once instead of ~8 times. - invisible_engine post-processing decodes/encodes the output once (chained in memory) instead of 2-4 times across stages. - Remove the orphaned get_model_id_for_profile (+ CONTROLNET_PROFILE); derive the --strength help from the strength constants (strength_default_help) so it cannot drift; share the --pipeline/--strength click options; simplify the retired --auto resolver. Net -835 lines. Tests added for the registry-routed visible pass, to_bgr, the polish/model/guidance wiring, and strength_default_help. CLAUDE.md updated for the new base module, the engine/registry changes, image_io.to_bgr, and the scan_head cache. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b1189549b8
commit
2fcd00ced0
@@ -261,8 +261,14 @@ class InvisibleEngine:
|
||||
vendor=vendor,
|
||||
)
|
||||
|
||||
# Post-processing: optional Humanizer, then restore original resolution.
|
||||
if humanize > 0.0:
|
||||
# Post-processing chain: decode the diffusion output ONCE, apply the
|
||||
# optional stages in memory in order (humanize -> restore original
|
||||
# resolution -> unsharp -> adaptive polish), and write ONCE. Previously
|
||||
# each stage independently imread/imwrote the full-res output, so a run
|
||||
# with several stages PNG-decoded+re-encoded the same image 2-4 times.
|
||||
# PNG is lossless, so the single-write output is byte-identical.
|
||||
needs_restore = target is not None # the input was resized before diffusion
|
||||
if humanize > 0.0 or unsharp > 0.0 or adaptive_polish or needs_restore:
|
||||
import cv2
|
||||
|
||||
from remove_ai_watermarks import image_io
|
||||
@@ -271,67 +277,43 @@ class InvisibleEngine:
|
||||
if out_cv is None:
|
||||
return out_path
|
||||
|
||||
if self._progress_callback:
|
||||
self._progress_callback(f"Applying Analog Humanizer (grain: {humanize})...")
|
||||
from remove_ai_watermarks.humanizer import apply_analog_humanizer
|
||||
if humanize > 0.0:
|
||||
if self._progress_callback:
|
||||
self._progress_callback(f"Applying Analog Humanizer (grain: {humanize})...")
|
||||
from remove_ai_watermarks.humanizer import apply_analog_humanizer
|
||||
|
||||
out_cv = apply_analog_humanizer(out_cv, grain_intensity=humanize, chromatic_shift=1)
|
||||
out_cv = apply_analog_humanizer(out_cv, grain_intensity=humanize, chromatic_shift=1)
|
||||
|
||||
# Restore original resolution
|
||||
# Restore original resolution if the input was resized for diffusion.
|
||||
if (out_cv.shape[1], out_cv.shape[0]) != orig_size:
|
||||
if self._progress_callback:
|
||||
self._progress_callback(
|
||||
f"Upscaling result back to original resolution {orig_size[0]}x{orig_size[1]}..."
|
||||
)
|
||||
# Using INTER_LANCZOS4 for high-quality upscaling back to original
|
||||
out_cv = cv2.resize(out_cv, orig_size, interpolation=cv2.INTER_LANCZOS4)
|
||||
|
||||
image_io.imwrite(out_path, out_cv)
|
||||
|
||||
else:
|
||||
# No humanize: still restore the original size if it was capped.
|
||||
import cv2
|
||||
|
||||
from remove_ai_watermarks import image_io
|
||||
|
||||
out_cv = image_io.imread(out_path, cv2.IMREAD_COLOR)
|
||||
if out_cv is not None and (out_cv.shape[1], out_cv.shape[0]) != orig_size:
|
||||
if self._progress_callback:
|
||||
self._progress_callback(
|
||||
f"Upscaling result back to original resolution {orig_size[0]}x{orig_size[1]}..."
|
||||
)
|
||||
out_cv = cv2.resize(out_cv, orig_size, interpolation=cv2.INTER_LANCZOS4)
|
||||
image_io.imwrite(out_path, out_cv)
|
||||
|
||||
# Final sharpening.
|
||||
if unsharp > 0.0:
|
||||
import cv2
|
||||
|
||||
from remove_ai_watermarks import image_io
|
||||
from remove_ai_watermarks.humanizer import unsharp_mask
|
||||
|
||||
out_cv = image_io.imread(out_path, cv2.IMREAD_COLOR)
|
||||
if out_cv is not None:
|
||||
if unsharp > 0.0:
|
||||
if self._progress_callback:
|
||||
self._progress_callback(f"Sharpening (unsharp mask: {unsharp})...")
|
||||
image_io.imwrite(out_path, unsharp_mask(out_cv, amount=unsharp))
|
||||
from remove_ai_watermarks.humanizer import unsharp_mask
|
||||
|
||||
# Adaptive polish (CLI default): restore the input's detail level in the
|
||||
# softened output, sparing text/edges. Self-limiting where there is no deficit.
|
||||
if adaptive_polish:
|
||||
import cv2
|
||||
import numpy as np
|
||||
out_cv = unsharp_mask(out_cv, amount=unsharp)
|
||||
|
||||
from remove_ai_watermarks import humanizer, image_io
|
||||
# Adaptive polish (CLI default): restore the input's detail level in the
|
||||
# softened output, sparing text/edges. Self-limiting where no deficit.
|
||||
if adaptive_polish:
|
||||
import numpy as np
|
||||
|
||||
from remove_ai_watermarks import humanizer
|
||||
|
||||
out_cv = image_io.imread(out_path, cv2.IMREAD_COLOR)
|
||||
if out_cv is not None:
|
||||
ref = cv2.cvtColor(np.array(reference_pil.convert("RGB")), cv2.COLOR_RGB2BGR)
|
||||
if (ref.shape[1], ref.shape[0]) != (out_cv.shape[1], out_cv.shape[0]):
|
||||
ref = cv2.resize(ref, (out_cv.shape[1], out_cv.shape[0]), interpolation=cv2.INTER_LANCZOS4)
|
||||
if self._progress_callback:
|
||||
self._progress_callback("Adaptive polish (sharpen + grain to the input's detail level)...")
|
||||
image_io.imwrite(out_path, humanizer.adaptive_polish(out_cv, ref, seed=seed))
|
||||
out_cv = humanizer.adaptive_polish(out_cv, ref, seed=seed)
|
||||
|
||||
image_io.imwrite(out_path, out_cv)
|
||||
|
||||
return out_path
|
||||
finally:
|
||||
|
||||
Reference in New Issue
Block a user