MetaQu commited on
Commit
ed4c5f6
·
verified ·
1 Parent(s): f0ff41c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +214 -62
app.py CHANGED
@@ -1,87 +1,239 @@
 
1
  import gradio as gr
2
  from transformers import pipeline
3
  from PIL import Image, ExifTags
4
- import cv2
5
  import numpy as np
6
 
7
- # Model Hugging Face untuk AI vs Human
8
- detector = pipeline("image-classification", model="microsoft/resnet-50")
 
 
 
 
 
9
 
10
- def analyze_image(image):
11
- # Convert ke format OpenCV
12
- img_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
 
 
 
 
 
13
 
14
- # ---- 1. Prediksi dengan model HF ----
15
- preds = detector(image)
16
- ai_score = 0.0
17
- human_score = 0.0
18
- for p in preds:
19
- if "artificial" in p["label"].lower() or "ai" in p["label"].lower():
20
- ai_score += p["score"]
21
- else:
22
- human_score += p["score"]
 
 
 
 
23
 
24
- # Normalisasi biar total 1
25
- total = ai_score + human_score
26
- if total > 0:
27
- ai_score /= total
28
- human_score /= total
 
 
 
 
 
 
29
 
30
- # ---- 2. Analisis blur/noise ----
31
- gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
32
- blur_score = cv2.Laplacian(gray, cv2.CV_64F).var()
33
- noise_score = np.std(gray)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- # Semakin kecil blur/noise → semakin cenderung AI
36
- blur_factor = 1.0 if blur_score < 100 else 0.0
37
- noise_factor = 1.0 if noise_score < 20 else 0.0
 
 
 
 
 
38
 
39
- # ---- 3. Cek metadata kamera ----
40
- has_metadata = False
41
  try:
42
- exif = image._getexif()
43
- if exif is not None:
44
- for tag, value in exif.items():
45
- decoded = ExifTags.TAGS.get(tag, tag)
46
- if decoded in ["Make", "Model"]:
47
- has_metadata = True
 
48
  except:
49
  pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- metadata_factor = 0.0 if has_metadata else 1.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- # ---- 4. Ensemble skor ----
54
- final_score = (
55
- 0.6 * ai_score +
56
- 0.2 * blur_factor +
57
- 0.1 * noise_factor +
58
- 0.1 * metadata_factor
59
- )
60
 
61
- result = "AI Generated" if final_score > 0.5 else "Foto Asli"
62
 
63
- # ---- 5. Hasil detail ----
64
- details = f"""
65
- 🔍 Hasil Deteksi:
66
- {result}
 
 
 
67
 
68
- Skor Model AI-detector: {ai_score:.2f}
69
- Skor Human: {human_score:.2f}
70
- Blur Score: {blur_score:.2f}
71
- Noise Score: {noise_score:.2f}
72
- Metadata Kamera: {"Ada" if has_metadata else "Tidak Ada"}
73
- Final Score: {final_score:.2f}
74
- """
75
- return details
 
 
 
76
 
77
  # Gradio UI
78
- demo = gr.Interface(
79
- fn=analyze_image,
80
  inputs=gr.Image(type="pil"),
81
- outputs="text",
82
- title="AI vs Real Image Detector",
83
- description="Upload foto untuk mendeteksi apakah gambar asli atau hasil AI."
84
  )
85
 
86
  if __name__ == "__main__":
87
- demo.launch()
 
1
+ # app.py
2
  import gradio as gr
3
  from transformers import pipeline
4
  from PIL import Image, ExifTags
 
5
  import numpy as np
6
 
7
+ # Try import cv2 (opencv-headless). If not available, fallback ke numpy-only functions.
8
+ try:
9
+ import cv2
10
+ HAS_CV2 = True
11
+ except Exception:
12
+ cv2 = None
13
+ HAS_CV2 = False
14
 
15
+ # ------------------------
16
+ # Load HF detector (may require torch installed in requirements.txt)
17
+ # ------------------------
18
+ try:
19
+ hf_detector = pipeline("image-classification", model="umm-maybe/AI-image-detector")
20
+ except Exception as e:
21
+ hf_detector = None
22
+ print("Warning: hf_detector gagal dimuat:", e)
23
 
24
+ # ------------------------
25
+ # Forensic helper functions (works with or without cv2)
26
+ # ------------------------
27
+ def pil_to_gray_array(img: Image.Image):
28
+ return np.array(img.convert("L"), dtype=np.float32)
29
+
30
+ def estimate_blur(img: Image.Image):
31
+ arr = pil_to_gray_array(img)
32
+ if HAS_CV2:
33
+ return float(cv2.Laplacian(arr.astype(np.uint8), cv2.CV_64F).var())
34
+ # fallback: gradient variance
35
+ gx, gy = np.gradient(arr)
36
+ return float(np.var(gx + gy))
37
 
38
+ def estimate_noise(img: Image.Image):
39
+ arr = pil_to_gray_array(img)
40
+ if HAS_CV2:
41
+ # remove low-frequency via gaussian blur then std
42
+ blurred = cv2.GaussianBlur(arr, (5,5), 0)
43
+ noise = arr - blurred
44
+ return float(np.std(noise))
45
+ # fallback
46
+ blurred = np.mean(arr)
47
+ noise = arr - blurred
48
+ return float(np.std(noise))
49
 
50
+ def block_highfreq_ratio(img: Image.Image, block=8):
51
+ # compute ratio of high-frequency energy per 8x8 block via FFT (fallback for DCT)
52
+ arr = pil_to_gray_array(img)
53
+ h, w = arr.shape
54
+ # pad to multiple of block
55
+ ph = ((block - (h % block)) % block)
56
+ pw = ((block - (w % block)) % block)
57
+ if ph or pw:
58
+ arr = np.pad(arr, ((0, ph), (0, pw)), mode='reflect')
59
+ H, W = arr.shape
60
+ total_energy = 0.0
61
+ low_energy = 0.0
62
+ # iterate blocks (vectorized)
63
+ for i in range(0, H, block):
64
+ for j in range(0, W, block):
65
+ b = arr[i:i+block, j:j+block]
66
+ # 2D FFT
67
+ F = np.fft.fft2(b)
68
+ mag = np.abs(F)
69
+ total_energy += mag.sum()
70
+ # low freq: center-ish -> take top-left 2x2 as low freq approx
71
+ low_energy += mag[0:2, 0:2].sum()
72
+ if total_energy <= 1e-9:
73
+ return 0.0
74
+ high_ratio = float((total_energy - low_energy) / total_energy) # 0..1
75
+ return high_ratio
76
 
77
+ def edge_std(img: Image.Image):
78
+ arr = pil_to_gray_array(img)
79
+ if HAS_CV2:
80
+ edges = cv2.Canny(arr.astype(np.uint8), 100, 200)
81
+ return float(np.std(edges))
82
+ gx, gy = np.gradient(arr)
83
+ edges = np.hypot(gx, gy)
84
+ return float(np.std(edges))
85
 
86
+ def has_camera_exif(img: Image.Image):
 
87
  try:
88
+ exif = img._getexif()
89
+ if not exif:
90
+ return False
91
+ for tag, val in exif.items():
92
+ name = ExifTags.TAGS.get(tag, tag)
93
+ if name in ("Make", "Model", "LensModel", "FNumber", "ExposureTime"):
94
+ return True
95
  except:
96
  pass
97
+ return False
98
+
99
+ # ------------------------
100
+ # Scoring / Ensemble logic
101
+ # ------------------------
102
+ def final_ai_score_from_components(hf_label, hf_conf, blur, noise, hfreq_ratio, edges, exif_present):
103
+ # hf_conf is 0..1
104
+ # 1) HF detector contribution
105
+ if hf_label is None:
106
+ hf_contrib = 0.0
107
+ else:
108
+ lab = hf_label.lower()
109
+ if any(x in lab for x in ("fake","artificial","ai")):
110
+ hf_contrib = hf_conf * 100.0
111
+ elif "human" in lab or "real" in lab:
112
+ # do not trust 'human' fully; translate into moderate ai signal
113
+ hf_contrib = (1.0 - hf_conf) * 100.0 * 0.6
114
+ else:
115
+ hf_contrib = (1.0 - hf_conf) * 100.0 * 0.8
116
+
117
+ # 2) Forensic contributions -> produce scores 0..100 where larger = more likely AI
118
+ # noise: low noise => AI-ish
119
+ noise_norm = noise / 100.0 # normalize roughly; adjust if needed
120
+ noise_score = max(0.0, min(1.0, 1.0 - noise_norm)) * 100.0
121
+
122
+ # blur: low variance (very smooth) => AI-ish
123
+ blur_norm = blur / 500.0
124
+ blur_score = max(0.0, min(1.0, 1.0 - blur_norm)) * 100.0
125
+
126
+ # high-frequency ratio: very low high-freq => too-smooth => AI-ish (we expect hfreq_ratio small -> AI)
127
+ # hfreq_ratio is 0..1, low -> AI
128
+ hfreq_score = max(0.0, min(1.0, 0.2 - hfreq_ratio) / 0.2) * 100.0 # thresholding at ~0.2
129
+
130
+ # edges: low edge std => AI-ish
131
+ edges_norm = edges / 30.0
132
+ edge_score = max(0.0, min(1.0, 1.0 - edges_norm)) * 100.0
133
+
134
+ # combine forensic scores (weights can be tuned)
135
+ forensic_score = (0.35 * noise_score + 0.30 * blur_score + 0.20 * hfreq_score + 0.15 * edge_score)
136
 
137
+ # 3) Combine HF + Forensic
138
+ combined = 0.6 * hf_contrib + 0.4 * forensic_score # 0..100
139
+
140
+ # 4) EXIF adjustment: if EXIF present, reduce AI score significantly
141
+ if exif_present:
142
+ combined = max(0.0, combined - 30.0)
143
+
144
+ # Clamp
145
+ combined = max(0.0, min(100.0, combined))
146
+ return combined, {
147
+ "hf_contrib": hf_contrib,
148
+ "forensic_score": forensic_score,
149
+ "noise_score": noise_score,
150
+ "blur_score": blur_score,
151
+ "hfreq_score": hfreq_score,
152
+ "edge_score": edge_score
153
+ }
154
+
155
+ # ------------------------
156
+ # Main detect function
157
+ # ------------------------
158
+ def detect_image(img: Image.Image):
159
+ try:
160
+ # ensure PIL image
161
+ if not isinstance(img, Image.Image):
162
+ img = Image.fromarray(np.array(img))
163
+
164
+ # HF detector inference (if available)
165
+ hf_label = None
166
+ hf_conf = 0.0
167
+ if hf_detector is not None:
168
+ try:
169
+ res = hf_detector(img, top_k=1)
170
+ if isinstance(res, list) and len(res) > 0:
171
+ hf_label = res[0].get("label", "")
172
+ hf_conf = float(res[0].get("score", 0.0))
173
+ except Exception as e:
174
+ # fallback: ignore
175
+ hf_label = None
176
+ hf_conf = 0.0
177
+
178
+ # Forensic measures
179
+ blur = estimate_blur(img)
180
+ noise = estimate_noise(img)
181
+ hfreq = block_highfreq_ratio(img)
182
+ edges = edge_std(img)
183
+ exif_ok = has_camera_exif(img)
184
+
185
+ # Compute final AI score (0..100)
186
+ ai_score, comps = final_ai_score_from_components(hf_label, hf_conf, blur, noise, hfreq, edges, exif_ok)
187
+ real_score = round(100.0 - ai_score, 2)
188
+ ai_score = round(ai_score, 2)
189
+
190
+ # Interpretations / labels
191
+ if ai_score >= 90:
192
+ verdict = "🤖 Gambar ini TERLALU MOGOK: Hasil AI (sangat tinggi)"
193
+ elif ai_score >= 60:
194
+ verdict = "🤖 Gambar ini kemungkinan besar DIHASILKAN AI"
195
+ elif ai_score <= 15:
196
+ verdict = "✅ Gambar ini tampak ASLI (sangat tinggi)"
197
+ elif ai_score <= 40:
198
+ verdict = "✅ Gambar ini kemungkinan besar ASLI"
199
+ else:
200
+ verdict = f"⚖️ Gambar ini {ai_score}% AI / {real_score}% Asli"
201
 
202
+ # Build output message
203
+ out = f"""
204
+ ### Hasil Deteksi:
205
+ {verdict}
 
 
 
206
 
207
+ **Persentase:** {ai_score}% AI / {real_score}% Asli
208
 
209
+ **Model Prediksi:** {hf_label if hf_label else 'N/A'} ({hf_conf:.2f})
210
+ **Forensik (angka):**
211
+ - Blur (var Laplacian / grad-var): {blur:.2f}
212
+ - Noise (std highpass): {noise:.2f}
213
+ - HighFreq Ratio (block FFT): {hfreq:.3f}
214
+ - Edge STD: {edges:.2f}
215
+ - EXIF Kamera: {"Ada" if exif_ok else "Tidak"}
216
 
217
+ **Komponen skor (internal):**
218
+ - hf_contrib: {comps['hf_contrib']:.2f}
219
+ - forensic_score: {comps['forensic_score']:.2f}
220
+ - noise_score: {comps['noise_score']:.2f}
221
+ - blur_score: {comps['blur_score']:.2f}
222
+ - hfreq_score: {comps['hfreq_score']:.2f}
223
+ - edge_score: {comps['edge_score']:.2f}
224
+ """
225
+ return out
226
+ except Exception as e:
227
+ return f"⚠️ Terjadi error saat deteksi: {str(e)}"
228
 
229
  # Gradio UI
230
+ iface = gr.Interface(
231
+ fn=detect_image,
232
  inputs=gr.Image(type="pil"),
233
+ outputs="markdown",
234
+ title="Improved Hybrid AI vs Real Detector",
235
+ description="Gabungan model HF + forensik (noise, blur, DCT/FFT block, edge, EXIF). Tidak ada jaminan 100%."
236
  )
237
 
238
  if __name__ == "__main__":
239
+ iface.launch()