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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -135
app.py CHANGED
@@ -5,169 +5,122 @@ import cv2
5
  from scipy import ndimage
6
 
7
  # ═══════════════════════════════════════════════════
8
- # 🔬 MEDIASHIELD PRO v2.1AVEC CORRECTION LUMIÈRE
9
  # ═══════════════════════════════════════════════════
10
 
11
- def analyze_chrominance_noise(img):
12
- """Détecte l'uniformité anormale du bruit chromatique."""
13
- ycrcb = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
14
- cr = ycrcb[:, :, 1].astype(np.float32)
15
- cb = ycrcb[:, :, 2].astype(np.float32)
16
-
17
- cr_var = ndimage.generic_filter(cr, np.var, size=5)
18
- cb_var = ndimage.generic_filter(cb, np.var, size=5)
19
-
20
- uniformity_cr = np.std(cr_var) / (np.mean(cr_var) + 1e-8)
21
- uniformity_cb = np.std(cb_var) / (np.mean(cb_var) + 1e-8)
22
-
23
- return (uniformity_cr + uniformity_cb) / 2, cr_var
24
-
25
- def find_peaks(signal, threshold_factor=0.3):
26
- threshold = np.max(signal) * threshold_factor
27
- peaks = []
28
- for i in range(1, len(signal)-1):
29
- if signal[i] > threshold and signal[i] > signal[i-1] and signal[i] > signal[i+1]:
30
- peaks.append(i)
31
- return peaks
32
-
33
- def compute_radial_profile(image):
34
- cy, cx = np.array(image.shape) // 2
35
- y, x = np.indices(image.shape)
36
- r = np.sqrt((x - cx)**2 + (y - cy)**2).astype(int)
37
- tbin = ndimage.mean(image, labels=r, index=np.arange(0, min(cx, cy)))
38
- return tbin[~np.isnan(tbin)]
39
-
40
- def detect_ringing(radial_profile):
41
- if len(radial_profile) < 10: return 0
42
- diff = np.diff(radial_profile)
43
- sign_changes = np.sum(diff[1:] * diff[:-1] < 0)
44
- return sign_changes / len(radial_profile)
45
-
46
- def detect_grid_and_ringing(img):
47
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
48
  f = np.fft.fft2(gray)
49
  fshift = np.fft.fftshift(f)
50
- magnitude = np.abs(fshift)
51
 
52
- rows, cols = gray.shape
53
- crow, ccol = rows//2, cols//2
54
- mask_radius = min(rows, cols) // 6
55
- y, x = np.ogrid[:rows, :cols]
56
- mask = (x - ccol)**2 + (y - crow)**2 <= mask_radius**2
57
- magnitude[mask] = 0
58
 
59
- proj_x = np.sum(magnitude, axis=0)
60
- proj_y = np.sum(magnitude, axis=1)
61
- grid_score = (len(find_peaks(proj_x, 0.2)) + len(find_peaks(proj_y, 0.2))) / 2
62
 
63
- radial = compute_radial_profile(magnitude)
64
- ringing = detect_ringing(radial)
65
-
66
- fft_vis = np.log(magnitude + 1)
67
- fft_vis = (fft_vis - fft_vis.min()) / (fft_vis.max() - fft_vis.min() + 1e-8) * 255
68
- fft_vis = cv2.applyColorMap(fft_vis.astype(np.uint8), cv2.COLORMAP_VIRIDIS)
69
-
70
- return grid_score, ringing, fft_vis
71
 
72
- def error_level_analysis(img, quality=85):
73
- encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality]
74
- _, enc = cv2.imencode('.jpg', cv2.cvtColor(img, cv2.COLOR_RGB2BGR), encode_param)
75
  dec = cv2.imdecode(enc, 1)
76
- dec_rgb = cv2.cvtColor(dec, cv2.COLOR_BGR2RGB)
77
- diff = cv2.absdiff(img, dec_rgb)
78
- ela_enhanced = cv2.convertScaleAbs(diff, alpha=5.0)
79
- return np.mean(diff), ela_enhanced
80
 
81
- def compute_ai_score(metrics):
82
- score = 0
 
 
 
 
 
 
 
 
 
 
83
  reasons = []
84
 
85
- if metrics['grid_score'] > 15:
86
- score += 30
87
- reasons.append("⚠️ Strong grid patterns (GAN signature)")
88
- if metrics['ringing'] > 0.25:
89
- score += 25
90
- reasons.append("⚠️ Circular artifacts (Diffusion signature)")
91
- if metrics['chrom_uniformity'] < 1.0:
92
- score += 20
93
- reasons.append("⚡ Unnatural chrominance noise")
94
- if metrics['ela_score'] < 1.5:
95
- score += 15
96
- reasons.append("⚠️ Suspicious compression history")
97
 
98
- return min(score, 100), reasons
99
-
100
- # ═══════════════════════════════════════════════════
101
- # 🎯 FONCTION ANALYSE (AVEC GESTION IMAGES SOMBRES)
102
- # ═══════════════════════════════════════════════════
 
 
 
 
103
 
104
- def analyze_image(img_input):
105
- if img_input is None: return None, "Erreur"
106
 
107
- # 1. Nettoyage format
108
- if img_input.shape[2] == 4:
109
- img = cv2.cvtColor(img_input, cv2.COLOR_RGBA2RGB)
 
 
 
110
  else:
111
- img = img_input.copy()
 
 
 
112
 
113
- # 2. Correction Gamma pour images sombres
114
- gray_check = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
115
- mean_brightness = np.mean(gray_check)
116
-
117
- # Image pour l'analyse (plus claire si besoin)
118
- if mean_brightness < 60:
119
- gamma = 0.6
120
- invGamma = 1.0 / gamma
121
- table = np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype("uint8")
122
- img_analysed = cv2.LUT(img, table)
123
- light_msg = f"🌙 Mode Sombre Activé (Luminosité: {mean_brightness:.1f})"
124
- else:
125
- img_analysed = img.copy()
126
- light_msg = "☀️ Luminosité Standard"
127
 
128
- # 3. Traitement forensic sur l'image corrigée
129
- chrom_u, chrom_v = analyze_chrominance_noise(img_analysed)
130
- grid_s, ring, fft_v = detect_grid_and_ringing(img_analysed)
131
- ela_s, ela_v = error_level_analysis(img_analysed)
132
-
133
- metrics = {
134
- 'chrom_uniformity': chrom_u, 'grid_score': grid_s,
135
- 'ringing': ring, 'ela_score': ela_s
136
- }
137
 
138
- ai_score, reasons = compute_ai_score(metrics)
139
 
140
- # Visualisation
141
  fig, axes = plt.subplots(2, 2, figsize=(12, 10))
142
- axes[0,0].imshow(img) # On montre l'originale
143
- axes[0,0].set_title(f'📸 Original ({light_msg})')
144
- axes[0,1].imshow(fft_v)
145
- axes[0,1].set_title('🔮 FFT Frequency')
146
- axes[1,0].imshow(ela_v)
147
- axes[1,0].set_title('🔍 ELA Analysis')
148
- axes[1,1].imshow(chrom_v, cmap='hot')
149
- axes[1,1].set_title('🌈 Chrominance Noise')
150
  for ax in axes.flatten(): ax.axis('off')
151
  plt.tight_layout()
152
-
153
- report = f"MEDIASHIELD REPORT\nScore: {ai_score}/100\n{light_msg}\n\nIndicateurs:\n" + "\n".join(reasons)
 
 
154
  return fig, report
155
 
156
- # ═══════════════════════════════════════════════════
157
- # 🎨 INTERFACE GRADIO
158
- # ═════��═════════════════════════════════════════════
159
-
160
- with gr.Blocks() as demo:
161
- gr.Markdown("# 🛡️ MediaShield PRO v2.1")
162
  with gr.Row():
163
  with gr.Column():
164
- input_img = gr.Image(label="Image", type="numpy")
165
- btn = gr.Button("ANALYSER", variant="primary")
166
  with gr.Column():
167
  out_plot = gr.Plot()
168
- out_txt = gr.Textbox(label="Rapport", lines=10)
169
-
170
- btn.click(analyze_image, inputs=input_img, outputs=[out_plot, out_txt])
171
 
172
- if __name__ == "__main__":
173
- demo.launch()
 
5
  from scipy import ndimage
6
 
7
  # ═══════════════════════════════════════════════════
8
+ # 🛡️ MEDIASHIELD PRO v2.3AUTHENTICITY & 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()