jukrapopk commited on
Commit
90eeca9
·
1 Parent(s): f0e37b4

python codebase + dockerfile + makefile

Browse files
Files changed (8) hide show
  1. .dockerignore +6 -0
  2. .gitignore +4 -0
  3. Dockerfile +20 -0
  4. Makefile +22 -0
  5. requirements.txt +8 -0
  6. src/app.py +71 -0
  7. src/utils.py +72 -0
  8. videos/classroom.mp4 +3 -0
.dockerignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ # Docker related
2
+ Dockerfile
3
+ docker-compose.yml
4
+ .dockerignore
5
+
6
+ ./postgres_data/*
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ .output
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+
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
+
12
+ COPY requirements.txt .
13
+
14
+ RUN pip install --no-cache-dir -r requirements.txt
15
+
16
+ COPY . .
17
+
18
+ EXPOSE 5000-5100
19
+
20
+ CMD ["python", "src/app.py"]
Makefile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ IMAGE_NAME := mock-video-feed
2
+ CONTAINER_NAME := mock-video-feed-container
3
+ DOCKERFILE := Dockerfile
4
+
5
+ build:
6
+ docker build -t $(IMAGE_NAME) -f $(DOCKERFILE) .
7
+
8
+ run:
9
+ docker stop $(CONTAINER_NAME) || true
10
+ docker rm $(CONTAINER_NAME) || true
11
+ docker run --name $(CONTAINER_NAME) -d -p 5000-5100:5000-5100 $(IMAGE_NAME)
12
+
13
+ stop:
14
+ docker stop $(CONTAINER_NAME) || true
15
+ docker rm $(CONTAINER_NAME) || true
16
+
17
+ clean:
18
+ docker rmi $(IMAGE_NAME) || true
19
+
20
+ rebuild: stop clean build
21
+
22
+ .PHONY: build run stop clean rebuild
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ opencv-python==4.11.0.86
2
+ ultralytics==8.3.133
3
+ Flask==3.1.0
4
+ flask-cors==5.0.1
5
+ PyThreadKiller==3.0.6
6
+ vidgear==0.3.3
7
+ selenium==4.32.0
8
+ webdriver_manager==4.0.2
src/app.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from vidgear.gears import CamGear
2
+ from flask import Flask
3
+ import threading
4
+ from utils import start_html_stream
5
+ import os
6
+ import time
7
+ import cv2
8
+ import socket
9
+
10
+ def get_video_file():
11
+ return os.path.join("videos", "classroom.mp4")
12
+
13
+ video_file = get_video_file()
14
+
15
+ cap = cv2.VideoCapture(video_file)
16
+ framerate = cap.get(cv2.CAP_PROP_FPS)
17
+ cap.release()
18
+
19
+ stream = CamGear(source=video_file).start()
20
+ app = Flask(__name__)
21
+ output_frame = [None]
22
+ lock = threading.Lock()
23
+
24
+ def get_available_port(start_port):
25
+ port = start_port
26
+ while True:
27
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
28
+ if s.connect_ex(('localhost', port)) != 0:
29
+ return port
30
+ port += 1
31
+
32
+ PORT = get_available_port(5000)
33
+
34
+ @app.route('/')
35
+ def html_stream():
36
+ return start_html_stream(output_frame, lock)
37
+
38
+ def start_flask():
39
+ app.run(host="0.0.0.0", port=PORT, debug=True, use_reloader=False)
40
+
41
+ def process_stream():
42
+ global stream
43
+ while True:
44
+ start_time = time.time()
45
+ frame = stream.read()
46
+ if frame is None:
47
+ stream.stop()
48
+ stream = CamGear(source=video_file).start()
49
+ continue
50
+
51
+ with lock:
52
+ output_frame[0] = frame.copy()
53
+
54
+ elapsed_time = time.time() - start_time
55
+ sleep_time = max(1.0 / framerate - elapsed_time, 0)
56
+ time.sleep(sleep_time)
57
+
58
+ def main():
59
+ flask_thread = threading.Thread(target=start_flask)
60
+ flask_thread.daemon = True
61
+ flask_thread.start()
62
+
63
+ stream_thread = threading.Thread(target=process_stream)
64
+ stream_thread.daemon = True
65
+ stream_thread.start()
66
+
67
+ flask_thread.join()
68
+ stream_thread.join()
69
+
70
+ if __name__ == "__main__":
71
+ main()
src/utils.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from selenium import webdriver
4
+ from selenium.webdriver.chrome.service import Service
5
+ from webdriver_manager.chrome import ChromeDriverManager
6
+ from selenium.webdriver.chrome.options import Options
7
+ import time
8
+ from flask import Response
9
+ import cv2
10
+ import time
11
+
12
+ def start_html_stream(output_frame, lock):
13
+ def generate():
14
+ while True:
15
+ with lock:
16
+ if output_frame[0] is None:
17
+ # TODO: Investigate why if remove this print or time.sleep (but not both at the same time), the stream does not work
18
+ # time.sleep(0.5)
19
+ print(f"{time.time()} - Frame is None.")
20
+ continue
21
+ (flag, encoded_image) = cv2.imencode(".jpg", output_frame[0])
22
+ if not flag:
23
+ continue
24
+ yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + bytearray(encoded_image) + b'\r\n')
25
+ return Response(generate(), mimetype="multipart/x-mixed-replace; boundary=frame")
26
+
27
+ def capture_webpage(driver):
28
+ screenshot = driver.get_screenshot_as_png()
29
+ image = np.frombuffer(screenshot, dtype=np.uint8)
30
+ image = cv2.imdecode(image, cv2.IMREAD_COLOR)
31
+
32
+ height, width, _ = image.shape
33
+ new_width = 1920
34
+ new_height = int(new_width * height / width)
35
+
36
+ image = cv2.resize(image, (new_width, new_height))
37
+
38
+ if new_height > 1080:
39
+ start_y = (new_height - 1080) // 2
40
+ image = image[start_y:start_y + 1080, :]
41
+
42
+ return image
43
+
44
+ def get_webpage_frames(stream_url):
45
+ chrome_options = Options()
46
+ chrome_options.add_argument("--headless")
47
+ chrome_options.add_argument("--window-size=1920x1080")
48
+
49
+ driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=chrome_options)
50
+ driver.get(stream_url)
51
+
52
+ frames_per_second = 10
53
+ frame_interval = 1 / frames_per_second
54
+
55
+ try:
56
+ while True:
57
+ image = capture_webpage(driver)
58
+ yield image
59
+ time.sleep(frame_interval)
60
+ finally:
61
+ driver.quit()
62
+
63
+ def get_mjpeg_frames(stream_url):
64
+ cap = cv2.VideoCapture(stream_url)
65
+
66
+ while True:
67
+ ret, frame = cap.read()
68
+ if not ret:
69
+ break
70
+ yield frame
71
+
72
+ cap.release()
videos/classroom.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a69bd5e39ff0e74286d13bcbfdadddd307b85decd2d9853db7518e0028785e31
3
+ size 13548133