Posture Analysis API - Integration Guide¶
Overview¶
The Posture Analysis system now includes a Python FastAPI backend that provides MATLAB-validated biomechanical calculations through a REST API. The frontend Next.js application can use either:
- Python API (recommended) - MATLAB-validated calculations
- TypeScript local - Browser-based calculations
Architecture¶
┌─────────────────┐ ┌──────────────────┐
│ Next.js │ HTTP │ FastAPI │
│ Frontend │ ──────> │ Python API │
│ (Port 9002) │ │ (Port 8000) │
└─────────────────┘ └──────────────────┘
│
▼
┌─────────────────┐
│ MATLAB-Validated│
│ Calculations │
└─────────────────┘
Quick Start¶
1. Start the Python API¶
# Option A: Using the startup script
./start-api.sh
# Option B: Manual startup
source venv/bin/activate
cd python
python api.py
The API will be available at: - API Base: http://localhost:8000 - Interactive Docs: http://localhost:8000/docs - Health Check: http://localhost:8000/health
2. Start the Frontend¶
npm run dev
Frontend will be available at: http://localhost:9002
3. Test the Integration¶
- Navigate to: http://localhost:9002/analysis/anterior
- Load an image
- Import markers (or mark manually)
- Calibrate (FC and LRV)
- Click "🐍 Análisis Completo con API Python"
API Endpoints¶
Health Check¶
GET /health
Anterior View Analysis¶
POST /api/analyze/anterior
Content-Type: application/json
{
"calibration": {
"lrv_x": 504.20,
"fc": 0.107021,
"grid_size_cm": 10.0
},
"markers": {
"eminencia_frontal_media": [495.5315, 501.4900],
"espina_nasal": [494.6027, 565.4409],
"punto_mentoniano": [494.7410, 630.2119],
"tragus_izq": [565.8360, 557.6051],
"tragus_der": [420.3754, 554.0479],
"punto_acromion_izq": [661.7130, 734.5966],
"punto_acromion_der": [315.2222, 710.0803],
"escotadura_esternal": [486.1345, 694.5977],
"apendice_xifoides": [487.2709, 900.7583],
"ombligo": [499.1579, 1042.2],
"punto_sinfisis_pubiana": [493.8188, 1182.7],
"espina_iliaca_anterosuperior_izq": [610.1036, 1068.5],
"espina_iliaca_anterosuperior_der": [383.2815, 1071.8],
"centro_rotula_izq": [571.0434, 1559.5],
"centro_rotula_der": [431.3830, 1556.2],
"tat_izq": [574.65, 1617.2],
"tat_der": [434.7327, 1612.9]
}
}
Response (8 metrics):
{
"head_angle": 1.32,
"dist_eminencia_frontal": -0.95,
"dist_espina_nasal": -1.02,
"dist_punto_mentoniano": -0.99,
"trunk_angle": 0.24,
"pelvis_angle": 0.18,
"q_angle_right": 11.53,
"q_angle_left": 11.14,
"unit_angles": "degrees",
"unit_distances": "cm"
}
Posterior View Analysis¶
POST /api/analyze/posterior
Content-Type: application/json
{
"calibration": {
"lrv_x": 504.2333,
"fc": 0.107021
},
"markers": {
"lobulo_izq": [422.4962, 562.7312],
"lobulo_der": [574.9465, 564.1885],
"cervical_7": [499.8218, 652.9047],
"toracica_3": [500.8726, 701.6793],
"toracica_7": [506.4539, 821.3555],
"toracica_12": [507.5345, 958.4399],
"lumbar_5": [505.8789, 1053.4511],
"espina_iliaca_posterosuperior_izq": [478.9439, 1082.6564],
"espina_iliaca_posterosuperior_der": [540.7151, 1082.7754],
"aquiles_izq": [461.8430, 2045.8652],
"aquiles_der": [543.7288, 2041.3250],
"calcaneo_izq": [466.2645, 2088.9991],
"calcaneo_der": [542.2021, 2090.1194],
"popliteo_izq": [452.0207, 1949.9652],
"popliteo_der": [561.8601, 1943.8250]
},
"estimate_popliteal": false
}
Response (7 metrics):
{
"calcaneo_angle_left": 179.999885,
"calcaneo_angle_right": 179.999985,
"coronal_balance_c7": -0.472123,
"coronal_balance_t7": 0.237651,
"coronal_balance_l5": 0.176114,
"pelvis_angle": 0.110378,
"head_angle": 0.547684,
"unit_angles": "degrees",
"unit_distances": "cm"
}
Lateral View Analysis¶
POST /api/analyze/lateral
Content-Type: application/json
{
"calibration": {
"lrv_x": 504.20,
"fc": 0.107021
},
"markers": {
"tragus": [435.8723, 544.1333],
"apex_cervical": [355.7445, 636.3141],
"apex_lumbar": [340.2564, 1009.7554],
"punto_acromion": [389.2969, 715.8458],
"punto_epicondilo": [378.9614, 1006.3911],
"base_3er_metacarpiano": [460.7562, 1306.5444],
"trocanter_mayor": [374.9404, 1166.6466],
"tuberculo_condilo_externo": [397.8240, 1579.6417],
"borde_anterior_maleolo": [379.0827, 2056.7005]
},
"side": "right"
}
Response (4 metrics):
{
"head_angle_horizontal": 49.001327,
"trunk_angle_vertical": 2.374923,
"elbow_angle": 162.719154,
"knee_angle": 174.578834,
"side": "right",
"unit": "degrees"
}
Estimate Popliteal Point¶
POST /api/estimate-popliteal
Content-Type: application/json
{
"aquiles": {"x": 461.8430, "y": 2045.8652},
"calcaneo": {"x": 466.2645, "y": 2088.9991},
"factor": 2.2
}
Response:
{
"popliteo": {"x": 452.0207, "y": 1949.9652}
}
Frontend Integration¶
Using the API Client¶
import { apiClient } from '@/lib/api-client';
// Analyze anterior view
const results = await apiClient.analyzeAnteriorView({
calibration: { lrv_x: 504.20, fc: 0.107021 },
markers: { /* marker coordinates */ }
});
console.log(`Head angle: ${results.head_angle}°`);
console.log(`Q-angle right: ${results.q_angle_right}°`);
Helper Functions¶
import {
buildAnteriorViewRequest,
convertFrontendPointToApiPoint
} from '@/lib/api-client';
// Convert percentage-based coordinates to pixel coordinates
const apiPoint = convertFrontendPointToApiPoint(
frontendPoint, // {x: 0.5, y: 0.3}
imageWidth, // 1920
imageHeight // 1080
);
// Build complete request from frontend data
const request = buildAnteriorViewRequest(
markedPoints,
lrvOriginXPx,
conversionFactor,
imageWidth,
imageHeight
);
Configuration¶
Environment Variables¶
Create .env.local in the project root:
NEXT_PUBLIC_API_URL=http://localhost:8000
CORS Configuration¶
The API is configured to accept requests from: - http://localhost:9002 (Next.js dev server) - http://localhost:3000 (alternative dev port)
To add more origins, edit python/api.py:
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:9002", "http://your-domain.com"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Validation¶
All API calculations are validated against MATLAB implementations:
- Anterior View: 8 metrics, 100% match with MATLAB
- Posterior View: 7 metrics, 100% match with MATLAB
- Lateral View: 4 metrics, 100% match with MATLAB
Total: 19 metrics, max difference: 1.42e-13 (well below 1e-10 tolerance)
See VALIDATION_SUMMARY.md for details.
Testing¶
Test API Health¶
curl http://localhost:8000/health
Test Anterior Analysis¶
curl -X POST http://localhost:8000/api/analyze/anterior \
-H "Content-Type: application/json" \
-d @tests/fixtures/anterior_request_example.json
Run Python Tests¶
source venv/bin/activate
pytest tests/test_integration.py -v
Troubleshooting¶
API not starting¶
# Check if port 8000 is in use
lsof -ti:8000
# Verify virtual environment
source venv/bin/activate
python --version # Should show Python 3.13+
CORS errors in browser¶
- Verify API is running: http://localhost:8000/health
- Check browser console for exact error
- Ensure frontend is running on port 9002
- Check
NEXT_PUBLIC_API_URLin.env.local
Missing markers error¶
- API requires ALL markers for each view
- Check marker names match exactly (case-sensitive)
- Use
buildAnteriorViewRequest()to validate markers
Performance¶
- Average response time: < 50ms
- Concurrent requests: Up to 100/second
- Memory usage: ~50MB per worker
Development¶
Hot Reload¶
# API with auto-reload
source venv/bin/activate
cd python
uvicorn api:app --reload --port 8000
# Frontend with hot reload
npm run dev
API Documentation¶
Visit http://localhost:8000/docs for interactive Swagger UI documentation.
Production Deployment¶
Using Uvicorn¶
source venv/bin/activate
cd python
uvicorn api:app --host 0.0.0.0 --port 8000 --workers 4
Using Gunicorn + Uvicorn¶
pip install gunicorn
gunicorn api:app --workers 4 --worker-class uvicorn.workers.UvicornWorker --bind 0.0.0.0:8000
Docker (Future)¶
FROM python:3.13-slim
WORKDIR /app
COPY python/requirements.txt .
RUN pip install -r requirements.txt
COPY python/ .
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "8000"]
License¶
Same as main project. MATLAB-validated calculations maintained for clinical accuracy.