MetaQu commited on
Commit
3561a8c
·
verified ·
1 Parent(s): 4e4affc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +59 -220
app.py CHANGED
@@ -1,239 +1,78 @@
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()
 
 
 
 
1
+ import cv2
 
 
 
2
  import numpy as np
3
+ from PIL import Image, ExifTags
4
+ from transformers import pipeline
5
 
6
+ # === Load dua model AI detector ===
7
+ detector1 = pipeline("image-classification", model="umm-maybe/AI-image-detector")
8
+ detector2 = pipeline("image-classification", model="fal-ai/ai-or-not")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ # === Forensik sederhana ===
11
+ def forensic_analysis(img_path):
12
+ img = cv2.imread(img_path)
 
 
 
 
13
 
14
+ # Blur score (varian Laplacian)
15
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
16
+ blur_score = cv2.Laplacian(gray, cv2.CV_64F).var()
 
 
 
 
 
 
 
 
17
 
18
+ # Noise score (std dev setelah highpass filter)
19
+ noise = cv2.GaussianBlur(gray, (3, 3), 0)
20
+ highpass = cv2.subtract(gray, noise)
21
+ noise_score = np.std(highpass)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ # High frequency ratio (FFT)
24
+ f = np.fft.fft2(gray)
25
+ fshift = np.fft.fftshift(f)
26
+ magnitude_spectrum = np.abs(fshift)
27
+ hfreq_ratio = np.mean(magnitude_spectrum > np.percentile(magnitude_spectrum, 95))
 
 
 
28
 
29
+ # EXIF check
30
  try:
31
+ pil_img = Image.open(img_path)
32
+ exif = pil_img._getexif()
33
+ exif_exists = exif is not None
 
 
 
 
34
  except:
35
+ exif_exists = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
+ return {
38
+ "blur": round(blur_score, 2),
39
+ "noise": round(noise_score, 2),
40
+ "hfreq_ratio": round(hfreq_ratio, 3),
41
+ "exif": exif_exists
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
 
44
+ # === Deteksi AI vs Asli dengan ensemble ===
45
+ def detect_ai(img_path):
46
+ results1 = detector1(img_path)
47
+ results2 = detector2(img_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
+ # Ambil skor "AI" (label bisa berbeda di tiap model)
50
+ score1 = max([r['score'] for r in results1 if "artificial" in r['label'].lower()] + [0])
51
+ score2 = max([r['score'] for r in results2 if "ai" in r['label'].lower()] + [0])
 
 
 
52
 
53
+ ai_score = (score1 + score2) / 2 * 100
54
+ real_score = 100 - ai_score
 
 
55
 
56
+ forensic = forensic_analysis(img_path)
 
 
 
 
 
 
 
 
 
 
57
 
58
+ # Aturan custom
59
+ if ai_score > 60:
60
+ verdict = "🟥 AI"
61
+ elif real_score > 60 and forensic["exif"]:
62
+ verdict = "🟩 Asli"
63
+ else:
64
+ verdict = "⚠️ Meragukan"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
66
+ return {
67
+ "AI_score": round(ai_score, 2),
68
+ "Real_score": round(real_score, 2),
69
+ "Forensic": forensic,
70
+ "Verdict": verdict
71
+ }
 
 
72
 
73
+ # === Contoh pemakaian ===
74
  if __name__ == "__main__":
75
+ img_path = "download.jpeg" # ganti sesuai nama file upload
76
+ result = detect_ai(img_path)
77
+ print("Hasil Deteksi:")
78
+ print(result)