harmesh95 commited on
Commit
e2af51e
Β·
1 Parent(s): cdd090f

Add YOLOv8 model weights with LFS tracking

Browse files
Files changed (31) hide show
  1. Dockerfile +2 -2
  2. backend/__init__.py β†’ __init__.py +0 -0
  3. backend/data_extraction/__init__.py β†’ app.log +0 -0
  4. app.py +0 -100
  5. backend/config.py β†’ config.py +1 -0
  6. {backend/feature_extraction β†’ data_extraction}/__init__.py +0 -0
  7. {backend/data_extraction β†’ data_extraction}/interaction_analyzer.py +2 -2
  8. {backend/data_extraction β†’ data_extraction}/person_tracker.py +2 -2
  9. {backend/preprocessing β†’ feature_extraction}/__init__.py +0 -0
  10. {backend/feature_extraction β†’ feature_extraction}/extractor.py +6 -6
  11. main.py +113 -0
  12. models/xgb_model.pkl +3 -0
  13. {backend/models β†’ models}/yolov8n-pose.pt +0 -0
  14. {backend/models β†’ models}/yolov8n.pt +0 -0
  15. {backend/services β†’ preprocessing}/__init__.py +0 -0
  16. {backend/preprocessing β†’ preprocessing}/preprocessor.py +0 -0
  17. requirements.txt +1 -0
  18. {backend/services/prediction β†’ services}/__init__.py +0 -0
  19. {backend/services/video_data_extraction β†’ services/prediction}/__init__.py +0 -0
  20. services/prediction/predictor.py +40 -0
  21. backend/services/prediction/predictor.py β†’ services/preprocessing/preprocessor.py +41 -43
  22. {backend/utils β†’ services/video_data_extraction}/__init__.py +0 -0
  23. {backend/services β†’ services}/video_data_extraction/video_preprocessor.py +22 -31
  24. utils/__init__.py +0 -0
  25. {backend/utils β†’ utils}/csv_utils.py +0 -0
  26. {backend/utils β†’ utils}/gpu.py +0 -0
  27. {backend/utils β†’ utils}/id_utils.py +0 -0
  28. {backend/utils β†’ utils}/interaction_utils.py +0 -0
  29. {backend/utils β†’ utils}/iou_utils.py +0 -0
  30. {backend/utils β†’ utils}/motion_utils.py +0 -0
  31. {backend/utils β†’ utils}/visualizer.py +0 -0
Dockerfile CHANGED
@@ -1,7 +1,7 @@
1
  # ------------------------------------------------------------
2
  # Base image
3
  # ------------------------------------------------------------
4
- FROM python:3.12-slim-bookworm
5
 
6
  # ------------------------------------------------------------
7
  # Environment
@@ -62,4 +62,4 @@ EXPOSE 8000
62
  # ------------------------------------------------------------
63
  # Start FastAPI with auto-reload (remove --reload for production)
64
  # ------------------------------------------------------------
65
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860", "--reload"]
 
1
  # ------------------------------------------------------------
2
  # Base image
3
  # ------------------------------------------------------------
4
+ FROM python:3.12-slim
5
 
6
  # ------------------------------------------------------------
7
  # Environment
 
62
  # ------------------------------------------------------------
63
  # Start FastAPI with auto-reload (remove --reload for production)
64
  # ------------------------------------------------------------
65
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
backend/__init__.py β†’ __init__.py RENAMED
File without changes
backend/data_extraction/__init__.py β†’ app.log RENAMED
File without changes
app.py DELETED
@@ -1,100 +0,0 @@
1
- import os
2
- import uuid
3
- import tempfile
4
- import pandas as pd
5
- from fastapi import FastAPI, UploadFile, File, HTTPException
6
- from fastapi.responses import FileResponse
7
- import uvicorn
8
-
9
- from backend.services.video_data_extraction.video_preprocessor import VideoDataExtractor
10
- from backend.services.prediction.predictor import ViolencePredictor
11
-
12
- app = FastAPI(title="Video Analysis Backend")
13
-
14
- processor = VideoDataExtractor()
15
- predictor = ViolencePredictor()
16
- jobs: dict[str, dict] = {}
17
-
18
-
19
- @app.get("/")
20
- def greet_json():
21
- return {"Hello": "World!"}
22
-
23
-
24
- @app.get("/health")
25
- async def health_check():
26
- return {"status": "ok", "message": "Service is running"}
27
-
28
-
29
- @app.post("/process-video/")
30
- async def process_video(file: UploadFile = File(...)):
31
- try:
32
- with tempfile.NamedTemporaryFile(delete=False, suffix=".mp4") as input_video:
33
- input_video.write(await file.read())
34
- input_path = input_video.name
35
-
36
- output_csv = tempfile.NamedTemporaryFile(delete=False, suffix=".csv").name
37
- output_video_path = tempfile.NamedTemporaryFile(
38
- delete=False, suffix=".mp4"
39
- ).name
40
-
41
- frame_w, frame_h, num_interactions = processor.extract_video_data(
42
- input_path,
43
- output_csv,
44
- output_folder=os.path.dirname(output_video_path),
45
- save_video=True,
46
- )
47
-
48
- job_id = str(uuid.uuid4())
49
- jobs[job_id] = {"csv": output_csv, "video": output_video_path}
50
-
51
- return {
52
- "job_id": job_id,
53
- "message": f"Processed video with {num_interactions} interactions",
54
- "frame_width": frame_w,
55
- "frame_height": frame_h,
56
- }
57
- except Exception as e:
58
- raise HTTPException(status_code=500, detail=str(e))
59
- finally:
60
- if os.path.exists(input_path):
61
- os.unlink(input_path)
62
-
63
-
64
- @app.get("/get-results/{job_id}")
65
- async def get_results(job_id: str):
66
- if job_id not in jobs:
67
- raise HTTPException(status_code=404, detail="Job ID not found")
68
- csv_path = jobs[job_id]["csv"]
69
- if not os.path.exists(csv_path):
70
- raise HTTPException(status_code=404, detail="CSV file not found")
71
- return FileResponse(
72
- csv_path, media_type="text/csv", filename="violence_analysis_results.csv"
73
- )
74
-
75
-
76
- @app.get("/sample-results/{job_id}")
77
- async def get_sample_results(job_id: str):
78
- if job_id not in jobs:
79
- raise HTTPException(status_code=404, detail="Job ID not found")
80
- csv_path = jobs[job_id]["csv"]
81
- if not os.path.exists(csv_path):
82
- raise HTTPException(status_code=404, detail="CSV file not found")
83
- df = pd.read_csv(csv_path)
84
- return df.head(5).to_dict(orient="records")
85
-
86
-
87
- @app.post("/predict/{job_id}")
88
- async def predict_violence(job_id: str):
89
- if job_id not in jobs:
90
- raise HTTPException(status_code=404, detail="Job ID not found")
91
- csv_path = jobs[job_id]["csv"]
92
- if not os.path.exists(csv_path):
93
- raise HTTPException(status_code=404, detail="CSV file not found")
94
- df = pd.read_csv(csv_path)
95
- preds = predictor.predict(df)
96
- return {"predictions": preds.tolist()}
97
-
98
-
99
- if __name__ == "__main__":
100
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
backend/config.py β†’ config.py RENAMED
@@ -3,6 +3,7 @@ import os
3
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
4
  DETECT_MODEL = os.path.join(BASE_DIR, "models", "yolov8n.pt")
5
  POSE_MODEL = os.path.join(BASE_DIR, "models", "yolov8n-pose.pt")
 
6
 
7
 
8
  # Thresholds and params
 
3
  BASE_DIR = os.path.dirname(os.path.abspath(__file__))
4
  DETECT_MODEL = os.path.join(BASE_DIR, "models", "yolov8n.pt")
5
  POSE_MODEL = os.path.join(BASE_DIR, "models", "yolov8n-pose.pt")
6
+ MAIN_MODEL = os.path.join(BASE_DIR, "models", "xgb_model.pkl")
7
 
8
 
9
  # Thresholds and params
{backend/feature_extraction β†’ data_extraction}/__init__.py RENAMED
File without changes
{backend/data_extraction β†’ data_extraction}/interaction_analyzer.py RENAMED
@@ -1,10 +1,10 @@
1
  import numpy as np
2
- from backend.utils.motion_utils import (
3
  calc_avg_speed,
4
  calc_motion_intensity,
5
  calc_sudden_movements,
6
  )
7
- from backend.utils.interaction_utils import (
8
  get_box_center,
9
  euclidean_distance,
10
  relative_distance,
 
1
  import numpy as np
2
+ from utils.motion_utils import (
3
  calc_avg_speed,
4
  calc_motion_intensity,
5
  calc_sudden_movements,
6
  )
7
+ from utils.interaction_utils import (
8
  get_box_center,
9
  euclidean_distance,
10
  relative_distance,
{backend/data_extraction β†’ data_extraction}/person_tracker.py RENAMED
@@ -1,6 +1,6 @@
1
  import numpy as np
2
- from backend.utils.iou_utils import calculate_iou
3
- from backend.utils.id_utils import get_new_id
4
 
5
 
6
  class PersonTracker:
 
1
  import numpy as np
2
+ from utils.iou_utils import calculate_iou
3
+ from utils.id_utils import get_new_id
4
 
5
 
6
  class PersonTracker:
{backend/preprocessing β†’ feature_extraction}/__init__.py RENAMED
File without changes
{backend/feature_extraction β†’ feature_extraction}/extractor.py RENAMED
@@ -1,10 +1,10 @@
1
  import torch
2
- from backend.config import DETECT_MODEL, POSE_MODEL, CONF_THRESHOLD
3
- from backend.utils.gpu import GPUConfigurator
4
- from backend.preprocessing.preprocessor import FramePreprocessor
5
- from backend.data_extraction.interaction_analyzer import InteractionAnalyzer
6
- from backend.data_extraction.person_tracker import PersonTracker
7
- from backend.utils.visualizer import Visualizer
8
  import numpy as np
9
  from ultralytics import YOLO
10
 
 
1
  import torch
2
+ from config import DETECT_MODEL, POSE_MODEL, CONF_THRESHOLD
3
+ from utils.gpu import GPUConfigurator
4
+ from preprocessing.preprocessor import FramePreprocessor
5
+ from data_extraction.interaction_analyzer import InteractionAnalyzer
6
+ from data_extraction.person_tracker import PersonTracker
7
+ from utils.visualizer import Visualizer
8
  import numpy as np
9
  from ultralytics import YOLO
10
 
main.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from services.prediction.predictor import ViolencePredictor
2
+ from services.video_data_extraction.video_preprocessor import VideoDataExtractor
3
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException
4
+ from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi.responses import JSONResponse
6
+ import numpy as np
7
+ import os
8
+ import logging
9
+ import uuid
10
+
11
+ # Initialize logging
12
+ logging.basicConfig(
13
+ level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
14
+ )
15
+ logger = logging.getLogger("main")
16
+
17
+ app = FastAPI(title="Violence Prediction System")
18
+
19
+ # βœ… Enable CORS
20
+ from fastapi.middleware.cors import CORSMiddleware
21
+
22
+ app.add_middleware(
23
+ CORSMiddleware,
24
+ allow_origins=["*"], # or ["http://localhost:5173"]
25
+ allow_credentials=True,
26
+ allow_methods=["*"],
27
+ allow_headers=["*"],
28
+ )
29
+
30
+ UPLOAD_DIR = "temp"
31
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
32
+ # Initialize shared service objects
33
+ try:
34
+ extractor = VideoDataExtractor()
35
+ predictor = ViolencePredictor()
36
+ logger.info("Initialized shared service objects")
37
+ except Exception as e:
38
+ logger.error(f"Failed to create service objects: {str(e)}")
39
+ # Create mock objects for testing
40
+ extractor = None
41
+ predictor = None
42
+
43
+
44
+ def to_python(obj):
45
+ if isinstance(obj, np.generic):
46
+ return obj.item()
47
+ elif isinstance(obj, np.ndarray):
48
+ return obj.tolist()
49
+ elif isinstance(obj, dict):
50
+ return {k: to_python(v) for k, v in obj.items()}
51
+ elif isinstance(obj, list):
52
+ return [to_python(i) for i in obj]
53
+ return obj
54
+
55
+
56
+ # Health Check endpoint
57
+ @app.get("/")
58
+ async def health():
59
+ return {"status": "ok", "message": "Violence Detection API is running"}
60
+
61
+
62
+ # Extract video data
63
+ @app.post("/analyze")
64
+ async def extract_data(mode: str = Form(...), file: UploadFile = File(...)):
65
+ if not extractor or not predictor:
66
+ raise HTTPException(status_code=500, detail="Service not initialized properly")
67
+
68
+ if not file.filename:
69
+ raise HTTPException(status_code=400, detail="No file provided")
70
+
71
+ # Create temp file with proper path
72
+ tmp_file_code = uuid.uuid4()
73
+ temp_path = os.path.join(UPLOAD_DIR, f"{tmp_file_code}_{file.filename}")
74
+
75
+ try:
76
+ # Save uploaded file
77
+ with open(temp_path, "wb") as f:
78
+ content = await file.read()
79
+ f.write(content)
80
+
81
+ logger.info(f"Processing file: {file.filename}, mode: {mode}")
82
+
83
+ # Extract video data
84
+ data = extractor.extract_video_data(temp_path)
85
+
86
+ if mode == "extract":
87
+ result = {"data": data.to_dict(orient="records")}
88
+ else:
89
+ prediction = predictor.predict(data)
90
+ prediction = to_python(prediction)
91
+ result = {"prediction": prediction}
92
+
93
+ return result
94
+
95
+ except Exception as e:
96
+ logger.error(f"Error processing video: {str(e)}")
97
+ raise HTTPException(
98
+ status_code=500, detail=f"Failed to process video: {str(e)}"
99
+ )
100
+ finally:
101
+ # Clean up temp file
102
+ try:
103
+ if os.path.exists(temp_path):
104
+ os.remove(temp_path)
105
+ except Exception as e:
106
+ logger.warning(f"Could not remove temp file: {e}")
107
+
108
+
109
+ # Run app
110
+ if __name__ == "__main__":
111
+ import uvicorn
112
+
113
+ uvicorn.run(app, host="0.0.0.0", port=8000)
models/xgb_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:301342df1ecd04297cca83af7172b5fded894ee9259657efc2cfaf030a7ab6d0
3
+ size 465468
{backend/models β†’ models}/yolov8n-pose.pt RENAMED
File without changes
{backend/models β†’ models}/yolov8n.pt RENAMED
File without changes
{backend/services β†’ preprocessing}/__init__.py RENAMED
File without changes
{backend/preprocessing β†’ preprocessing}/preprocessor.py RENAMED
File without changes
requirements.txt CHANGED
@@ -1,6 +1,7 @@
1
  fastapi
2
  uvicorn[standard]
3
  numpy
 
4
  opencv-python
5
  pandas
6
  scikit-learn
 
1
  fastapi
2
  uvicorn[standard]
3
  numpy
4
+ xgboost
5
  opencv-python
6
  pandas
7
  scikit-learn
{backend/services/prediction β†’ services}/__init__.py RENAMED
File without changes
{backend/services/video_data_extraction β†’ services/prediction}/__init__.py RENAMED
File without changes
services/prediction/predictor.py ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import joblib
2
+ from config import MAIN_MODEL
3
+ import pandas as pd
4
+
5
+
6
+ class ViolencePredictor:
7
+ def __init__(self):
8
+ self.model = joblib.load(MAIN_MODEL)
9
+
10
+ def _preprocess_data_pdict(self, data: pd.DataFrame) -> pd.DataFrame:
11
+ cols_to_drop = [
12
+ "video_name",
13
+ "frame_index",
14
+ "timestamp",
15
+ "frame_width",
16
+ "frame_height",
17
+ "person1_id",
18
+ "person2_id",
19
+ "person1_idx",
20
+ "person2_idx",
21
+ ]
22
+ data = data.drop(columns=cols_to_drop)
23
+ return data
24
+
25
+ def predict(self, data):
26
+ data = self._preprocess_data_pdict(data)
27
+ y_pred = self.model.predict(data)
28
+ print(y_pred)
29
+
30
+ return y_pred
31
+
32
+
33
+ if __name__ == "__main__":
34
+ import pandas as pd
35
+
36
+ data = pd.read_csv("data/fight_train.csv")
37
+ data = data[0:20]
38
+ print("dataloaded")
39
+ VP = ViolencePredictor()
40
+ VP.predict(data)
backend/services/prediction/predictor.py β†’ services/preprocessing/preprocessor.py RENAMED
@@ -1,43 +1,44 @@
1
  import numpy as np
 
2
  from sklearn.preprocessing import MinMaxScaler
3
 
4
 
5
- class ViolencePredictor:
6
  def __init__(self):
 
7
  self.scaler = MinMaxScaler()
8
 
9
- def preprocess_data(self, df):
10
- # Normalize coordinates, distances and keypoints
11
- # Drop confidence columns
12
- # Scale selected columns
13
- # Similar as existing code...
14
  """
15
- Preprocess the data by normalizing box coordinates, center coordinates, distances, and keypoints.
 
16
  """
 
 
17
  # Normalize box coordinates
18
  frame_height = df["frame_height"]
19
  frame_width = df["frame_width"]
20
- df["box1_x_min"] = df["box1_x_min"] / frame_width
21
- df["box1_y_min"] = df["box1_y_min"] / frame_height
22
- df["box1_x_max"] = df["box1_x_max"] / frame_width
23
- df["box1_y_max"] = df["box1_y_max"] / frame_height
24
 
25
- df["box2_x_min"] = df["box2_x_min"] / frame_width
26
- df["box2_y_min"] = df["box2_y_min"] / frame_height
27
- df["box2_x_max"] = df["box2_x_max"] / frame_width
28
- df["box2_y_max"] = df["box2_y_max"] / frame_height
 
29
 
30
  # Normalize center coordinates
31
- df["center1_x"] = df["center1_x"] / frame_width
32
- df["center1_y"] = df["center1_y"] / frame_height
33
-
34
- df["center2_x"] = df["center2_x"] / frame_width
35
- df["center2_y"] = df["center2_y"] / frame_height
 
 
36
 
37
  # Normalize distances
38
  max_distance = np.sqrt(frame_width**2 + frame_height**2)
39
- df["distance"] = df["distance"] / max_distance
40
- df["relative_distance"] = df["relative_distance"] / max_distance
 
41
 
42
  # Drop confidence columns
43
  drop_columns = (
@@ -45,31 +46,28 @@ class ViolencePredictor:
45
  + [f"person2_kp{i}_conf" for i in range(17)]
46
  + [f"relative_kp{i}_conf" for i in range(17)]
47
  )
48
-
49
- existing_columns = [col for col in drop_columns if col in df.columns]
50
- df = df.drop(columns=existing_columns)
51
 
52
  # Normalize keypoints
53
  for i in range(17):
54
  for prefix in ["person1_kp", "person2_kp", "relative_kp"]:
55
- x_col = f"{prefix}{i}_x"
56
- y_col = f"{prefix}{i}_y"
 
 
57
 
58
- if x_col in df.columns:
59
- df[x_col] = df[x_col] / frame_width
60
- if y_col in df.columns:
61
- df[y_col] = df[y_col] / frame_height
 
 
 
 
 
 
 
62
 
63
- # Scale specific columns
64
- df["distance"] = self.scaler.fit_transform(df[["distance"]])
65
- df["relative_distance"] = self.scaler.fit_transform(df[["relative_distance"]])
66
- df["motion_average_speed"] = self.scaler.fit_transform(
67
- df[["motion_average_speed"]]
68
- )
69
- df["motion_motion_intensity"] = self.scaler.fit_transform(
70
- df[["motion_motion_intensity"]]
71
- )
72
  return df
73
-
74
- def predict(self, data):
75
- return 0
 
1
  import numpy as np
2
+ import pandas as pd
3
  from sklearn.preprocessing import MinMaxScaler
4
 
5
 
6
+ class DataPreprocessor:
7
  def __init__(self):
8
+ # Initialize scaler (use transform() only during inference)
9
  self.scaler = MinMaxScaler()
10
 
11
+ def preprocess_data(self, df: pd.DataFrame) -> pd.DataFrame:
 
 
 
 
12
  """
13
+ Preprocess the data by normalizing box coordinates, center coordinates,
14
+ distances, and keypoints.
15
  """
16
+ df = df.copy() # prevent modifying original
17
+
18
  # Normalize box coordinates
19
  frame_height = df["frame_height"]
20
  frame_width = df["frame_width"]
 
 
 
 
21
 
22
+ for prefix in ["box1", "box2"]:
23
+ for coord in ["x_min", "x_max"]:
24
+ df[f"{prefix}_{coord}"] = df[f"{prefix}_{coord}"] / frame_width
25
+ for coord in ["y_min", "y_max"]:
26
+ df[f"{prefix}_{coord}"] = df[f"{prefix}_{coord}"] / frame_height
27
 
28
  # Normalize center coordinates
29
+ for axis in ["x", "y"]:
30
+ df[f"center1_{axis}"] = df[f"center1_{axis}"] / (
31
+ frame_width if axis == "x" else frame_height
32
+ )
33
+ df[f"center2_{axis}"] = df[f"center2_{axis}"] / (
34
+ frame_width if axis == "x" else frame_height
35
+ )
36
 
37
  # Normalize distances
38
  max_distance = np.sqrt(frame_width**2 + frame_height**2)
39
+ for col in ["distance", "relative_distance"]:
40
+ if col in df.columns:
41
+ df[col] = df[col] / max_distance
42
 
43
  # Drop confidence columns
44
  drop_columns = (
 
46
  + [f"person2_kp{i}_conf" for i in range(17)]
47
  + [f"relative_kp{i}_conf" for i in range(17)]
48
  )
49
+ df = df.drop(
50
+ columns=[c for c in drop_columns if c in df.columns], errors="ignore"
51
+ )
52
 
53
  # Normalize keypoints
54
  for i in range(17):
55
  for prefix in ["person1_kp", "person2_kp", "relative_kp"]:
56
+ if f"{prefix}{i}_x" in df.columns:
57
+ df[f"{prefix}{i}_x"] = df[f"{prefix}{i}_x"] / frame_width
58
+ if f"{prefix}{i}_y" in df.columns:
59
+ df[f"{prefix}{i}_y"] = df[f"{prefix}{i}_y"] / frame_height
60
 
61
+ # Scale motion/distance columns
62
+ for col in [
63
+ "distance",
64
+ "relative_distance",
65
+ "motion_average_speed",
66
+ "motion_motion_intensity",
67
+ ]:
68
+ if col in df.columns:
69
+ df[col] = self.scaler.fit_transform(
70
+ df[[col]]
71
+ ) # change to transform() in production
72
 
 
 
 
 
 
 
 
 
 
73
  return df
 
 
 
{backend/utils β†’ services/video_data_extraction}/__init__.py RENAMED
File without changes
{backend/services β†’ services}/video_data_extraction/video_preprocessor.py RENAMED
@@ -2,8 +2,8 @@ import os
2
  import cv2
3
  import torch
4
  import pandas as pd
5
- from backend.feature_extraction.extractor import VideoFeatureExtractor
6
- from backend.utils.csv_utils import _create_interaction_row
7
 
8
 
9
  class VideoDataExtractor:
@@ -13,23 +13,23 @@ class VideoDataExtractor:
13
  def extract_video_data(
14
  self,
15
  video_path,
16
- output_csv_path,
17
  output_folder=None,
18
  show_video=False,
19
  save_video=False,
20
  ):
21
  """
22
- Extract data from a video file.
23
 
24
  Args:
25
  video_path: Path to input video
26
- output_csv_path: Path to save CSV output
27
- output_folder: Folder to save output video
28
  show_video: Whether to display video during processing
29
  save_video: Whether to save output video
30
 
31
  Returns:
32
- Tuple of (frame_width, frame_height, num_interactions)
33
  """
34
  cap = None
35
  video_writer = None
@@ -51,16 +51,13 @@ class VideoDataExtractor:
51
 
52
  video_name = os.path.splitext(os.path.basename(video_path))[0]
53
 
54
- # Set frame skip based on resolution
55
  batch_size, frame_skip = self.extractor.preprocessor.set_resolution_config(
56
  frame_width, frame_height
57
  )
58
  self.extractor.preprocessor.frame_skip = frame_skip
59
 
60
- print(f"Processing video: {frame_width}x{frame_height} at {fps} fps")
61
- print(f"Using frame_skip: {frame_skip}")
62
-
63
- # Initialize video writer if needed
64
  if output_folder and save_video:
65
  os.makedirs(output_folder, exist_ok=True)
66
  output_video_path = os.path.join(
@@ -73,7 +70,7 @@ class VideoDataExtractor:
73
  (frame_width, frame_height),
74
  )
75
 
76
- # Reset extractor for new video
77
  self.extractor.reset()
78
 
79
  # Process frames
@@ -83,13 +80,11 @@ class VideoDataExtractor:
83
  if not ret:
84
  break
85
 
86
- # Extract features
87
  frame_data, annotated_frame = self.extractor.extract_features(
88
  frame, frame_idx
89
  )
90
 
91
  if frame_data is not None:
92
- # Process interactions
93
  for interaction in frame_data["interactions"]:
94
  interaction_id = (
95
  interaction["person1_id"],
@@ -108,34 +103,30 @@ class VideoDataExtractor:
108
  )
109
  csv_data.append(row)
110
 
111
- # Write frame to output video
112
  if video_writer is not None and annotated_frame is not None:
113
  video_writer.write(annotated_frame)
114
 
115
- # Show video if enabled
116
  if show_video and annotated_frame is not None:
117
  cv2.imshow("Video Data Extraction", annotated_frame)
118
- key = cv2.waitKey(1) & 0xFF
119
- if key == ord("q"):
120
  break
121
 
122
- # Clear memory periodically
123
  if frame_idx % 100 == 0:
124
  torch.cuda.empty_cache()
125
 
 
126
  if csv_data:
127
  df = pd.DataFrame(csv_data)
128
-
129
- if os.path.exists(output_csv_path):
130
- # Append to existing CSV
131
- df.to_csv(output_csv_path, mode="a", header=False, index=False)
132
- print(f"Appended {len(csv_data)} interactions to {output_csv_path}")
133
- else:
134
- # Save new CSV
135
- df.to_csv(output_csv_path, index=False)
136
- print(f"Saved {len(csv_data)} interactions to {output_csv_path}")
137
-
138
- return frame_width, frame_height, len(csv_data)
139
 
140
  finally:
141
  if cap is not None:
 
2
  import cv2
3
  import torch
4
  import pandas as pd
5
+ from feature_extraction.extractor import VideoFeatureExtractor
6
+ from utils.csv_utils import _create_interaction_row
7
 
8
 
9
  class VideoDataExtractor:
 
13
  def extract_video_data(
14
  self,
15
  video_path,
16
+ output_csv_path=None,
17
  output_folder=None,
18
  show_video=False,
19
  save_video=False,
20
  ):
21
  """
22
+ Extract interaction data from a video file and return a DataFrame.
23
 
24
  Args:
25
  video_path: Path to input video
26
+ output_csv_path: Optional path to save CSV output
27
+ output_folder: Optional folder to save annotated video
28
  show_video: Whether to display video during processing
29
  save_video: Whether to save output video
30
 
31
  Returns:
32
+ pandas.DataFrame containing extracted interactions
33
  """
34
  cap = None
35
  video_writer = None
 
51
 
52
  video_name = os.path.splitext(os.path.basename(video_path))[0]
53
 
54
+ # Configure resolution-based settings
55
  batch_size, frame_skip = self.extractor.preprocessor.set_resolution_config(
56
  frame_width, frame_height
57
  )
58
  self.extractor.preprocessor.frame_skip = frame_skip
59
 
60
+ # Initialize video writer if required
 
 
 
61
  if output_folder and save_video:
62
  os.makedirs(output_folder, exist_ok=True)
63
  output_video_path = os.path.join(
 
70
  (frame_width, frame_height),
71
  )
72
 
73
+ # Reset extractor for a fresh start
74
  self.extractor.reset()
75
 
76
  # Process frames
 
80
  if not ret:
81
  break
82
 
 
83
  frame_data, annotated_frame = self.extractor.extract_features(
84
  frame, frame_idx
85
  )
86
 
87
  if frame_data is not None:
 
88
  for interaction in frame_data["interactions"]:
89
  interaction_id = (
90
  interaction["person1_id"],
 
103
  )
104
  csv_data.append(row)
105
 
 
106
  if video_writer is not None and annotated_frame is not None:
107
  video_writer.write(annotated_frame)
108
 
 
109
  if show_video and annotated_frame is not None:
110
  cv2.imshow("Video Data Extraction", annotated_frame)
111
+ if cv2.waitKey(1) & 0xFF == ord("q"):
 
112
  break
113
 
 
114
  if frame_idx % 100 == 0:
115
  torch.cuda.empty_cache()
116
 
117
+ # βœ… Return only the DataFrame
118
  if csv_data:
119
  df = pd.DataFrame(csv_data)
120
+ if output_csv_path:
121
+ df.to_csv(
122
+ output_csv_path,
123
+ mode="a" if os.path.exists(output_csv_path) else "w",
124
+ header=not os.path.exists(output_csv_path),
125
+ index=False,
126
+ )
127
+ return df
128
+ else:
129
+ return pd.DataFrame() # empty DataFrame if nothing found
 
130
 
131
  finally:
132
  if cap is not None:
utils/__init__.py ADDED
File without changes
{backend/utils β†’ utils}/csv_utils.py RENAMED
File without changes
{backend/utils β†’ utils}/gpu.py RENAMED
File without changes
{backend/utils β†’ utils}/id_utils.py RENAMED
File without changes
{backend/utils β†’ utils}/interaction_utils.py RENAMED
File without changes
{backend/utils β†’ utils}/iou_utils.py RENAMED
File without changes
{backend/utils β†’ utils}/motion_utils.py RENAMED
File without changes
{backend/utils β†’ utils}/visualizer.py RENAMED
File without changes