Spaces:
Running
Running
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel, Field | |
| import tensorflow as tf | |
| import numpy as np | |
| # ===================================================== | |
| # LOAD MODEL | |
| # ===================================================== | |
| MODEL_PATH = "model_cnn_best.h5" | |
| model = tf.keras.models.load_model(MODEL_PATH) | |
| # ===================================================== | |
| # FASTAPI | |
| # ===================================================== | |
| app = FastAPI( | |
| title="CNN Pose Classifier API", | |
| version="1.0.0", | |
| description="API klasifikasi aktivitas menggunakan CNN + MediaPipe Pose" | |
| ) | |
| # ===================================================== | |
| # CORS | |
| # ===================================================== | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # ganti dengan domain website jika production | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # ===================================================== | |
| # REQUEST MODEL | |
| # ===================================================== | |
| class PoseInput(BaseModel): | |
| features: list[float] = Field( | |
| ..., | |
| min_length=66, | |
| max_length=66, | |
| description="66 pose features (x,y landmark)" | |
| ) | |
| # ===================================================== | |
| # ROOT | |
| # ===================================================== | |
| def home(): | |
| return { | |
| "status": "running", | |
| "message": "CNN Pose Classifier API", | |
| "input_shape": [66], | |
| "output": "binary classification" | |
| } | |
| # ===================================================== | |
| # HEALTH CHECK | |
| # ===================================================== | |
| def health(): | |
| return { | |
| "status": "healthy" | |
| } | |
| # ===================================================== | |
| # PREDICTION | |
| # ===================================================== | |
| def predict(data: PoseInput): | |
| try: | |
| # ----------------------------- | |
| # Convert ke numpy | |
| # ----------------------------- | |
| features = np.array( | |
| data.features, | |
| dtype=np.float32 | |
| ) | |
| # ----------------------------- | |
| # Validasi jumlah fitur | |
| # ----------------------------- | |
| if features.shape != (66,): | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Input harus terdiri dari 66 fitur." | |
| ) | |
| # ----------------------------- | |
| # Validasi NaN dan Inf | |
| # ----------------------------- | |
| if np.isnan(features).any(): | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Input mengandung NaN." | |
| ) | |
| if np.isinf(features).any(): | |
| raise HTTPException( | |
| status_code=400, | |
| detail="Input mengandung Infinity." | |
| ) | |
| # ----------------------------- | |
| # Reshape sesuai model | |
| # (batch,66,1) | |
| # ----------------------------- | |
| features = features.reshape( | |
| 1, | |
| 66, | |
| 1 | |
| ) | |
| # ============================= | |
| # DEBUG INPUT | |
| # ============================= | |
| print("=" * 60) | |
| print("DEBUG INPUT CNN") | |
| print("=" * 60) | |
| print("Shape :", features.shape) | |
| print("Min :", np.min(features)) | |
| print("Max :", np.max(features)) | |
| print("Mean :", np.mean(features)) | |
| flat = features.flatten() | |
| print("All 66 features:") | |
| for i, value in enumerate(flat): | |
| print(f"{i:02d}: {value:.6f}") | |
| # ----------------------------- | |
| # Predict | |
| # ----------------------------- | |
| prediction = model.predict( | |
| features, | |
| verbose=0 | |
| ) | |
| # ============================= | |
| # DEBUG OUTPUT | |
| # ============================= | |
| print("Raw prediction :", prediction) | |
| print("Probability :", float(prediction[0][0])) | |
| print("=" * 60) | |
| probability = float(prediction[0][0]) | |
| if probability >= 0.5: | |
| label = "berbahaya" | |
| else: | |
| label = "aman" | |
| return { | |
| "success": True, | |
| "prediction": label, | |
| "probability": round(probability, 4), | |
| "confidence": round( | |
| max(probability, 1 - probability), | |
| 4 | |
| ) | |
| } | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| raise HTTPException( | |
| status_code=500, | |
| detail=str(e) | |
| ) |