NOBODY204 commited on
Commit
a1cf855
ยท
verified ยท
1 Parent(s): 3524290

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +140 -93
app.py CHANGED
@@ -1,126 +1,173 @@
1
  import gradio as gr
2
  import numpy as np
3
- import cv2
4
  import matplotlib.pyplot as plt
5
- from PIL import Image, ImageChops, ImageEnhance
6
  from scipy import ndimage
7
- import io, warnings
8
-
9
- warnings.filterwarnings("ignore")
10
 
11
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
12
- # ๐Ÿ›ก๏ธ MEDIASHIELD v3.1 โ€” NANO BANANA EDITION
13
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
14
 
15
- def apply_gamma_correction(img):
16
- """Prรฉpare l'image pour l'analyse si elle est trop sombre (Archives)."""
17
- gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
18
- brightness = np.mean(gray)
19
- if brightness < 65:
20
- gamma = 0.6
21
- invGamma = 1.0 / gamma
22
- table = np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype("uint8")
23
- return cv2.LUT(img, table), f"๐ŸŒ™ Mode Sombre (Lumiรจre: {brightness:.1f})"
24
- return img, "โ˜€๏ธ Lumiรจre Standard"
25
-
26
- def detect_nano_signatures(img):
27
- """Dรฉtecte les gradients trop parfaits et anomalies de Gemini."""
28
- gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
29
- # Analyse de la variance des gradients (Laplacien)
30
- laplacian = cv2.Laplacian(gray, cv2.CV_64F)
31
- smoothness_score = np.var(laplacian)
32
 
33
- # FFT pour dรฉtection de pรฉriodicitรฉ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  f = np.fft.fft2(gray)
35
  fshift = np.fft.fftshift(f)
36
- magnitude_spectrum = 20 * np.log(np.abs(fshift) + 1)
37
 
38
- # Un score de smoothness < 4.5 est un fort indicateur IA
39
- is_nano_candidate = (smoothness_score < 4.5)
40
- return is_nano_candidate, smoothness_score, magnitude_spectrum
41
-
42
- def verify_natural_grain(img):
43
- """Distingue le lissage IA du grain organique."""
44
- gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
45
- local_var = ndimage.generic_filter(gray, np.var, size=3)
46
- avg_var = np.mean(local_var)
47
- return (avg_var < 0.8), avg_var
48
-
49
- def analyze_forensics(img_input):
50
- if img_input is None: return None, "โŒ Aucune image chargรฉe."
51
 
52
- # 1. Traitement Image
53
- img_cv = cv2.cvtColor(img_input, cv2.COLOR_RGBA2RGB) if img_input.shape[2] == 4 else img_input.copy()
54
- img_ready, light_msg = apply_gamma_correction(img_cv)
55
- pil_img = Image.fromarray(img_ready)
56
-
57
- # 2. Dรฉtection Spรฉcifique Nano Banana
58
- is_nano, s_score, fft_map = detect_nano_signatures(img_ready)
59
-
60
- # 3. Autres analyses Forensic
61
- is_too_smooth, grain_val = verify_natural_grain(img_ready)
62
 
63
- # 4. Chrominance & ELA
64
- ycrcb = cv2.cvtColor(img_ready, cv2.COLOR_RGB2YCrCb)
65
- cr_var = ndimage.generic_filter(ycrcb[:,:,1], np.var, size=5)
66
- chroma_uniformity = np.std(cr_var) / (np.mean(cr_var) + 1e-8)
67
 
68
- buf = io.BytesIO()
69
- pil_img.save(buf, format="JPEG", quality=90)
70
- recomp = Image.open(io.BytesIO(buf.getvalue())).convert("RGB")
71
- ela_diff = ImageChops.difference(pil_img, recomp)
72
- ela_vis = ImageEnhance.Brightness(ela_diff).enhance(10)
73
-
74
- # 5. Calcul du Score Final
 
 
 
 
 
 
 
 
 
75
  score = 0
76
  reasons = []
77
-
78
- if is_nano:
79
- score += 40
80
- reasons.append(f"๐Ÿšจ Signature NANO BANANA : Gradients trop lisses (Score: {s_score:.2f})")
81
 
82
- if is_too_smooth:
 
 
 
 
 
 
83
  score += 20
84
- reasons.append(f"โšก Texture suspecte (Grain faible: {grain_val:.2f})")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  else:
86
- score -= 15
87
- reasons.append("โœ… Grain organique dรฉtectรฉ (Photo rรฉelle)")
88
 
89
- if chroma_uniformity < 0.9:
90
- score += 25
91
- reasons.append("โš ๏ธ Bruit chromatique uniforme (Signature IA)")
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- score = max(0, min(100, score))
94
- verdict = "๐Ÿ”ด IA Dร‰TECTร‰E" if score > 55 else "๐ŸŸ  SUSPECT" if score > 35 else "๐ŸŸข AUTHENTIQUE"
 
 
95
 
96
- # 6. Visualisation
97
- fig, axes = plt.subplots(1, 3, figsize=(15, 5))
98
- axes[0].imshow(img_cv); axes[0].set_title(f'Original ({light_msg})')
99
- axes[1].imshow(fft_map, cmap='magma'); axes[1].set_title('Spectre Nano (FFT)')
100
- axes[2].imshow(ela_vis); axes[2].set_title('Analyse ELA')
101
- for ax in axes: ax.axis('off')
102
- plt.tight_layout()
103
 
104
- report = f"๐Ÿ›ก๏ธ RAPPORT MEDIASHIELD v3.1\nVERDICT : {verdict}\nIndice de confiance IA : {score}/100\n\nDiagnostic :\n" + "\n".join(reasons)
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  return fig, report
106
 
107
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
108
- # INTERFACE UTILISATEUR
109
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•๏ฟฝ๏ฟฝโ•โ•โ•โ•โ•โ•โ•โ•
110
 
111
- with gr.Blocks(title="MediaShield v3.1", theme=gr.themes.Soft()) as demo:
112
- gr.Markdown("# ๐Ÿ›ก๏ธ MediaShield v3.1 (Nano Edition)")
113
- gr.Markdown("Expertise Forensic optimisรฉe pour les archives et la dรฉtection des modรจles Gemini.")
114
-
115
  with gr.Row():
116
- with gr.Column(scale=1):
117
- input_file = gr.Image(label="Charger l'archive", type="numpy")
118
- run_btn = gr.Button("LANCER L'ANALYSE", variant="primary")
119
- with gr.Column(scale=2):
120
- output_plot = gr.Plot(label="Preuves visuelles")
121
- output_text = gr.Textbox(label="Rapport d'expertise", lines=10)
122
-
123
- run_btn.click(analyze_forensics, inputs=input_file, outputs=[output_plot, output_text])
124
 
125
  if __name__ == "__main__":
126
  demo.launch()
 
1
  import gradio as gr
2
  import numpy as np
 
3
  import matplotlib.pyplot as plt
4
+ import cv2
5
  from scipy import ndimage
 
 
 
6
 
7
  # โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
8
+ # ๐Ÿ”ฌ MEDIASHIELD PRO v2.1 โ€“ AVEC 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()