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 # ===================================================== @app.get("/") def home(): return { "status": "running", "message": "CNN Pose Classifier API", "input_shape": [66], "output": "binary classification" } # ===================================================== # HEALTH CHECK # ===================================================== @app.get("/health") def health(): return { "status": "healthy" } # ===================================================== # PREDICTION # ===================================================== @app.post("/predict") 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) )