Files
Victor KuznetsovandClaude Opus 5.5 1dc70df289 Upgrade to OpenCV 5 with frozen Hershey templates and tighten video marks
- Lock OpenCV 5 and cap NumPy below 2.4 through uv constraint-dependencies so
  PaddleX 3.7 keeps resolving; Dependabot ignores mirror the cap.
- OpenCV 5 draws Hershey putText glyphs differently, so the Dola and Kling
  video templates ship as committed OpenCV 4 renders with a recipe script, and
  the OpenArt alpha recipe now refuses any OpenCV but 4.x.
- Break Sora/Veo cross-matches in favor of Veo under valid Google AI-video
  provenance, and require the full Sora mascot shape for mascot-only matches.
- Let the Doubao engine detect marks tinted by saturated backgrounds.
- Share C2PA claim-generator, credential-level and vendor-platform helpers,
  JPEG suffixes and the DCT basis instead of per-module copies.
- Log model download failures, document the optional GITHUB_TOKEN, mark the
  photo and source classifier model cards archived, and refresh the lock.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 19:19:00 -07:00

101 lines
4.1 KiB
Python

"""Build the PROVISIONAL synthetic OpenArt alpha asset.
Unlike ``visible_alpha_solve.py``, this does NOT recover an alpha map from a
controlled capture: OpenArt (openart.ai) requires an account to generate
images (no anonymous or API-key access, confirmed 2026-09-21 via its own
help center and MCP docs), and creating an account is outside what this
project's tooling is allowed to do on its own. No committed capture exists
under ``data/calibration/openart/``.
This script instead PROCEDURALLY DRAWS a reconstruction of OpenArt's own
brand mark (the bowtie/infinity icon followed by the "OpenArt" wordmark, as
shown on openart.ai's own marketing pages) as a semi-transparent white
overlay, matching the general rendering style (near-white, low-saturation,
anti-aliased) that every other bundled text-mark asset in this module was
solved from. It is a reconstruction from the public logo, not a recovered
watermark alpha map, and ``openart_engine.py`` documents that provenance
gap explicitly. Replace this asset (and re-run the engine's calibration)
the moment a real captured OpenArt export is available under
``data/calibration/openart/``.
The wordmark is a Hershey ``cv2.putText`` render, which OpenCV 5 draws with
different glyphs than the OpenCV 4 build the committed asset came from, so the
script refuses to run on any OpenCV other than 4.x.
Usage::
uv run --no-project --with 'opencv-python-headless>=4.8,<5' --with numpy \
python scripts/build_openart_alpha.py
"""
# cv2/numpy boundary: third-party libs ship no usable element types; relax the
# unknown-type rules for this file only (mirrors visible_alpha_solve.py).
# pyright: reportUnknownMemberType=false, reportUnknownArgumentType=false, reportUnknownVariableType=false, reportUnknownParameterType=false, reportMissingTypeArgument=false, reportMissingTypeStubs=false, reportMissingImports=false, reportArgumentType=false, reportAssignmentType=false, reportReturnType=false, reportCallIssue=false, reportIndexIssue=false, reportOperatorIssue=false
from __future__ import annotations
import sys
from pathlib import Path
import cv2
import numpy as np
sys.path.insert(0, str(Path(__file__).parent.parent))
_ROOT = Path(__file__).resolve().parents[1]
_ASSET = _ROOT / "src" / "remove_ai_watermarks" / "assets" / "openart_alpha.png"
# Canvas sized to comfortably fit "OpenArt" set bold at this height plus the
# icon and inter-glyph gap, at a resolution close to the other bundled
# assets (doubao_alpha.png is 335x83).
_W, _H = 360, 90
_ALPHA_PEAK = 173 # matches doubao_alpha.png's measured peak (~68% opacity)
def _draw_infinity_icon(canvas: np.ndarray, cx: int, cy: int, size: int) -> None:
"""Draw the bowtie/infinity glyph left of the wordmark (two mirrored lobes)."""
half = size // 2
# Two overlapping filled triangles meeting at the center, echoing the
# angular "infinity" mark on openart.ai's own site header.
left_lobe = np.array(
[[cx - size, cy - half], [cx - size, cy + half], [cx, cy]],
dtype=np.int32,
)
right_lobe = np.array(
[[cx + size, cy - half], [cx + size, cy + half], [cx, cy]],
dtype=np.int32,
)
cv2.fillConvexPoly(canvas, left_lobe, (_ALPHA_PEAK,), lineType=cv2.LINE_AA)
cv2.fillConvexPoly(canvas, right_lobe, (_ALPHA_PEAK,), lineType=cv2.LINE_AA)
def build() -> np.ndarray:
canvas = np.zeros((_H, _W), np.uint8)
icon_cx, icon_cy, icon_size = 34, _H // 2, 22
_draw_infinity_icon(canvas, icon_cx, icon_cy, icon_size)
text_x = icon_cx + icon_size + 18
cv2.putText(
canvas,
"OpenArt",
(text_x, _H // 2 + 16),
cv2.FONT_HERSHEY_DUPLEX,
1.3,
(_ALPHA_PEAK,),
thickness=2,
lineType=cv2.LINE_AA,
)
return canvas
def main() -> None:
if not cv2.__version__.startswith("4."):
raise SystemExit(f"OpenCV {cv2.__version__} draws different glyphs; run with OpenCV 4.x")
canvas = build()
_ASSET.parent.mkdir(parents=True, exist_ok=True)
cv2.imwrite(str(_ASSET), canvas)
print(f"Wrote {_ASSET} ({canvas.shape[1]}x{canvas.shape[0]}, peak={int(canvas.max())})")
if __name__ == "__main__":
main()