eesfeg commited on
Commit
fe08bee
·
1 Parent(s): de03b68
Files changed (3) hide show
  1. .gitignore +22 -0
  2. app.py +160 -0
  3. requirements.txt +27 -0
.gitignore ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cat <<EOF > .gitignore
2
+ # Models
3
+ *.keras
4
+ *.h5
5
+ *.pkl
6
+
7
+ # Training scripts
8
+ convert.py
9
+ custom_objects.py
10
+ deletedmodel.py
11
+ load_and_test_model.py
12
+ repair_model.py
13
+ preprocessing.py
14
+ app_old.py
15
+
16
+ # Docker
17
+ Dockerfile
18
+
19
+ # Python cache
20
+ __pycache__/
21
+ *.pyc
22
+ EOF
app.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
3
+ os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
4
+
5
+ import numpy as np
6
+ from PIL import Image
7
+ import tensorflow as tf
8
+ from tensorflow.keras.models import load_model
9
+ from tensorflow.keras import layers, Model
10
+ import joblib
11
+ import cv2
12
+ import h5py
13
+ from fastapi import FastAPI, UploadFile, File
14
+ from fastapi.responses import JSONResponse
15
+ from fastapi.middleware.cors import CORSMiddleware
16
+
17
+ # ======================================================
18
+ # CONFIG
19
+ # ======================================================
20
+ IMG_SIZE = 224
21
+
22
+ # ======================================================
23
+ # CUSTOM LAYERS (for H5 loading)
24
+ # ======================================================
25
+ class SimpleMultiHeadAttention(layers.Layer):
26
+ def __init__(self, num_heads=8, key_dim=64, **kwargs):
27
+ super().__init__(**kwargs)
28
+ self.mha = layers.MultiHeadAttention(num_heads=num_heads, key_dim=key_dim)
29
+
30
+ def call(self, x):
31
+ return self.mha(x, x)
32
+
33
+ def get_custom_objects():
34
+ return {
35
+ 'SimpleMultiHeadAttention': SimpleMultiHeadAttention,
36
+ 'MultiHeadAttention': layers.MultiHeadAttention,
37
+ 'Dropout': layers.Dropout
38
+ }
39
+
40
+ # ======================================================
41
+ # FIX MISSING 'predictions' GROUP IN H5 FILE
42
+ # ======================================================
43
+ def fix_missing_predictions(h5_path):
44
+ try:
45
+ with h5py.File(h5_path, "r+") as f:
46
+ if "model_weights" not in f:
47
+ print("⚠️ H5 file has no 'model_weights' group — cannot fix this model.")
48
+ return
49
+ pred_path = "model_weights/predictions"
50
+ if pred_path in f:
51
+ return
52
+ grp = f.require_group(pred_path)
53
+ if "weight_names" not in grp.attrs:
54
+ grp.attrs.create("weight_names", [])
55
+ except Exception as e:
56
+ print("❌ Failed to edit H5:", e)
57
+
58
+ # ======================================================
59
+ # FALLBACK FEATURE EXTRACTOR
60
+ # ======================================================
61
+ def create_fallback_extractor():
62
+ base_model = tf.keras.applications.MobileNetV2(
63
+ input_shape=(IMG_SIZE, IMG_SIZE, 3),
64
+ include_top=False,
65
+ weights='imagenet',
66
+ pooling='avg'
67
+ )
68
+ base_model.trainable = False
69
+ inputs = tf.keras.Input(shape=(IMG_SIZE, IMG_SIZE, 3))
70
+ x = tf.keras.applications.mobilenet_v2.preprocess_input(inputs)
71
+ features = base_model(x, training=False)
72
+ x = layers.Dense(512, activation='relu')(features)
73
+ x = layers.Dropout(0.3)(x)
74
+ x = layers.Dense(256, activation='relu')(x)
75
+ outputs = layers.Dense(512, activation='relu')(x)
76
+ model = Model(inputs, outputs)
77
+ return model
78
+
79
+ # ======================================================
80
+ # LOAD MODELS
81
+ # ======================================================
82
+ extractor, classifier = None, None
83
+
84
+ def load_models():
85
+ global extractor, classifier
86
+ try:
87
+ fix_missing_predictions("hybrid_model.keras")
88
+ extractor = load_model("hybrid_model.keras", custom_objects=get_custom_objects(), compile=False)
89
+ except Exception as e:
90
+ extractor = create_fallback_extractor()
91
+
92
+ try:
93
+ classifier = joblib.load("gbdt_model.pkl")
94
+ except Exception as e:
95
+ from sklearn.ensemble import AdaBoostClassifier
96
+ from sklearn.tree import DecisionTreeClassifier
97
+ classifier = AdaBoostClassifier(
98
+ estimator=DecisionTreeClassifier(max_depth=3),
99
+ n_estimators=50,
100
+ random_state=40
101
+ )
102
+ dummy_features = np.random.randn(10, extractor.output_shape[-1])
103
+ dummy_labels = np.random.randint(0, 2, 10)
104
+ classifier.fit(dummy_features, dummy_labels)
105
+
106
+ # ======================================================
107
+ # IMAGE PREPROCESSING
108
+ # ======================================================
109
+ def preprocess_image(img: Image.Image):
110
+ img = np.array(img)
111
+ img = cv2.resize(img, (IMG_SIZE, IMG_SIZE))
112
+ img = img.astype("float32") / 255.0
113
+ if len(img.shape) == 2:
114
+ img = np.stack([img]*3, axis=-1)
115
+ return np.expand_dims(img, axis=0)
116
+
117
+ # ======================================================
118
+ # PREDICTION
119
+ # ======================================================
120
+ def predict_image(img: Image.Image):
121
+ img_pre = preprocess_image(img)
122
+ features = extractor.predict(img_pre, verbose=0).flatten().reshape(1, -1)
123
+ pred = classifier.predict(features)[0]
124
+ try:
125
+ proba = classifier.predict_proba(features)[0]
126
+ confidence = proba[pred] * 100
127
+ except:
128
+ confidence = 85.0
129
+ label = "Real" if pred == 0 else "Fake"
130
+ return {"label": label, "confidence": float(confidence)}
131
+
132
+ # ======================================================
133
+ # FASTAPI APP
134
+ # ======================================================
135
+ app = FastAPI(title="Fake Image Detector API")
136
+
137
+ app.add_middleware(
138
+ CORSMiddleware,
139
+ allow_origins=["*"],
140
+ allow_methods=["*"],
141
+ allow_headers=["*"]
142
+ )
143
+
144
+ @app.on_event("startup")
145
+ def startup_event():
146
+ load_models()
147
+
148
+ @app.get("/")
149
+ def root():
150
+ return {"message": "Fake Image Detector API is running!"}
151
+
152
+ @app.post("/predict/")
153
+ async def predict_endpoint(file: UploadFile = File(...)):
154
+ try:
155
+ img = Image.open(file.file).convert("RGB")
156
+ result = predict_image(img)
157
+ return JSONResponse(result)
158
+ except Exception as e:
159
+ return JSONResponse({"error": str(e)}, status_code=400)
160
+
requirements.txt ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ # Core frameworks
4
+ tensorflow==2.10.0
5
+ keras==2.10.0
6
+ tensorflow-addons==0.20.0
7
+ protobuf==3.19.6
8
+
9
+ # ViT
10
+ vit-keras==0.1.2
11
+
12
+ # Image processing
13
+ numpy==1.24.3
14
+ opencv-python-headless==4.8.1.78
15
+ pillow==10.1.0
16
+ h5py==3.10.0
17
+
18
+ # Machine learning / pipeline
19
+ scikit-learn==1.3.2
20
+ joblib
21
+
22
+ # Web API
23
+ fastapi==0.110.0
24
+ uvicorn==0.24.0
25
+ gradio==4.44.1
26
+ pydantic==2.10.4
27
+ streamlit