NOBODY204 commited on
Commit
41bc579
·
verified ·
1 Parent(s): 80ca449

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -60
app.py CHANGED
@@ -5,122 +5,247 @@ import cv2
5
  from scipy import ndimage
6
 
7
  # ═══════════════════════════════════════════════════
8
- # 🛡️ MEDIASHIELD PRO v2.3 – AUTHENTICITY & DEEPFAKE DETECTOR
 
 
 
9
  # ═══════════════════════════════════════════════════
10
 
 
 
 
 
 
 
 
 
 
 
11
  def get_sensor_noise_fingerprint(img):
12
- """Extrait le bruit hautes fréquences pour vérifier si c'est un capteur physique."""
 
 
 
 
 
13
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
14
- # Filtre médian pour isoler le bruit du signal
15
  blurred = cv2.medianBlur(gray.astype(np.uint8), 3).astype(np.float32)
16
  noise = cv2.absdiff(gray, blurred)
17
- # Une image IA a un bruit très faible ou trop régulier
 
 
18
  noise_density = np.std(noise)
19
- return noise_density, noise
 
 
 
 
 
20
 
21
  def analyze_frequency_domain(img):
22
- """Analyse FFT pour détecter les grilles de génération IA."""
 
 
 
 
23
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
24
  f = np.fft.fft2(gray)
25
  fshift = np.fft.fftshift(f)
26
  mag = np.abs(fshift)
27
-
28
- # On ignore le centre (formes) pour voir les artefacts de structure
29
  h, w = gray.shape
30
- cy, cx = h//2, w//2
31
- mag[cy-20:cy+20, cx-20:cx+20] = 0
32
-
33
- # Detection de pics anormaux (signatures de Nano Banana / GAN)
 
34
  peak_score = np.max(mag) / (np.mean(mag) + 1e-8)
35
-
36
  vis = np.log(mag + 1)
37
  vis = cv2.normalize(vis, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
38
  return peak_score, cv2.applyColorMap(vis, cv2.COLORMAP_VIRIDIS)
39
 
 
40
  def error_level_analysis(img, quality=92):
41
- """Détecte les manipulations locales ou l'absence de compression JPEG réelle."""
42
- _, enc = cv2.imencode('.jpg', cv2.cvtColor(img, cv2.COLOR_RGB2BGR), [int(cv2.IMWRITE_JPEG_QUALITY), quality])
 
 
 
 
 
 
 
 
 
43
  dec = cv2.imdecode(enc, 1)
44
  diff = cv2.absdiff(img, cv2.cvtColor(dec, cv2.COLOR_BGR2RGB))
45
  ela_score = np.mean(diff)
46
- return ela_score, cv2.convertScaleAbs(diff, alpha=5.0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  def detect_deepfake(img):
49
- # 1. Analyse du bruit de capteur (Le point faible de l'IA)
50
  noise_density, noise_map = get_sensor_noise_fingerprint(img)
51
-
52
- # 2. Analyse fréquentielle
53
  freq_score, fft_map = analyze_frequency_domain(img)
54
-
55
- # 3. ELA
56
- ela_s, ela_map = error_level_analysis(img)
57
-
58
- # --- LOGIQUE DE SCORING ---
 
 
 
 
 
 
 
59
  ai_confidence = 0
60
  reasons = []
61
-
62
- # L'IA a souvent un bruit trop faible (< 1.5)
63
- if noise_density < 1.8:
64
- ai_confidence += 40
65
- reasons.append("⚠️ Absence de grain photonique (Signature IA/Lissage)")
66
-
67
- # Pics de fréquence (Grilles de diffusion)
68
- if freq_score > 150:
 
 
 
 
 
 
69
  ai_confidence += 30
70
- reasons.append("⚠️ Artefacts de grille détectés (Pattern de génération)")
71
-
72
- # ELA trop homogène (Propre aux fichiers purement numériques)
73
- if ela_s < 1.0:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  ai_confidence += 20
75
- reasons.append("⚠️ Compression trop parfaite (Image non-optique)")
 
 
 
 
 
 
 
 
 
76
 
77
- # Plafond de sécurité
78
  final_score = min(ai_confidence, 100)
79
-
 
80
  if final_score > 55:
81
  label = "🚨 DEEPFAKE / GENERATED"
82
  color = "red"
83
- elif final_score > 30:
84
  label = "⚖️ SUSPICIEUX / MODIFIÉ"
85
  color = "orange"
86
  else:
87
  label = "✅ AUTHENTIQUE (CAMERA)"
88
  color = "green"
89
-
90
- return final_score, label, reasons, fft_map, ela_map, noise_map
 
 
 
 
 
91
 
92
  # ═══════════════════════════════════════════════════
93
  # 🎨 GRADIO INTERFACE
94
  # ═══════════════════════════════════════════════════
95
 
96
  def process(input_img):
97
- if input_img is None: return None, "Veuillez charger une image."
98
-
99
- score, label, reasons, fft, ela, noise = detect_deepfake(input_img)
100
-
 
 
 
101
  fig, axes = plt.subplots(2, 2, figsize=(12, 10))
102
- axes[0,0].imshow(input_img); axes[0,0].set_title("Original")
103
- axes[0,1].imshow(fft); axes[0,1].set_title("FFT (Grilles IA)")
104
- axes[1,0].imshow(ela); axes[1,0].set_title("ELA (Compression)")
105
- axes[1,1].imshow(noise, cmap='gray'); axes[1,1].set_title("Sensor Noise Fingerprint")
106
- for ax in axes.flatten(): ax.axis('off')
 
107
  plt.tight_layout()
108
-
109
- report = f"RÉSULTAT : {label}\nIndice de probabilité IA : {score}%\n\nAnalyse :\n"
110
- report += "\n".join(reasons) if reasons else "Aucune trace de génération détectée."
111
-
 
 
 
 
 
 
 
112
  return fig, report
113
 
 
114
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
115
- gr.Markdown("# 🛡️ MediaShield PRO v2.3\n### Analyse Forensic : Authentique vs Deepfake")
 
 
 
 
116
  with gr.Row():
117
  with gr.Column():
118
- in_img = gr.Image(label="Charger une image (JPG/PNG)", type="numpy")
119
  run_btn = gr.Button("LANCER L'ANALYSE", variant="primary")
120
  with gr.Column():
121
  out_plot = gr.Plot()
122
- out_text = gr.Textbox(label="Rapport d'Expertise", lines=8)
123
-
124
  run_btn.click(process, inputs=in_img, outputs=[out_plot, out_text])
125
 
126
- demo.launch()
 
 
 
5
  from scipy import ndimage
6
 
7
  # ═══════════════════════════════════════════════════
8
+ # 🛡️ IMAGESHIELD PRO v2.4 – AUTHENTICITY & DEEPFAKE DETECTOR
9
+ # ACoNum / Trusted Sound 2026 — Sami Meddeb
10
+ # Fix v2.4 : seuils recalibrés pour éviter les faux positifs
11
+ # (photos studio professionnelles, éclairage fort, JPEG haute qualité)
12
  # ═══════════════════════════════════════════════════
13
 
14
+ # ── SEUILS CALIBRÉS v2.4 ────────────────────────────────────
15
+ # Problème v2.3 : photos studio → grain faible → détecté comme IA
16
+ # Solution : seuils plus stricts + score combiné obligatoire
17
+
18
+ NOISE_THRESHOLD = 0.55 # était 1.8 — studio pro légit peut avoir < 1.0
19
+ FREQ_THRESHOLD = 500 # était 150 — trop sensible aux JPEG
20
+ ELA_THRESHOLD = 0.25 # était 1.0 — photos JPEG légit ont ELA faible
21
+ MIN_SIGNALS = 2 # il faut AU MOINS 2 signaux pour crier deepfake
22
+
23
+
24
  def get_sensor_noise_fingerprint(img):
25
+ """
26
+ Extrait le bruit hautes fréquences pour vérifier si c'est un capteur physique.
27
+ IMPORTANT : photos studio avec éclairage professionnel ont naturellement
28
+ moins de grain — ne pas confondre avec signature IA.
29
+ On normalise par la luminosité moyenne pour corriger ce biais.
30
+ """
31
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
 
32
  blurred = cv2.medianBlur(gray.astype(np.uint8), 3).astype(np.float32)
33
  noise = cv2.absdiff(gray, blurred)
34
+
35
+ # Normalisation par la luminosité pour compenser l'éclairage studio
36
+ mean_lum = np.mean(gray)
37
  noise_density = np.std(noise)
38
+ # Correction : photo très lumineuse → grain naturellement réduit
39
+ # On ramène à une base commune
40
+ corrected_density = noise_density * (128.0 / (mean_lum + 1e-8))
41
+
42
+ return corrected_density, noise
43
+
44
 
45
  def analyze_frequency_domain(img):
46
+ """
47
+ Analyse FFT pour détecter les grilles de génération IA.
48
+ Les GAN/Diffusion produisent des pics très réguliers à fréquences spécifiques.
49
+ Une photo JPEG légitime même compressée n'a PAS ces pics réguliers.
50
+ """
51
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
52
  f = np.fft.fft2(gray)
53
  fshift = np.fft.fftshift(f)
54
  mag = np.abs(fshift)
55
+
 
56
  h, w = gray.shape
57
+ cy, cx = h // 2, w // 2
58
+ mag[cy - 20:cy + 20, cx - 20:cx + 20] = 0
59
+
60
+ # Ratio max/mean : un vrai GAN a des pics TRÈS anormaux (> 500)
61
+ # Une photo réelle même avec artefacts JPEG reste < 400
62
  peak_score = np.max(mag) / (np.mean(mag) + 1e-8)
63
+
64
  vis = np.log(mag + 1)
65
  vis = cv2.normalize(vis, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
66
  return peak_score, cv2.applyColorMap(vis, cv2.COLORMAP_VIRIDIS)
67
 
68
+
69
  def error_level_analysis(img, quality=92):
70
+ """
71
+ ELA : détecte les manipulations locales.
72
+ IMPORTANT : une photo JPEG légit, même haute qualité, a une ELA faible.
73
+ On cherche une ELA ANORMALEMENT basse (image purement synthétique)
74
+ OU des zones avec ELA irrégulière (copier-coller, manipulation locale).
75
+ Seuil abaissé à 0.25 pour ne pas pénaliser les vraies photos.
76
+ """
77
+ _, enc = cv2.imencode(
78
+ '.jpg', cv2.cvtColor(img, cv2.COLOR_RGB2BGR),
79
+ [int(cv2.IMWRITE_JPEG_QUALITY), quality]
80
+ )
81
  dec = cv2.imdecode(enc, 1)
82
  diff = cv2.absdiff(img, cv2.cvtColor(dec, cv2.COLOR_BGR2RGB))
83
  ela_score = np.mean(diff)
84
+
85
+ # Variance spatiale : une image IA a souvent une ELA trop UNIFORME
86
+ ela_variance = np.std(diff)
87
+
88
+ return ela_score, ela_variance, cv2.convertScaleAbs(diff, alpha=5.0)
89
+
90
+
91
+ def detect_face_artifacts(img):
92
+ """
93
+ Détecte les artefacts typiques des deepfakes sur les visages :
94
+ - Bords flous autour du visage
95
+ - Transition peau/arrière-plan trop nette (upsampling)
96
+ - Symétrie excessive
97
+ """
98
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
99
+ # Gradient de Sobel pour détecter les discontinuités anormales
100
+ sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=3)
101
+ sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=3)
102
+ gradient_mag = np.sqrt(sobelx**2 + sobely**2)
103
+
104
+ # Un deepfake facial a des gradients trop lisses dans les zones de peau
105
+ # et trop nets aux bords du visage généré
106
+ gradient_std = np.std(gradient_mag)
107
+ gradient_mean = np.mean(gradient_mag)
108
+ ratio = gradient_std / (gradient_mean + 1e-8)
109
+
110
+ # Ratio < 1.2 = trop uniforme = suspect
111
+ return ratio
112
+
113
 
114
  def detect_deepfake(img):
115
+ # 1. Bruit de capteur (corrigé pour éclairage studio)
116
  noise_density, noise_map = get_sensor_noise_fingerprint(img)
117
+
118
+ # 2. Analyse fréquentielle FFT
119
  freq_score, fft_map = analyze_frequency_domain(img)
120
+
121
+ # 3. ELA + variance
122
+ ela_s, ela_var, ela_map = error_level_analysis(img)
123
+
124
+ # 4. Artefacts de gradient (visage/bords)
125
+ gradient_ratio = detect_face_artifacts(img)
126
+
127
+ # ─── LOGIQUE DE SCORING v2.4 ─────────────────────────────
128
+ # Règle : au moins 2 signaux positifs pour déclarer "deepfake"
129
+ # Chaque signal contribue des points SEULEMENT s'il est significatif
130
+
131
+ signals_triggered = 0
132
  ai_confidence = 0
133
  reasons = []
134
+
135
+ # Signal 1 Bruit de capteur anormalement faible
136
+ # Seuil 0.55 (corrigé luminosité) — les vrais studios restent > 0.6
137
+ if noise_density < NOISE_THRESHOLD:
138
+ ai_confidence += 35
139
+ signals_triggered += 1
140
+ reasons.append(
141
+ f"⚠️ Bruit photonique absent (densité={noise_density:.2f} < {NOISE_THRESHOLD}) — "
142
+ "Absence de grain capteur physique"
143
+ )
144
+
145
+ # Signal 2 — Pics de fréquence GAN (grille de diffusion)
146
+ # Seuil 500 — uniquement les vraies grilles IA
147
+ if freq_score > FREQ_THRESHOLD:
148
  ai_confidence += 30
149
+ signals_triggered += 1
150
+ reasons.append(
151
+ f"⚠️ Grille IA détectée en FFT (score={freq_score:.0f} > {FREQ_THRESHOLD}) — "
152
+ "Pattern de génération diffusion/GAN"
153
+ )
154
+
155
+ # Signal 3 — ELA trop parfaite ET variance nulle
156
+ # Seuil 0.25 + variance < 0.5 = image purement synthétique
157
+ if ela_s < ELA_THRESHOLD and ela_var < 0.5:
158
+ ai_confidence += 25
159
+ signals_triggered += 1
160
+ reasons.append(
161
+ f"⚠️ Compression parfaite ELA={ela_s:.3f} σ={ela_var:.3f} — "
162
+ "Image non issue d'un capteur optique réel"
163
+ )
164
+
165
+ # Signal 4 — Gradient trop uniforme (deepfake facial)
166
+ if gradient_ratio < 1.2:
167
  ai_confidence += 20
168
+ signals_triggered += 1
169
+ reasons.append(
170
+ f"⚠️ Gradients trop lisses (ratio={gradient_ratio:.2f} < 1.2) — "
171
+ "Absence de texture naturelle du capteur"
172
+ )
173
+
174
+ # ─── RÈGLE DE SÉCURITÉ : minimum 2 signaux ──────────────
175
+ if signals_triggered < MIN_SIGNALS:
176
+ # Un seul signal → suspicieux mais pas deepfake
177
+ ai_confidence = min(ai_confidence, 28)
178
 
 
179
  final_score = min(ai_confidence, 100)
180
+
181
+ # ─── VERDICT ─────────────────────────────────────────────
182
  if final_score > 55:
183
  label = "🚨 DEEPFAKE / GENERATED"
184
  color = "red"
185
+ elif final_score > 28:
186
  label = "⚖️ SUSPICIEUX / MODIFIÉ"
187
  color = "orange"
188
  else:
189
  label = "✅ AUTHENTIQUE (CAMERA)"
190
  color = "green"
191
+
192
+ return (
193
+ final_score, label, reasons,
194
+ fft_map, ela_map, noise_map,
195
+ noise_density, freq_score, ela_s, signals_triggered
196
+ )
197
+
198
 
199
  # ═══════════════════════════════════════════════════
200
  # 🎨 GRADIO INTERFACE
201
  # ═══════════════════════════════════════════════════
202
 
203
  def process(input_img):
204
+ if input_img is None:
205
+ return None, "Veuillez charger une image."
206
+
207
+ (score, label, reasons,
208
+ fft, ela, noise,
209
+ noise_val, freq_val, ela_val, n_signals) = detect_deepfake(input_img)
210
+
211
  fig, axes = plt.subplots(2, 2, figsize=(12, 10))
212
+ axes[0, 0].imshow(input_img); axes[0, 0].set_title("Original")
213
+ axes[0, 1].imshow(fft); axes[0, 1].set_title("FFT Grilles IA")
214
+ axes[1, 0].imshow(ela); axes[1, 0].set_title("ELA Compression")
215
+ axes[1, 1].imshow(noise, cmap='gray'); axes[1, 1].set_title("Bruit Capteur Corrigé")
216
+ for ax in axes.flatten():
217
+ ax.axis('off')
218
  plt.tight_layout()
219
+
220
+ report = f"RÉSULTAT : {label}\n"
221
+ report += f"Probabilité IA : {score}% | Signaux déclenchés : {n_signals}/{MIN_SIGNALS} minimum\n"
222
+ report += f"\nMesures brutes :\n"
223
+ report += f" • Bruit capteur corrigé : {noise_val:.3f} (seuil < {NOISE_THRESHOLD})\n"
224
+ report += f" • Pic FFT : {freq_val:.0f} (seuil > {FREQ_THRESHOLD})\n"
225
+ report += f" • ELA moyenne : {ela_val:.4f} (seuil < {ELA_THRESHOLD})\n"
226
+ report += f"\nSignaux actifs :\n"
227
+ report += "\n".join(reasons) if reasons else " Aucune trace de génération synthétique détectée."
228
+ report += "\n\n── ImageShield PRO v2.4 · ACoNum / Trusted Sound 2026 ──"
229
+
230
  return fig, report
231
 
232
+
233
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
234
+ gr.Markdown(
235
+ "# 🛡️ ImageShield PRO v2.4\n"
236
+ "### Analyse Forensic : Authentique vs Deepfake\n"
237
+ "_Fix v2.4 : seuils recalibrés — photos studio, éclairage fort et JPEG haute qualité correctement classifiés_"
238
+ )
239
  with gr.Row():
240
  with gr.Column():
241
+ in_img = gr.Image(label="Charger une image (JPG/PNG)", type="numpy")
242
  run_btn = gr.Button("LANCER L'ANALYSE", variant="primary")
243
  with gr.Column():
244
  out_plot = gr.Plot()
245
+ out_text = gr.Textbox(label="Rapport d'Expertise", lines=12)
246
+
247
  run_btn.click(process, inputs=in_img, outputs=[out_plot, out_text])
248
 
249
+ demo.launch()
250
+
251
+