Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -26,7 +26,7 @@ ALLOWED_EXT = {"png", "jpg", "jpeg", "webp"}
|
|
| 26 |
ROOT_DIR = "."
|
| 27 |
MODEL_FILES = ["ckpt_best_v4_epoch8.pth", "ckpt_best_v4_epoch14.pth"]
|
| 28 |
|
| 29 |
-
# Threshold murni
|
| 30 |
MODEL_THRESHOLD = 0.615
|
| 31 |
|
| 32 |
# ==========================================
|
|
@@ -95,14 +95,15 @@ def startup_event():
|
|
| 95 |
raise RuntimeError(f"Startup digagalkan server: {e}")
|
| 96 |
|
| 97 |
# ==========================================
|
| 98 |
-
# 🔬 LAB FORENSIK DIGITAL -
|
| 99 |
# ==========================================
|
| 100 |
def run_full_forensic_pipeline(img_pil: Image.Image, img_cv, file_bytes, filename):
|
| 101 |
img_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
| 102 |
h, w, c = img_cv.shape
|
| 103 |
|
| 104 |
-
#
|
| 105 |
poin_penalti_fake = 0
|
|
|
|
| 106 |
|
| 107 |
# Deteksi Otomatis Jalur Kompresi WhatsApp via Nama Berkas
|
| 108 |
fname_lower = filename.lower()
|
|
@@ -117,6 +118,7 @@ def run_full_forensic_pipeline(img_pil: Image.Image, img_cv, file_bytes, filenam
|
|
| 117 |
exif = img_pil._getexif()
|
| 118 |
step1 = "Ada metadata EXIF (Kamera Asli)" if exif else "Metadata kosong (Khas Gambar AI/Screenshot)"
|
| 119 |
if not exif and not is_whatsapp:
|
|
|
|
| 120 |
poin_penalti_fake += 1
|
| 121 |
|
| 122 |
# [Step 2/12] Analisis Pixel (Komite Binary + TTA)
|
|
@@ -131,17 +133,19 @@ def run_full_forensic_pipeline(img_pil: Image.Image, img_cv, file_bytes, filenam
|
|
| 131 |
preds14 = F.softmax(models_ensemble[1](batch_t), dim=1).cpu().numpy()
|
| 132 |
probs_epoch14 = np.mean(preds14, axis=0)
|
| 133 |
|
| 134 |
-
# Perhitungan bobot kombinasi biner untuk kelas FAKE (indeks 1)
|
| 135 |
prob_fake_raw = float((0.4 * probs_epoch8[1]) + (0.6 * probs_epoch14[1]))
|
| 136 |
step2 = f"Score Indikasi AI: {prob_fake_raw * 100:.1f}%"
|
| 137 |
|
| 138 |
-
|
|
|
|
|
|
|
| 139 |
poin_penalti_fake += 1
|
| 140 |
|
| 141 |
# [Step 3/12] Analisis Pola Sensor CFA
|
| 142 |
diff_cfa = np.mean(np.abs(img_gray[1:, :] - img_gray[:-1, :]))
|
| 143 |
step3 = "Pola interpolasi mulus alami" if diff_cfa < 15.0 else "Anomali interpolasi piksel buatan terdeteksi"
|
| 144 |
if diff_cfa >= 15.0 and not is_whatsapp:
|
|
|
|
| 145 |
poin_penalti_fake += 1
|
| 146 |
|
| 147 |
# [Step 4/12] Pencarian jejak Hex/Binary
|
|
@@ -197,15 +201,14 @@ def run_full_forensic_pipeline(img_pil: Image.Image, img_cv, file_bytes, filenam
|
|
| 197 |
poin_penalti_fake += 1
|
| 198 |
|
| 199 |
# =======================================================
|
| 200 |
-
#
|
| 201 |
# =======================================================
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
confidence = prob_fake_raw * 100
|
| 206 |
else:
|
| 207 |
prediction = "REAL"
|
| 208 |
-
confidence = (
|
| 209 |
|
| 210 |
forensic_logs = {
|
| 211 |
"step_1": f"[Step 1/12] Metadata: {step1}",
|
|
@@ -231,9 +234,9 @@ def run_full_forensic_pipeline(img_pil: Image.Image, img_cv, file_bytes, filenam
|
|
| 231 |
def root():
|
| 232 |
return {
|
| 233 |
"status": "online",
|
| 234 |
-
"engine": "
|
| 235 |
"models_loaded": len(models_ensemble) == 2,
|
| 236 |
-
"
|
| 237 |
}
|
| 238 |
|
| 239 |
@app.post("/predict")
|
|
@@ -257,22 +260,22 @@ async def predict(file: UploadFile = File(...)):
|
|
| 257 |
if img_cv is None:
|
| 258 |
raise ValueError("File korup atau struktur piksel tidak valid")
|
| 259 |
|
| 260 |
-
# Eksekusi pipeline
|
| 261 |
prediction, confidence, raw_score, forensic_steps, is_monochrome, active_penalties = run_full_forensic_pipeline(
|
| 262 |
img_pil, img_cv, file_bytes, filename
|
| 263 |
)
|
| 264 |
|
| 265 |
return {
|
| 266 |
"filename": filename,
|
| 267 |
-
"prediction": prediction,
|
| 268 |
-
"confidence": f"{confidence}%",
|
| 269 |
-
"raw_deep_learning_score": f"{raw_score}%",
|
| 270 |
"active_fake_indicators": f"{active_penalties} dari 5",
|
| 271 |
-
"is_monochrome_detected": is_monochrome,
|
| 272 |
-
"forensic_analysis_logs": forensic_steps
|
| 273 |
}
|
| 274 |
except Exception as e:
|
| 275 |
-
raise HTTPException(status_code=500, detail=f"Gagal memproses analisis forensik: {str(e)}")
|
| 276 |
|
| 277 |
@app.post("/save-feedback")
|
| 278 |
async def save_feedback(file: UploadFile = File(...), correct_label: str = Form(...)):
|
|
|
|
| 26 |
ROOT_DIR = "."
|
| 27 |
MODEL_FILES = ["ckpt_best_v4_epoch8.pth", "ckpt_best_v4_epoch14.pth"]
|
| 28 |
|
| 29 |
+
# Threshold murni untuk filter internal model Deep Learning
|
| 30 |
MODEL_THRESHOLD = 0.615
|
| 31 |
|
| 32 |
# ==========================================
|
|
|
|
| 95 |
raise RuntimeError(f"Startup digagalkan server: {e}")
|
| 96 |
|
| 97 |
# ==========================================
|
| 98 |
+
# 🔬 LAB FORENSIK DIGITAL - WITH WHATSAPP BYPASS
|
| 99 |
# ==========================================
|
| 100 |
def run_full_forensic_pipeline(img_pil: Image.Image, img_cv, file_bytes, filename):
|
| 101 |
img_gray = cv2.cvtColor(img_cv, cv2.COLOR_BGR2GRAY)
|
| 102 |
h, w, c = img_cv.shape
|
| 103 |
|
| 104 |
+
# Inisialisasi Poin Penalti untuk Penggabungan Keputusan
|
| 105 |
poin_penalti_fake = 0
|
| 106 |
+
total_aturan = 5
|
| 107 |
|
| 108 |
# Deteksi Otomatis Jalur Kompresi WhatsApp via Nama Berkas
|
| 109 |
fname_lower = filename.lower()
|
|
|
|
| 118 |
exif = img_pil._getexif()
|
| 119 |
step1 = "Ada metadata EXIF (Kamera Asli)" if exif else "Metadata kosong (Khas Gambar AI/Screenshot)"
|
| 120 |
if not exif and not is_whatsapp:
|
| 121 |
+
# Penalti dinonaktifkan untuk WhatsApp karena kompresi sistem WA pasti menghapus EXIF
|
| 122 |
poin_penalti_fake += 1
|
| 123 |
|
| 124 |
# [Step 2/12] Analisis Pixel (Komite Binary + TTA)
|
|
|
|
| 133 |
preds14 = F.softmax(models_ensemble[1](batch_t), dim=1).cpu().numpy()
|
| 134 |
probs_epoch14 = np.mean(preds14, axis=0)
|
| 135 |
|
|
|
|
| 136 |
prob_fake_raw = float((0.4 * probs_epoch8[1]) + (0.6 * probs_epoch14[1]))
|
| 137 |
step2 = f"Score Indikasi AI: {prob_fake_raw * 100:.1f}%"
|
| 138 |
|
| 139 |
+
# Aturan Khusus WhatsApp: Naikkan batas threshold model AI agar tidak sensitif pada noise rusak WA
|
| 140 |
+
THRESHOLD_MODEL_ACTUAL = 0.85 if is_whatsapp else MODEL_THRESHOLD
|
| 141 |
+
if prob_fake_raw >= THRESHOLD_MODEL_ACTUAL:
|
| 142 |
poin_penalti_fake += 1
|
| 143 |
|
| 144 |
# [Step 3/12] Analisis Pola Sensor CFA
|
| 145 |
diff_cfa = np.mean(np.abs(img_gray[1:, :] - img_gray[:-1, :]))
|
| 146 |
step3 = "Pola interpolasi mulus alami" if diff_cfa < 15.0 else "Anomali interpolasi piksel buatan terdeteksi"
|
| 147 |
if diff_cfa >= 15.0 and not is_whatsapp:
|
| 148 |
+
# Penalti dinonaktifkan untuk WhatsApp karena kompresi WA merusak interpolasi alami piksel sensor
|
| 149 |
poin_penalti_fake += 1
|
| 150 |
|
| 151 |
# [Step 4/12] Pencarian jejak Hex/Binary
|
|
|
|
| 201 |
poin_penalti_fake += 1
|
| 202 |
|
| 203 |
# =======================================================
|
| 204 |
+
# KETOK PALU KEPUTUSAN GABUNGAN MATEMATIKA MULTI-EVIDENCE
|
| 205 |
# =======================================================
|
| 206 |
+
if poin_penalti_fake >= 3:
|
| 207 |
+
prediction = "AI" # Menyesuaikan label tampilan sistem frontend web Anda ("AI" / "REAL")
|
| 208 |
+
confidence = (poin_penalti_fake / total_aturan) * 100
|
|
|
|
| 209 |
else:
|
| 210 |
prediction = "REAL"
|
| 211 |
+
confidence = ((total_aturan - poin_penalti_fake) / total_aturan) * 100
|
| 212 |
|
| 213 |
forensic_logs = {
|
| 214 |
"step_1": f"[Step 1/12] Metadata: {step1}",
|
|
|
|
| 234 |
def root():
|
| 235 |
return {
|
| 236 |
"status": "online",
|
| 237 |
+
"engine": "Hybrid Integrated Forensic Engine (WhatsApp Bypass Fixed)",
|
| 238 |
"models_loaded": len(models_ensemble) == 2,
|
| 239 |
+
"hybrid_voting_rules_count": 5
|
| 240 |
}
|
| 241 |
|
| 242 |
@app.post("/predict")
|
|
|
|
| 260 |
if img_cv is None:
|
| 261 |
raise ValueError("File korup atau struktur piksel tidak valid")
|
| 262 |
|
| 263 |
+
# Eksekusi pipeline terintegrasi baru dengan parameter operan filename
|
| 264 |
prediction, confidence, raw_score, forensic_steps, is_monochrome, active_penalties = run_full_forensic_pipeline(
|
| 265 |
img_pil, img_cv, file_bytes, filename
|
| 266 |
)
|
| 267 |
|
| 268 |
return {
|
| 269 |
"filename": filename,
|
| 270 |
+
"prediction": prediction,
|
| 271 |
+
"confidence": f"{confidence}%",
|
| 272 |
+
"raw_deep_learning_score": f"{raw_score}%",
|
| 273 |
"active_fake_indicators": f"{active_penalties} dari 5",
|
| 274 |
+
"is_monochrome_detected": is_monochrome,
|
| 275 |
+
"forensic_analysis_logs": forensic_steps
|
| 276 |
}
|
| 277 |
except Exception as e:
|
| 278 |
+
raise HTTPException(status_code=500, detail=f"Gagal memproses analisis gabungan forensik: {str(e)}")
|
| 279 |
|
| 280 |
@app.post("/save-feedback")
|
| 281 |
async def save_feedback(file: UploadFile = File(...), correct_label: str = Form(...)):
|