NOBODY204 commited on
Commit
cc6c032
·
verified ·
1 Parent(s): f0d0b97

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +45 -41
app.py CHANGED
@@ -5,7 +5,7 @@ import cv2
5
  from scipy import ndimage
6
 
7
  # ═══════════════════════════════════════════════════
8
- # 🛡️ MEDIASHIELD PRO v2.4Spécial Photos Réelles / IA Partielle
9
  # ═══════════════════════════════════════════════════
10
 
11
  def estimate_sensor_noise(img):
@@ -16,74 +16,66 @@ def estimate_sensor_noise(img):
16
  return np.std(noise)
17
 
18
  def analyze_local_variance(img):
19
- """Compare le centre (visage) aux bords (fond) pour détecter un montage IA."""
20
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
21
  h, w = gray.shape
22
- # Zone centrale vs Zone bordure
23
  center = gray[h//4:3*h//4, w//4:3*w//4]
24
  edge = gray[0:h//4, 0:w//4]
25
-
26
  def get_var(z):
27
  return np.var(z - cv2.GaussianBlur(z, (5,5), 0))
28
-
29
  v_c, v_e = get_var(center), get_var(edge)
30
  return abs(v_c - v_e) / (v_e + 1e-8)
31
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  def analyze_chrominance_noise(img):
33
  ycrcb = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
34
- cr = ycrcb[:, :, 1].astype(np.float32)
35
- cb = ycrcb[:, :, 2].astype(np.float32)
36
- cr_var = ndimage.generic_filter(cr, np.var, size=5)
37
- cb_var = ndimage.generic_filter(cb, np.var, size=5)
38
  u_cr = np.std(cr_var) / (np.mean(cr_var) + 1e-8)
39
  u_cb = np.std(cb_var) / (np.mean(cb_var) + 1e-8)
40
- return (u_cr + u_cb) / 2, cr_var
41
 
42
  def detect_grid_and_ringing(img):
43
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
44
- f = np.fft.fft2(gray)
45
- fshift = np.fft.fftshift(f)
46
  magnitude = np.abs(fshift)
47
  rows, cols = gray.shape
48
  crow, ccol = rows//2, cols//2
49
  magnitude[(np.ogrid[:rows,:cols][0]-crow)**2 + (np.ogrid[:rows,:cols][1]-ccol)**2 <= (min(rows,cols)//10)**2] = 0
50
-
51
  def get_peaks(sig):
52
  thresh = np.max(sig) * 0.25
53
- return [i for i in range(1, len(sig)-1) if sig[i] > thresh and sig[i] > sig[i-1] and sig[i] > sig[i+1] and i%8 != 0] # Filtre anti-JPEG
54
-
55
  grid_s = (len(get_peaks(np.sum(magnitude, 0))) + len(get_peaks(np.sum(magnitude, 1)))) / 2
56
  ringing = np.mean(magnitude) / (np.max(magnitude) + 1e-8)
57
  fft_vis = cv2.applyColorMap(cv2.normalize(np.log(magnitude+1), None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8), cv2.COLORMAP_VIRIDIS)
58
  return grid_s, ringing, fft_vis
59
 
60
  def error_level_analysis(img, quality=90):
61
- encode_param = [int(cv2.IMWRITE_JPEG_QUALITY), quality]
62
- _, enc = cv2.imencode('.jpg', cv2.cvtColor(img, cv2.COLOR_RGB2BGR), encode_param)
63
- dec_rgb = cv2.cvtColor(cv2.imdecode(enc, 1), cv2.COLOR_BGR2RGB)
64
- diff = cv2.absdiff(img, dec_rgb)
65
  return np.mean(diff), cv2.normalize(diff, None, 0, 255, cv2.NORM_MINMAX)
66
 
67
  def compute_ai_score_v4(metrics):
68
- score = 0
69
- reasons = []
70
-
71
- # 1. Signatures IA classiques
72
  if metrics['grid_score'] > 25: score += 30; reasons.append("⚠️ Motifs réguliers (GAN possible)")
73
  if metrics['ringing'] > 0.3: score += 25; reasons.append("⚠️ Artefacts circulaires")
74
-
75
- # 2. Analyse du grain (Bruit Capteur)
76
  if metrics['sensor_noise'] < 1.5: score += 25; reasons.append("⚡ Image trop lisse (IA probable)")
77
  elif metrics['sensor_noise'] > 4.0: score -= 25; reasons.append("✅ Bruit naturel (Photo réelle)")
78
-
79
- # 3. Analyse Locale (Détection de montage)
80
- if metrics['local_diff'] > 1.8: score += 30; reasons.append("🚨 Incohérence de texture (IA locale / Montage)")
81
-
82
- # 4. Anti Faux-Positifs (Filtre compression réseaux sociaux)
83
  if metrics['ela_score'] > 2.5 and metrics['grid_score'] < 8:
84
- score *= 0.3
85
- reasons.append("✅ Structure typique d'une photo réelle compressée")
86
-
87
  return int(max(0, min(score, 100))), reasons
88
 
89
  def analyze_image(img_input):
@@ -94,36 +86,48 @@ def analyze_image(img_input):
94
  mean_b = np.mean(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY))
95
  img_analysed = cv2.LUT(img, np.array([((i/255.0)**(1/0.6))*255 for i in range(256)]).astype("uint8")) if mean_b < 60 else img.copy()
96
 
97
- # Calculs
98
- chrom_u, chrom_map = analyze_chrominance_noise(img_analysed)
99
  grid_s, ring, fft_v = detect_grid_and_ringing(img_analysed)
100
  ela_s, ela_v = error_level_analysis(img_analysed)
101
  sensor_n = estimate_sensor_noise(img_analysed)
102
  local_d = analyze_local_variance(img_analysed)
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
  metrics = {'chrom_uniformity': chrom_u, 'grid_score': grid_s, 'ringing': ring, 'ela_score': ela_s, 'sensor_noise': sensor_n, 'local_diff': local_d}
105
  ai_score, reasons = compute_ai_score_v4(metrics)
106
 
107
  fig, axes = plt.subplots(2, 2, figsize=(10, 8))
108
- axes[0,0].imshow(img); axes[0,0].set_title('Original')
109
  axes[0,1].imshow(fft_v); axes[0,1].set_title('Fréquences (FFT)')
110
  axes[1,0].imshow(ela_v); axes[1,0].set_title('ELA (Compression)')
111
- axes[1,1].imshow(chrom_map, cmap='hot'); axes[1,1].set_title('Bruit Chromatique')
112
  for ax in axes.flatten(): ax.axis('off')
113
  plt.tight_layout()
114
 
115
- report = f"🛡️ MEDIASHIELD v2.4\nScore AI : {ai_score}/100\n\n" + ("\n".join(reasons) if reasons else "✅ Authentique")
116
  return fig, report
117
 
118
  with gr.Blocks() as demo:
119
- gr.Markdown("# 🛡️ MediaShield PRO v2.4\nSouveraineté numérique ACoNum")
120
  with gr.Row():
121
  with gr.Column():
122
  in_img = gr.Image(type="numpy")
123
- btn = gr.Button("ANALYSER", variant="primary")
124
  with gr.Column():
125
  out_plt = gr.Plot()
126
- out_txt = gr.Textbox(label="Rapport Forensic", lines=10)
127
  btn.click(analyze_image, in_img, [out_plt, out_txt])
128
 
129
  if __name__ == "__main__":
 
5
  from scipy import ndimage
6
 
7
  # ═══════════════════════════════════════════════════
8
+ # 🛡️ MEDIASHIELD PRO v2.5Localized AI Detection
9
  # ═══════════════════════════════════════════════════
10
 
11
  def estimate_sensor_noise(img):
 
16
  return np.std(noise)
17
 
18
  def analyze_local_variance(img):
19
+ """Compare le centre aux bords pour détecter un montage IA."""
20
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
21
  h, w = gray.shape
 
22
  center = gray[h//4:3*h//4, w//4:3*w//4]
23
  edge = gray[0:h//4, 0:w//4]
 
24
  def get_var(z):
25
  return np.var(z - cv2.GaussianBlur(z, (5,5), 0))
 
26
  v_c, v_e = get_var(center), get_var(edge)
27
  return abs(v_c - v_e) / (v_e + 1e-8)
28
 
29
+ def compute_noise_incoherence_map(img):
30
+ """Génère une carte thermique où l'IA lissée apparaît en rouge brillant."""
31
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
32
+ # Isoler le grain
33
+ grain = gray - cv2.medianBlur(gray, 7)
34
+ # Calculer la variance locale (IA = faible variance)
35
+ grain_var = ndimage.generic_filter(grain, np.var, size=15)
36
+ # Inverser pour que l'IA soit "chaude" (brillante)
37
+ incoherence_map = np.max(grain_var) - grain_var
38
+ vis = cv2.normalize(incoherence_map, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
39
+ return cv2.applyColorMap(vis, cv2.COLORMAP_JET)
40
+
41
  def analyze_chrominance_noise(img):
42
  ycrcb = cv2.cvtColor(img, cv2.COLOR_RGB2YCrCb)
43
+ cr_var = ndimage.generic_filter(ycrcb[:,:,1].astype(np.float32), np.var, size=5)
44
+ cb_var = ndimage.generic_filter(ycrcb[:,:,2].astype(np.float32), np.var, size=5)
 
 
45
  u_cr = np.std(cr_var) / (np.mean(cr_var) + 1e-8)
46
  u_cb = np.std(cb_var) / (np.mean(cb_var) + 1e-8)
47
+ return (u_cr + u_cb) / 2
48
 
49
  def detect_grid_and_ringing(img):
50
  gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY).astype(np.float32)
51
+ fshift = np.fft.fftshift(np.fft.fft2(gray))
 
52
  magnitude = np.abs(fshift)
53
  rows, cols = gray.shape
54
  crow, ccol = rows//2, cols//2
55
  magnitude[(np.ogrid[:rows,:cols][0]-crow)**2 + (np.ogrid[:rows,:cols][1]-ccol)**2 <= (min(rows,cols)//10)**2] = 0
 
56
  def get_peaks(sig):
57
  thresh = np.max(sig) * 0.25
58
+ return [i for i in range(1, len(sig)-1) if sig[i] > thresh and sig[i] > sig[i-1] and sig[i] > sig[i+1] and i%8 != 0]
 
59
  grid_s = (len(get_peaks(np.sum(magnitude, 0))) + len(get_peaks(np.sum(magnitude, 1)))) / 2
60
  ringing = np.mean(magnitude) / (np.max(magnitude) + 1e-8)
61
  fft_vis = cv2.applyColorMap(cv2.normalize(np.log(magnitude+1), None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8), cv2.COLORMAP_VIRIDIS)
62
  return grid_s, ringing, fft_vis
63
 
64
  def error_level_analysis(img, quality=90):
65
+ _, enc = cv2.imencode('.jpg', cv2.cvtColor(img, cv2.COLOR_RGB2BGR), [int(cv2.IMWRITE_JPEG_QUALITY), quality])
66
+ dec = cv2.cvtColor(cv2.imdecode(enc, 1), cv2.COLOR_BGR2RGB)
67
+ diff = cv2.absdiff(img, dec)
 
68
  return np.mean(diff), cv2.normalize(diff, None, 0, 255, cv2.NORM_MINMAX)
69
 
70
  def compute_ai_score_v4(metrics):
71
+ score, reasons = 0, []
 
 
 
72
  if metrics['grid_score'] > 25: score += 30; reasons.append("⚠️ Motifs réguliers (GAN possible)")
73
  if metrics['ringing'] > 0.3: score += 25; reasons.append("⚠️ Artefacts circulaires")
 
 
74
  if metrics['sensor_noise'] < 1.5: score += 25; reasons.append("⚡ Image trop lisse (IA probable)")
75
  elif metrics['sensor_noise'] > 4.0: score -= 25; reasons.append("✅ Bruit naturel (Photo réelle)")
76
+ if metrics['local_diff'] > 1.6: score += 35; reasons.append("🚨 Montage local détecté (Incohérence)")
 
 
 
 
77
  if metrics['ela_score'] > 2.5 and metrics['grid_score'] < 8:
78
+ score *= 0.3; reasons.append("✅ Structure cohérente avec compression réelle")
 
 
79
  return int(max(0, min(score, 100))), reasons
80
 
81
  def analyze_image(img_input):
 
86
  mean_b = np.mean(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY))
87
  img_analysed = cv2.LUT(img, np.array([((i/255.0)**(1/0.6))*255 for i in range(256)]).astype("uint8")) if mean_b < 60 else img.copy()
88
 
89
+ # Analyses
90
+ chrom_u = analyze_chrominance_noise(img_analysed)
91
  grid_s, ring, fft_v = detect_grid_and_ringing(img_analysed)
92
  ela_s, ela_v = error_level_analysis(img_analysed)
93
  sensor_n = estimate_sensor_noise(img_analysed)
94
  local_d = analyze_local_variance(img_analysed)
95
+ noise_map = compute_noise_incoherence_map(img_analysed)
96
+
97
+ # Détection de la zone suspecte et dessin du rectangle
98
+ img_boxed = img.copy()
99
+ if local_d > 1.3: # Seuil de détection de montage
100
+ gray_map = cv2.cvtColor(noise_map, cv2.COLOR_BGR2GRAY)
101
+ _, _, _, max_loc = cv2.minMaxLoc(gray_map)
102
+ h, w = img.shape[:2]
103
+ size = int(min(h, w) * 0.25)
104
+ top_left = (max(0, max_loc[0] - size//2), max(0, max_loc[1] - size//2))
105
+ bottom_right = (min(w, max_loc[0] + size//2), min(h, max_loc[1] + size//2))
106
+ cv2.rectangle(img_boxed, top_left, bottom_right, (255, 0, 0), 4)
107
 
108
  metrics = {'chrom_uniformity': chrom_u, 'grid_score': grid_s, 'ringing': ring, 'ela_score': ela_s, 'sensor_noise': sensor_n, 'local_diff': local_d}
109
  ai_score, reasons = compute_ai_score_v4(metrics)
110
 
111
  fig, axes = plt.subplots(2, 2, figsize=(10, 8))
112
+ axes[0,0].imshow(img_boxed); axes[0,0].set_title('Détection Locale (IA)')
113
  axes[0,1].imshow(fft_v); axes[0,1].set_title('Fréquences (FFT)')
114
  axes[1,0].imshow(ela_v); axes[1,0].set_title('ELA (Compression)')
115
+ axes[1,1].imshow(noise_map); axes[1,1].set_title('Heatmap Incohérence')
116
  for ax in axes.flatten(): ax.axis('off')
117
  plt.tight_layout()
118
 
119
+ report = f"🛡️ MEDIASHIELD v2.5\nScore AI : {ai_score}/100\n\n" + ("\n".join(reasons) if reasons else "✅ Authentique")
120
  return fig, report
121
 
122
  with gr.Blocks() as demo:
123
+ gr.Markdown("# 🛡️ MediaShield PRO v2.5\nSami Meddeb - ACoNum")
124
  with gr.Row():
125
  with gr.Column():
126
  in_img = gr.Image(type="numpy")
127
+ btn = gr.Button("DÉMARRER L'ANALYSE", variant="primary")
128
  with gr.Column():
129
  out_plt = gr.Plot()
130
+ out_txt = gr.Textbox(label="Résultat Forensic", lines=10)
131
  btn.click(analyze_image, in_img, [out_plt, out_txt])
132
 
133
  if __name__ == "__main__":