techSnipe commited on
Commit
83ee618
·
verified ·
1 Parent(s): 17f256a

Upload folder using huggingface_hub

Browse files
Files changed (13) hide show
  1. .env.example +11 -0
  2. Dockerfile +39 -0
  3. api.py +177 -0
  4. download_model.py +14 -0
  5. gait_analysis.py +527 -0
  6. generate_report.py +123 -0
  7. main.py +54 -0
  8. models/pose_deploy.prototxt +2332 -0
  9. requirements.txt +8 -0
  10. rules_by_age.json +103 -0
  11. run_test.py +111 -0
  12. save_results.py +42 -0
  13. yolo11n-pose.pt +3 -0
.env.example ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ============================================================
2
+ # Gait Analysis API — Environment Variables
3
+ # ============================================================
4
+ # Copy this file to .env and fill in your values.
5
+ # .env is gitignored and must NEVER be committed.
6
+
7
+ # Optional: HuggingFace token (for private model downloads via huggingface_hub)
8
+ # HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxx
9
+
10
+ # Optional: Override the YOLO model filename used at startup
11
+ # MODEL_NAME=yolo11n-pose.pt
Dockerfile ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile for Gait Analysis Microservice
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies required by OpenCV and YOLO
5
+ RUN apt-get update && apt-get install -y \
6
+ libglib2.0-0 \
7
+ libsm6 \
8
+ libxext6 \
9
+ libxrender-dev \
10
+ libgl1-mesa-glx \
11
+ git \
12
+ && rm -rf /var/lib/apt/lists/*
13
+
14
+ # Set the working directory in the container
15
+ WORKDIR /app
16
+
17
+ # Copy the requirements file into the container
18
+ COPY requirements.txt .
19
+
20
+ # Install ALL Python dependencies in a single layer for efficiency
21
+ RUN pip install --no-cache-dir -r requirements.txt
22
+
23
+ # Copy the rest of the backend files into the container
24
+ COPY . /app
25
+
26
+ # ---- Download YOLO model at BUILD time ----
27
+ # This avoids a cold-start penalty on every container restart.
28
+ # The model (~6 MB) is baked into the image layer.
29
+ RUN python download_model.py
30
+
31
+ # HuggingFace Spaces requires the app to run as a non-root user
32
+ RUN useradd -m -u 1000 appuser && chown -R appuser /app
33
+ USER appuser
34
+
35
+ # Expose the port that HuggingFace Spaces routes to
36
+ EXPOSE 7860
37
+
38
+ # Command to run the application using Uvicorn
39
+ CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "7860"]
api.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import shutil
3
+ import tempfile
4
+ from typing import List, Dict, Any, Optional
5
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException
6
+ from fastapi.responses import JSONResponse
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from pydantic import BaseModel, Field
9
+
10
+ # Import your existing analyzer logic
11
+ from gait_analysis import EnhancedGaitAnalyzer
12
+
13
+ app = FastAPI(
14
+ title="Gait Analysis Inference Microservice",
15
+ description="Microservice to process video with YOLO11 and return gait metrics. Upload a video to extract clinical gait analysis features including stability, symmetry, and developmental observations.",
16
+ version="1.0.0",
17
+ docs_url="/docs",
18
+ redoc_url="/redoc"
19
+ )
20
+
21
+ # Standard CORS configuration
22
+ app.add_middleware(
23
+ CORSMiddleware,
24
+ allow_origins=["*"],
25
+ allow_credentials=True,
26
+ allow_methods=["*"],
27
+ allow_headers=["*"],
28
+ )
29
+
30
+ # --- Pydantic Models for Swagger Documentation ---
31
+
32
+ class HealthResponse(BaseModel):
33
+ status: str = Field(..., example="ok")
34
+ message: str = Field(..., example="Gait Analysis Inference Service is running.")
35
+
36
+ class GaitSummary(BaseModel):
37
+ stability_score: float = Field(..., description="Calculated stability score (out of 100). Higher is better.")
38
+ symmetry_score: float = Field(..., description="Calculated symmetry score (out of 100). Higher is better.")
39
+ weight_shift_score: float = Field(..., description="Calculated weight shift score (out of 100). Higher is better.")
40
+ strength_score: float = Field(..., description="Calculated strength score (out of 100). Higher is better.")
41
+ phase_asymmetry_percent: float = Field(..., description="Percentage of asymmetry between left and right stance times")
42
+ overall_gait_quality: str = Field(..., description="Overall assessment of gait quality (e.g. acceptable, concerning)")
43
+ walking_condition: str = Field(..., description="E.g., Independent or Assisted")
44
+
45
+ class GaitObservations(BaseModel):
46
+ trunk: str = Field(..., description="Observation of trunk movement (e.g. 'Neutral', 'Rotated')")
47
+ head_neck: str = Field(..., description="Observation of head and neck (e.g. 'Neutral & mobile')")
48
+ arms_hands: str = Field(..., description="Observation of arms and hands (e.g. 'Free arm swing')")
49
+ lower_limbs: str = Field(..., description="Observation of lower limbs")
50
+ symmetry: str = Field(..., description="General symmetry observation")
51
+ weight_distribution: str = Field(..., description="Observation of weight distribution")
52
+ postural_control: str = Field(..., description="Observation of postural control")
53
+
54
+ class WeightDistribution(BaseModel):
55
+ overall_imbalance_percent: float = Field(..., description="Percentage of imbalance")
56
+ primary_weight_side: str = Field(..., description="Primary weight side (left/right/balanced)")
57
+ weight_shift_issues: List[str] = Field(..., description="Identified weight shift issues")
58
+ hip_drop_analysis: List[Dict[str, str]] = Field(..., description="Hip drop analysis")
59
+
60
+ class PosturalStrength(BaseModel):
61
+ trunk_sway_severity: str = Field(..., description="Severity of trunk sway")
62
+ shoulder_stability: str = Field(..., description="Shoulder stability observation")
63
+ forward_lean: str = Field(..., description="Forward lean observation")
64
+ knee_stability: str = Field(..., description="Knee stability observation")
65
+ overall_strength_assessment: str = Field(..., description="Overall strength assessment")
66
+
67
+ class GaitPhases(BaseModel):
68
+ stance_time_l: int = Field(..., description="Number of frames the left foot is in stance phase")
69
+ stance_time_r: int = Field(..., description="Number of frames the right foot is in stance phase")
70
+ swing_time_l: int = Field(..., description="Number of frames the left foot is in swing phase")
71
+ swing_time_r: int = Field(..., description="Number of frames the right foot is in swing phase")
72
+ double_support_frames: int = Field(..., description="Number of frames where both feet are in stance phase")
73
+ phase_asymmetry: float = Field(..., description="Phase asymmetry percentage")
74
+
75
+ class GaitAnalysisData(BaseModel):
76
+ frames: List[Dict[str, Any]] = Field(..., description="List of processed frame data including keypoints and angles")
77
+ summary: GaitSummary = Field(..., description="Summary metrics of the gait analysis")
78
+ weight_distribution: WeightDistribution = Field(..., description="Weight distribution analysis")
79
+ postural_strength: PosturalStrength = Field(..., description="Postural strength assessment")
80
+ gait_phases: GaitPhases = Field(..., description="Gait phase breakdown")
81
+ risk_indicators: List[str] = Field(..., description="List of identified risk factors")
82
+ flags: Dict[str, Any] = Field(..., description="Specific flags or markers triggered during analysis")
83
+ observations: GaitObservations = Field(..., description="Clinical observations of body segments")
84
+ compensatory_strategies: List[str] = Field(..., description="Identified compensatory movement strategies")
85
+ reflex_influence: str = Field(..., description="Notes on primitive reflex influence")
86
+ safety_note: str = Field(..., description="Any constraints or limitations of the analysis")
87
+ clinical_notes: List[str] = Field(..., description="Clinical notes on findings")
88
+
89
+ class AnalyzeResponse(BaseModel):
90
+ status: str = Field(..., example="success")
91
+ data: GaitAnalysisData = Field(..., description="The complete gait analysis results")
92
+
93
+ # --------------------------------------------------
94
+
95
+ # We initialize the analyzer globally so the YOLO model is loaded into memory ONCE on startup.
96
+ # This prevents "cold starts" where the heavy model has to reload for every single request.
97
+ analyzer = None
98
+
99
+ @app.on_event("startup")
100
+ async def startup_event():
101
+ global analyzer
102
+ print("Initializing YOLO framework and loading model into memory...")
103
+ # Initialize with default placeholders; we dynamically update context per request
104
+ analyzer = EnhancedGaitAnalyzer(model_name="yolo11n-pose.pt")
105
+ print("Model initialized and ready for inference!")
106
+
107
+ @app.get(
108
+ "/",
109
+ response_model=HealthResponse,
110
+ tags=["System"],
111
+ summary="Health Check",
112
+ description="Simple health check endpoint to ping from the main backend."
113
+ )
114
+ def health_check():
115
+ return {"status": "ok", "message": "Gait Analysis Inference Service is running."}
116
+
117
+ @app.post(
118
+ "/analyze",
119
+ response_model=AnalyzeResponse,
120
+ tags=["Analysis"],
121
+ summary="Analyze Video",
122
+ description="Upload a video to perform full gait analysis. This endpoint loads the video, detects poses, and calculates clinical gait metrics."
123
+ )
124
+ async def analyze_video(
125
+ file: UploadFile = File(..., description="The highly compressed video file (e.g., .mp4, .mov)"),
126
+ age: Optional[int] = Form(None, description="Age of the patient for developmental benchmarks (determines expected normative values). Defaults to 4 if not provided."),
127
+ independent_walking: bool = Form(True, description="Whether the patient is walking independently")
128
+ ):
129
+ if not file.filename:
130
+ raise HTTPException(status_code=400, detail="No file uploaded")
131
+
132
+ if age is not None and age > 11:
133
+ raise HTTPException(status_code=400, detail="OVER_AGE: Patient age exceeds the supported range (maximum 11 years) for this analysis.")
134
+
135
+ # Default age to 4 if not provided
136
+ process_age = age if age is not None else 4
137
+
138
+ # Create a temporary directory to store the incoming video
139
+ temp_dir = tempfile.mkdtemp()
140
+ temp_video_path = os.path.join(temp_dir, file.filename)
141
+
142
+ try:
143
+ # 1. Save the incoming stream to a temporary disk file
144
+ with open(temp_video_path, "wb") as buffer:
145
+ shutil.copyfileobj(file.file, buffer)
146
+
147
+ print(f"Received video: {file.filename} (Age: {process_age}, Independent: {independent_walking})")
148
+
149
+ # 2. Update the analyzer's dynamic properties for this specific request
150
+ analyzer.age = process_age
151
+ analyzer.independent_walking = independent_walking
152
+
153
+ # 3. Process the video
154
+ results = analyzer.process_video(temp_video_path)
155
+
156
+ # 4. Return the massive JSON payload back to the main backend server
157
+ # We use JSONResponse directly to avoid Pydantic serialization overhead for 'frames' list,
158
+ # but the Swagger documentation is still powered by the AnalyzeResponse model.
159
+ return JSONResponse(content={"status": "success", "data": results})
160
+
161
+ except ValueError as ve:
162
+ print(f"Validation Error: {ve}")
163
+ # Return a 400 Bad Request since this is a client/input issue
164
+ raise HTTPException(status_code=400, detail=str(ve))
165
+ except Exception as e:
166
+ print(f"Error running inference: {e}")
167
+ raise HTTPException(status_code=500, detail=str(e))
168
+
169
+ finally:
170
+ # 5. Cleanup memory/disk: delete the video so the server doesn't run out of storage
171
+ if os.path.exists(temp_dir):
172
+ shutil.rmtree(temp_dir, ignore_errors=True)
173
+
174
+ if __name__ == "__main__":
175
+ import uvicorn
176
+ # Use standard uvicorn runner for local testing
177
+ uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True)
download_model.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ultralytics import YOLO
2
+ import os
3
+
4
+ def download_yolo_model():
5
+ print("Checking and downloading YOLO11 model...")
6
+ try:
7
+ # This will automatically download the model if not present
8
+ model = YOLO("yolo11n-pose.pt")
9
+ print("Model yolo11n-pose.pt is ready!")
10
+ except Exception as e:
11
+ print(f"Error initializing YOLO model: {e}")
12
+
13
+ if __name__ == "__main__":
14
+ download_yolo_model()
gait_analysis.py ADDED
@@ -0,0 +1,527 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import os
4
+ from ultralytics import YOLO
5
+ from collections import deque
6
+
7
+ class EnhancedGaitAnalyzer:
8
+ def __init__(self, model_name="yolo11n-pose.pt", age=4, independent_walking=True):
9
+ """
10
+ Enhanced Gait Analyzer with weight shifting, postural strength, AND original gait analysis.
11
+ """
12
+ self.model = YOLO(model_name)
13
+ self.age = age
14
+ self.independent_walking = independent_walking
15
+
16
+ # COCO 17-Keypoint Mapping
17
+ self.KEYPOINT_MAP = {
18
+ 0: "Nose", 1: "LEye", 2: "REye", 3: "LEar", 4: "REar",
19
+ 5: "LShoulder", 6: "RShoulder", 7: "LElbow", 8: "RElbow",
20
+ 9: "LWrist", 10: "RWrist", 11: "LHip", 12: "RHip",
21
+ 13: "LKnee", 14: "RKnee", 15: "LAnkle", 16: "RAnkle"
22
+ }
23
+
24
+ # Thresholds
25
+ self.WEIGHT_SHIFT_THRESHOLD = 0.15
26
+ self.HIP_DROP_THRESHOLD = 10
27
+ self.POSTURE_SWAY_THRESHOLD = 20
28
+
29
+ def process_video(self, video_path):
30
+ """Process video and extract gait analysis metrics."""
31
+ if not os.path.exists(video_path):
32
+ raise FileNotFoundError(f"Video file not found: {video_path}")
33
+
34
+ results = self.model(video_path, stream=True, verbose=False)
35
+ frames_data = []
36
+
37
+ for i, r in enumerate(results):
38
+ if r.keypoints is None or r.keypoints.data.shape[0] == 0:
39
+ frames_data.append({"frame": i, "keypoints": []})
40
+ continue
41
+
42
+ kpts = r.keypoints.data[0].cpu().numpy()
43
+ points = []
44
+ for kp in kpts:
45
+ x, y, conf = kp
46
+ if conf > 0.3:
47
+ points.append((float(x), float(y)))
48
+ else:
49
+ points.append(None)
50
+
51
+ frames_data.append({"frame": int(i), "keypoints": points})
52
+
53
+ return self._make_serializable(self.analyze_gait(frames_data))
54
+
55
+ def _make_serializable(self, obj):
56
+ """Recursively convert numpy data types to native python types for JSON serialization."""
57
+ if isinstance(obj, dict):
58
+ return {k: self._make_serializable(v) for k, v in obj.items()}
59
+ elif isinstance(obj, list):
60
+ return [self._make_serializable(v) for v in obj]
61
+ elif isinstance(obj, tuple):
62
+ return tuple(self._make_serializable(v) for v in obj)
63
+ elif isinstance(obj, (np.integer, int)):
64
+ return int(obj)
65
+ elif isinstance(obj, (np.floating, float)):
66
+ return float(obj)
67
+ elif isinstance(obj, np.ndarray):
68
+ return self._make_serializable(obj.tolist())
69
+ else:
70
+ return obj
71
+
72
+ def calculate_angle(self, p1, p2, p3):
73
+ """Calculate angle between three points."""
74
+ if p1 is None or p2 is None or p3 is None:
75
+ return None
76
+
77
+ a = np.array(p1)
78
+ b = np.array(p2)
79
+ c = np.array(p3)
80
+
81
+ ba = a - b
82
+ bc = c - b
83
+
84
+ norm_ba = np.linalg.norm(ba)
85
+ norm_bc = np.linalg.norm(bc)
86
+
87
+ if norm_ba == 0 or norm_bc == 0:
88
+ return None
89
+
90
+ cosine_angle = np.dot(ba, bc) / (norm_ba * norm_bc)
91
+ angle = np.arccos(np.clip(cosine_angle, -1.0, 1.0))
92
+
93
+ return np.degrees(angle)
94
+
95
+ def calculate_weight_distribution(self, left_ankle, right_ankle, left_hip, right_hip):
96
+ """Calculate weight distribution based on hip and ankle positions."""
97
+ if not all([left_ankle, right_ankle, left_hip, right_hip]):
98
+ return None, None, None
99
+
100
+ left_hip_y = left_hip[1]
101
+ right_hip_y = right_hip[1]
102
+
103
+ # In image coordinates, Y increases downward.
104
+ # A higher hip (lower Y value) = stance/weight-bearing side.
105
+ # So if left_hip_y < right_hip_y, the LEFT hip is higher → weight on LEFT.
106
+ weight_on_left = (left_hip_y - right_hip_y)
107
+ total_weight = abs(weight_on_left) + 1
108
+ weight_ratio = (weight_on_left + total_weight) / (2 * total_weight)
109
+ weight_ratio = np.clip(weight_ratio, 0, 1)
110
+
111
+ imbalance_percent = abs(weight_ratio - 0.5) * 200
112
+
113
+ if imbalance_percent < 10:
114
+ primary_side = "balanced"
115
+ elif weight_ratio > 0.5:
116
+ primary_side = "left"
117
+ else:
118
+ primary_side = "right"
119
+
120
+ return weight_ratio, primary_side, imbalance_percent
121
+
122
+ def detect_hip_drop(self, left_hip, right_hip):
123
+ """Detect Trendelenburg gait (hip drop on swing side)."""
124
+ if not left_hip or not right_hip:
125
+ return None, None
126
+
127
+ hip_height_diff = abs(left_hip[1] - right_hip[1])
128
+
129
+ if hip_height_diff > self.HIP_DROP_THRESHOLD:
130
+ if left_hip[1] > right_hip[1]:
131
+ return "left_hip_drop", "right_side_weakness"
132
+ else:
133
+ return "right_hip_drop", "left_side_weakness"
134
+
135
+ return None, None
136
+
137
+ def assess_postural_strength(self, frames_data):
138
+ """Assess postural strength."""
139
+ trunk_sway = []
140
+ shoulder_heights = []
141
+ head_forward_lean = []
142
+ knee_stability = []
143
+
144
+ for frame_data in frames_data:
145
+ kps = frame_data.get("keypoints", [])
146
+ if not kps or len(kps) < 17:
147
+ continue
148
+
149
+ if kps[5] and kps[6]:
150
+ shoulder_center = (kps[5][0] + kps[6][0]) / 2
151
+ trunk_sway.append(shoulder_center)
152
+ shoulder_diff = abs(kps[5][1] - kps[6][1])
153
+ shoulder_heights.append(shoulder_diff)
154
+
155
+ if kps[0] and kps[5] and kps[6]:
156
+ nose_to_shoulder = kps[0][0] - (kps[5][0] + kps[6][0]) / 2
157
+ head_forward_lean.append(nose_to_shoulder)
158
+
159
+ if "angles" in frame_data:
160
+ lk = frame_data["angles"].get("left_knee")
161
+ rk = frame_data["angles"].get("right_knee")
162
+ if lk and rk:
163
+ if lk < 120 and rk < 120:
164
+ knee_stability.append("weak")
165
+ elif lk > 160 and rk > 160:
166
+ knee_stability.append("stiff")
167
+ else:
168
+ knee_stability.append("normal")
169
+
170
+ return {
171
+ "trunk_sway": np.std(trunk_sway) if trunk_sway else 0,
172
+ "shoulder_hiking": np.mean(shoulder_heights) if shoulder_heights else 0,
173
+ "forward_lean": np.mean(np.abs(head_forward_lean)) if head_forward_lean else 0,
174
+ "knee_stability": knee_stability
175
+ }
176
+
177
+ def analyze_gait(self, frames_data):
178
+ """Complete gait analysis with all features."""
179
+
180
+ # Validation for visibility and person detection
181
+ total_frames = len(frames_data)
182
+
183
+ # Check minimum video length — need at least ~5 seconds (150 frames at 30fps)
184
+ # for enough gait cycles to compute meaningful metrics.
185
+ if total_frames < 150:
186
+ raise ValueError("VIDEO_TOO_SHORT: Video is too short for meaningful analysis. Please provide a longer video.")
187
+
188
+ valid_frames = 0
189
+ for frame_data in frames_data:
190
+ kps = frame_data.get("keypoints", [])
191
+ # A valid frame has enough confident keypoints (at least 8 keypoints)
192
+ if kps and len([kp for kp in kps if kp is not None]) >= 8:
193
+ valid_frames += 1
194
+
195
+ if valid_frames == 0:
196
+ raise ValueError("NO_PERSON_DETECTED: No person could be detected in the video.")
197
+
198
+ visibility_ratio = valid_frames / total_frames
199
+ if visibility_ratio < 0.3:
200
+ raise ValueError("POOR_VISIBILITY: Unable to detect clear movement in most frames. This may be due to bad video quality, loose clothing, bad recording angle, or the subject not being clearly visible.")
201
+
202
+ analysis_results = {
203
+ "frames": [],
204
+ "summary": {},
205
+ "weight_distribution": {
206
+ "overall_imbalance_percent": 0,
207
+ "primary_weight_side": "unknown",
208
+ "weight_shift_issues": [],
209
+ "hip_drop_analysis": []
210
+ },
211
+ "postural_strength": {
212
+ "trunk_sway_severity": "normal",
213
+ "shoulder_stability": "normal",
214
+ "forward_lean": "normal",
215
+ "knee_stability": "normal",
216
+ "overall_strength_assessment": "adequate"
217
+ },
218
+ "gait_phases": {
219
+ "stance_time_l": 0,
220
+ "stance_time_r": 0,
221
+ "swing_time_l": 0,
222
+ "swing_time_r": 0,
223
+ "double_support_frames": 0,
224
+ "phase_asymmetry": 0
225
+ },
226
+ "risk_indicators": [],
227
+ "flags": {},
228
+ "observations": {
229
+ "trunk": "Neutral",
230
+ "head_neck": "Neutral & mobile",
231
+ "arms_hands": "Free arm swing",
232
+ "lower_limbs": "Not observed",
233
+ "symmetry": "Symmetrical",
234
+ "weight_distribution": "Equal",
235
+ "postural_control": "Good"
236
+ },
237
+ "compensatory_strategies": [],
238
+ "reflex_influence": "None observed",
239
+ "safety_note": "",
240
+ "clinical_notes": []
241
+ }
242
+
243
+ left_knee_angles = []
244
+ right_knee_angles = []
245
+ left_hip_angles = []
246
+ right_hip_angles = []
247
+ left_ankle_heights = []
248
+ right_ankle_heights = []
249
+ weight_distributions = []
250
+ hip_drops = []
251
+ trunk_rotations = []
252
+ arm_heights = []
253
+ nose_positions = []
254
+
255
+ # First Pass: Calculate angles, weights, and collect gait phase data
256
+ for frame_data in frames_data:
257
+ kps = frame_data["keypoints"]
258
+ if not kps or len(kps) < 17:
259
+ analysis_results["frames"].append(frame_data)
260
+ continue
261
+
262
+ # Angles
263
+ l_knee = self.calculate_angle(kps[11], kps[13], kps[15])
264
+ r_knee = self.calculate_angle(kps[12], kps[14], kps[16])
265
+ l_hip = self.calculate_angle(kps[5], kps[11], kps[13])
266
+ r_hip = self.calculate_angle(kps[6], kps[12], kps[14])
267
+
268
+ frame_data["angles"] = {
269
+ "left_knee": l_knee,
270
+ "right_knee": r_knee,
271
+ "left_hip": l_hip,
272
+ "right_hip": r_hip
273
+ }
274
+
275
+ if l_knee: left_knee_angles.append(l_knee)
276
+ if r_knee: right_knee_angles.append(r_knee)
277
+ if l_hip: left_hip_angles.append(l_hip)
278
+ if r_hip: right_hip_angles.append(r_hip)
279
+
280
+ # Weight Distribution Analysis
281
+ weight_ratio, primary_side, imbalance = self.calculate_weight_distribution(
282
+ kps[15], kps[16], kps[11], kps[12]
283
+ )
284
+
285
+ if weight_ratio is not None:
286
+ weight_distributions.append({
287
+ "ratio": weight_ratio,
288
+ "primary_side": primary_side,
289
+ "imbalance_percent": imbalance
290
+ })
291
+ frame_data["weight_distribution"] = {
292
+ "ratio": weight_ratio,
293
+ "primary_side": primary_side,
294
+ "imbalance_percent": imbalance
295
+ }
296
+
297
+ # Hip Drop Detection
298
+ hip_drop, weakness = self.detect_hip_drop(kps[11], kps[12])
299
+ if hip_drop:
300
+ hip_drops.append({"type": hip_drop, "weakness": weakness})
301
+
302
+ # Trunk rotation
303
+ if kps[5] and kps[6]:
304
+ trunk_rotations.append(abs(kps[5][0] - kps[6][0]))
305
+
306
+ # Arm height for guarding
307
+ if kps[5] and kps[9]:
308
+ arm_heights.append(kps[5][1] - kps[9][1])
309
+
310
+ # Ankle heights for gait phase detection
311
+ if kps[15]:
312
+ left_ankle_heights.append(kps[15][1])
313
+ else:
314
+ left_ankle_heights.append(None)
315
+
316
+ if kps[16]:
317
+ right_ankle_heights.append(kps[16][1])
318
+ else:
319
+ right_ankle_heights.append(None)
320
+
321
+ # Nose position for head stability
322
+ if kps[0]:
323
+ nose_positions.append(kps[0][1])
324
+
325
+ analysis_results["frames"].append(frame_data)
326
+
327
+ # Gait Phase Detection
328
+ def detect_phases(heights):
329
+ phases = []
330
+ if not heights: return phases
331
+ valid_heights = [h for h in heights if h is not None]
332
+ if not valid_heights: return [None] * len(heights)
333
+ ground_threshold = np.percentile(valid_heights, 80)
334
+ for h in heights:
335
+ if h is None: phases.append(None)
336
+ elif h >= ground_threshold: phases.append("stance")
337
+ else: phases.append("swing")
338
+ return phases
339
+
340
+ l_phases = detect_phases(left_ankle_heights)
341
+ r_phases = detect_phases(right_ankle_heights)
342
+
343
+ stance_time_l = l_phases.count("stance")
344
+ swing_time_l = l_phases.count("swing")
345
+ stance_time_r = r_phases.count("stance")
346
+ swing_time_r = r_phases.count("swing")
347
+
348
+ double_support_frames = 0
349
+ for lp, rp in zip(l_phases, r_phases):
350
+ if lp == "stance" and rp == "stance":
351
+ double_support_frames += 1
352
+
353
+ phase_asymmetry = 0
354
+ if max(stance_time_l, stance_time_r) > 0:
355
+ phase_asymmetry = (abs(stance_time_l - stance_time_r) / max(stance_time_l, stance_time_r)) * 100
356
+
357
+ analysis_results["gait_phases"] = {
358
+ "stance_time_l": stance_time_l,
359
+ "stance_time_r": stance_time_r,
360
+ "swing_time_l": swing_time_l,
361
+ "swing_time_r": swing_time_r,
362
+ "double_support_frames": double_support_frames,
363
+ "phase_asymmetry": phase_asymmetry
364
+ }
365
+
366
+ # Postural Strength Assessment
367
+ strength_assessment = self.assess_postural_strength(frames_data)
368
+
369
+ # Analyze Weight Distribution
370
+ overall_imbalance = 0
371
+ primary_side = "balanced"
372
+
373
+ if weight_distributions:
374
+ imbalances = [w["imbalance_percent"] for w in weight_distributions]
375
+ overall_imbalance = np.mean(imbalances)
376
+ primary_sides = [w["primary_side"] for w in weight_distributions]
377
+ primary_side = max(set([s for s in primary_sides if s != "balanced"]),
378
+ key=primary_sides.count, default="balanced")
379
+
380
+ analysis_results["weight_distribution"]["overall_imbalance_percent"] = overall_imbalance
381
+ analysis_results["weight_distribution"]["primary_weight_side"] = primary_side
382
+
383
+ if overall_imbalance > self.WEIGHT_SHIFT_THRESHOLD * 100:
384
+ analysis_results["weight_distribution"]["weight_shift_issues"].append(
385
+ f"Significant weight shift ({overall_imbalance:.1f}% imbalance) - "
386
+ f"predominantly loading {primary_side} side"
387
+ )
388
+ analysis_results["observations"]["weight_distribution"] = f"Asymmetrical - favoring {primary_side}"
389
+ analysis_results["clinical_notes"].append(
390
+ f"WEIGHT DISTRIBUTION: Child shows {overall_imbalance:.1f}% weight shift toward {primary_side} side. "
391
+ f"Suggests pain avoidance, weakness, or motor planning issue on opposite side."
392
+ )
393
+
394
+ if hip_drops:
395
+ for drop in hip_drops:
396
+ analysis_results["weight_distribution"]["hip_drop_analysis"].append(drop)
397
+ analysis_results["clinical_notes"].append(
398
+ f"HIP DROP: {drop['type']} indicates {drop['weakness']} - "
399
+ f"weakness in hip abductors or gluteus medius"
400
+ )
401
+
402
+ # Postural Strength Scoring
403
+ trunk_sway = strength_assessment["trunk_sway"]
404
+ if trunk_sway > self.POSTURE_SWAY_THRESHOLD:
405
+ analysis_results["postural_strength"]["trunk_sway_severity"] = "excessive"
406
+ analysis_results["observations"]["postural_control"] = "Weak - excessive sway"
407
+ analysis_results["clinical_notes"].append(
408
+ f"TRUNK SWAY: Significant lateral sway (std: {trunk_sway:.1f}px). "
409
+ f"Suggests weak core stability."
410
+ )
411
+
412
+ shoulder_hike = strength_assessment["shoulder_hiking"]
413
+ if shoulder_hike > 5:
414
+ analysis_results["postural_strength"]["shoulder_stability"] = "hiking"
415
+ analysis_results["compensatory_strategies"].append("Shoulder hiking for stability")
416
+ analysis_results["clinical_notes"].append(
417
+ f"SHOULDER HIKING: Upper trapezius compensation for core/trunk weakness."
418
+ )
419
+
420
+ forward_lean = strength_assessment["forward_lean"]
421
+ if forward_lean > 15:
422
+ analysis_results["postural_strength"]["forward_lean"] = "excessive"
423
+ analysis_results["clinical_notes"].append(
424
+ f"FORWARD HEAD POSTURE: Head forward {forward_lean:.1f}px. "
425
+ f"Weak neck/shoulder stabilizers or balance compensation."
426
+ )
427
+
428
+ knee_issues = strength_assessment["knee_stability"]
429
+ if knee_issues.count("weak") > len(knee_issues) * 0.5:
430
+ analysis_results["postural_strength"]["knee_stability"] = "weak"
431
+ analysis_results["clinical_notes"].append(
432
+ f"KNEE WEAKNESS: Insufficient extension/flexion control. "
433
+ f"Quad weakness or motor planning issues."
434
+ )
435
+
436
+ weakness_indicators = sum([
437
+ trunk_sway > self.POSTURE_SWAY_THRESHOLD,
438
+ shoulder_hike > 5,
439
+ forward_lean > 15,
440
+ knee_issues.count("weak") > len(knee_issues) * 0.5
441
+ ])
442
+
443
+ if weakness_indicators >= 2:
444
+ analysis_results["postural_strength"]["overall_strength_assessment"] = "inadequate"
445
+ analysis_results["observations"]["postural_control"] = "Floppy/weak - multiple issues"
446
+ analysis_results["clinical_notes"].append(
447
+ f"POSTURAL ASSESSMENT: Low muscle tone/weakness ({weakness_indicators} issues detected). "
448
+ f"Recommend strength assessment."
449
+ )
450
+
451
+ # Original Observations Logic
452
+ # Trunk
453
+ if trunk_rotations:
454
+ sway = np.std(trunk_rotations)
455
+ if sway > 15:
456
+ analysis_results["observations"]["trunk"] = "Stiff / reduced rotation"
457
+ analysis_results["compensatory_strategies"].append("Trunk used for balance")
458
+ elif sway > 10:
459
+ analysis_results["observations"]["trunk"] = "Rotated"
460
+ analysis_results["compensatory_strategies"].append("Trunk rotation")
461
+
462
+ # Head & Neck (vertical stability)
463
+ if nose_positions:
464
+ neck_stiffness = np.std(nose_positions)
465
+ if neck_stiffness < 2:
466
+ analysis_results["observations"]["head_neck"] = "Fixed / stiff"
467
+ analysis_results["compensatory_strategies"].append("Neck stiffness for stability")
468
+
469
+ # Arms
470
+ if arm_heights:
471
+ avg_arm_h = np.mean(arm_heights)
472
+ if avg_arm_h < -10:
473
+ analysis_results["observations"]["arms_hands"] = "Hands guarded"
474
+ analysis_results["compensatory_strategies"].append("Hands used for security")
475
+
476
+ # Reflex Influence Detection
477
+ signs = []
478
+ if analysis_results["observations"]["trunk"] == "Rotated":
479
+ signs.append("ATNR influence")
480
+ if "Hands guarded" in analysis_results["observations"]["arms_hands"]:
481
+ signs.append("Palmar influence")
482
+ if "Neck stiffness" in str(analysis_results["compensatory_strategies"]):
483
+ signs.append("Fear Paralysis influence")
484
+
485
+ if len(signs) >= 2:
486
+ analysis_results["reflex_influence"] = "Movement patterns suggest immature postural and protective responses consistent with incomplete primitive reflex integration."
487
+ analysis_results["observations"]["symmetry"] = "Mild asymmetry"
488
+ else:
489
+ analysis_results["reflex_influence"] = ""
490
+
491
+ # Numerical Safety Checks
492
+ num_steps = stance_time_l // 10
493
+ is_safe = self.independent_walking and num_steps >= 5 and len(frames_data) > 90
494
+
495
+ if not is_safe:
496
+ analysis_results["safety_note"] = "Numerical interpretation limited due to context."
497
+
498
+ # Stability: measures trunk steadiness only. Weight imbalance has its own score.
499
+ # Cap the trunk sway penalty at 50 points so the score stays meaningful.
500
+ trunk_sway_penalty = min(50, np.std(trunk_rotations) * 2 if trunk_rotations else 0)
501
+ stability_score = max(0, 100 - trunk_sway_penalty)
502
+
503
+ # Symmetry: based on gait phase asymmetry. Each 1% asymmetry = 1 point deducted.
504
+ sym_score = max(0, 100 - phase_asymmetry)
505
+
506
+ # Strength: each weakness indicator costs 25 points
507
+ strength_score = max(0, 100 - (weakness_indicators * 25))
508
+
509
+ # Weight shift: overall_imbalance is already 0-100% so maps directly
510
+ weight_shift_score = max(0, 100 - overall_imbalance) if weight_distributions else 100
511
+
512
+ analysis_results["summary"] = {
513
+ "stability_score": stability_score,
514
+ "symmetry_score": sym_score,
515
+ "weight_shift_score": weight_shift_score,
516
+ "strength_score": strength_score,
517
+ "phase_asymmetry_percent": phase_asymmetry,
518
+ "overall_gait_quality": "concerning" if weakness_indicators >= 2 else "acceptable",
519
+ "walking_condition": "Independent" if self.independent_walking else "Assisted"
520
+ }
521
+
522
+ return analysis_results
523
+
524
+
525
+ if __name__ == "__main__":
526
+ analyzer = EnhancedGaitAnalyzer(age=10)
527
+ print("Enhanced Gait Analyzer initialized with all features.")
generate_report.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from gait_analysis import EnhancedGaitAnalyzer
2
+ import os
3
+ from datetime import datetime
4
+
5
+ def generate_markdown_report(results, video_path, output_path):
6
+ """
7
+ Generate a strictly structured gait analysis report.
8
+ """
9
+ summary = results.get("summary", {})
10
+ observations = results.get("observations", {})
11
+ strategies = results.get("compensatory_strategies", [])
12
+ reflex_influence = results.get("reflex_influence", "None observed")
13
+ safety_note = results.get("safety_note", "")
14
+ weight_dist = results.get("weight_distribution", {})
15
+ postural_strength = results.get("postural_strength", {})
16
+ clinical_notes = results.get("clinical_notes", [])
17
+
18
+ # 1. Walking Condition
19
+ walking_condition = summary.get("walking_condition", "Not specified")
20
+
21
+ # 2. Observed Movement Patterns
22
+ patterns = f"""### Observed Movement Patterns
23
+
24
+ - **Trunk:** {observations.get('trunk', 'N/A')}
25
+ - **Head & Neck:** {observations.get('head_neck', 'N/A')}
26
+ - **Arms & Hands:** {observations.get('arms_hands', 'N/A')}
27
+ - **Lower Limbs:** {observations.get('lower_limbs', 'N/A')}
28
+ - **Symmetry:** {observations.get('symmetry', 'N/A')}
29
+ - **Weight Distribution:** {observations.get('weight_distribution', 'N/A')}
30
+ - **Postural Control:** {observations.get('postural_control', 'N/A')}"""
31
+
32
+ # 3. Compensatory Strategies
33
+ comp_strategies = "None identified"
34
+ if strategies:
35
+ comp_strategies = "\n".join([f"- {s}" for s in strategies])
36
+
37
+ # 4. Clinical Notes
38
+ clinical_notes_text = "None noted"
39
+ if clinical_notes:
40
+ clinical_notes_text = "\n".join([f"- {n}" for n in clinical_notes])
41
+
42
+ # 5. Integrated Movement Summary
43
+ # Combine stats into a summary
44
+ asymmetry = summary.get("phase_asymmetry_percent", 0)
45
+ stability = summary.get("stability_score", 0)
46
+ strength = summary.get("strength_score", 0)
47
+ weight_shift = summary.get("weight_shift_score", 0)
48
+ gait_quality = summary.get("overall_gait_quality", "unknown")
49
+
50
+ integrated_summary = f"""### Integrated Movement Summary
51
+
52
+ The subject demonstrates a **{walking_condition.lower()}** walking pattern with a stability score of **{stability:.0f}/100**, strength score of **{strength:.0f}/100**, and weight shift score of **{weight_shift:.0f}/100**.
53
+ Overall gait quality appears **{gait_quality}**.
54
+ Movement symmetry shows a **{asymmetry:.1f}%** phase variance.
55
+
56
+ {reflex_influence}
57
+
58
+ {safety_note}"""
59
+
60
+ # Final Report Assembly
61
+ report = f"""# Gait Analysis Report
62
+
63
+ ## Walking Condition
64
+ **{walking_condition}**
65
+
66
+ {patterns}
67
+
68
+ ### Compensatory Strategies
69
+ {comp_strategies}
70
+
71
+ ### Clinical Notes
72
+ {clinical_notes_text}
73
+
74
+ {integrated_summary}
75
+
76
+ ---
77
+ *Generated by Gait Analysis System v3.0*
78
+ """
79
+
80
+ # Write to file
81
+ with open(output_path, 'w', encoding='utf-8') as f:
82
+ f.write(report)
83
+
84
+ print(f"✅ Strict Report generated successfully: {output_path}")
85
+ return output_path
86
+
87
+
88
+ def main():
89
+ # Path to test.mp4
90
+ video_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "test.mp4"))
91
+
92
+ print(f"Looking for video at: {video_path}")
93
+ if not os.path.exists(video_path):
94
+ print("Error: test.mp4 not found!")
95
+ return
96
+
97
+ print("Initializing EnhancedGaitAnalyzer...")
98
+ analyzer = EnhancedGaitAnalyzer(age=4)
99
+
100
+ print("Processing video... this might take a moment.")
101
+ try:
102
+ results = analyzer.process_video(video_path)
103
+
104
+ # Generate report filename with timestamp
105
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
106
+ report_filename = f"gait_analysis_report_{timestamp}.md"
107
+ report_path = os.path.join(os.path.dirname(video_path), report_filename)
108
+
109
+ # Generate markdown report
110
+ generate_markdown_report(results, video_path, report_path)
111
+
112
+ print(f"\n{'='*60}")
113
+ print(f"Report saved to: {report_path}")
114
+ print(f"{'='*60}\n")
115
+
116
+ except Exception as e:
117
+ print(f"An error occurred during processing: {e}")
118
+ import traceback
119
+ traceback.print_exc()
120
+
121
+
122
+ if __name__ == "__main__":
123
+ main()
main.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, UploadFile, File, HTTPException
2
+ from fastapi.middleware.cors import CORSMiddleware
3
+ import shutil
4
+ import os
5
+ import uuid
6
+ from gait_analysis import EnhancedGaitAnalyzer
7
+ import uvicorn
8
+
9
+ app = FastAPI()
10
+
11
+ # Allow CORS for frontend
12
+ app.add_middleware(
13
+ CORSMiddleware,
14
+ allow_origins=["*"],
15
+ allow_credentials=True,
16
+ allow_methods=["*"],
17
+ allow_headers=["*"],
18
+ )
19
+
20
+ # Initialize Analyzer
21
+ analyzer = EnhancedGaitAnalyzer()
22
+
23
+ UPLOAD_DIR = "uploads"
24
+ if not os.path.exists(UPLOAD_DIR):
25
+ os.makedirs(UPLOAD_DIR)
26
+
27
+ @app.get("/")
28
+ def read_root():
29
+ return {"message": "Gait Analysis API with OpenPose is running"}
30
+
31
+ @app.post("/analyze")
32
+ async def analyze_video(file: UploadFile = File(...)):
33
+ try:
34
+ # Save uploaded file
35
+ file_ext = file.filename.split(".")[-1]
36
+ filename = f"{uuid.uuid4()}.{file_ext}"
37
+ file_path = os.path.join(UPLOAD_DIR, filename)
38
+
39
+ with open(file_path, "wb") as buffer:
40
+ shutil.copyfileobj(file.file, buffer)
41
+
42
+ # Process video
43
+ results = analyzer.process_video(file_path)
44
+
45
+ # Cleanup
46
+ # os.remove(file_path) # Keep for debugging for now or serve back
47
+
48
+ return {"filename": filename, "results": results}
49
+
50
+ except Exception as e:
51
+ raise HTTPException(status_code=500, detail=str(e))
52
+
53
+ if __name__ == "__main__":
54
+ uvicorn.run(app, host="0.0.0.0", port=8000)
models/pose_deploy.prototxt ADDED
@@ -0,0 +1,2332 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: "OpenPose - BODY_25"
2
+ input: "image"
3
+ input_dim: 1 # This value will be defined at runtime
4
+ input_dim: 3
5
+ input_dim: 16 # This value will be defined at runtime
6
+ input_dim: 16 # This value will be defined at runtime
7
+ layer {
8
+ name: "conv1_1"
9
+ type: "Convolution"
10
+ bottom: "image"
11
+ top: "conv1_1"
12
+ convolution_param {
13
+ num_output: 64
14
+ pad: 1
15
+ kernel_size: 3
16
+ }
17
+ }
18
+ layer {
19
+ name: "relu1_1"
20
+ type: "ReLU"
21
+ bottom: "conv1_1"
22
+ top: "conv1_1"
23
+ }
24
+ layer {
25
+ name: "conv1_2"
26
+ type: "Convolution"
27
+ bottom: "conv1_1"
28
+ top: "conv1_2"
29
+ convolution_param {
30
+ num_output: 64
31
+ pad: 1
32
+ kernel_size: 3
33
+ }
34
+ }
35
+ layer {
36
+ name: "relu1_2"
37
+ type: "ReLU"
38
+ bottom: "conv1_2"
39
+ top: "conv1_2"
40
+ }
41
+ layer {
42
+ name: "pool1_stage1"
43
+ type: "Pooling"
44
+ bottom: "conv1_2"
45
+ top: "pool1_stage1"
46
+ pooling_param {
47
+ pool: MAX
48
+ kernel_size: 2
49
+ stride: 2
50
+ }
51
+ }
52
+ layer {
53
+ name: "conv2_1"
54
+ type: "Convolution"
55
+ bottom: "pool1_stage1"
56
+ top: "conv2_1"
57
+ convolution_param {
58
+ num_output: 128
59
+ pad: 1
60
+ kernel_size: 3
61
+ }
62
+ }
63
+ layer {
64
+ name: "relu2_1"
65
+ type: "ReLU"
66
+ bottom: "conv2_1"
67
+ top: "conv2_1"
68
+ }
69
+ layer {
70
+ name: "conv2_2"
71
+ type: "Convolution"
72
+ bottom: "conv2_1"
73
+ top: "conv2_2"
74
+ convolution_param {
75
+ num_output: 128
76
+ pad: 1
77
+ kernel_size: 3
78
+ }
79
+ }
80
+ layer {
81
+ name: "relu2_2"
82
+ type: "ReLU"
83
+ bottom: "conv2_2"
84
+ top: "conv2_2"
85
+ }
86
+ layer {
87
+ name: "pool2_stage1"
88
+ type: "Pooling"
89
+ bottom: "conv2_2"
90
+ top: "pool2_stage1"
91
+ pooling_param {
92
+ pool: MAX
93
+ kernel_size: 2
94
+ stride: 2
95
+ }
96
+ }
97
+ layer {
98
+ name: "conv3_1"
99
+ type: "Convolution"
100
+ bottom: "pool2_stage1"
101
+ top: "conv3_1"
102
+ convolution_param {
103
+ num_output: 256
104
+ pad: 1
105
+ kernel_size: 3
106
+ }
107
+ }
108
+ layer {
109
+ name: "relu3_1"
110
+ type: "ReLU"
111
+ bottom: "conv3_1"
112
+ top: "conv3_1"
113
+ }
114
+ layer {
115
+ name: "conv3_2"
116
+ type: "Convolution"
117
+ bottom: "conv3_1"
118
+ top: "conv3_2"
119
+ convolution_param {
120
+ num_output: 256
121
+ pad: 1
122
+ kernel_size: 3
123
+ }
124
+ }
125
+ layer {
126
+ name: "relu3_2"
127
+ type: "ReLU"
128
+ bottom: "conv3_2"
129
+ top: "conv3_2"
130
+ }
131
+ layer {
132
+ name: "conv3_3"
133
+ type: "Convolution"
134
+ bottom: "conv3_2"
135
+ top: "conv3_3"
136
+ convolution_param {
137
+ num_output: 256
138
+ pad: 1
139
+ kernel_size: 3
140
+ }
141
+ }
142
+ layer {
143
+ name: "relu3_3"
144
+ type: "ReLU"
145
+ bottom: "conv3_3"
146
+ top: "conv3_3"
147
+ }
148
+ layer {
149
+ name: "conv3_4"
150
+ type: "Convolution"
151
+ bottom: "conv3_3"
152
+ top: "conv3_4"
153
+ convolution_param {
154
+ num_output: 256
155
+ pad: 1
156
+ kernel_size: 3
157
+ }
158
+ }
159
+ layer {
160
+ name: "relu3_4"
161
+ type: "ReLU"
162
+ bottom: "conv3_4"
163
+ top: "conv3_4"
164
+ }
165
+ layer {
166
+ name: "pool3_stage1"
167
+ type: "Pooling"
168
+ bottom: "conv3_4"
169
+ top: "pool3_stage1"
170
+ pooling_param {
171
+ pool: MAX
172
+ kernel_size: 2
173
+ stride: 2
174
+ }
175
+ }
176
+ layer {
177
+ name: "conv4_1"
178
+ type: "Convolution"
179
+ bottom: "pool3_stage1"
180
+ top: "conv4_1"
181
+ convolution_param {
182
+ num_output: 512
183
+ pad: 1
184
+ kernel_size: 3
185
+ }
186
+ }
187
+ layer {
188
+ name: "relu4_1"
189
+ type: "ReLU"
190
+ bottom: "conv4_1"
191
+ top: "conv4_1"
192
+ }
193
+ layer {
194
+ name: "conv4_2"
195
+ type: "Convolution"
196
+ bottom: "conv4_1"
197
+ top: "conv4_2"
198
+ convolution_param {
199
+ num_output: 512
200
+ pad: 1
201
+ kernel_size: 3
202
+ }
203
+ }
204
+ layer {
205
+ name: "prelu4_2"
206
+ type: "PReLU"
207
+ bottom: "conv4_2"
208
+ top: "conv4_2"
209
+ }
210
+ layer {
211
+ name: "conv4_3_CPM"
212
+ type: "Convolution"
213
+ bottom: "conv4_2"
214
+ top: "conv4_3_CPM"
215
+ convolution_param {
216
+ num_output: 256
217
+ pad: 1
218
+ kernel_size: 3
219
+ }
220
+ }
221
+ layer {
222
+ name: "prelu4_3_CPM"
223
+ type: "PReLU"
224
+ bottom: "conv4_3_CPM"
225
+ top: "conv4_3_CPM"
226
+ }
227
+ layer {
228
+ name: "conv4_4_CPM"
229
+ type: "Convolution"
230
+ bottom: "conv4_3_CPM"
231
+ top: "conv4_4_CPM"
232
+ convolution_param {
233
+ num_output: 128
234
+ pad: 1
235
+ kernel_size: 3
236
+ }
237
+ }
238
+ layer {
239
+ name: "prelu4_4_CPM"
240
+ type: "PReLU"
241
+ bottom: "conv4_4_CPM"
242
+ top: "conv4_4_CPM"
243
+ }
244
+ layer {
245
+ name: "Mconv1_stage0_L2_0"
246
+ type: "Convolution"
247
+ bottom: "conv4_4_CPM"
248
+ top: "Mconv1_stage0_L2_0"
249
+ convolution_param {
250
+ num_output: 96
251
+ pad: 1
252
+ kernel_size: 3
253
+ }
254
+ }
255
+ layer {
256
+ name: "Mprelu1_stage0_L2_0"
257
+ type: "PReLU"
258
+ bottom: "Mconv1_stage0_L2_0"
259
+ top: "Mconv1_stage0_L2_0"
260
+ }
261
+ layer {
262
+ name: "Mconv1_stage0_L2_1"
263
+ type: "Convolution"
264
+ bottom: "Mconv1_stage0_L2_0"
265
+ top: "Mconv1_stage0_L2_1"
266
+ convolution_param {
267
+ num_output: 96
268
+ pad: 1
269
+ kernel_size: 3
270
+ }
271
+ }
272
+ layer {
273
+ name: "Mprelu1_stage0_L2_1"
274
+ type: "PReLU"
275
+ bottom: "Mconv1_stage0_L2_1"
276
+ top: "Mconv1_stage0_L2_1"
277
+ }
278
+ layer {
279
+ name: "Mconv1_stage0_L2_2"
280
+ type: "Convolution"
281
+ bottom: "Mconv1_stage0_L2_1"
282
+ top: "Mconv1_stage0_L2_2"
283
+ convolution_param {
284
+ num_output: 96
285
+ pad: 1
286
+ kernel_size: 3
287
+ }
288
+ }
289
+ layer {
290
+ name: "Mprelu1_stage0_L2_2"
291
+ type: "PReLU"
292
+ bottom: "Mconv1_stage0_L2_2"
293
+ top: "Mconv1_stage0_L2_2"
294
+ }
295
+ layer {
296
+ name: "Mconv1_stage0_L2_concat"
297
+ type: "Concat"
298
+ bottom: "Mconv1_stage0_L2_0"
299
+ bottom: "Mconv1_stage0_L2_1"
300
+ bottom: "Mconv1_stage0_L2_2"
301
+ top: "Mconv1_stage0_L2_concat"
302
+ concat_param {
303
+ axis: 1
304
+ }
305
+ }
306
+ layer {
307
+ name: "Mconv2_stage0_L2_0"
308
+ type: "Convolution"
309
+ bottom: "Mconv1_stage0_L2_concat"
310
+ top: "Mconv2_stage0_L2_0"
311
+ convolution_param {
312
+ num_output: 96
313
+ pad: 1
314
+ kernel_size: 3
315
+ }
316
+ }
317
+ layer {
318
+ name: "Mprelu2_stage0_L2_0"
319
+ type: "PReLU"
320
+ bottom: "Mconv2_stage0_L2_0"
321
+ top: "Mconv2_stage0_L2_0"
322
+ }
323
+ layer {
324
+ name: "Mconv2_stage0_L2_1"
325
+ type: "Convolution"
326
+ bottom: "Mconv2_stage0_L2_0"
327
+ top: "Mconv2_stage0_L2_1"
328
+ convolution_param {
329
+ num_output: 96
330
+ pad: 1
331
+ kernel_size: 3
332
+ }
333
+ }
334
+ layer {
335
+ name: "Mprelu2_stage0_L2_1"
336
+ type: "PReLU"
337
+ bottom: "Mconv2_stage0_L2_1"
338
+ top: "Mconv2_stage0_L2_1"
339
+ }
340
+ layer {
341
+ name: "Mconv2_stage0_L2_2"
342
+ type: "Convolution"
343
+ bottom: "Mconv2_stage0_L2_1"
344
+ top: "Mconv2_stage0_L2_2"
345
+ convolution_param {
346
+ num_output: 96
347
+ pad: 1
348
+ kernel_size: 3
349
+ }
350
+ }
351
+ layer {
352
+ name: "Mprelu2_stage0_L2_2"
353
+ type: "PReLU"
354
+ bottom: "Mconv2_stage0_L2_2"
355
+ top: "Mconv2_stage0_L2_2"
356
+ }
357
+ layer {
358
+ name: "Mconv2_stage0_L2_concat"
359
+ type: "Concat"
360
+ bottom: "Mconv2_stage0_L2_0"
361
+ bottom: "Mconv2_stage0_L2_1"
362
+ bottom: "Mconv2_stage0_L2_2"
363
+ top: "Mconv2_stage0_L2_concat"
364
+ concat_param {
365
+ axis: 1
366
+ }
367
+ }
368
+ layer {
369
+ name: "Mconv3_stage0_L2_0"
370
+ type: "Convolution"
371
+ bottom: "Mconv2_stage0_L2_concat"
372
+ top: "Mconv3_stage0_L2_0"
373
+ convolution_param {
374
+ num_output: 96
375
+ pad: 1
376
+ kernel_size: 3
377
+ }
378
+ }
379
+ layer {
380
+ name: "Mprelu3_stage0_L2_0"
381
+ type: "PReLU"
382
+ bottom: "Mconv3_stage0_L2_0"
383
+ top: "Mconv3_stage0_L2_0"
384
+ }
385
+ layer {
386
+ name: "Mconv3_stage0_L2_1"
387
+ type: "Convolution"
388
+ bottom: "Mconv3_stage0_L2_0"
389
+ top: "Mconv3_stage0_L2_1"
390
+ convolution_param {
391
+ num_output: 96
392
+ pad: 1
393
+ kernel_size: 3
394
+ }
395
+ }
396
+ layer {
397
+ name: "Mprelu3_stage0_L2_1"
398
+ type: "PReLU"
399
+ bottom: "Mconv3_stage0_L2_1"
400
+ top: "Mconv3_stage0_L2_1"
401
+ }
402
+ layer {
403
+ name: "Mconv3_stage0_L2_2"
404
+ type: "Convolution"
405
+ bottom: "Mconv3_stage0_L2_1"
406
+ top: "Mconv3_stage0_L2_2"
407
+ convolution_param {
408
+ num_output: 96
409
+ pad: 1
410
+ kernel_size: 3
411
+ }
412
+ }
413
+ layer {
414
+ name: "Mprelu3_stage0_L2_2"
415
+ type: "PReLU"
416
+ bottom: "Mconv3_stage0_L2_2"
417
+ top: "Mconv3_stage0_L2_2"
418
+ }
419
+ layer {
420
+ name: "Mconv3_stage0_L2_concat"
421
+ type: "Concat"
422
+ bottom: "Mconv3_stage0_L2_0"
423
+ bottom: "Mconv3_stage0_L2_1"
424
+ bottom: "Mconv3_stage0_L2_2"
425
+ top: "Mconv3_stage0_L2_concat"
426
+ concat_param {
427
+ axis: 1
428
+ }
429
+ }
430
+ layer {
431
+ name: "Mconv4_stage0_L2_0"
432
+ type: "Convolution"
433
+ bottom: "Mconv3_stage0_L2_concat"
434
+ top: "Mconv4_stage0_L2_0"
435
+ convolution_param {
436
+ num_output: 96
437
+ pad: 1
438
+ kernel_size: 3
439
+ }
440
+ }
441
+ layer {
442
+ name: "Mprelu4_stage0_L2_0"
443
+ type: "PReLU"
444
+ bottom: "Mconv4_stage0_L2_0"
445
+ top: "Mconv4_stage0_L2_0"
446
+ }
447
+ layer {
448
+ name: "Mconv4_stage0_L2_1"
449
+ type: "Convolution"
450
+ bottom: "Mconv4_stage0_L2_0"
451
+ top: "Mconv4_stage0_L2_1"
452
+ convolution_param {
453
+ num_output: 96
454
+ pad: 1
455
+ kernel_size: 3
456
+ }
457
+ }
458
+ layer {
459
+ name: "Mprelu4_stage0_L2_1"
460
+ type: "PReLU"
461
+ bottom: "Mconv4_stage0_L2_1"
462
+ top: "Mconv4_stage0_L2_1"
463
+ }
464
+ layer {
465
+ name: "Mconv4_stage0_L2_2"
466
+ type: "Convolution"
467
+ bottom: "Mconv4_stage0_L2_1"
468
+ top: "Mconv4_stage0_L2_2"
469
+ convolution_param {
470
+ num_output: 96
471
+ pad: 1
472
+ kernel_size: 3
473
+ }
474
+ }
475
+ layer {
476
+ name: "Mprelu4_stage0_L2_2"
477
+ type: "PReLU"
478
+ bottom: "Mconv4_stage0_L2_2"
479
+ top: "Mconv4_stage0_L2_2"
480
+ }
481
+ layer {
482
+ name: "Mconv4_stage0_L2_concat"
483
+ type: "Concat"
484
+ bottom: "Mconv4_stage0_L2_0"
485
+ bottom: "Mconv4_stage0_L2_1"
486
+ bottom: "Mconv4_stage0_L2_2"
487
+ top: "Mconv4_stage0_L2_concat"
488
+ concat_param {
489
+ axis: 1
490
+ }
491
+ }
492
+ layer {
493
+ name: "Mconv5_stage0_L2_0"
494
+ type: "Convolution"
495
+ bottom: "Mconv4_stage0_L2_concat"
496
+ top: "Mconv5_stage0_L2_0"
497
+ convolution_param {
498
+ num_output: 96
499
+ pad: 1
500
+ kernel_size: 3
501
+ }
502
+ }
503
+ layer {
504
+ name: "Mprelu5_stage0_L2_0"
505
+ type: "PReLU"
506
+ bottom: "Mconv5_stage0_L2_0"
507
+ top: "Mconv5_stage0_L2_0"
508
+ }
509
+ layer {
510
+ name: "Mconv5_stage0_L2_1"
511
+ type: "Convolution"
512
+ bottom: "Mconv5_stage0_L2_0"
513
+ top: "Mconv5_stage0_L2_1"
514
+ convolution_param {
515
+ num_output: 96
516
+ pad: 1
517
+ kernel_size: 3
518
+ }
519
+ }
520
+ layer {
521
+ name: "Mprelu5_stage0_L2_1"
522
+ type: "PReLU"
523
+ bottom: "Mconv5_stage0_L2_1"
524
+ top: "Mconv5_stage0_L2_1"
525
+ }
526
+ layer {
527
+ name: "Mconv5_stage0_L2_2"
528
+ type: "Convolution"
529
+ bottom: "Mconv5_stage0_L2_1"
530
+ top: "Mconv5_stage0_L2_2"
531
+ convolution_param {
532
+ num_output: 96
533
+ pad: 1
534
+ kernel_size: 3
535
+ }
536
+ }
537
+ layer {
538
+ name: "Mprelu5_stage0_L2_2"
539
+ type: "PReLU"
540
+ bottom: "Mconv5_stage0_L2_2"
541
+ top: "Mconv5_stage0_L2_2"
542
+ }
543
+ layer {
544
+ name: "Mconv5_stage0_L2_concat"
545
+ type: "Concat"
546
+ bottom: "Mconv5_stage0_L2_0"
547
+ bottom: "Mconv5_stage0_L2_1"
548
+ bottom: "Mconv5_stage0_L2_2"
549
+ top: "Mconv5_stage0_L2_concat"
550
+ concat_param {
551
+ axis: 1
552
+ }
553
+ }
554
+ layer {
555
+ name: "Mconv6_stage0_L2"
556
+ type: "Convolution"
557
+ bottom: "Mconv5_stage0_L2_concat"
558
+ top: "Mconv6_stage0_L2"
559
+ convolution_param {
560
+ num_output: 256
561
+ pad: 0
562
+ kernel_size: 1
563
+ }
564
+ }
565
+ layer {
566
+ name: "Mprelu6_stage0_L2"
567
+ type: "PReLU"
568
+ bottom: "Mconv6_stage0_L2"
569
+ top: "Mconv6_stage0_L2"
570
+ }
571
+ layer {
572
+ name: "Mconv7_stage0_L2"
573
+ type: "Convolution"
574
+ bottom: "Mconv6_stage0_L2"
575
+ top: "Mconv7_stage0_L2"
576
+ convolution_param {
577
+ num_output: 52
578
+ pad: 0
579
+ kernel_size: 1
580
+ }
581
+ }
582
+ layer {
583
+ name: "concat_stage1_L2"
584
+ type: "Concat"
585
+ bottom: "conv4_4_CPM"
586
+ bottom: "Mconv7_stage0_L2"
587
+ top: "concat_stage1_L2"
588
+ concat_param {
589
+ axis: 1
590
+ }
591
+ }
592
+ layer {
593
+ name: "Mconv1_stage1_L2_0"
594
+ type: "Convolution"
595
+ bottom: "concat_stage1_L2"
596
+ top: "Mconv1_stage1_L2_0"
597
+ convolution_param {
598
+ num_output: 128
599
+ pad: 1
600
+ kernel_size: 3
601
+ }
602
+ }
603
+ layer {
604
+ name: "Mprelu1_stage1_L2_0"
605
+ type: "PReLU"
606
+ bottom: "Mconv1_stage1_L2_0"
607
+ top: "Mconv1_stage1_L2_0"
608
+ }
609
+ layer {
610
+ name: "Mconv1_stage1_L2_1"
611
+ type: "Convolution"
612
+ bottom: "Mconv1_stage1_L2_0"
613
+ top: "Mconv1_stage1_L2_1"
614
+ convolution_param {
615
+ num_output: 128
616
+ pad: 1
617
+ kernel_size: 3
618
+ }
619
+ }
620
+ layer {
621
+ name: "Mprelu1_stage1_L2_1"
622
+ type: "PReLU"
623
+ bottom: "Mconv1_stage1_L2_1"
624
+ top: "Mconv1_stage1_L2_1"
625
+ }
626
+ layer {
627
+ name: "Mconv1_stage1_L2_2"
628
+ type: "Convolution"
629
+ bottom: "Mconv1_stage1_L2_1"
630
+ top: "Mconv1_stage1_L2_2"
631
+ convolution_param {
632
+ num_output: 128
633
+ pad: 1
634
+ kernel_size: 3
635
+ }
636
+ }
637
+ layer {
638
+ name: "Mprelu1_stage1_L2_2"
639
+ type: "PReLU"
640
+ bottom: "Mconv1_stage1_L2_2"
641
+ top: "Mconv1_stage1_L2_2"
642
+ }
643
+ layer {
644
+ name: "Mconv1_stage1_L2_concat"
645
+ type: "Concat"
646
+ bottom: "Mconv1_stage1_L2_0"
647
+ bottom: "Mconv1_stage1_L2_1"
648
+ bottom: "Mconv1_stage1_L2_2"
649
+ top: "Mconv1_stage1_L2_concat"
650
+ concat_param {
651
+ axis: 1
652
+ }
653
+ }
654
+ layer {
655
+ name: "Mconv2_stage1_L2_0"
656
+ type: "Convolution"
657
+ bottom: "Mconv1_stage1_L2_concat"
658
+ top: "Mconv2_stage1_L2_0"
659
+ convolution_param {
660
+ num_output: 128
661
+ pad: 1
662
+ kernel_size: 3
663
+ }
664
+ }
665
+ layer {
666
+ name: "Mprelu2_stage1_L2_0"
667
+ type: "PReLU"
668
+ bottom: "Mconv2_stage1_L2_0"
669
+ top: "Mconv2_stage1_L2_0"
670
+ }
671
+ layer {
672
+ name: "Mconv2_stage1_L2_1"
673
+ type: "Convolution"
674
+ bottom: "Mconv2_stage1_L2_0"
675
+ top: "Mconv2_stage1_L2_1"
676
+ convolution_param {
677
+ num_output: 128
678
+ pad: 1
679
+ kernel_size: 3
680
+ }
681
+ }
682
+ layer {
683
+ name: "Mprelu2_stage1_L2_1"
684
+ type: "PReLU"
685
+ bottom: "Mconv2_stage1_L2_1"
686
+ top: "Mconv2_stage1_L2_1"
687
+ }
688
+ layer {
689
+ name: "Mconv2_stage1_L2_2"
690
+ type: "Convolution"
691
+ bottom: "Mconv2_stage1_L2_1"
692
+ top: "Mconv2_stage1_L2_2"
693
+ convolution_param {
694
+ num_output: 128
695
+ pad: 1
696
+ kernel_size: 3
697
+ }
698
+ }
699
+ layer {
700
+ name: "Mprelu2_stage1_L2_2"
701
+ type: "PReLU"
702
+ bottom: "Mconv2_stage1_L2_2"
703
+ top: "Mconv2_stage1_L2_2"
704
+ }
705
+ layer {
706
+ name: "Mconv2_stage1_L2_concat"
707
+ type: "Concat"
708
+ bottom: "Mconv2_stage1_L2_0"
709
+ bottom: "Mconv2_stage1_L2_1"
710
+ bottom: "Mconv2_stage1_L2_2"
711
+ top: "Mconv2_stage1_L2_concat"
712
+ concat_param {
713
+ axis: 1
714
+ }
715
+ }
716
+ layer {
717
+ name: "Mconv3_stage1_L2_0"
718
+ type: "Convolution"
719
+ bottom: "Mconv2_stage1_L2_concat"
720
+ top: "Mconv3_stage1_L2_0"
721
+ convolution_param {
722
+ num_output: 128
723
+ pad: 1
724
+ kernel_size: 3
725
+ }
726
+ }
727
+ layer {
728
+ name: "Mprelu3_stage1_L2_0"
729
+ type: "PReLU"
730
+ bottom: "Mconv3_stage1_L2_0"
731
+ top: "Mconv3_stage1_L2_0"
732
+ }
733
+ layer {
734
+ name: "Mconv3_stage1_L2_1"
735
+ type: "Convolution"
736
+ bottom: "Mconv3_stage1_L2_0"
737
+ top: "Mconv3_stage1_L2_1"
738
+ convolution_param {
739
+ num_output: 128
740
+ pad: 1
741
+ kernel_size: 3
742
+ }
743
+ }
744
+ layer {
745
+ name: "Mprelu3_stage1_L2_1"
746
+ type: "PReLU"
747
+ bottom: "Mconv3_stage1_L2_1"
748
+ top: "Mconv3_stage1_L2_1"
749
+ }
750
+ layer {
751
+ name: "Mconv3_stage1_L2_2"
752
+ type: "Convolution"
753
+ bottom: "Mconv3_stage1_L2_1"
754
+ top: "Mconv3_stage1_L2_2"
755
+ convolution_param {
756
+ num_output: 128
757
+ pad: 1
758
+ kernel_size: 3
759
+ }
760
+ }
761
+ layer {
762
+ name: "Mprelu3_stage1_L2_2"
763
+ type: "PReLU"
764
+ bottom: "Mconv3_stage1_L2_2"
765
+ top: "Mconv3_stage1_L2_2"
766
+ }
767
+ layer {
768
+ name: "Mconv3_stage1_L2_concat"
769
+ type: "Concat"
770
+ bottom: "Mconv3_stage1_L2_0"
771
+ bottom: "Mconv3_stage1_L2_1"
772
+ bottom: "Mconv3_stage1_L2_2"
773
+ top: "Mconv3_stage1_L2_concat"
774
+ concat_param {
775
+ axis: 1
776
+ }
777
+ }
778
+ layer {
779
+ name: "Mconv4_stage1_L2_0"
780
+ type: "Convolution"
781
+ bottom: "Mconv3_stage1_L2_concat"
782
+ top: "Mconv4_stage1_L2_0"
783
+ convolution_param {
784
+ num_output: 128
785
+ pad: 1
786
+ kernel_size: 3
787
+ }
788
+ }
789
+ layer {
790
+ name: "Mprelu4_stage1_L2_0"
791
+ type: "PReLU"
792
+ bottom: "Mconv4_stage1_L2_0"
793
+ top: "Mconv4_stage1_L2_0"
794
+ }
795
+ layer {
796
+ name: "Mconv4_stage1_L2_1"
797
+ type: "Convolution"
798
+ bottom: "Mconv4_stage1_L2_0"
799
+ top: "Mconv4_stage1_L2_1"
800
+ convolution_param {
801
+ num_output: 128
802
+ pad: 1
803
+ kernel_size: 3
804
+ }
805
+ }
806
+ layer {
807
+ name: "Mprelu4_stage1_L2_1"
808
+ type: "PReLU"
809
+ bottom: "Mconv4_stage1_L2_1"
810
+ top: "Mconv4_stage1_L2_1"
811
+ }
812
+ layer {
813
+ name: "Mconv4_stage1_L2_2"
814
+ type: "Convolution"
815
+ bottom: "Mconv4_stage1_L2_1"
816
+ top: "Mconv4_stage1_L2_2"
817
+ convolution_param {
818
+ num_output: 128
819
+ pad: 1
820
+ kernel_size: 3
821
+ }
822
+ }
823
+ layer {
824
+ name: "Mprelu4_stage1_L2_2"
825
+ type: "PReLU"
826
+ bottom: "Mconv4_stage1_L2_2"
827
+ top: "Mconv4_stage1_L2_2"
828
+ }
829
+ layer {
830
+ name: "Mconv4_stage1_L2_concat"
831
+ type: "Concat"
832
+ bottom: "Mconv4_stage1_L2_0"
833
+ bottom: "Mconv4_stage1_L2_1"
834
+ bottom: "Mconv4_stage1_L2_2"
835
+ top: "Mconv4_stage1_L2_concat"
836
+ concat_param {
837
+ axis: 1
838
+ }
839
+ }
840
+ layer {
841
+ name: "Mconv5_stage1_L2_0"
842
+ type: "Convolution"
843
+ bottom: "Mconv4_stage1_L2_concat"
844
+ top: "Mconv5_stage1_L2_0"
845
+ convolution_param {
846
+ num_output: 128
847
+ pad: 1
848
+ kernel_size: 3
849
+ }
850
+ }
851
+ layer {
852
+ name: "Mprelu5_stage1_L2_0"
853
+ type: "PReLU"
854
+ bottom: "Mconv5_stage1_L2_0"
855
+ top: "Mconv5_stage1_L2_0"
856
+ }
857
+ layer {
858
+ name: "Mconv5_stage1_L2_1"
859
+ type: "Convolution"
860
+ bottom: "Mconv5_stage1_L2_0"
861
+ top: "Mconv5_stage1_L2_1"
862
+ convolution_param {
863
+ num_output: 128
864
+ pad: 1
865
+ kernel_size: 3
866
+ }
867
+ }
868
+ layer {
869
+ name: "Mprelu5_stage1_L2_1"
870
+ type: "PReLU"
871
+ bottom: "Mconv5_stage1_L2_1"
872
+ top: "Mconv5_stage1_L2_1"
873
+ }
874
+ layer {
875
+ name: "Mconv5_stage1_L2_2"
876
+ type: "Convolution"
877
+ bottom: "Mconv5_stage1_L2_1"
878
+ top: "Mconv5_stage1_L2_2"
879
+ convolution_param {
880
+ num_output: 128
881
+ pad: 1
882
+ kernel_size: 3
883
+ }
884
+ }
885
+ layer {
886
+ name: "Mprelu5_stage1_L2_2"
887
+ type: "PReLU"
888
+ bottom: "Mconv5_stage1_L2_2"
889
+ top: "Mconv5_stage1_L2_2"
890
+ }
891
+ layer {
892
+ name: "Mconv5_stage1_L2_concat"
893
+ type: "Concat"
894
+ bottom: "Mconv5_stage1_L2_0"
895
+ bottom: "Mconv5_stage1_L2_1"
896
+ bottom: "Mconv5_stage1_L2_2"
897
+ top: "Mconv5_stage1_L2_concat"
898
+ concat_param {
899
+ axis: 1
900
+ }
901
+ }
902
+ layer {
903
+ name: "Mconv6_stage1_L2"
904
+ type: "Convolution"
905
+ bottom: "Mconv5_stage1_L2_concat"
906
+ top: "Mconv6_stage1_L2"
907
+ convolution_param {
908
+ num_output: 512
909
+ pad: 0
910
+ kernel_size: 1
911
+ }
912
+ }
913
+ layer {
914
+ name: "Mprelu6_stage1_L2"
915
+ type: "PReLU"
916
+ bottom: "Mconv6_stage1_L2"
917
+ top: "Mconv6_stage1_L2"
918
+ }
919
+ layer {
920
+ name: "Mconv7_stage1_L2"
921
+ type: "Convolution"
922
+ bottom: "Mconv6_stage1_L2"
923
+ top: "Mconv7_stage1_L2"
924
+ convolution_param {
925
+ num_output: 52
926
+ pad: 0
927
+ kernel_size: 1
928
+ }
929
+ }
930
+ layer {
931
+ name: "concat_stage2_L2"
932
+ type: "Concat"
933
+ bottom: "conv4_4_CPM"
934
+ bottom: "Mconv7_stage1_L2"
935
+ top: "concat_stage2_L2"
936
+ concat_param {
937
+ axis: 1
938
+ }
939
+ }
940
+ layer {
941
+ name: "Mconv1_stage2_L2_0"
942
+ type: "Convolution"
943
+ bottom: "concat_stage2_L2"
944
+ top: "Mconv1_stage2_L2_0"
945
+ convolution_param {
946
+ num_output: 128
947
+ pad: 1
948
+ kernel_size: 3
949
+ }
950
+ }
951
+ layer {
952
+ name: "Mprelu1_stage2_L2_0"
953
+ type: "PReLU"
954
+ bottom: "Mconv1_stage2_L2_0"
955
+ top: "Mconv1_stage2_L2_0"
956
+ }
957
+ layer {
958
+ name: "Mconv1_stage2_L2_1"
959
+ type: "Convolution"
960
+ bottom: "Mconv1_stage2_L2_0"
961
+ top: "Mconv1_stage2_L2_1"
962
+ convolution_param {
963
+ num_output: 128
964
+ pad: 1
965
+ kernel_size: 3
966
+ }
967
+ }
968
+ layer {
969
+ name: "Mprelu1_stage2_L2_1"
970
+ type: "PReLU"
971
+ bottom: "Mconv1_stage2_L2_1"
972
+ top: "Mconv1_stage2_L2_1"
973
+ }
974
+ layer {
975
+ name: "Mconv1_stage2_L2_2"
976
+ type: "Convolution"
977
+ bottom: "Mconv1_stage2_L2_1"
978
+ top: "Mconv1_stage2_L2_2"
979
+ convolution_param {
980
+ num_output: 128
981
+ pad: 1
982
+ kernel_size: 3
983
+ }
984
+ }
985
+ layer {
986
+ name: "Mprelu1_stage2_L2_2"
987
+ type: "PReLU"
988
+ bottom: "Mconv1_stage2_L2_2"
989
+ top: "Mconv1_stage2_L2_2"
990
+ }
991
+ layer {
992
+ name: "Mconv1_stage2_L2_concat"
993
+ type: "Concat"
994
+ bottom: "Mconv1_stage2_L2_0"
995
+ bottom: "Mconv1_stage2_L2_1"
996
+ bottom: "Mconv1_stage2_L2_2"
997
+ top: "Mconv1_stage2_L2_concat"
998
+ concat_param {
999
+ axis: 1
1000
+ }
1001
+ }
1002
+ layer {
1003
+ name: "Mconv2_stage2_L2_0"
1004
+ type: "Convolution"
1005
+ bottom: "Mconv1_stage2_L2_concat"
1006
+ top: "Mconv2_stage2_L2_0"
1007
+ convolution_param {
1008
+ num_output: 128
1009
+ pad: 1
1010
+ kernel_size: 3
1011
+ }
1012
+ }
1013
+ layer {
1014
+ name: "Mprelu2_stage2_L2_0"
1015
+ type: "PReLU"
1016
+ bottom: "Mconv2_stage2_L2_0"
1017
+ top: "Mconv2_stage2_L2_0"
1018
+ }
1019
+ layer {
1020
+ name: "Mconv2_stage2_L2_1"
1021
+ type: "Convolution"
1022
+ bottom: "Mconv2_stage2_L2_0"
1023
+ top: "Mconv2_stage2_L2_1"
1024
+ convolution_param {
1025
+ num_output: 128
1026
+ pad: 1
1027
+ kernel_size: 3
1028
+ }
1029
+ }
1030
+ layer {
1031
+ name: "Mprelu2_stage2_L2_1"
1032
+ type: "PReLU"
1033
+ bottom: "Mconv2_stage2_L2_1"
1034
+ top: "Mconv2_stage2_L2_1"
1035
+ }
1036
+ layer {
1037
+ name: "Mconv2_stage2_L2_2"
1038
+ type: "Convolution"
1039
+ bottom: "Mconv2_stage2_L2_1"
1040
+ top: "Mconv2_stage2_L2_2"
1041
+ convolution_param {
1042
+ num_output: 128
1043
+ pad: 1
1044
+ kernel_size: 3
1045
+ }
1046
+ }
1047
+ layer {
1048
+ name: "Mprelu2_stage2_L2_2"
1049
+ type: "PReLU"
1050
+ bottom: "Mconv2_stage2_L2_2"
1051
+ top: "Mconv2_stage2_L2_2"
1052
+ }
1053
+ layer {
1054
+ name: "Mconv2_stage2_L2_concat"
1055
+ type: "Concat"
1056
+ bottom: "Mconv2_stage2_L2_0"
1057
+ bottom: "Mconv2_stage2_L2_1"
1058
+ bottom: "Mconv2_stage2_L2_2"
1059
+ top: "Mconv2_stage2_L2_concat"
1060
+ concat_param {
1061
+ axis: 1
1062
+ }
1063
+ }
1064
+ layer {
1065
+ name: "Mconv3_stage2_L2_0"
1066
+ type: "Convolution"
1067
+ bottom: "Mconv2_stage2_L2_concat"
1068
+ top: "Mconv3_stage2_L2_0"
1069
+ convolution_param {
1070
+ num_output: 128
1071
+ pad: 1
1072
+ kernel_size: 3
1073
+ }
1074
+ }
1075
+ layer {
1076
+ name: "Mprelu3_stage2_L2_0"
1077
+ type: "PReLU"
1078
+ bottom: "Mconv3_stage2_L2_0"
1079
+ top: "Mconv3_stage2_L2_0"
1080
+ }
1081
+ layer {
1082
+ name: "Mconv3_stage2_L2_1"
1083
+ type: "Convolution"
1084
+ bottom: "Mconv3_stage2_L2_0"
1085
+ top: "Mconv3_stage2_L2_1"
1086
+ convolution_param {
1087
+ num_output: 128
1088
+ pad: 1
1089
+ kernel_size: 3
1090
+ }
1091
+ }
1092
+ layer {
1093
+ name: "Mprelu3_stage2_L2_1"
1094
+ type: "PReLU"
1095
+ bottom: "Mconv3_stage2_L2_1"
1096
+ top: "Mconv3_stage2_L2_1"
1097
+ }
1098
+ layer {
1099
+ name: "Mconv3_stage2_L2_2"
1100
+ type: "Convolution"
1101
+ bottom: "Mconv3_stage2_L2_1"
1102
+ top: "Mconv3_stage2_L2_2"
1103
+ convolution_param {
1104
+ num_output: 128
1105
+ pad: 1
1106
+ kernel_size: 3
1107
+ }
1108
+ }
1109
+ layer {
1110
+ name: "Mprelu3_stage2_L2_2"
1111
+ type: "PReLU"
1112
+ bottom: "Mconv3_stage2_L2_2"
1113
+ top: "Mconv3_stage2_L2_2"
1114
+ }
1115
+ layer {
1116
+ name: "Mconv3_stage2_L2_concat"
1117
+ type: "Concat"
1118
+ bottom: "Mconv3_stage2_L2_0"
1119
+ bottom: "Mconv3_stage2_L2_1"
1120
+ bottom: "Mconv3_stage2_L2_2"
1121
+ top: "Mconv3_stage2_L2_concat"
1122
+ concat_param {
1123
+ axis: 1
1124
+ }
1125
+ }
1126
+ layer {
1127
+ name: "Mconv4_stage2_L2_0"
1128
+ type: "Convolution"
1129
+ bottom: "Mconv3_stage2_L2_concat"
1130
+ top: "Mconv4_stage2_L2_0"
1131
+ convolution_param {
1132
+ num_output: 128
1133
+ pad: 1
1134
+ kernel_size: 3
1135
+ }
1136
+ }
1137
+ layer {
1138
+ name: "Mprelu4_stage2_L2_0"
1139
+ type: "PReLU"
1140
+ bottom: "Mconv4_stage2_L2_0"
1141
+ top: "Mconv4_stage2_L2_0"
1142
+ }
1143
+ layer {
1144
+ name: "Mconv4_stage2_L2_1"
1145
+ type: "Convolution"
1146
+ bottom: "Mconv4_stage2_L2_0"
1147
+ top: "Mconv4_stage2_L2_1"
1148
+ convolution_param {
1149
+ num_output: 128
1150
+ pad: 1
1151
+ kernel_size: 3
1152
+ }
1153
+ }
1154
+ layer {
1155
+ name: "Mprelu4_stage2_L2_1"
1156
+ type: "PReLU"
1157
+ bottom: "Mconv4_stage2_L2_1"
1158
+ top: "Mconv4_stage2_L2_1"
1159
+ }
1160
+ layer {
1161
+ name: "Mconv4_stage2_L2_2"
1162
+ type: "Convolution"
1163
+ bottom: "Mconv4_stage2_L2_1"
1164
+ top: "Mconv4_stage2_L2_2"
1165
+ convolution_param {
1166
+ num_output: 128
1167
+ pad: 1
1168
+ kernel_size: 3
1169
+ }
1170
+ }
1171
+ layer {
1172
+ name: "Mprelu4_stage2_L2_2"
1173
+ type: "PReLU"
1174
+ bottom: "Mconv4_stage2_L2_2"
1175
+ top: "Mconv4_stage2_L2_2"
1176
+ }
1177
+ layer {
1178
+ name: "Mconv4_stage2_L2_concat"
1179
+ type: "Concat"
1180
+ bottom: "Mconv4_stage2_L2_0"
1181
+ bottom: "Mconv4_stage2_L2_1"
1182
+ bottom: "Mconv4_stage2_L2_2"
1183
+ top: "Mconv4_stage2_L2_concat"
1184
+ concat_param {
1185
+ axis: 1
1186
+ }
1187
+ }
1188
+ layer {
1189
+ name: "Mconv5_stage2_L2_0"
1190
+ type: "Convolution"
1191
+ bottom: "Mconv4_stage2_L2_concat"
1192
+ top: "Mconv5_stage2_L2_0"
1193
+ convolution_param {
1194
+ num_output: 128
1195
+ pad: 1
1196
+ kernel_size: 3
1197
+ }
1198
+ }
1199
+ layer {
1200
+ name: "Mprelu5_stage2_L2_0"
1201
+ type: "PReLU"
1202
+ bottom: "Mconv5_stage2_L2_0"
1203
+ top: "Mconv5_stage2_L2_0"
1204
+ }
1205
+ layer {
1206
+ name: "Mconv5_stage2_L2_1"
1207
+ type: "Convolution"
1208
+ bottom: "Mconv5_stage2_L2_0"
1209
+ top: "Mconv5_stage2_L2_1"
1210
+ convolution_param {
1211
+ num_output: 128
1212
+ pad: 1
1213
+ kernel_size: 3
1214
+ }
1215
+ }
1216
+ layer {
1217
+ name: "Mprelu5_stage2_L2_1"
1218
+ type: "PReLU"
1219
+ bottom: "Mconv5_stage2_L2_1"
1220
+ top: "Mconv5_stage2_L2_1"
1221
+ }
1222
+ layer {
1223
+ name: "Mconv5_stage2_L2_2"
1224
+ type: "Convolution"
1225
+ bottom: "Mconv5_stage2_L2_1"
1226
+ top: "Mconv5_stage2_L2_2"
1227
+ convolution_param {
1228
+ num_output: 128
1229
+ pad: 1
1230
+ kernel_size: 3
1231
+ }
1232
+ }
1233
+ layer {
1234
+ name: "Mprelu5_stage2_L2_2"
1235
+ type: "PReLU"
1236
+ bottom: "Mconv5_stage2_L2_2"
1237
+ top: "Mconv5_stage2_L2_2"
1238
+ }
1239
+ layer {
1240
+ name: "Mconv5_stage2_L2_concat"
1241
+ type: "Concat"
1242
+ bottom: "Mconv5_stage2_L2_0"
1243
+ bottom: "Mconv5_stage2_L2_1"
1244
+ bottom: "Mconv5_stage2_L2_2"
1245
+ top: "Mconv5_stage2_L2_concat"
1246
+ concat_param {
1247
+ axis: 1
1248
+ }
1249
+ }
1250
+ layer {
1251
+ name: "Mconv6_stage2_L2"
1252
+ type: "Convolution"
1253
+ bottom: "Mconv5_stage2_L2_concat"
1254
+ top: "Mconv6_stage2_L2"
1255
+ convolution_param {
1256
+ num_output: 512
1257
+ pad: 0
1258
+ kernel_size: 1
1259
+ }
1260
+ }
1261
+ layer {
1262
+ name: "Mprelu6_stage2_L2"
1263
+ type: "PReLU"
1264
+ bottom: "Mconv6_stage2_L2"
1265
+ top: "Mconv6_stage2_L2"
1266
+ }
1267
+ layer {
1268
+ name: "Mconv7_stage2_L2"
1269
+ type: "Convolution"
1270
+ bottom: "Mconv6_stage2_L2"
1271
+ top: "Mconv7_stage2_L2"
1272
+ convolution_param {
1273
+ num_output: 52
1274
+ pad: 0
1275
+ kernel_size: 1
1276
+ }
1277
+ }
1278
+ layer {
1279
+ name: "concat_stage3_L2"
1280
+ type: "Concat"
1281
+ bottom: "conv4_4_CPM"
1282
+ bottom: "Mconv7_stage2_L2"
1283
+ top: "concat_stage3_L2"
1284
+ concat_param {
1285
+ axis: 1
1286
+ }
1287
+ }
1288
+ layer {
1289
+ name: "Mconv1_stage3_L2_0"
1290
+ type: "Convolution"
1291
+ bottom: "concat_stage3_L2"
1292
+ top: "Mconv1_stage3_L2_0"
1293
+ convolution_param {
1294
+ num_output: 128
1295
+ pad: 1
1296
+ kernel_size: 3
1297
+ }
1298
+ }
1299
+ layer {
1300
+ name: "Mprelu1_stage3_L2_0"
1301
+ type: "PReLU"
1302
+ bottom: "Mconv1_stage3_L2_0"
1303
+ top: "Mconv1_stage3_L2_0"
1304
+ }
1305
+ layer {
1306
+ name: "Mconv1_stage3_L2_1"
1307
+ type: "Convolution"
1308
+ bottom: "Mconv1_stage3_L2_0"
1309
+ top: "Mconv1_stage3_L2_1"
1310
+ convolution_param {
1311
+ num_output: 128
1312
+ pad: 1
1313
+ kernel_size: 3
1314
+ }
1315
+ }
1316
+ layer {
1317
+ name: "Mprelu1_stage3_L2_1"
1318
+ type: "PReLU"
1319
+ bottom: "Mconv1_stage3_L2_1"
1320
+ top: "Mconv1_stage3_L2_1"
1321
+ }
1322
+ layer {
1323
+ name: "Mconv1_stage3_L2_2"
1324
+ type: "Convolution"
1325
+ bottom: "Mconv1_stage3_L2_1"
1326
+ top: "Mconv1_stage3_L2_2"
1327
+ convolution_param {
1328
+ num_output: 128
1329
+ pad: 1
1330
+ kernel_size: 3
1331
+ }
1332
+ }
1333
+ layer {
1334
+ name: "Mprelu1_stage3_L2_2"
1335
+ type: "PReLU"
1336
+ bottom: "Mconv1_stage3_L2_2"
1337
+ top: "Mconv1_stage3_L2_2"
1338
+ }
1339
+ layer {
1340
+ name: "Mconv1_stage3_L2_concat"
1341
+ type: "Concat"
1342
+ bottom: "Mconv1_stage3_L2_0"
1343
+ bottom: "Mconv1_stage3_L2_1"
1344
+ bottom: "Mconv1_stage3_L2_2"
1345
+ top: "Mconv1_stage3_L2_concat"
1346
+ concat_param {
1347
+ axis: 1
1348
+ }
1349
+ }
1350
+ layer {
1351
+ name: "Mconv2_stage3_L2_0"
1352
+ type: "Convolution"
1353
+ bottom: "Mconv1_stage3_L2_concat"
1354
+ top: "Mconv2_stage3_L2_0"
1355
+ convolution_param {
1356
+ num_output: 128
1357
+ pad: 1
1358
+ kernel_size: 3
1359
+ }
1360
+ }
1361
+ layer {
1362
+ name: "Mprelu2_stage3_L2_0"
1363
+ type: "PReLU"
1364
+ bottom: "Mconv2_stage3_L2_0"
1365
+ top: "Mconv2_stage3_L2_0"
1366
+ }
1367
+ layer {
1368
+ name: "Mconv2_stage3_L2_1"
1369
+ type: "Convolution"
1370
+ bottom: "Mconv2_stage3_L2_0"
1371
+ top: "Mconv2_stage3_L2_1"
1372
+ convolution_param {
1373
+ num_output: 128
1374
+ pad: 1
1375
+ kernel_size: 3
1376
+ }
1377
+ }
1378
+ layer {
1379
+ name: "Mprelu2_stage3_L2_1"
1380
+ type: "PReLU"
1381
+ bottom: "Mconv2_stage3_L2_1"
1382
+ top: "Mconv2_stage3_L2_1"
1383
+ }
1384
+ layer {
1385
+ name: "Mconv2_stage3_L2_2"
1386
+ type: "Convolution"
1387
+ bottom: "Mconv2_stage3_L2_1"
1388
+ top: "Mconv2_stage3_L2_2"
1389
+ convolution_param {
1390
+ num_output: 128
1391
+ pad: 1
1392
+ kernel_size: 3
1393
+ }
1394
+ }
1395
+ layer {
1396
+ name: "Mprelu2_stage3_L2_2"
1397
+ type: "PReLU"
1398
+ bottom: "Mconv2_stage3_L2_2"
1399
+ top: "Mconv2_stage3_L2_2"
1400
+ }
1401
+ layer {
1402
+ name: "Mconv2_stage3_L2_concat"
1403
+ type: "Concat"
1404
+ bottom: "Mconv2_stage3_L2_0"
1405
+ bottom: "Mconv2_stage3_L2_1"
1406
+ bottom: "Mconv2_stage3_L2_2"
1407
+ top: "Mconv2_stage3_L2_concat"
1408
+ concat_param {
1409
+ axis: 1
1410
+ }
1411
+ }
1412
+ layer {
1413
+ name: "Mconv3_stage3_L2_0"
1414
+ type: "Convolution"
1415
+ bottom: "Mconv2_stage3_L2_concat"
1416
+ top: "Mconv3_stage3_L2_0"
1417
+ convolution_param {
1418
+ num_output: 128
1419
+ pad: 1
1420
+ kernel_size: 3
1421
+ }
1422
+ }
1423
+ layer {
1424
+ name: "Mprelu3_stage3_L2_0"
1425
+ type: "PReLU"
1426
+ bottom: "Mconv3_stage3_L2_0"
1427
+ top: "Mconv3_stage3_L2_0"
1428
+ }
1429
+ layer {
1430
+ name: "Mconv3_stage3_L2_1"
1431
+ type: "Convolution"
1432
+ bottom: "Mconv3_stage3_L2_0"
1433
+ top: "Mconv3_stage3_L2_1"
1434
+ convolution_param {
1435
+ num_output: 128
1436
+ pad: 1
1437
+ kernel_size: 3
1438
+ }
1439
+ }
1440
+ layer {
1441
+ name: "Mprelu3_stage3_L2_1"
1442
+ type: "PReLU"
1443
+ bottom: "Mconv3_stage3_L2_1"
1444
+ top: "Mconv3_stage3_L2_1"
1445
+ }
1446
+ layer {
1447
+ name: "Mconv3_stage3_L2_2"
1448
+ type: "Convolution"
1449
+ bottom: "Mconv3_stage3_L2_1"
1450
+ top: "Mconv3_stage3_L2_2"
1451
+ convolution_param {
1452
+ num_output: 128
1453
+ pad: 1
1454
+ kernel_size: 3
1455
+ }
1456
+ }
1457
+ layer {
1458
+ name: "Mprelu3_stage3_L2_2"
1459
+ type: "PReLU"
1460
+ bottom: "Mconv3_stage3_L2_2"
1461
+ top: "Mconv3_stage3_L2_2"
1462
+ }
1463
+ layer {
1464
+ name: "Mconv3_stage3_L2_concat"
1465
+ type: "Concat"
1466
+ bottom: "Mconv3_stage3_L2_0"
1467
+ bottom: "Mconv3_stage3_L2_1"
1468
+ bottom: "Mconv3_stage3_L2_2"
1469
+ top: "Mconv3_stage3_L2_concat"
1470
+ concat_param {
1471
+ axis: 1
1472
+ }
1473
+ }
1474
+ layer {
1475
+ name: "Mconv4_stage3_L2_0"
1476
+ type: "Convolution"
1477
+ bottom: "Mconv3_stage3_L2_concat"
1478
+ top: "Mconv4_stage3_L2_0"
1479
+ convolution_param {
1480
+ num_output: 128
1481
+ pad: 1
1482
+ kernel_size: 3
1483
+ }
1484
+ }
1485
+ layer {
1486
+ name: "Mprelu4_stage3_L2_0"
1487
+ type: "PReLU"
1488
+ bottom: "Mconv4_stage3_L2_0"
1489
+ top: "Mconv4_stage3_L2_0"
1490
+ }
1491
+ layer {
1492
+ name: "Mconv4_stage3_L2_1"
1493
+ type: "Convolution"
1494
+ bottom: "Mconv4_stage3_L2_0"
1495
+ top: "Mconv4_stage3_L2_1"
1496
+ convolution_param {
1497
+ num_output: 128
1498
+ pad: 1
1499
+ kernel_size: 3
1500
+ }
1501
+ }
1502
+ layer {
1503
+ name: "Mprelu4_stage3_L2_1"
1504
+ type: "PReLU"
1505
+ bottom: "Mconv4_stage3_L2_1"
1506
+ top: "Mconv4_stage3_L2_1"
1507
+ }
1508
+ layer {
1509
+ name: "Mconv4_stage3_L2_2"
1510
+ type: "Convolution"
1511
+ bottom: "Mconv4_stage3_L2_1"
1512
+ top: "Mconv4_stage3_L2_2"
1513
+ convolution_param {
1514
+ num_output: 128
1515
+ pad: 1
1516
+ kernel_size: 3
1517
+ }
1518
+ }
1519
+ layer {
1520
+ name: "Mprelu4_stage3_L2_2"
1521
+ type: "PReLU"
1522
+ bottom: "Mconv4_stage3_L2_2"
1523
+ top: "Mconv4_stage3_L2_2"
1524
+ }
1525
+ layer {
1526
+ name: "Mconv4_stage3_L2_concat"
1527
+ type: "Concat"
1528
+ bottom: "Mconv4_stage3_L2_0"
1529
+ bottom: "Mconv4_stage3_L2_1"
1530
+ bottom: "Mconv4_stage3_L2_2"
1531
+ top: "Mconv4_stage3_L2_concat"
1532
+ concat_param {
1533
+ axis: 1
1534
+ }
1535
+ }
1536
+ layer {
1537
+ name: "Mconv5_stage3_L2_0"
1538
+ type: "Convolution"
1539
+ bottom: "Mconv4_stage3_L2_concat"
1540
+ top: "Mconv5_stage3_L2_0"
1541
+ convolution_param {
1542
+ num_output: 128
1543
+ pad: 1
1544
+ kernel_size: 3
1545
+ }
1546
+ }
1547
+ layer {
1548
+ name: "Mprelu5_stage3_L2_0"
1549
+ type: "PReLU"
1550
+ bottom: "Mconv5_stage3_L2_0"
1551
+ top: "Mconv5_stage3_L2_0"
1552
+ }
1553
+ layer {
1554
+ name: "Mconv5_stage3_L2_1"
1555
+ type: "Convolution"
1556
+ bottom: "Mconv5_stage3_L2_0"
1557
+ top: "Mconv5_stage3_L2_1"
1558
+ convolution_param {
1559
+ num_output: 128
1560
+ pad: 1
1561
+ kernel_size: 3
1562
+ }
1563
+ }
1564
+ layer {
1565
+ name: "Mprelu5_stage3_L2_1"
1566
+ type: "PReLU"
1567
+ bottom: "Mconv5_stage3_L2_1"
1568
+ top: "Mconv5_stage3_L2_1"
1569
+ }
1570
+ layer {
1571
+ name: "Mconv5_stage3_L2_2"
1572
+ type: "Convolution"
1573
+ bottom: "Mconv5_stage3_L2_1"
1574
+ top: "Mconv5_stage3_L2_2"
1575
+ convolution_param {
1576
+ num_output: 128
1577
+ pad: 1
1578
+ kernel_size: 3
1579
+ }
1580
+ }
1581
+ layer {
1582
+ name: "Mprelu5_stage3_L2_2"
1583
+ type: "PReLU"
1584
+ bottom: "Mconv5_stage3_L2_2"
1585
+ top: "Mconv5_stage3_L2_2"
1586
+ }
1587
+ layer {
1588
+ name: "Mconv5_stage3_L2_concat"
1589
+ type: "Concat"
1590
+ bottom: "Mconv5_stage3_L2_0"
1591
+ bottom: "Mconv5_stage3_L2_1"
1592
+ bottom: "Mconv5_stage3_L2_2"
1593
+ top: "Mconv5_stage3_L2_concat"
1594
+ concat_param {
1595
+ axis: 1
1596
+ }
1597
+ }
1598
+ layer {
1599
+ name: "Mconv6_stage3_L2"
1600
+ type: "Convolution"
1601
+ bottom: "Mconv5_stage3_L2_concat"
1602
+ top: "Mconv6_stage3_L2"
1603
+ convolution_param {
1604
+ num_output: 512
1605
+ pad: 0
1606
+ kernel_size: 1
1607
+ }
1608
+ }
1609
+ layer {
1610
+ name: "Mprelu6_stage3_L2"
1611
+ type: "PReLU"
1612
+ bottom: "Mconv6_stage3_L2"
1613
+ top: "Mconv6_stage3_L2"
1614
+ }
1615
+ layer {
1616
+ name: "Mconv7_stage3_L2"
1617
+ type: "Convolution"
1618
+ bottom: "Mconv6_stage3_L2"
1619
+ top: "Mconv7_stage3_L2"
1620
+ convolution_param {
1621
+ num_output: 52
1622
+ pad: 0
1623
+ kernel_size: 1
1624
+ }
1625
+ }
1626
+ layer {
1627
+ name: "concat_stage0_L1"
1628
+ type: "Concat"
1629
+ bottom: "conv4_4_CPM"
1630
+ bottom: "Mconv7_stage3_L2"
1631
+ top: "concat_stage0_L1"
1632
+ concat_param {
1633
+ axis: 1
1634
+ }
1635
+ }
1636
+ layer {
1637
+ name: "Mconv1_stage0_L1_0"
1638
+ type: "Convolution"
1639
+ bottom: "concat_stage0_L1"
1640
+ top: "Mconv1_stage0_L1_0"
1641
+ convolution_param {
1642
+ num_output: 96
1643
+ pad: 1
1644
+ kernel_size: 3
1645
+ }
1646
+ }
1647
+ layer {
1648
+ name: "Mprelu1_stage0_L1_0"
1649
+ type: "PReLU"
1650
+ bottom: "Mconv1_stage0_L1_0"
1651
+ top: "Mconv1_stage0_L1_0"
1652
+ }
1653
+ layer {
1654
+ name: "Mconv1_stage0_L1_1"
1655
+ type: "Convolution"
1656
+ bottom: "Mconv1_stage0_L1_0"
1657
+ top: "Mconv1_stage0_L1_1"
1658
+ convolution_param {
1659
+ num_output: 96
1660
+ pad: 1
1661
+ kernel_size: 3
1662
+ }
1663
+ }
1664
+ layer {
1665
+ name: "Mprelu1_stage0_L1_1"
1666
+ type: "PReLU"
1667
+ bottom: "Mconv1_stage0_L1_1"
1668
+ top: "Mconv1_stage0_L1_1"
1669
+ }
1670
+ layer {
1671
+ name: "Mconv1_stage0_L1_2"
1672
+ type: "Convolution"
1673
+ bottom: "Mconv1_stage0_L1_1"
1674
+ top: "Mconv1_stage0_L1_2"
1675
+ convolution_param {
1676
+ num_output: 96
1677
+ pad: 1
1678
+ kernel_size: 3
1679
+ }
1680
+ }
1681
+ layer {
1682
+ name: "Mprelu1_stage0_L1_2"
1683
+ type: "PReLU"
1684
+ bottom: "Mconv1_stage0_L1_2"
1685
+ top: "Mconv1_stage0_L1_2"
1686
+ }
1687
+ layer {
1688
+ name: "Mconv1_stage0_L1_concat"
1689
+ type: "Concat"
1690
+ bottom: "Mconv1_stage0_L1_0"
1691
+ bottom: "Mconv1_stage0_L1_1"
1692
+ bottom: "Mconv1_stage0_L1_2"
1693
+ top: "Mconv1_stage0_L1_concat"
1694
+ concat_param {
1695
+ axis: 1
1696
+ }
1697
+ }
1698
+ layer {
1699
+ name: "Mconv2_stage0_L1_0"
1700
+ type: "Convolution"
1701
+ bottom: "Mconv1_stage0_L1_concat"
1702
+ top: "Mconv2_stage0_L1_0"
1703
+ convolution_param {
1704
+ num_output: 96
1705
+ pad: 1
1706
+ kernel_size: 3
1707
+ }
1708
+ }
1709
+ layer {
1710
+ name: "Mprelu2_stage0_L1_0"
1711
+ type: "PReLU"
1712
+ bottom: "Mconv2_stage0_L1_0"
1713
+ top: "Mconv2_stage0_L1_0"
1714
+ }
1715
+ layer {
1716
+ name: "Mconv2_stage0_L1_1"
1717
+ type: "Convolution"
1718
+ bottom: "Mconv2_stage0_L1_0"
1719
+ top: "Mconv2_stage0_L1_1"
1720
+ convolution_param {
1721
+ num_output: 96
1722
+ pad: 1
1723
+ kernel_size: 3
1724
+ }
1725
+ }
1726
+ layer {
1727
+ name: "Mprelu2_stage0_L1_1"
1728
+ type: "PReLU"
1729
+ bottom: "Mconv2_stage0_L1_1"
1730
+ top: "Mconv2_stage0_L1_1"
1731
+ }
1732
+ layer {
1733
+ name: "Mconv2_stage0_L1_2"
1734
+ type: "Convolution"
1735
+ bottom: "Mconv2_stage0_L1_1"
1736
+ top: "Mconv2_stage0_L1_2"
1737
+ convolution_param {
1738
+ num_output: 96
1739
+ pad: 1
1740
+ kernel_size: 3
1741
+ }
1742
+ }
1743
+ layer {
1744
+ name: "Mprelu2_stage0_L1_2"
1745
+ type: "PReLU"
1746
+ bottom: "Mconv2_stage0_L1_2"
1747
+ top: "Mconv2_stage0_L1_2"
1748
+ }
1749
+ layer {
1750
+ name: "Mconv2_stage0_L1_concat"
1751
+ type: "Concat"
1752
+ bottom: "Mconv2_stage0_L1_0"
1753
+ bottom: "Mconv2_stage0_L1_1"
1754
+ bottom: "Mconv2_stage0_L1_2"
1755
+ top: "Mconv2_stage0_L1_concat"
1756
+ concat_param {
1757
+ axis: 1
1758
+ }
1759
+ }
1760
+ layer {
1761
+ name: "Mconv3_stage0_L1_0"
1762
+ type: "Convolution"
1763
+ bottom: "Mconv2_stage0_L1_concat"
1764
+ top: "Mconv3_stage0_L1_0"
1765
+ convolution_param {
1766
+ num_output: 96
1767
+ pad: 1
1768
+ kernel_size: 3
1769
+ }
1770
+ }
1771
+ layer {
1772
+ name: "Mprelu3_stage0_L1_0"
1773
+ type: "PReLU"
1774
+ bottom: "Mconv3_stage0_L1_0"
1775
+ top: "Mconv3_stage0_L1_0"
1776
+ }
1777
+ layer {
1778
+ name: "Mconv3_stage0_L1_1"
1779
+ type: "Convolution"
1780
+ bottom: "Mconv3_stage0_L1_0"
1781
+ top: "Mconv3_stage0_L1_1"
1782
+ convolution_param {
1783
+ num_output: 96
1784
+ pad: 1
1785
+ kernel_size: 3
1786
+ }
1787
+ }
1788
+ layer {
1789
+ name: "Mprelu3_stage0_L1_1"
1790
+ type: "PReLU"
1791
+ bottom: "Mconv3_stage0_L1_1"
1792
+ top: "Mconv3_stage0_L1_1"
1793
+ }
1794
+ layer {
1795
+ name: "Mconv3_stage0_L1_2"
1796
+ type: "Convolution"
1797
+ bottom: "Mconv3_stage0_L1_1"
1798
+ top: "Mconv3_stage0_L1_2"
1799
+ convolution_param {
1800
+ num_output: 96
1801
+ pad: 1
1802
+ kernel_size: 3
1803
+ }
1804
+ }
1805
+ layer {
1806
+ name: "Mprelu3_stage0_L1_2"
1807
+ type: "PReLU"
1808
+ bottom: "Mconv3_stage0_L1_2"
1809
+ top: "Mconv3_stage0_L1_2"
1810
+ }
1811
+ layer {
1812
+ name: "Mconv3_stage0_L1_concat"
1813
+ type: "Concat"
1814
+ bottom: "Mconv3_stage0_L1_0"
1815
+ bottom: "Mconv3_stage0_L1_1"
1816
+ bottom: "Mconv3_stage0_L1_2"
1817
+ top: "Mconv3_stage0_L1_concat"
1818
+ concat_param {
1819
+ axis: 1
1820
+ }
1821
+ }
1822
+ layer {
1823
+ name: "Mconv4_stage0_L1_0"
1824
+ type: "Convolution"
1825
+ bottom: "Mconv3_stage0_L1_concat"
1826
+ top: "Mconv4_stage0_L1_0"
1827
+ convolution_param {
1828
+ num_output: 96
1829
+ pad: 1
1830
+ kernel_size: 3
1831
+ }
1832
+ }
1833
+ layer {
1834
+ name: "Mprelu4_stage0_L1_0"
1835
+ type: "PReLU"
1836
+ bottom: "Mconv4_stage0_L1_0"
1837
+ top: "Mconv4_stage0_L1_0"
1838
+ }
1839
+ layer {
1840
+ name: "Mconv4_stage0_L1_1"
1841
+ type: "Convolution"
1842
+ bottom: "Mconv4_stage0_L1_0"
1843
+ top: "Mconv4_stage0_L1_1"
1844
+ convolution_param {
1845
+ num_output: 96
1846
+ pad: 1
1847
+ kernel_size: 3
1848
+ }
1849
+ }
1850
+ layer {
1851
+ name: "Mprelu4_stage0_L1_1"
1852
+ type: "PReLU"
1853
+ bottom: "Mconv4_stage0_L1_1"
1854
+ top: "Mconv4_stage0_L1_1"
1855
+ }
1856
+ layer {
1857
+ name: "Mconv4_stage0_L1_2"
1858
+ type: "Convolution"
1859
+ bottom: "Mconv4_stage0_L1_1"
1860
+ top: "Mconv4_stage0_L1_2"
1861
+ convolution_param {
1862
+ num_output: 96
1863
+ pad: 1
1864
+ kernel_size: 3
1865
+ }
1866
+ }
1867
+ layer {
1868
+ name: "Mprelu4_stage0_L1_2"
1869
+ type: "PReLU"
1870
+ bottom: "Mconv4_stage0_L1_2"
1871
+ top: "Mconv4_stage0_L1_2"
1872
+ }
1873
+ layer {
1874
+ name: "Mconv4_stage0_L1_concat"
1875
+ type: "Concat"
1876
+ bottom: "Mconv4_stage0_L1_0"
1877
+ bottom: "Mconv4_stage0_L1_1"
1878
+ bottom: "Mconv4_stage0_L1_2"
1879
+ top: "Mconv4_stage0_L1_concat"
1880
+ concat_param {
1881
+ axis: 1
1882
+ }
1883
+ }
1884
+ layer {
1885
+ name: "Mconv5_stage0_L1_0"
1886
+ type: "Convolution"
1887
+ bottom: "Mconv4_stage0_L1_concat"
1888
+ top: "Mconv5_stage0_L1_0"
1889
+ convolution_param {
1890
+ num_output: 96
1891
+ pad: 1
1892
+ kernel_size: 3
1893
+ }
1894
+ }
1895
+ layer {
1896
+ name: "Mprelu5_stage0_L1_0"
1897
+ type: "PReLU"
1898
+ bottom: "Mconv5_stage0_L1_0"
1899
+ top: "Mconv5_stage0_L1_0"
1900
+ }
1901
+ layer {
1902
+ name: "Mconv5_stage0_L1_1"
1903
+ type: "Convolution"
1904
+ bottom: "Mconv5_stage0_L1_0"
1905
+ top: "Mconv5_stage0_L1_1"
1906
+ convolution_param {
1907
+ num_output: 96
1908
+ pad: 1
1909
+ kernel_size: 3
1910
+ }
1911
+ }
1912
+ layer {
1913
+ name: "Mprelu5_stage0_L1_1"
1914
+ type: "PReLU"
1915
+ bottom: "Mconv5_stage0_L1_1"
1916
+ top: "Mconv5_stage0_L1_1"
1917
+ }
1918
+ layer {
1919
+ name: "Mconv5_stage0_L1_2"
1920
+ type: "Convolution"
1921
+ bottom: "Mconv5_stage0_L1_1"
1922
+ top: "Mconv5_stage0_L1_2"
1923
+ convolution_param {
1924
+ num_output: 96
1925
+ pad: 1
1926
+ kernel_size: 3
1927
+ }
1928
+ }
1929
+ layer {
1930
+ name: "Mprelu5_stage0_L1_2"
1931
+ type: "PReLU"
1932
+ bottom: "Mconv5_stage0_L1_2"
1933
+ top: "Mconv5_stage0_L1_2"
1934
+ }
1935
+ layer {
1936
+ name: "Mconv5_stage0_L1_concat"
1937
+ type: "Concat"
1938
+ bottom: "Mconv5_stage0_L1_0"
1939
+ bottom: "Mconv5_stage0_L1_1"
1940
+ bottom: "Mconv5_stage0_L1_2"
1941
+ top: "Mconv5_stage0_L1_concat"
1942
+ concat_param {
1943
+ axis: 1
1944
+ }
1945
+ }
1946
+ layer {
1947
+ name: "Mconv6_stage0_L1"
1948
+ type: "Convolution"
1949
+ bottom: "Mconv5_stage0_L1_concat"
1950
+ top: "Mconv6_stage0_L1"
1951
+ convolution_param {
1952
+ num_output: 256
1953
+ pad: 0
1954
+ kernel_size: 1
1955
+ }
1956
+ }
1957
+ layer {
1958
+ name: "Mprelu6_stage0_L1"
1959
+ type: "PReLU"
1960
+ bottom: "Mconv6_stage0_L1"
1961
+ top: "Mconv6_stage0_L1"
1962
+ }
1963
+ layer {
1964
+ name: "Mconv7_stage0_L1"
1965
+ type: "Convolution"
1966
+ bottom: "Mconv6_stage0_L1"
1967
+ top: "Mconv7_stage0_L1"
1968
+ convolution_param {
1969
+ num_output: 26
1970
+ pad: 0
1971
+ kernel_size: 1
1972
+ }
1973
+ }
1974
+ layer {
1975
+ name: "concat_stage1_L1"
1976
+ type: "Concat"
1977
+ bottom: "conv4_4_CPM"
1978
+ bottom: "Mconv7_stage0_L1"
1979
+ bottom: "Mconv7_stage3_L2"
1980
+ top: "concat_stage1_L1"
1981
+ concat_param {
1982
+ axis: 1
1983
+ }
1984
+ }
1985
+ layer {
1986
+ name: "Mconv1_stage1_L1_0"
1987
+ type: "Convolution"
1988
+ bottom: "concat_stage1_L1"
1989
+ top: "Mconv1_stage1_L1_0"
1990
+ convolution_param {
1991
+ num_output: 128
1992
+ pad: 1
1993
+ kernel_size: 3
1994
+ }
1995
+ }
1996
+ layer {
1997
+ name: "Mprelu1_stage1_L1_0"
1998
+ type: "PReLU"
1999
+ bottom: "Mconv1_stage1_L1_0"
2000
+ top: "Mconv1_stage1_L1_0"
2001
+ }
2002
+ layer {
2003
+ name: "Mconv1_stage1_L1_1"
2004
+ type: "Convolution"
2005
+ bottom: "Mconv1_stage1_L1_0"
2006
+ top: "Mconv1_stage1_L1_1"
2007
+ convolution_param {
2008
+ num_output: 128
2009
+ pad: 1
2010
+ kernel_size: 3
2011
+ }
2012
+ }
2013
+ layer {
2014
+ name: "Mprelu1_stage1_L1_1"
2015
+ type: "PReLU"
2016
+ bottom: "Mconv1_stage1_L1_1"
2017
+ top: "Mconv1_stage1_L1_1"
2018
+ }
2019
+ layer {
2020
+ name: "Mconv1_stage1_L1_2"
2021
+ type: "Convolution"
2022
+ bottom: "Mconv1_stage1_L1_1"
2023
+ top: "Mconv1_stage1_L1_2"
2024
+ convolution_param {
2025
+ num_output: 128
2026
+ pad: 1
2027
+ kernel_size: 3
2028
+ }
2029
+ }
2030
+ layer {
2031
+ name: "Mprelu1_stage1_L1_2"
2032
+ type: "PReLU"
2033
+ bottom: "Mconv1_stage1_L1_2"
2034
+ top: "Mconv1_stage1_L1_2"
2035
+ }
2036
+ layer {
2037
+ name: "Mconv1_stage1_L1_concat"
2038
+ type: "Concat"
2039
+ bottom: "Mconv1_stage1_L1_0"
2040
+ bottom: "Mconv1_stage1_L1_1"
2041
+ bottom: "Mconv1_stage1_L1_2"
2042
+ top: "Mconv1_stage1_L1_concat"
2043
+ concat_param {
2044
+ axis: 1
2045
+ }
2046
+ }
2047
+ layer {
2048
+ name: "Mconv2_stage1_L1_0"
2049
+ type: "Convolution"
2050
+ bottom: "Mconv1_stage1_L1_concat"
2051
+ top: "Mconv2_stage1_L1_0"
2052
+ convolution_param {
2053
+ num_output: 128
2054
+ pad: 1
2055
+ kernel_size: 3
2056
+ }
2057
+ }
2058
+ layer {
2059
+ name: "Mprelu2_stage1_L1_0"
2060
+ type: "PReLU"
2061
+ bottom: "Mconv2_stage1_L1_0"
2062
+ top: "Mconv2_stage1_L1_0"
2063
+ }
2064
+ layer {
2065
+ name: "Mconv2_stage1_L1_1"
2066
+ type: "Convolution"
2067
+ bottom: "Mconv2_stage1_L1_0"
2068
+ top: "Mconv2_stage1_L1_1"
2069
+ convolution_param {
2070
+ num_output: 128
2071
+ pad: 1
2072
+ kernel_size: 3
2073
+ }
2074
+ }
2075
+ layer {
2076
+ name: "Mprelu2_stage1_L1_1"
2077
+ type: "PReLU"
2078
+ bottom: "Mconv2_stage1_L1_1"
2079
+ top: "Mconv2_stage1_L1_1"
2080
+ }
2081
+ layer {
2082
+ name: "Mconv2_stage1_L1_2"
2083
+ type: "Convolution"
2084
+ bottom: "Mconv2_stage1_L1_1"
2085
+ top: "Mconv2_stage1_L1_2"
2086
+ convolution_param {
2087
+ num_output: 128
2088
+ pad: 1
2089
+ kernel_size: 3
2090
+ }
2091
+ }
2092
+ layer {
2093
+ name: "Mprelu2_stage1_L1_2"
2094
+ type: "PReLU"
2095
+ bottom: "Mconv2_stage1_L1_2"
2096
+ top: "Mconv2_stage1_L1_2"
2097
+ }
2098
+ layer {
2099
+ name: "Mconv2_stage1_L1_concat"
2100
+ type: "Concat"
2101
+ bottom: "Mconv2_stage1_L1_0"
2102
+ bottom: "Mconv2_stage1_L1_1"
2103
+ bottom: "Mconv2_stage1_L1_2"
2104
+ top: "Mconv2_stage1_L1_concat"
2105
+ concat_param {
2106
+ axis: 1
2107
+ }
2108
+ }
2109
+ layer {
2110
+ name: "Mconv3_stage1_L1_0"
2111
+ type: "Convolution"
2112
+ bottom: "Mconv2_stage1_L1_concat"
2113
+ top: "Mconv3_stage1_L1_0"
2114
+ convolution_param {
2115
+ num_output: 128
2116
+ pad: 1
2117
+ kernel_size: 3
2118
+ }
2119
+ }
2120
+ layer {
2121
+ name: "Mprelu3_stage1_L1_0"
2122
+ type: "PReLU"
2123
+ bottom: "Mconv3_stage1_L1_0"
2124
+ top: "Mconv3_stage1_L1_0"
2125
+ }
2126
+ layer {
2127
+ name: "Mconv3_stage1_L1_1"
2128
+ type: "Convolution"
2129
+ bottom: "Mconv3_stage1_L1_0"
2130
+ top: "Mconv3_stage1_L1_1"
2131
+ convolution_param {
2132
+ num_output: 128
2133
+ pad: 1
2134
+ kernel_size: 3
2135
+ }
2136
+ }
2137
+ layer {
2138
+ name: "Mprelu3_stage1_L1_1"
2139
+ type: "PReLU"
2140
+ bottom: "Mconv3_stage1_L1_1"
2141
+ top: "Mconv3_stage1_L1_1"
2142
+ }
2143
+ layer {
2144
+ name: "Mconv3_stage1_L1_2"
2145
+ type: "Convolution"
2146
+ bottom: "Mconv3_stage1_L1_1"
2147
+ top: "Mconv3_stage1_L1_2"
2148
+ convolution_param {
2149
+ num_output: 128
2150
+ pad: 1
2151
+ kernel_size: 3
2152
+ }
2153
+ }
2154
+ layer {
2155
+ name: "Mprelu3_stage1_L1_2"
2156
+ type: "PReLU"
2157
+ bottom: "Mconv3_stage1_L1_2"
2158
+ top: "Mconv3_stage1_L1_2"
2159
+ }
2160
+ layer {
2161
+ name: "Mconv3_stage1_L1_concat"
2162
+ type: "Concat"
2163
+ bottom: "Mconv3_stage1_L1_0"
2164
+ bottom: "Mconv3_stage1_L1_1"
2165
+ bottom: "Mconv3_stage1_L1_2"
2166
+ top: "Mconv3_stage1_L1_concat"
2167
+ concat_param {
2168
+ axis: 1
2169
+ }
2170
+ }
2171
+ layer {
2172
+ name: "Mconv4_stage1_L1_0"
2173
+ type: "Convolution"
2174
+ bottom: "Mconv3_stage1_L1_concat"
2175
+ top: "Mconv4_stage1_L1_0"
2176
+ convolution_param {
2177
+ num_output: 128
2178
+ pad: 1
2179
+ kernel_size: 3
2180
+ }
2181
+ }
2182
+ layer {
2183
+ name: "Mprelu4_stage1_L1_0"
2184
+ type: "PReLU"
2185
+ bottom: "Mconv4_stage1_L1_0"
2186
+ top: "Mconv4_stage1_L1_0"
2187
+ }
2188
+ layer {
2189
+ name: "Mconv4_stage1_L1_1"
2190
+ type: "Convolution"
2191
+ bottom: "Mconv4_stage1_L1_0"
2192
+ top: "Mconv4_stage1_L1_1"
2193
+ convolution_param {
2194
+ num_output: 128
2195
+ pad: 1
2196
+ kernel_size: 3
2197
+ }
2198
+ }
2199
+ layer {
2200
+ name: "Mprelu4_stage1_L1_1"
2201
+ type: "PReLU"
2202
+ bottom: "Mconv4_stage1_L1_1"
2203
+ top: "Mconv4_stage1_L1_1"
2204
+ }
2205
+ layer {
2206
+ name: "Mconv4_stage1_L1_2"
2207
+ type: "Convolution"
2208
+ bottom: "Mconv4_stage1_L1_1"
2209
+ top: "Mconv4_stage1_L1_2"
2210
+ convolution_param {
2211
+ num_output: 128
2212
+ pad: 1
2213
+ kernel_size: 3
2214
+ }
2215
+ }
2216
+ layer {
2217
+ name: "Mprelu4_stage1_L1_2"
2218
+ type: "PReLU"
2219
+ bottom: "Mconv4_stage1_L1_2"
2220
+ top: "Mconv4_stage1_L1_2"
2221
+ }
2222
+ layer {
2223
+ name: "Mconv4_stage1_L1_concat"
2224
+ type: "Concat"
2225
+ bottom: "Mconv4_stage1_L1_0"
2226
+ bottom: "Mconv4_stage1_L1_1"
2227
+ bottom: "Mconv4_stage1_L1_2"
2228
+ top: "Mconv4_stage1_L1_concat"
2229
+ concat_param {
2230
+ axis: 1
2231
+ }
2232
+ }
2233
+ layer {
2234
+ name: "Mconv5_stage1_L1_0"
2235
+ type: "Convolution"
2236
+ bottom: "Mconv4_stage1_L1_concat"
2237
+ top: "Mconv5_stage1_L1_0"
2238
+ convolution_param {
2239
+ num_output: 128
2240
+ pad: 1
2241
+ kernel_size: 3
2242
+ }
2243
+ }
2244
+ layer {
2245
+ name: "Mprelu5_stage1_L1_0"
2246
+ type: "PReLU"
2247
+ bottom: "Mconv5_stage1_L1_0"
2248
+ top: "Mconv5_stage1_L1_0"
2249
+ }
2250
+ layer {
2251
+ name: "Mconv5_stage1_L1_1"
2252
+ type: "Convolution"
2253
+ bottom: "Mconv5_stage1_L1_0"
2254
+ top: "Mconv5_stage1_L1_1"
2255
+ convolution_param {
2256
+ num_output: 128
2257
+ pad: 1
2258
+ kernel_size: 3
2259
+ }
2260
+ }
2261
+ layer {
2262
+ name: "Mprelu5_stage1_L1_1"
2263
+ type: "PReLU"
2264
+ bottom: "Mconv5_stage1_L1_1"
2265
+ top: "Mconv5_stage1_L1_1"
2266
+ }
2267
+ layer {
2268
+ name: "Mconv5_stage1_L1_2"
2269
+ type: "Convolution"
2270
+ bottom: "Mconv5_stage1_L1_1"
2271
+ top: "Mconv5_stage1_L1_2"
2272
+ convolution_param {
2273
+ num_output: 128
2274
+ pad: 1
2275
+ kernel_size: 3
2276
+ }
2277
+ }
2278
+ layer {
2279
+ name: "Mprelu5_stage1_L1_2"
2280
+ type: "PReLU"
2281
+ bottom: "Mconv5_stage1_L1_2"
2282
+ top: "Mconv5_stage1_L1_2"
2283
+ }
2284
+ layer {
2285
+ name: "Mconv5_stage1_L1_concat"
2286
+ type: "Concat"
2287
+ bottom: "Mconv5_stage1_L1_0"
2288
+ bottom: "Mconv5_stage1_L1_1"
2289
+ bottom: "Mconv5_stage1_L1_2"
2290
+ top: "Mconv5_stage1_L1_concat"
2291
+ concat_param {
2292
+ axis: 1
2293
+ }
2294
+ }
2295
+ layer {
2296
+ name: "Mconv6_stage1_L1"
2297
+ type: "Convolution"
2298
+ bottom: "Mconv5_stage1_L1_concat"
2299
+ top: "Mconv6_stage1_L1"
2300
+ convolution_param {
2301
+ num_output: 512
2302
+ pad: 0
2303
+ kernel_size: 1
2304
+ }
2305
+ }
2306
+ layer {
2307
+ name: "Mprelu6_stage1_L1"
2308
+ type: "PReLU"
2309
+ bottom: "Mconv6_stage1_L1"
2310
+ top: "Mconv6_stage1_L1"
2311
+ }
2312
+ layer {
2313
+ name: "Mconv7_stage1_L1"
2314
+ type: "Convolution"
2315
+ bottom: "Mconv6_stage1_L1"
2316
+ top: "Mconv7_stage1_L1"
2317
+ convolution_param {
2318
+ num_output: 26
2319
+ pad: 0
2320
+ kernel_size: 1
2321
+ }
2322
+ }
2323
+ layer {
2324
+ name: "net_output"
2325
+ type: "Concat"
2326
+ bottom: "Mconv7_stage1_L1"
2327
+ bottom: "Mconv7_stage3_L2"
2328
+ top: "net_output"
2329
+ concat_param {
2330
+ axis: 1
2331
+ }
2332
+ }
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi==0.115.6
2
+ uvicorn==0.34.0
3
+ numpy==1.26.4
4
+ opencv-python-headless==4.10.0.84
5
+ scipy==1.13.1
6
+ python-multipart==0.0.20
7
+ ultralytics==8.3.50
8
+ huggingface_hub==0.27.1
rules_by_age.json ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "description": "Biomechanical gait analysis benchmarks and red flags by age group. Reference: Trupti Didi Clinical Benchmarks.",
3
+ "age_groups": [
4
+ {
5
+ "range": "1-2",
6
+ "name": "Early Toddlers",
7
+ "benchmarks": {
8
+ "heel_strike": "Not expected",
9
+ "cadence_bpm": "150-180",
10
+ "swing_knee_flexion": "45-55°",
11
+ "stance_knee_angle": "> 150°",
12
+ "asymmetry_threshold_percent": 15,
13
+ "trunk_sway_threshold": 25
14
+ },
15
+ "clinical_notes": "Wide base of support and high-guard arm position are normal at this stage."
16
+ },
17
+ {
18
+ "range": "3-4",
19
+ "name": "Preschoolers",
20
+ "benchmarks": {
21
+ "heel_strike": "Inconsistent/Emerging",
22
+ "cadence_bpm": "130-150",
23
+ "swing_knee_flexion": "55-60°",
24
+ "stance_knee_angle": "> 160°",
25
+ "asymmetry_threshold_percent": 10,
26
+ "trunk_sway_threshold": 20
27
+ },
28
+ "clinical_notes": "Heel strike should start becoming consistent. Persistent knee flexion in stance (>20deg) after age 3 is a red flag."
29
+ },
30
+ {
31
+ "range": "5-7",
32
+ "name": "Early School Age",
33
+ "benchmarks": {
34
+ "heel_strike": "Expected/Consistent",
35
+ "cadence_bpm": "110-130",
36
+ "swing_knee_flexion": "60-65°",
37
+ "stance_knee_angle": "> 170°",
38
+ "asymmetry_threshold_percent": 8,
39
+ "trunk_sway_threshold": 15
40
+ },
41
+ "clinical_notes": "Gait pattern should be well-established and similar to adult morphology."
42
+ },
43
+ {
44
+ "range": "8+",
45
+ "name": "Mature/Adult",
46
+ "benchmarks": {
47
+ "heel_strike": "Mandatory",
48
+ "cadence_bpm": "90-120",
49
+ "swing_knee_flexion": "60-70°",
50
+ "stance_knee_angle": "> 175°",
51
+ "asymmetry_threshold_percent": 8,
52
+ "trunk_sway_threshold": 12
53
+ },
54
+ "clinical_notes": "Adult-like symmetry and stability expected. High trunk sway or lack of heel strike indicates pathology."
55
+ }
56
+ ],
57
+ "global_red_flags": [
58
+ {
59
+ "type": "Asymmetrical Loading",
60
+ "description": "Significant difference between left and right limb metrics.",
61
+ "severity": "moderate"
62
+ },
63
+ {
64
+ "type": "Instability",
65
+ "description": "High trunk sway or variable stride patterns.",
66
+ "severity": "high"
67
+ },
68
+ {
69
+ "type": "Developmental Delay",
70
+ "description": "Gait markers significantly below age-group benchmarks.",
71
+ "severity": "moderate"
72
+ }
73
+ ],
74
+ "parameter_thresholds": {
75
+ "step_length_symmetry": {
76
+ "typical": 7,
77
+ "mild": 12,
78
+ "consistent": 15
79
+ },
80
+ "stance_time_symmetry": {
81
+ "typical": 5,
82
+ "load_avoidance": 8
83
+ },
84
+ "swing_time_symmetry": {
85
+ "typical": 5,
86
+ "control_related": 8
87
+ },
88
+ "stride_consistency": "High SD indicates instability/immature control"
89
+ },
90
+ "primitive_reflex_influence": {
91
+ "moro": "Difficulty releasing support",
92
+ "fear_paralysis": "Freezing",
93
+ "tlr": "Extensor posture",
94
+ "atnr": "Trunk rotation",
95
+ "palmar": "Fisted hands"
96
+ },
97
+ "safety_rules": {
98
+ "min_steps": 5,
99
+ "min_duration_seconds": 3,
100
+ "requires_independent_walking": true,
101
+ "limitation_note": "Numerical interpretation limited due to context."
102
+ }
103
+ }
run_test.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from gait_analysis import EnhancedGaitAnalyzer
2
+ import os
3
+ import json
4
+ from generate_report import generate_markdown_report
5
+ from datetime import datetime
6
+
7
+ def main():
8
+ # Path to test.mp4
9
+ # The script is in backend/, test.mp4 is in root/ (../test.mp4)
10
+ video_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "atmin.mp4"))
11
+
12
+ print(f"Looking for video at: {video_path}")
13
+ if not os.path.exists(video_path):
14
+ print("Error: test.mp4 not found!")
15
+ return
16
+
17
+ print("Initializing EnhancedGaitAnalyzer...")
18
+ analyzer = EnhancedGaitAnalyzer(age=4) # Assuming age 4 for test
19
+
20
+ print("Processing video... this might take a moment.")
21
+ try:
22
+ results = analyzer.process_video(video_path)
23
+
24
+ # Generate report
25
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
26
+ report_filename = f"gait_analysis_report_{timestamp}.md"
27
+ report_path = os.path.join(os.path.dirname(video_path), report_filename)
28
+ generate_markdown_report(results, video_path, report_path)
29
+
30
+ # Helper function to safely format numbers
31
+ def fmt(value, decimals=2):
32
+ """Format numeric value or return N/A"""
33
+ if value == 'N/A' or value is None:
34
+ return 'N/A'
35
+ try:
36
+ return f"{float(value):.{decimals}f}"
37
+ except (ValueError, TypeError):
38
+ return 'N/A'
39
+
40
+ # Extract new observation outputs
41
+ summary = results.get("summary", {})
42
+ observations = results.get("observations", {})
43
+ strategies = results.get("compensatory_strategies", [])
44
+ reflex_influence = results.get("reflex_influence", "None observed")
45
+ safety_note = results.get("safety_note", "")
46
+ weight_dist = results.get("weight_distribution", {})
47
+ postural_strength = results.get("postural_strength", {})
48
+ clinical_notes = results.get("clinical_notes", [])
49
+
50
+ print("\n" + "="*60)
51
+ print("CLINICAL GAIT ANALYSIS RESULTS")
52
+ print("="*60)
53
+
54
+ # 1. Walking Condition
55
+ walking_condition = summary.get("walking_condition", "Not specified")
56
+ print(f"\n🚶 WALKING CONDITION: {walking_condition.upper()}")
57
+
58
+ # 2. Observed Movement Patterns
59
+ print(f"\n� OBSERVED MOVEMENT PATTERNS:")
60
+ print(f" Trunk: {observations.get('trunk', 'N/A')}")
61
+ print(f" Head & Neck: {observations.get('head_neck', 'N/A')}")
62
+ print(f" Arms & Hands: {observations.get('arms_hands', 'N/A')}")
63
+ print(f" Lower Limbs: {observations.get('lower_limbs', 'N/A')}")
64
+ print(f" Symmetry: {observations.get('symmetry', 'N/A')}")
65
+
66
+ # 3. Compensatory Strategies
67
+ print(f"\n💡 COMPENSATORY STRATEGIES:")
68
+ if strategies:
69
+ for strategy in strategies:
70
+ print(f" - {strategy}")
71
+ else:
72
+ print(" - None identified")
73
+
74
+ print(f"\n📝 CLINICAL NOTES:")
75
+ if clinical_notes:
76
+ for note in clinical_notes:
77
+ print(f" - {note}")
78
+ else:
79
+ print(" - None noted")
80
+
81
+ # 4. Integrated Movement Summary
82
+ asymmetry = summary.get("phase_asymmetry_percent", 0)
83
+ stability = summary.get("stability_score", 0)
84
+ strength = summary.get("strength_score", 0)
85
+ weight_shift = summary.get("weight_shift_score", 0)
86
+ gait_quality = summary.get("overall_gait_quality", "unknown")
87
+
88
+ print(f"\n📋 INTEGRATED MOVEMENT SUMMARY:")
89
+ print(f" Walking Condition: {walking_condition.upper()}")
90
+ print(f" Overall Gait Quality: {gait_quality.upper()}")
91
+ print(f" Stability Score: {stability:.0f}/100 | Strength Score: {strength:.0f}/100 | Weight Shift Score: {weight_shift:.0f}/100")
92
+ print(f" Movement symmetry shows a {asymmetry:.1f}% phase variance.")
93
+
94
+ if reflex_influence:
95
+ print(f"\n {reflex_influence}")
96
+
97
+ if safety_note:
98
+ print(f"\n ⚠️ {safety_note}")
99
+
100
+ print("\n" + "="*60)
101
+ print("NOTE: This is NOT a medical diagnosis. Consult a healthcare")
102
+ print("professional for proper assessment and treatment.")
103
+ print("="*60 + "\n")
104
+
105
+ except Exception as e:
106
+ print(f"An error occurred during processing: {e}")
107
+ import traceback
108
+ traceback.print_exc()
109
+
110
+ if __name__ == "__main__":
111
+ main()
save_results.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from gait_analysis import EnhancedGaitAnalyzer
2
+ import os
3
+ import json
4
+
5
+ def main():
6
+ # Path to test.mp4
7
+ video_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "test.mp4"))
8
+
9
+ print(f"Looking for video at: {video_path}")
10
+ if not os.path.exists(video_path):
11
+ print("Error: test.mp4 not found!")
12
+ return
13
+
14
+ print("Initializing EnhancedGaitAnalyzer...")
15
+ analyzer = EnhancedGaitAnalyzer()
16
+
17
+ print("Processing video... this might take a moment.")
18
+ try:
19
+ results = analyzer.process_video(video_path)
20
+
21
+ # Save to JSON file
22
+ output_file = os.path.join(os.path.dirname(video_path), "analysis_results.json")
23
+ with open(output_file, 'w') as f:
24
+ json.dump(results, f, indent=2, default=str)
25
+
26
+ print(f"\n✅ Results saved to: {output_file}")
27
+
28
+ # Print summary
29
+ summary = results.get("summary", {})
30
+
31
+ print("\n" + "="*60)
32
+ print("SUMMARY")
33
+ print("="*60)
34
+ print(json.dumps(summary, indent=2))
35
+
36
+ except Exception as e:
37
+ print(f"An error occurred during processing: {e}")
38
+ import traceback
39
+ traceback.print_exc()
40
+
41
+ if __name__ == "__main__":
42
+ main()
yolo11n-pose.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:869e83fcdffdc7371fa4e34cd8e51c838cc729571d1635e5141e3075e9319dc0
3
+ size 6255593