diff --git a/artifacts/visualizations/from_gemini_random/bit_plane_consistency.png b/artifacts/visualizations/from_gemini_random/bit_plane_consistency.png new file mode 100644 index 0000000..448c749 Binary files /dev/null and b/artifacts/visualizations/from_gemini_random/bit_plane_consistency.png differ diff --git a/artifacts/visualizations/from_gemini_random/frequency_analysis.png b/artifacts/visualizations/from_gemini_random/frequency_analysis.png new file mode 100644 index 0000000..7d2dee8 Binary files /dev/null and b/artifacts/visualizations/from_gemini_random/frequency_analysis.png differ diff --git a/artifacts/visualizations/from_gemini_random/lsb_analysis.png b/artifacts/visualizations/from_gemini_random/lsb_analysis.png new file mode 100644 index 0000000..56ce027 Binary files /dev/null and b/artifacts/visualizations/from_gemini_random/lsb_analysis.png differ diff --git a/artifacts/visualizations/from_gemini_random/noise_pattern.png b/artifacts/visualizations/from_gemini_random/noise_pattern.png new file mode 100644 index 0000000..a7219de Binary files /dev/null and b/artifacts/visualizations/from_gemini_random/noise_pattern.png differ diff --git a/artifacts/visualizations/from_gemini_random/report.json b/artifacts/visualizations/from_gemini_random/report.json new file mode 100644 index 0000000..959203e --- /dev/null +++ b/artifacts/visualizations/from_gemini_random/report.json @@ -0,0 +1,571 @@ +{ + "n_images": 88, + "noise": { + "mean_correlation": 0.0026255021803081036, + "max_correlation": 0.7905227541923523, + "avg_std": 0.009135379455983639 + }, + "frequency": { + "overall_phase_coherence": 0.10221139341592789, + "top_carriers": [ + { + "position": [ + 256, + 256 + ], + "frequency": [ + 0, + 0 + ], + "magnitude": 27891198.0, + "coherence": 1.0, + "combined_score": 17.143821716308594 + }, + { + "position": [ + 255, + 256 + ], + "frequency": [ + -1, + 0 + ], + "magnitude": 3603189.5, + "coherence": 0.8163063526153564, + "combined_score": 12.32404613494873 + }, + { + "position": [ + 257, + 256 + ], + "frequency": [ + 1, + 0 + ], + "magnitude": 3603189.5, + "coherence": 0.8163063526153564, + "combined_score": 12.32404613494873 + }, + { + "position": [ + 256, + 255 + ], + "frequency": [ + 0, + -1 + ], + "magnitude": 3387103.75, + "coherence": 0.7231201529502869, + "combined_score": 10.87246322631836 + }, + { + "position": [ + 256, + 257 + ], + "frequency": [ + 0, + 1 + ], + "magnitude": 3387103.75, + "coherence": 0.7231201529502869, + "combined_score": 10.87246322631836 + }, + { + "position": [ + 208, + 344 + ], + "frequency": [ + -48, + 88 + ], + "magnitude": 35576.125, + "coherence": 0.8377835750579834, + "combined_score": 8.779518127441406 + }, + { + "position": [ + 304, + 168 + ], + "frequency": [ + 48, + -88 + ], + "magnitude": 35576.125, + "coherence": 0.8377835750579834, + "combined_score": 8.779518127441406 + }, + { + "position": [ + 254, + 256 + ], + "frequency": [ + -2, + 0 + ], + "magnitude": 1486295.0, + "coherence": 0.604218065738678, + "combined_score": 8.587024688720703 + }, + { + "position": [ + 258, + 256 + ], + "frequency": [ + 2, + 0 + ], + "magnitude": 1486295.0, + "coherence": 0.604218065738678, + "combined_score": 8.587024688720703 + }, + { + "position": [ + 64, + 256 + ], + "frequency": [ + -192, + 0 + ], + "magnitude": 23282.326171875, + "coherence": 0.829474687576294, + "combined_score": 8.340776443481445 + }, + { + "position": [ + 448, + 256 + ], + "frequency": [ + 192, + 0 + ], + "magnitude": 23282.326171875, + "coherence": 0.829474687576294, + "combined_score": 8.340776443481445 + }, + { + "position": [ + 208, + 168 + ], + "frequency": [ + -48, + -88 + ], + "magnitude": 33208.3046875, + "coherence": 0.775635302066803, + "combined_score": 8.074817657470703 + }, + { + "position": [ + 304, + 344 + ], + "frequency": [ + 48, + 88 + ], + "magnitude": 33208.3046875, + "coherence": 0.775635302066803, + "combined_score": 8.074817657470703 + }, + { + "position": [ + 46, + 256 + ], + "frequency": [ + -210, + 0 + ], + "magnitude": 20412.423828125, + "coherence": 0.8131496906280518, + "combined_score": 8.069655418395996 + }, + { + "position": [ + 466, + 256 + ], + "frequency": [ + 210, + 0 + ], + "magnitude": 20412.423828125, + "coherence": 0.8131496906280518, + "combined_score": 8.069655418395996 + }, + { + "position": [ + 160, + 256 + ], + "frequency": [ + -96, + 0 + ], + "magnitude": 49148.40234375, + "coherence": 0.739071249961853, + "combined_score": 7.983905792236328 + }, + { + "position": [ + 352, + 256 + ], + "frequency": [ + 96, + 0 + ], + "magnitude": 49148.40234375, + "coherence": 0.739071249961853, + "combined_score": 7.983905792236328 + }, + { + "position": [ + 252, + 256 + ], + "frequency": [ + -4, + 0 + ], + "magnitude": 790061.8125, + "coherence": 0.5862882137298584, + "combined_score": 7.961716175079346 + }, + { + "position": [ + 260, + 256 + ], + "frequency": [ + 4, + 0 + ], + "magnitude": 790061.8125, + "coherence": 0.5862882137298584, + "combined_score": 7.961716175079346 + }, + { + "position": [ + 18, + 256 + ], + "frequency": [ + -238, + 0 + ], + "magnitude": 19611.361328125, + "coherence": 0.8038652539253235, + "combined_score": 7.945335865020752 + } + ] + }, + "bit_planes": { + "0": { + "mean_consistency": 0.08478139340877533, + "high_consistency_ratio": 0.0, + "entropy": 4.276840129984718 + }, + "1": { + "mean_consistency": 0.08489435166120529, + "high_consistency_ratio": 0.0, + "entropy": 4.277498640191018 + }, + "2": { + "mean_consistency": 0.08504554629325867, + "high_consistency_ratio": 0.0, + "entropy": 4.277922445666714 + }, + "3": { + "mean_consistency": 0.08617954701185226, + "high_consistency_ratio": 0.0, + "entropy": 4.296973619811812 + }, + "4": { + "mean_consistency": 0.09815216809511185, + "high_consistency_ratio": 0.0, + "entropy": 4.396063772756372 + }, + "5": { + "mean_consistency": 0.13747233152389526, + "high_consistency_ratio": 0.0, + "entropy": 4.541127948532454 + }, + "6": { + "mean_consistency": 0.26652395725250244, + "high_consistency_ratio": 0.0, + "entropy": 4.586399223468949 + }, + "7": { + "mean_consistency": 0.2957964837551117, + "high_consistency_ratio": 0.000843048095703125, + "entropy": 4.749796416349521 + } + }, + "lsb": { + "R": { + "mean_consistency": 0.08503350344571199, + "consistent_ratio": 0.003711700439453125 + }, + "G": { + "mean_consistency": 0.08481433174826881, + "consistent_ratio": 0.0037689208984375 + }, + "B": { + "mean_consistency": 0.08481701937588777, + "consistent_ratio": 0.003795623779296875 + } + }, + "watermark_spectrum_peaks": [ + { + "position": [ + 256, + 168 + ], + "frequency": [ + 0, + -88 + ], + "magnitude": 5.904152870178223, + "phase": 0.26778438687324524 + }, + { + "position": [ + 256, + 344 + ], + "frequency": [ + 0, + 88 + ], + "magnitude": 5.904152870178223, + "phase": -0.26778438687324524 + }, + { + "position": [ + 160, + 256 + ], + "frequency": [ + -96, + 0 + ], + "magnitude": 5.29414176940918, + "phase": 2.2876338958740234 + }, + { + "position": [ + 352, + 256 + ], + "frequency": [ + 96, + 0 + ], + "magnitude": 5.29414176940918, + "phase": -2.2876338958740234 + }, + { + "position": [ + 208, + 344 + ], + "frequency": [ + -48, + 88 + ], + "magnitude": 5.22776460647583, + "phase": 0.22210296988487244 + }, + { + "position": [ + 304, + 168 + ], + "frequency": [ + 48, + -88 + ], + "magnitude": 5.22776460647583, + "phase": -0.22210296988487244 + }, + { + "position": [ + 208, + 168 + ], + "frequency": [ + -48, + -88 + ], + "magnitude": 5.153881072998047, + "phase": -2.5221827030181885 + }, + { + "position": [ + 304, + 344 + ], + "frequency": [ + 48, + 88 + ], + "magnitude": 5.153881072998047, + "phase": 2.5221827030181885 + }, + { + "position": [ + 256, + 80 + ], + "frequency": [ + 0, + -176 + ], + "magnitude": 4.930533409118652, + "phase": -0.16202668845653534 + }, + { + "position": [ + 256, + 432 + ], + "frequency": [ + 0, + 176 + ], + "magnitude": 4.930533409118652, + "phase": 0.16202668845653534 + }, + { + "position": [ + 208, + 256 + ], + "frequency": [ + -48, + 0 + ], + "magnitude": 4.8365912437438965, + "phase": 1.1018832921981812 + }, + { + "position": [ + 304, + 256 + ], + "frequency": [ + 48, + 0 + ], + "magnitude": 4.8365912437438965, + "phase": -1.1018832921981812 + }, + { + "position": [ + 160, + 168 + ], + "frequency": [ + -96, + -88 + ], + "magnitude": 4.823460102081299, + "phase": 1.7629518508911133 + }, + { + "position": [ + 352, + 344 + ], + "frequency": [ + 96, + 88 + ], + "magnitude": 4.823460102081299, + "phase": -1.7629518508911133 + }, + { + "position": [ + 256, + 64 + ], + "frequency": [ + 0, + -192 + ], + "magnitude": 4.730844974517822, + "phase": 1.1893998384475708 + }, + { + "position": [ + 256, + 448 + ], + "frequency": [ + 0, + 192 + ], + "magnitude": 4.730844974517822, + "phase": -1.1893998384475708 + }, + { + "position": [ + 64, + 256 + ], + "frequency": [ + -192, + 0 + ], + "magnitude": 4.723679542541504, + "phase": -0.8789771199226379 + }, + { + "position": [ + 448, + 256 + ], + "frequency": [ + 192, + 0 + ], + "magnitude": 4.723679542541504, + "phase": 0.8789771199226379 + }, + { + "position": [ + 160, + 80 + ], + "frequency": [ + -96, + -176 + ], + "magnitude": 4.679676055908203, + "phase": -1.9577183723449707 + }, + { + "position": [ + 352, + 432 + ], + "frequency": [ + 96, + 176 + ], + "magnitude": 4.679676055908203, + "phase": 1.9577183723449707 + } + ] +} \ No newline at end of file diff --git a/artifacts/visualizations/from_gemini_random/vertical_profile.png b/artifacts/visualizations/from_gemini_random/vertical_profile.png new file mode 100644 index 0000000..a70b482 Binary files /dev/null and b/artifacts/visualizations/from_gemini_random/vertical_profile.png differ diff --git a/artifacts/visualizations/from_gemini_random/watermark_signal.png b/artifacts/visualizations/from_gemini_random/watermark_signal.png new file mode 100644 index 0000000..c8fbf81 Binary files /dev/null and b/artifacts/visualizations/from_gemini_random/watermark_signal.png differ diff --git a/src/extraction/synthid_bypass.py b/src/extraction/synthid_bypass.py index bdb0072..6df88ad 100644 --- a/src/extraction/synthid_bypass.py +++ b/src/extraction/synthid_bypass.py @@ -24,7 +24,6 @@ from scipy.fft import fft2, ifft2, fftshift, ifftshift from scipy import ndimage from typing import Optional, Dict, List, Tuple from dataclasses import dataclass -import pywt from PIL import Image @@ -1558,6 +1557,39 @@ class SpectralCodebook: self.n_black_refs = 0 self.n_white_refs = 0 + @staticmethod + def _list_reference_images( + directory: str, + max_images: int = None, + ) -> list: + """ + Collect .png/.jpg/.jpeg/.webp in a directory, excluding common unwatermarked + baseline filenames (e.g. black.jpg, white.png) when folders also hold those. + """ + import glob + + excl = { + 'black.jpg', 'black.jpeg', 'black.png', + 'white.jpg', 'white.jpeg', 'white.png', + } + paths = [] + for ext in ('*.png', '*.jpg', '*.jpeg', '*.webp'): + paths.extend(glob.glob(os.path.join(directory, ext))) + paths.extend(glob.glob(os.path.join(directory, ext.upper()))) + # Dedupe while preserving sort order + seen = set() + out = [] + for p in sorted(paths): + base = os.path.basename(p).lower() + if base in excl: + continue + if p not in seen: + seen.add(p) + out.append(p) + if max_images: + out = out[:max_images] + return out + def extract_from_references(self, black_dir: str, white_dir: str = None, max_images: int = None): """ @@ -1567,19 +1599,21 @@ class SpectralCodebook: For white images: watermark = 255 - pixel values (deviations from 255). Args: - black_dir: Directory of pure-black Gemini PNG images - white_dir: Optional directory of pure-white Gemini PNG images + black_dir: Directory of pure-black SynthID-watermarked images (.png/.jpg/.webp) + white_dir: Optional directory of pure-white watermarked images max_images: Max images to use per color (None = all) - """ - import glob + Files named black.jpg / white.png (etc.) are skipped so unwatermarked baselines + in the same folder are not averaged with watermarked outputs. + """ # --- Black images --- - black_files = sorted(glob.glob(os.path.join(black_dir, '*.png'))) - if max_images: - black_files = black_files[:max_images] + black_files = self._list_reference_images(black_dir, max_images=max_images) if not black_files: - raise ValueError(f"No PNG files found in {black_dir}") + raise ValueError( + f"No image files (.png/.jpg/.jpeg/.webp) found in {black_dir} " + f"(after excluding black.* / white.* baselines)" + ) print(f"Extracting spectral envelope from {len(black_files)} black images...") @@ -1650,9 +1684,7 @@ class SpectralCodebook: # --- White images (optional) --- if white_dir: - white_files = sorted(glob.glob(os.path.join(white_dir, '*.png'))) - if max_images: - white_files = white_files[:max_images] + white_files = self._list_reference_images(white_dir, max_images=max_images) if white_files: print(f"\nExtracting from {len(white_files)} white images...")