rizkynindra commited on
Commit
18ac549
·
1 Parent(s): c0af61f

huggingface ribet

Browse files
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .idea
.idea/copilot.data.migration.ask.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="AskMigrationStateService">
4
+ <option name="migrationStatus" value="COMPLETED" />
5
+ </component>
6
+ </project>
.idea/copilot.data.migration.ask2agent.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Ask2AgentMigrationStateService">
4
+ <option name="migrationStatus" value="COMPLETED" />
5
+ </component>
6
+ </project>
.idea/misc.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="Python 3.10 (squadrone)" />
5
+ </component>
6
+ </project>
.idea/squadrone-huggingface.iml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module version="4">
3
+ <component name="PyDocumentationSettings">
4
+ <option name="format" value="PLAIN" />
5
+ <option name="myDocStringFormat" value="Plain" />
6
+ </component>
7
+ </module>
.idea/workspace.xml CHANGED
@@ -4,7 +4,26 @@
4
  <option name="autoReloadType" value="SELECTIVE" />
5
  </component>
6
  <component name="ChangeListManager">
7
- <list default="true" id="82a48ce3-b0fe-4383-a5c5-1857889128ea" name="Changes" comment="" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  <option name="SHOW_DIALOG" value="false" />
9
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
10
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -20,9 +39,9 @@
20
  <component name="Git.Settings">
21
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
22
  </component>
23
- <component name="ProjectColorInfo"><![CDATA[{
24
- "associatedIndex": 3
25
- }]]></component>
26
  <component name="ProjectId" id="33zr2jYSXA7j2m2NetACLNED8HB" />
27
  <component name="ProjectViewState">
28
  <option name="hideEmptyMiddlePackages" value="true" />
@@ -34,21 +53,24 @@
34
  "RunOnceActivity.ShowReadmeOnStart": "true",
35
  "RunOnceActivity.git.unshallow": "true",
36
  "git-widget-placeholder": "main",
37
- "last_opened_file_path": "/Users/rizkynindra.sukma/PycharmProjects/squadrone"
38
  }
39
  }]]></component>
40
  <component name="RecentsManager">
41
  <key name="CopyFile.RECENT_KEYS">
 
 
42
  <recent name="$PROJECT_DIR$" />
43
  </key>
44
  </component>
45
  <component name="SharedIndexes">
46
  <attachedChunks>
47
  <set>
48
- <option value="bundled-python-sdk-82724e2b1abb-e2d783800521-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-251.28293.52" />
49
  </set>
50
  </attachedChunks>
51
  </component>
 
52
  <component name="TaskManager">
53
  <task active="true" id="Default" summary="Default task">
54
  <changelist id="82a48ce3-b0fe-4383-a5c5-1857889128ea" name="Changes" comment="" />
 
4
  <option name="autoReloadType" value="SELECTIVE" />
5
  </component>
6
  <component name="ChangeListManager">
7
+ <list default="true" id="82a48ce3-b0fe-4383-a5c5-1857889128ea" name="Changes" comment="">
8
+ <change afterPath="$PROJECT_DIR$/.idea/copilot.data.migration.ask.xml" afterDir="false" />
9
+ <change afterPath="$PROJECT_DIR$/.idea/copilot.data.migration.ask2agent.xml" afterDir="false" />
10
+ <change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
11
+ <change afterPath="$PROJECT_DIR$/.idea/squadrone-huggingface.iml" afterDir="false" />
12
+ <change afterPath="$PROJECT_DIR$/model/best_26102025.pt" afterDir="false" />
13
+ <change afterPath="$PROJECT_DIR$/static/share_screen.js" afterDir="false" />
14
+ <change afterPath="$PROJECT_DIR$/static/style.css" afterDir="false" />
15
+ <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
16
+ <change beforePath="$PROJECT_DIR$/Dockerfile" beforeDir="false" afterPath="$PROJECT_DIR$/Dockerfile" afterDir="false" />
17
+ <change beforePath="$PROJECT_DIR$/app2.py" beforeDir="false" afterPath="$PROJECT_DIR$/app2.py" afterDir="false" />
18
+ <change beforePath="$PROJECT_DIR$/model/best (1).pt" beforeDir="false" />
19
+ <change beforePath="$PROJECT_DIR$/model/best.pt" beforeDir="false" />
20
+ <change beforePath="$PROJECT_DIR$/model/best2.pt" beforeDir="false" />
21
+ <change beforePath="$PROJECT_DIR$/model/best5.pt" beforeDir="false" />
22
+ <change beforePath="$PROJECT_DIR$/model/best_100.pt" beforeDir="false" />
23
+ <change beforePath="$PROJECT_DIR$/model/best_4.pt" beforeDir="false" />
24
+ <change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
25
+ <change beforePath="$PROJECT_DIR$/templates/screen_share.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/screen_share.html" afterDir="false" />
26
+ </list>
27
  <option name="SHOW_DIALOG" value="false" />
28
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
29
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
 
39
  <component name="Git.Settings">
40
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
41
  </component>
42
+ <component name="ProjectColorInfo">{
43
+ &quot;associatedIndex&quot;: 3
44
+ }</component>
45
  <component name="ProjectId" id="33zr2jYSXA7j2m2NetACLNED8HB" />
46
  <component name="ProjectViewState">
47
  <option name="hideEmptyMiddlePackages" value="true" />
 
53
  "RunOnceActivity.ShowReadmeOnStart": "true",
54
  "RunOnceActivity.git.unshallow": "true",
55
  "git-widget-placeholder": "main",
56
+ "last_opened_file_path": "D:/SIDE/MACHINE LEARNING PROJECT/squadrone-huggingface"
57
  }
58
  }]]></component>
59
  <component name="RecentsManager">
60
  <key name="CopyFile.RECENT_KEYS">
61
+ <recent name="D:\SIDE\MACHINE LEARNING PROJECT\squadrone-huggingface" />
62
+ <recent name="D:\SIDE\MACHINE LEARNING PROJECT\squadrone-huggingface\model" />
63
  <recent name="$PROJECT_DIR$" />
64
  </key>
65
  </component>
66
  <component name="SharedIndexes">
67
  <attachedChunks>
68
  <set>
69
+ <option value="bundled-python-sdk-a5bc9544c897-aa17d162503b-com.jetbrains.pycharm.community.sharedIndexes.bundled-PC-243.23654.177" />
70
  </set>
71
  </attachedChunks>
72
  </component>
73
+ <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
74
  <component name="TaskManager">
75
  <task active="true" id="Default" summary="Default task">
76
  <changelist id="82a48ce3-b0fe-4383-a5c5-1857889128ea" name="Changes" comment="" />
Dockerfile CHANGED
@@ -24,4 +24,5 @@ EXPOSE 7860
24
  ENV PORT=7860
25
 
26
  # Run Flask app
27
- CMD ["python", "app2.py"]
 
 
24
  ENV PORT=7860
25
 
26
  # Run Flask app
27
+ # CMD ["python", "app2.py"]
28
+ CMD ["waitress-serve", "--port=7860", "app2:app"]
app2.py CHANGED
@@ -7,6 +7,15 @@ import io
7
  from PIL import Image
8
  import time
9
  import torch
 
 
 
 
 
 
 
 
 
10
 
11
  print(f"PyTorch CUDA available: {torch.cuda.is_available()}")
12
  print(f"CUDA device count: {torch.cuda.device_count()}")
@@ -14,13 +23,14 @@ if torch.cuda.is_available():
14
  print(f"CUDA device name: {torch.cuda.get_device_name(0)}")
15
 
16
  app = Flask(__name__)
 
17
 
18
  @app.route('/audio/<path:filename>')
19
  def serve_audio(filename):
20
  return send_from_directory('audio', filename)
21
 
22
  # Load YOLO model
23
- model = YOLO('model/best_100_fix.pt')
24
 
25
  #use GPU
26
  # model.to('cuda')
@@ -35,13 +45,13 @@ human_detection_state = {
35
  'first_detected_at': None,
36
  'is_alarm_active': False,
37
  'last_detection_time': 0,
38
- 'detection_threshold': 2.0 # 2 seconds
39
  }
40
 
41
 
42
  @app.route('/')
43
  def index():
44
- return render_template('screen_share_bck.html')
45
 
46
 
47
  @app.route('/detect', methods=['POST'])
@@ -87,8 +97,13 @@ def detect():
87
  x1, y1, x2, y2 = map(int, box)
88
  class_name = model.names[int(cls)]
89
 
 
 
 
 
 
90
  # Check if this is a human/person detection
91
- if class_name.lower() in ['person', 'human']:
92
  human_detected = True
93
 
94
  detections.append({
@@ -161,4 +176,4 @@ def reset_alarm():
161
 
162
  if __name__ == '__main__':
163
  # Use threaded mode for better performance
164
- app.run(debug=True, host='0.0.0.0', port=7860, threaded=True)
 
7
  from PIL import Image
8
  import time
9
  import torch
10
+ import logging
11
+ from datetime import datetime
12
+
13
+ # Configure logging for manusia detection
14
+ logging.basicConfig(
15
+ filename='manusia_detection.log',
16
+ level=logging.INFO,
17
+ format='%(asctime)s - %(message)s'
18
+ )
19
 
20
  print(f"PyTorch CUDA available: {torch.cuda.is_available()}")
21
  print(f"CUDA device count: {torch.cuda.device_count()}")
 
23
  print(f"CUDA device name: {torch.cuda.get_device_name(0)}")
24
 
25
  app = Flask(__name__)
26
+ app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024 # Limit uploads to 5MB
27
 
28
  @app.route('/audio/<path:filename>')
29
  def serve_audio(filename):
30
  return send_from_directory('audio', filename)
31
 
32
  # Load YOLO model
33
+ model = YOLO('model/best_26102025.pt')
34
 
35
  #use GPU
36
  # model.to('cuda')
 
45
  'first_detected_at': None,
46
  'is_alarm_active': False,
47
  'last_detection_time': 0,
48
+ 'detection_threshold': 0.05 # 1 seconds
49
  }
50
 
51
 
52
  @app.route('/')
53
  def index():
54
+ return render_template('screen_share.html')
55
 
56
 
57
  @app.route('/detect', methods=['POST'])
 
97
  x1, y1, x2, y2 = map(int, box)
98
  class_name = model.names[int(cls)]
99
 
100
+ # save to log if manusia detected
101
+ if class_name.lower() == 'manusia':
102
+ detection_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
103
+ logging.info(f"Detected 'manusia' at {detection_time} with confidence {score:.4f}")
104
+
105
  # Check if this is a human/person detection
106
+ if class_name.lower() == 'manusia':
107
  human_detected = True
108
 
109
  detections.append({
 
176
 
177
  if __name__ == '__main__':
178
  # Use threaded mode for better performance
179
+ app.run(debug=True, host='0.0.0.0', port=5000, threaded=True)
model/best.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:889a947d31042a6198ec9ad3c70efb9555e03b40789fce9698303558df1fabe6
3
- size 5445523
 
 
 
 
model/best2.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:24814a8738c47ded7f1bbffad09987f641454830dd449fa257537cd91914bdc0
3
- size 5450643
 
 
 
 
model/best5.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:c8420a7d77cc16e14164ca6eb1dc2e0e8887752c074b13979bade652c783b300
3
- size 5448147
 
 
 
 
model/best_100.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:e59b8996303a92da0351907dbb32b75a3d347ad1cdf888a43be41bf32d842a42
3
- size 5457107
 
 
 
 
model/{best (1).pt → best_26102025.pt} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:af5a0a7f1436251cc471db30d5f40caafc8af639bba58f396279f383a38e1169
3
- size 5444371
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b5598c88b08ef6b75a2927ca3931f3491f8ec71e9d4bc8422de4521e6e4d822e
3
+ size 22508650
model/best_4.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:0bbff7d27045c9585a55a4ee3b19a495e2cf07c5339d1eedd3cfed0aebe38852
3
- size 5448147
 
 
 
 
requirements.txt CHANGED
@@ -1,113 +1,60 @@
1
- altair==5.5.0
2
- altgraph==0.17.4
3
- appnope==0.1.4
4
- asttokens==3.0.0
5
- attrs==25.1.0
6
- backcall==0.2.0
7
- beautifulsoup4==4.13.3
8
- bleach==6.2.0
9
- blinker==1.9.0
10
- bottle==0.13.2
11
- cachetools==5.5.2
12
- certifi==2025.1.31
13
- cffi==1.17.1
14
- charset-normalizer==3.4.1
15
- click==8.1.8
16
- clr_loader==0.2.7.post0
17
- colorama==0.4.6
18
- contourpy==1.3.1
19
- cycler==0.12.1
20
- decorator==5.2.1
21
- defusedxml==0.7.1
22
- docopt==0.6.2
23
- executing==2.2.0
24
- fastjsonschema==2.21.1
25
- filelock==3.17.0
26
- flask
27
- fonttools==4.56.0
28
- fsspec==2025.3.0
29
- gitdb==4.0.12
30
- GitPython==3.1.44
31
- idna==3.10
32
- ipython==8.12.3
33
- jedi==0.19.2
34
- Jinja2==3.1.6
35
- jsonschema==4.23.0
36
- jsonschema-specifications==2024.10.1
37
- jupyter_client==8.6.3
38
- jupyter_core==5.7.2
39
- jupyterlab_pygments==0.3.0
40
- kiwisolver==1.4.8
41
- lap==0.5.12
42
- macholib==1.16.3
43
- MarkupSafe==3.0.2
44
- matplotlib==3.10.1
45
- matplotlib-inline==0.1.7
46
- mistune==3.1.2
47
- mpmath==1.3.0
48
- narwhals==1.29.1
49
- nbclient==0.10.2
50
- nbconvert==7.16.6
51
- nbformat==5.10.4
52
- networkx==3.4.2
53
- numpy==2.1.1
54
- opencv-python-headless
55
- packaging==24.2
56
- pandas==2.2.3
57
- pandocfilters==1.5.1
58
- parso==0.8.4
59
- pefile==2023.2.7
60
- pexpect==4.9.0
61
- pickleshare==0.7.5
62
- pillow==11.1.0
63
- pipreqs==0.5.0
64
- platformdirs==4.3.6
65
- prompt_toolkit==3.0.50
66
- protobuf==5.29.3
67
- proxy_tools==0.1.0
68
- psutil==7.0.0
69
- ptyprocess==0.7.0
70
- pure_eval==0.2.3
71
- py-cpuinfo==9.0.0
72
- pyarrow==19.0.1
73
- pycparser==2.22
74
- pydeck==0.9.1
75
- Pygments==2.19.1
76
- pyinstaller==6.12.0
77
- pyinstaller-hooks-contrib==2025.1
78
- pyparsing==3.2.1
79
- python-dateutil==2.9.0.post0
80
- pythonnet==3.0.5
81
- pytz==2025.1
82
- pywebview==5.4
83
- PyYAML==6.0.2
84
- pyzmq==26.2.1
85
- referencing==0.36.2
86
- requests==2.32.3
87
- rpds-py==0.23.1
88
- scipy==1.15.2
89
- seaborn==0.13.2
90
- six==1.17.0
91
- smmap==5.0.2
92
- soupsieve==2.6
93
- stack-data==0.6.3
94
- streamlit==1.43.1
95
- streamlit-desktop-app==0.3.4
96
- sympy==1.13.1
97
- tenacity==9.0.0
98
- tinycss2==1.4.0
99
- toml==0.10.2
100
- torch==2.6.0
101
- torchvision==0.21.0
102
- tornado==6.4.2
103
- tqdm==4.67.1
104
- traitlets==5.14.3
105
- typing_extensions==4.12.2
106
- tzdata==2025.1
107
- ultralytics==8.3.85
108
- ultralytics-thop==2.0.14
109
- urllib3==2.3.0
110
- watchdog==6.0.0
111
- wcwidth==0.2.13
112
- webencodings==0.5.1
113
- yarg==0.1.9
 
1
+ opencv-python~=4.11.0.86
2
+ numpy~=2.1.1
3
+ defusedxml~=0.7.1
4
+ ipython~=8.12.3
5
+ future~=1.0.0
6
+ pillow~=11.1.0
7
+ pip~=23.2.1
8
+ attrs~=25.1.0
9
+ wheel~=0.41.2
10
+ protobuf~=5.29.3
11
+ filelock~=3.17.0
12
+ torch~=2.6.0
13
+ ultralytics-thop~=2.0.14
14
+ PyYAML~=6.0.2
15
+ typing_extensions~=4.12.2
16
+ MarkupSafe~=3.0.2
17
+ Werkzeug~=3.1.3
18
+ click~=8.1.8
19
+ Jinja2~=3.1.6
20
+ blinker~=1.9.0
21
+ itsdangerous~=2.2.0
22
+ charset-normalizer~=3.4.1
23
+ setuptools~=68.2.0
24
+ pytz~=2025.1
25
+ cffi~=1.17.1
26
+ pyinstaller~=6.12.0
27
+ scipy~=1.15.2
28
+ matplotlib~=3.10.1
29
+ mpmath~=1.3.0
30
+ sympy~=1.13.1
31
+ packaging~=24.2
32
+ platformdirs~=4.3.6
33
+ decorator~=5.2.1
34
+ ultralytics~=8.3.85
35
+ tqdm~=4.67.1
36
+ torchvision~=0.21.0
37
+ psutil~=7.0.0
38
+ colorama~=0.4.6
39
+ networkx~=3.4.2
40
+ urllib3~=2.3.0
41
+ fsspec~=2025.3.0
42
+ requests~=2.32.3
43
+ pyarrow~=19.0.1
44
+ pandas~=2.2.3
45
+ altair~=5.5.0
46
+ gevent~=24.11.1
47
+ pywin32~=308
48
+ certifi~=2025.1.31
49
+ six~=1.17.0
50
+ python-dateutil~=2.9.0.post0
51
+ idna~=3.10
52
+ contourpy~=1.3.1
53
+ fonttools~=4.56.0
54
+ pyparsing~=3.2.1
55
+ cycler~=0.12.1
56
+ tornado~=6.4.2
57
+ kiwisolver~=1.4.8
58
+ Pygments~=2.19.1
59
+ lap~=0.5.12
60
+ Flask~=3.1.2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static/share_screen.js ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const startButton = document.getElementById('startButton');
2
+ const stopButton = document.getElementById('stopButton');
3
+ const startDetectionButton = document.getElementById('startDetectionButton');
4
+ const pauseDetectionButton = document.getElementById('pauseDetectionButton');
5
+ const screenVideo = document.getElementById('screenVideo');
6
+ const statusElement = document.getElementById('status');
7
+ const detectionCanvas = document.getElementById('detectionCanvas');
8
+ const detectionResults = document.getElementById('detectionResults');
9
+ const fpsCounter = document.getElementById('fpsCounter');
10
+ const humanAlarm = document.getElementById('humanAlarm');
11
+ const toggleBoundingBoxButton = document.getElementById('toggleBoundingBoxButton');
12
+
13
+ let mediaStream = null;
14
+ let capturedFrame = null;
15
+ let detectionActive = false;
16
+ let detectionInProgress = false;
17
+ let animationFrameId = null;
18
+ let lastDetectionTime = 0;
19
+ let frameCount = 0;
20
+ let lastFpsUpdateTime = 0;
21
+ let alarmActive = false;
22
+ let alarmTimeoutId = null;
23
+ let showAllBoundingBoxes = false;
24
+ let latestDetections = [];
25
+
26
+ toggleBoundingBoxButton.addEventListener('click', function() {
27
+ showAllBoundingBoxes = !showAllBoundingBoxes;
28
+ this.textContent = showAllBoundingBoxes ? 'Show Only "manusia"' : 'Show All';
29
+ });
30
+
31
+ const humanDetectionAudio = new Audio('/audio/Danger Alarm.mp3');
32
+ const MIN_DETECTION_INTERVAL = 100; // ms between detection requests
33
+
34
+ startButton.addEventListener('click', async () => {
35
+ try {
36
+ statusElement.textContent = 'Requesting screen access...';
37
+
38
+ mediaStream = await navigator.mediaDevices.getDisplayMedia({
39
+ video: { cursor: "always" },
40
+ audio: false
41
+ });
42
+
43
+ screenVideo.srcObject = mediaStream;
44
+
45
+ screenVideo.onloadedmetadata = () => {
46
+ detectionCanvas.width = screenVideo.videoWidth;
47
+ detectionCanvas.height = screenVideo.videoHeight;
48
+ };
49
+
50
+ startButton.disabled = true;
51
+ stopButton.disabled = false;
52
+ startDetectionButton.disabled = false;
53
+ toggleBoundingBoxButton.disabled = false;
54
+
55
+ statusElement.textContent = 'Screen sharing active';
56
+
57
+ mediaStream.getVideoTracks()[0].addEventListener('ended', () => {
58
+ stopScreenSharing();
59
+ });
60
+
61
+ } catch (error) {
62
+ console.error('Error accessing screen:', error);
63
+ statusElement.textContent = `Error: ${error.message || 'Could not access screen'}`;
64
+ }
65
+ });
66
+
67
+ stopButton.addEventListener('click', stopScreenSharing);
68
+
69
+ function stopScreenSharing() {
70
+ stopDetection();
71
+
72
+ if (mediaStream) {
73
+ mediaStream.getTracks().forEach(track => track.stop());
74
+ screenVideo.srcObject = null;
75
+ }
76
+
77
+ startButton.disabled = false;
78
+ stopButton.disabled = true;
79
+ startDetectionButton.disabled = true;
80
+ pauseDetectionButton.disabled = true;
81
+ toggleBoundingBoxButton.disabled = true;
82
+ statusElement.textContent = 'Screen sharing stopped';
83
+ }
84
+
85
+ startDetectionButton.addEventListener('click', startDetection);
86
+ pauseDetectionButton.addEventListener('click', pauseDetection);
87
+
88
+ function startDetection() {
89
+ if (!screenVideo.srcObject) {
90
+ statusElement.textContent = 'No video stream available';
91
+ return;
92
+ }
93
+
94
+ detectionActive = true;
95
+ startDetectionButton.disabled = true;
96
+ pauseDetectionButton.disabled = false;
97
+ toggleBoundingBoxButton.disabled = false;
98
+ statusElement.textContent = 'Real-time detection active';
99
+
100
+ lastFpsUpdateTime = performance.now();
101
+ frameCount = 0;
102
+ detectLoop();
103
+ }
104
+
105
+ function pauseDetection() {
106
+ detectionActive = false;
107
+ startDetectionButton.disabled = false;
108
+ pauseDetectionButton.disabled = true;
109
+ statusElement.textContent = 'Detection paused';
110
+
111
+ if (animationFrameId) {
112
+ cancelAnimationFrame(animationFrameId);
113
+ animationFrameId = null;
114
+ }
115
+
116
+ if (alarmActive) {
117
+ resetAlarm();
118
+ }
119
+ }
120
+
121
+ function stopDetection() {
122
+ detectionActive = false;
123
+ if (animationFrameId) {
124
+ cancelAnimationFrame(animationFrameId);
125
+ animationFrameId = null;
126
+ }
127
+ }
128
+
129
+ function captureVideoFrame() {
130
+ if (!screenVideo.srcObject) {
131
+ return false;
132
+ }
133
+
134
+ const canvas = document.createElement('canvas');
135
+ canvas.width = detectionCanvas.width;
136
+ canvas.height = detectionCanvas.height;
137
+ const ctx = canvas.getContext('2d');
138
+ ctx.drawImage(screenVideo, 0, 0, canvas.width, canvas.height);
139
+ capturedFrame = canvas.toDataURL('image/jpeg', 0.7);
140
+ return true;
141
+ }
142
+
143
+ async function detectObjects() {
144
+ if (!capturedFrame || detectionInProgress) {
145
+ return;
146
+ }
147
+
148
+ try {
149
+ detectionInProgress = true;
150
+
151
+ const response = await fetch('/detect', {
152
+ method: 'POST',
153
+ headers: { 'Content-Type': 'application/json' },
154
+ body: JSON.stringify({ image: capturedFrame })
155
+ });
156
+
157
+ if (!response.ok) {
158
+ throw new Error(`Server returned ${response.status}`);
159
+ }
160
+
161
+ const result = await response.json();
162
+
163
+ if (result.success) {
164
+ displayDetectionResults(result);
165
+ latestDetections = result.detections;
166
+ handleAlarmStatus(result.alarm);
167
+ } else {
168
+ throw new Error(result.error || 'Detection failed');
169
+ }
170
+
171
+ } catch (error) {
172
+ console.error('Error in detection:', error);
173
+ statusElement.textContent = `Error: ${error.message}`;
174
+ } finally {
175
+ detectionInProgress = false;
176
+ }
177
+ }
178
+
179
+ function detectLoop() {
180
+ if (!detectionActive) return;
181
+
182
+ frameCount++;
183
+ const now = performance.now();
184
+ const elapsed = now - lastFpsUpdateTime;
185
+
186
+ if (elapsed >= 1000) {
187
+ const fps = Math.round((frameCount / elapsed) * 1000);
188
+ fpsCounter.textContent = `${fps} FPS`;
189
+ frameCount = 0;
190
+ lastFpsUpdateTime = now;
191
+ }
192
+
193
+ const ctx = detectionCanvas.getContext('2d');
194
+ ctx.clearRect(0, 0, detectionCanvas.width, detectionCanvas.height);
195
+ ctx.drawImage(screenVideo, 0, 0, detectionCanvas.width, detectionCanvas.height);
196
+
197
+ if (latestDetections && latestDetections.length) {
198
+ drawDetectionBoxes(latestDetections);
199
+ }
200
+
201
+ if (!detectionInProgress && now - lastDetectionTime >= MIN_DETECTION_INTERVAL) {
202
+ if (captureVideoFrame()) {
203
+ lastDetectionTime = now;
204
+ detectObjects();
205
+ }
206
+ }
207
+
208
+ animationFrameId = requestAnimationFrame(detectLoop);
209
+ }
210
+
211
+ function handleAlarmStatus(alarmStatus) {
212
+ if (alarmStatus.active) {
213
+ if (!alarmActive) {
214
+ triggerAlarm();
215
+ }
216
+ } else {
217
+ if (alarmActive) {
218
+ resetAlarm();
219
+ }
220
+ }
221
+ }
222
+
223
+ function triggerAlarm() {
224
+ if (alarmActive) return;
225
+ alarmActive = true;
226
+ humanAlarm.classList.add('alarm-active');
227
+ humanDetectionAudio.currentTime = 0;
228
+ humanDetectionAudio.play().catch(err => console.log('Audio play error:', err));
229
+
230
+ // stop alarm after 3 seconds
231
+ if (alarmTimeoutId) clearTimeout(alarmTimeoutId);
232
+ alarmTimeoutId = setTimeout(() => {
233
+ resetAlarm();
234
+ }, 3000);
235
+ }
236
+
237
+ function resetAlarm() {
238
+ alarmActive = false;
239
+ humanAlarm.classList.remove('alarm-active');
240
+ humanDetectionAudio.pause();
241
+ humanDetectionAudio.currentTime = 0;
242
+ if (alarmTimeoutId) {
243
+ clearTimeout(alarmTimeoutId);
244
+ alarmTimeoutId = null;
245
+ }
246
+
247
+ fetch('/reset_alarm', {
248
+ method: 'POST',
249
+ headers: { 'Content-Type': 'application/json' }
250
+ }).catch(err => console.log('Error resetting alarm on server:', err));
251
+ }
252
+
253
+ function displayDetectionResults(result) {
254
+ let html = '<h3>Detection Results</h3>';
255
+
256
+ const manusiaDetections = result.detections
257
+ ? result.detections.filter(detection => detection.class === 'manusia')
258
+ : [];
259
+
260
+ const now = new Date();
261
+ const pad = n => n.toString().padStart(2, '0');
262
+ const detection_time = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
263
+
264
+
265
+ if (manusiaDetections.length) {
266
+ html += '<ul>';
267
+ manusiaDetections.forEach(detection => {
268
+ html += `Detected <strong>${detection.class} at ${detection_time} with confidence (${(detection.confidence * 100).toFixed(2)}%)</strong>`;
269
+ });
270
+ html += '</ul>';
271
+ } else {
272
+ html += '<p>No manusia detected.</p>';
273
+ }
274
+
275
+ detectionResults.innerHTML = html;
276
+ }
277
+
278
+ function drawDetectionBoxes(detections) {
279
+ if (!detections || !detections.length) return;
280
+
281
+ const canvas = detectionCanvas;
282
+ const ctx = canvas.getContext('2d');
283
+
284
+ detections.forEach(detection => {
285
+ if (showAllBoundingBoxes || detection.class === 'manusia') {
286
+ const { box, class: className, confidence } = detection;
287
+ const [x1, y1, x2, y2] = box;
288
+
289
+ ctx.strokeStyle = 'lime';
290
+ ctx.lineWidth = 3;
291
+ ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
292
+
293
+ ctx.font = '16px Arial';
294
+ const label = `${className} ${(confidence * 100).toFixed(1)}%`;
295
+ const textWidth = ctx.measureText(label).width;
296
+
297
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
298
+ ctx.fillRect(x1, y1 - 25, textWidth + 10, 25);
299
+
300
+ ctx.fillStyle = 'white';
301
+ ctx.fillText(label, x1 + 5, y1 - 7);
302
+ }
303
+ });
304
+ }
static/style.css ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: Arial, sans-serif;
3
+ margin: 0;
4
+ padding: 20px;
5
+ background-color: #f0f0f0;
6
+ }
7
+ .container {
8
+ max-width: 1000px;
9
+ margin: 0 auto;
10
+ background-color: white;
11
+ padding: 20px;
12
+ border-radius: 8px;
13
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
14
+ }
15
+ h1 {
16
+ color: #333;
17
+ text-align: center;
18
+ }
19
+ .video-container {
20
+ margin: 20px 0;
21
+ text-align: center;
22
+ position: relative;
23
+ }
24
+ video {
25
+ max-width: 100%;
26
+ border: 1px solid #ddd;
27
+ border-radius: 4px;
28
+ }
29
+ canvas {
30
+ position: absolute;
31
+ left: 0;
32
+ top: 0;
33
+ max-width: 100%;
34
+ }
35
+ button {
36
+ background-color: #4CAF50;
37
+ border: none;
38
+ color: white;
39
+ padding: 10px 20px;
40
+ text-align: center;
41
+ text-decoration: none;
42
+ display: inline-block;
43
+ font-size: 16px;
44
+ margin: 10px 5px;
45
+ cursor: pointer;
46
+ border-radius: 4px;
47
+ }
48
+ button:hover {
49
+ background-color: #45a049;
50
+ }
51
+ button:disabled {
52
+ background-color: #cccccc;
53
+ cursor: not-allowed;
54
+ }
55
+ .controls {
56
+ text-align: center;
57
+ margin-bottom: 20px;
58
+ }
59
+ .status {
60
+ text-align: center;
61
+ color: #666;
62
+ margin: 10px 0;
63
+ font-style: italic;
64
+ }
65
+ .detection-info {
66
+ margin-top: 20px;
67
+ padding: 10px;
68
+ border: 1px solid #ddd;
69
+ border-radius: 4px;
70
+ max-height: 200px;
71
+ overflow-y: auto;
72
+ }
73
+ .fps-counter {
74
+ position: absolute;
75
+ top: 10px;
76
+ right: 10px;
77
+ background-color: rgba(0,0,0,0.5);
78
+ color: white;
79
+ padding: 5px;
80
+ border-radius: 3px;
81
+ font-size: 12px;
82
+ }
83
+ .alarm {
84
+ background-color: #ff0000;
85
+ color: white;
86
+ padding: 15px;
87
+ margin-top: 10px;
88
+ text-align: center;
89
+ font-weight: bold;
90
+ font-size: 18px;
91
+ border-radius: 4px;
92
+ display: none;
93
+ }
94
+ @keyframes alarm-flash {
95
+ 0%, 100% { opacity: 1; }
96
+ 50% { opacity: 0.5; }
97
+ }
98
+ .alarm-active {
99
+ display: block;
100
+ animation: alarm-flash 1s infinite;
101
+ }
templates/screen_share.html CHANGED
@@ -3,531 +3,39 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Squadrone</title>
7
- <style>
8
- body {
9
- font-family: Arial, sans-serif;
10
- margin: 0;
11
- padding: 20px;
12
- background-color: #f0f0f0;
13
- }
14
- .container {
15
- max-width: 1000px;
16
- margin: 0 auto;
17
- background-color: white;
18
- padding: 20px;
19
- border-radius: 8px;
20
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
21
- }
22
- h1 {
23
- color: #333;
24
- text-align: center;
25
- }
26
- .video-container {
27
- margin: 20px 0;
28
- text-align: center;
29
- position: relative;
30
- }
31
- video {
32
- max-width: 100%;
33
- border: 1px solid #ddd;
34
- border-radius: 4px;
35
- }
36
- canvas {
37
- position: absolute;
38
- left: 0;
39
- top: 0;
40
- max-width: 100%;
41
- }
42
- button {
43
- background-color: #4CAF50;
44
- border: none;
45
- color: white;
46
- padding: 10px 20px;
47
- text-align: center;
48
- text-decoration: none;
49
- display: inline-block;
50
- font-size: 16px;
51
- margin: 10px 5px;
52
- cursor: pointer;
53
- border-radius: 4px;
54
- }
55
- button:hover {
56
- background-color: #45a049;
57
- }
58
- button:disabled {
59
- background-color: #cccccc;
60
- cursor: not-allowed;
61
- }
62
- .controls {
63
- text-align: center;
64
- margin-bottom: 20px;
65
- }
66
- .status {
67
- text-align: center;
68
- color: #666;
69
- margin: 10px 0;
70
- font-style: italic;
71
- }
72
- .detection-info {
73
- margin-top: 20px;
74
- padding: 10px;
75
- border: 1px solid #ddd;
76
- border-radius: 4px;
77
- max-height: 200px;
78
- overflow-y: auto;
79
- }
80
- .fps-counter {
81
- position: absolute;
82
- top: 10px;
83
- right: 10px;
84
- background-color: rgba(0,0,0,0.5);
85
- color: white;
86
- padding: 5px;
87
- border-radius: 3px;
88
- font-size: 12px;
89
- }
90
- .alarm {
91
- background-color: #ff0000;
92
- color: white;
93
- padding: 15px;
94
- margin-top: 10px;
95
- text-align: center;
96
- font-weight: bold;
97
- font-size: 18px;
98
- border-radius: 4px;
99
- display: none;
100
- }
101
- @keyframes alarm-flash {
102
- 0%, 100% { opacity: 1; }
103
- 50% { opacity: 0.5; }
104
- }
105
- .alarm-active {
106
- display: block;
107
- animation: alarm-flash 1s infinite;
108
- }
109
- </style>
110
  </head>
111
  <body>
112
  <div class="container">
113
- <h1>Squadrone v1.0.1</h1>
114
 
115
  <div class="controls">
116
- <button id="startButton">Start Screen Share</button>
117
- <button id="stopButton" disabled>Stop Screen Share</button>
118
  <button id="startDetectionButton" disabled>Start Detection</button>
119
  <button id="pauseDetectionButton" disabled>Pause Detection</button>
 
120
  </div>
121
 
122
  <div class="status" id="status">Ready to share screen</div>
123
 
124
- <!-- Alarm sound -->
125
- <div class="alarm" id="humanAlarm">ALERT: Human detected for extended period!</div>
126
-
127
-
128
  <div class="video-container">
129
- <video id="screenVideo" autoplay playsinline muted></video>
130
  <canvas id="detectionCanvas"></canvas>
131
  <div id="fpsCounter" class="fps-counter">0 FPS</div>
132
  </div>
133
 
 
 
 
134
  <div class="detection-info" id="detectionResults">
135
  <h3>Detection Results</h3>
136
  <p>No detection performed yet.</p>
137
  </div>
138
  </div>
139
 
140
- <script>
141
- const startButton = document.getElementById('startButton');
142
- const stopButton = document.getElementById('stopButton');
143
- const startDetectionButton = document.getElementById('startDetectionButton');
144
- const pauseDetectionButton = document.getElementById('pauseDetectionButton');
145
- const screenVideo = document.getElementById('screenVideo');
146
- const statusElement = document.getElementById('status');
147
- const detectionCanvas = document.getElementById('detectionCanvas');
148
- const detectionResults = document.getElementById('detectionResults');
149
- const fpsCounter = document.getElementById('fpsCounter');
150
- const humanAlarm = document.getElementById('humanAlarm');
151
-
152
- let mediaStream = null;
153
- let capturedFrame = null;
154
- let detectionActive = false;
155
- let detectionInProgress = false;
156
- let animationFrameId = null;
157
- let lastDetectionTime = 0;
158
- let frameCount = 0;
159
- let lastFpsUpdateTime = 0;
160
- let alarmActive = false;
161
-
162
- // Add this line for the alarm sound
163
- const humanDetectionAudio = new Audio('/audio/enemy_spotted.mp3');
164
- // Detection throttle settings
165
- const MIN_DETECTION_INTERVAL = 100; // ms between detection requests
166
-
167
- startButton.addEventListener('click', async () => {
168
- try {
169
- statusElement.textContent = 'Requesting screen access...';
170
-
171
- // Request screen capture
172
- mediaStream = await navigator.mediaDevices.getDisplayMedia({
173
- video: {
174
- cursor: "always"
175
- },
176
- audio: false
177
- });
178
-
179
- // Connect the media stream to the video element
180
- screenVideo.srcObject = mediaStream;
181
-
182
- // Wait for video to be loaded
183
- screenVideo.onloadedmetadata = () => {
184
- // Set canvas dimensions to match video
185
- detectionCanvas.width = screenVideo.videoWidth;
186
- detectionCanvas.height = screenVideo.videoHeight;
187
- };
188
-
189
- // Enable buttons
190
- startButton.disabled = true;
191
- stopButton.disabled = false;
192
- startDetectionButton.disabled = false;
193
-
194
- statusElement.textContent = 'Screen sharing active';
195
-
196
- // Listen for the end of stream
197
- mediaStream.getVideoTracks()[0].addEventListener('ended', () => {
198
- stopScreenSharing();
199
- });
200
-
201
- } catch (error) {
202
- console.error('Error accessing screen:', error);
203
- statusElement.textContent = `Error: ${error.message || 'Could not access screen'}`;
204
- }
205
- });
206
-
207
- stopButton.addEventListener('click', stopScreenSharing);
208
-
209
- function stopScreenSharing() {
210
- stopDetection();
211
-
212
- if (mediaStream) {
213
- mediaStream.getTracks().forEach(track => track.stop());
214
- screenVideo.srcObject = null;
215
- }
216
-
217
- startButton.disabled = false;
218
- stopButton.disabled = true;
219
- startDetectionButton.disabled = true;
220
- pauseDetectionButton.disabled = true;
221
- statusElement.textContent = 'Screen sharing stopped';
222
- }
223
-
224
- startDetectionButton.addEventListener('click', startDetection);
225
- pauseDetectionButton.addEventListener('click', pauseDetection);
226
-
227
- function startDetection() {
228
- if (!screenVideo.srcObject) {
229
- statusElement.textContent = 'No video stream available';
230
- return;
231
- }
232
-
233
- detectionActive = true;
234
- startDetectionButton.disabled = true;
235
- pauseDetectionButton.disabled = false;
236
- statusElement.textContent = 'Real-time detection active';
237
-
238
- // Start the detection loop
239
- lastFpsUpdateTime = performance.now();
240
- frameCount = 0;
241
- detectLoop();
242
- }
243
-
244
- function pauseDetection() {
245
- detectionActive = false;
246
- startDetectionButton.disabled = false;
247
- pauseDetectionButton.disabled = true;
248
- statusElement.textContent = 'Detection paused';
249
-
250
- if (animationFrameId) {
251
- cancelAnimationFrame(animationFrameId);
252
- animationFrameId = null;
253
- }
254
-
255
- // Reset alarm
256
- if (alarmActive) {
257
- resetAlarm();
258
- }
259
- }
260
-
261
- function stopDetection() {
262
- detectionActive = false;
263
- if (animationFrameId) {
264
- cancelAnimationFrame(animationFrameId);
265
- animationFrameId = null;
266
- }
267
- }
268
-
269
- function captureVideoFrame() {
270
- if (!screenVideo.srcObject) {
271
- return false;
272
- }
273
-
274
- const canvas = document.createElement('canvas');
275
- canvas.width = detectionCanvas.width;
276
- canvas.height = detectionCanvas.height;
277
- const ctx = canvas.getContext('2d');
278
-
279
- // Draw the current video frame on a temporary canvas
280
- ctx.drawImage(screenVideo, 0, 0, canvas.width, canvas.height);
281
-
282
- // Store the captured frame as data URL - use lower quality for better performance
283
- capturedFrame = canvas.toDataURL('image/jpeg', 0.7);
284
- return true;
285
- }
286
-
287
- function detectLoop() {
288
- if (!detectionActive) return;
289
-
290
- // Calculate FPS
291
- frameCount++;
292
- const now = performance.now();
293
- const elapsed = now - lastFpsUpdateTime;
294
-
295
- if (elapsed >= 1000) { // Update FPS once per second
296
- const fps = Math.round((frameCount / elapsed) * 1000);
297
- fpsCounter.textContent = `${fps} FPS`;
298
- frameCount = 0;
299
- lastFpsUpdateTime = now;
300
- }
301
-
302
- // Update the video display without clearing the bounding boxes
303
- const ctx = detectionCanvas.getContext('2d');
304
- ctx.drawImage(screenVideo, 0, 0, detectionCanvas.width, detectionCanvas.height);
305
-
306
- // Check if we should send a new detection request
307
- if (!detectionInProgress && now - lastDetectionTime >= MIN_DETECTION_INTERVAL) {
308
- if (captureVideoFrame()) {
309
- lastDetectionTime = now;
310
- detectObjects();
311
- }
312
- }
313
-
314
- // Continue the loop
315
- animationFrameId = requestAnimationFrame(detectLoop);
316
- }
317
-
318
- async function detectObjects() {
319
- if (!capturedFrame || detectionInProgress) {
320
- return;
321
- }
322
-
323
- try {
324
- detectionInProgress = true;
325
-
326
- // Send the captured frame to your Flask backend
327
- const response = await fetch('/detect', {
328
- method: 'POST',
329
- headers: {
330
- 'Content-Type': 'application/json'
331
- },
332
- body: JSON.stringify({
333
- image: capturedFrame
334
- })
335
- });
336
-
337
- if (!response.ok) {
338
- throw new Error(`Server returned ${response.status}`);
339
- }
340
-
341
- const result = await response.json();
342
-
343
- if (result.success) {
344
- // Display detection results
345
- displayDetectionResults(result);
346
-
347
- // Store the latest detections to be drawn in the detectLoop
348
- latestDetections = result.detections;
349
-
350
- // Check alarm status from server
351
- handleAlarmStatus(result.alarm);
352
- } else {
353
- throw new Error(result.error || 'Detection failed');
354
- }
355
-
356
- } catch (error) {
357
- console.error('Error in detection:', error);
358
- statusElement.textContent = `Error: ${error.message}`;
359
- } finally {
360
- detectionInProgress = false;
361
- }
362
- }
363
-
364
- // Add a global variable to store latest detections
365
- let latestDetections = [];
366
-
367
- function detectLoop() {
368
- if (!detectionActive) return;
369
-
370
- // Calculate FPS
371
- frameCount++;
372
- const now = performance.now();
373
- const elapsed = now - lastFpsUpdateTime;
374
-
375
- if (elapsed >= 1000) { // Update FPS once per second
376
- const fps = Math.round((frameCount / elapsed) * 1000);
377
- fpsCounter.textContent = `${fps} FPS`;
378
- frameCount = 0;
379
- lastFpsUpdateTime = now;
380
- }
381
-
382
- // Clear canvas and update the video display
383
- const ctx = detectionCanvas.getContext('2d');
384
- ctx.clearRect(0, 0, detectionCanvas.width, detectionCanvas.height);
385
- ctx.drawImage(screenVideo, 0, 0, detectionCanvas.width, detectionCanvas.height);
386
-
387
- // Draw detection boxes from the latest results
388
- if (latestDetections && latestDetections.length) {
389
- drawDetectionBoxes(latestDetections);
390
- }
391
-
392
- // Check if we should send a new detection request
393
- if (!detectionInProgress && now - lastDetectionTime >= MIN_DETECTION_INTERVAL) {
394
- if (captureVideoFrame()) {
395
- lastDetectionTime = now;
396
- detectObjects();
397
- }
398
- }
399
-
400
- // Continue the loop
401
- animationFrameId = requestAnimationFrame(detectLoop);
402
- }
403
-
404
- // Add function to handle alarm status
405
- function handleAlarmStatus(alarmStatus) {
406
- if (alarmStatus.active) {
407
- if (!alarmActive) {
408
- triggerAlarm();
409
- }
410
- } else {
411
- if (alarmActive) {
412
- resetAlarm();
413
- }
414
- }
415
- }
416
-
417
- function triggerAlarm() {
418
- alarmActive = true;
419
- humanAlarm.classList.add('alarm-active');
420
-
421
- // Play alarm sound
422
- humanDetectionAudio.play().catch(err => console.log('Audio play error:', err));
423
-
424
- // Update status
425
- // statusElement.textContent = 'ALERT: Human detected!';
426
- }
427
-
428
- function resetAlarm() {
429
- alarmActive = false;
430
- humanAlarm.classList.remove('alarm-active');
431
- humanDetectionAudio.pause();
432
- humanDetectionAudio.currentTime = 0;
433
-
434
- // Reset on server side too
435
- fetch('/reset_alarm', {
436
- method: 'POST',
437
- headers: {
438
- 'Content-Type': 'application/json'
439
- }
440
- }).catch(err => console.log('Error resetting alarm on server:', err));
441
- }
442
-
443
- function displayDetectionResults(result) {
444
- let html = '<h3>Detection Results</h3>';
445
-
446
- if (result.detections && result.detections.length) {
447
- html += '<ul>';
448
- result.detections.forEach(detection => {
449
- html += `<li>${detection.class} (${(detection.confidence * 100).toFixed(2)}%)</li>`;
450
- });
451
- html += '</ul>';
452
- } else {
453
- html += '<p>No objects detected.</p>';
454
- }
455
-
456
- detectionResults.innerHTML = html;
457
- }
458
-
459
- function drawDetectionBoxes(detections) {
460
- if (!detections || !detections.length) return;
461
-
462
- const canvas = detectionCanvas;
463
- const ctx = canvas.getContext('2d');
464
-
465
- // No need to clear or redraw the video frame here, that's done in detectLoop
466
-
467
- // Draw detection boxes
468
- detections.forEach(detection => {
469
- const { box, class: className, confidence } = detection;
470
- const [x1, y1, x2, y2] = box;
471
-
472
- ctx.strokeStyle = 'lime';
473
- ctx.lineWidth = 3;
474
- ctx.strokeRect(x1, y1, x2-x1, y2-y1);
475
-
476
- // Draw label
477
- ctx.fillStyle = 'lime';
478
- ctx.font = '16px Arial';
479
- const label = `${className} ${(confidence * 100).toFixed(1)}%`;
480
- const textWidth = ctx.measureText(label).width;
481
-
482
- ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
483
- ctx.fillRect(x1, y1 - 25, textWidth + 10, 25);
484
-
485
- ctx.fillStyle = 'white';
486
- ctx.fillText(label, x1 + 5, y1 - 7);
487
- });
488
- }
489
-
490
-
491
- <!-- function drawDetectionBoxes(detections) {-->
492
- <!-- // if (!detections || !detections.length) return;-->
493
-
494
- <!-- const canvas = detectionCanvas;-->
495
- <!-- const ctx = canvas.getContext('2d');-->
496
-
497
- <!-- // Clear previous drawings-->
498
- <!-- ctx.clearRect(0, 0, canvas.width, canvas.height);-->
499
-
500
- <!-- // Draw current video frame-->
501
- <!-- ctx.drawImage(screenVideo, 0, 0, canvas.width, canvas.height);-->
502
-
503
- <!-- // Draw detection boxes only for humans-->
504
- <!-- if (detections && detections.length) {-->
505
- <!-- detections.forEach(detection => {-->
506
- <!-- const { box, class: className, confidence } = detection;-->
507
-
508
- <!-- // Only draw boxes for human/person detections-->
509
- <!-- if (className.toLowerCase() === 'person' || className.toLowerCase() === 'human') {-->
510
- <!-- const [x1, y1, x2, y2] = box;-->
511
-
512
- <!-- // Red color for humans-->
513
- <!-- ctx.strokeStyle = 'red';-->
514
- <!-- ctx.lineWidth = 3;-->
515
- <!-- ctx.strokeRect(x1, y1, x2-x1, y2-y1);-->
516
-
517
- <!-- // Draw label-->
518
- <!-- ctx.font = '16px Arial';-->
519
- <!-- const label = `${className} ${(confidence * 100).toFixed(1)}%`;-->
520
- <!-- const textWidth = ctx.measureText(label).width;-->
521
-
522
- <!-- ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';-->
523
- <!-- ctx.fillRect(x1, y1 - 25, textWidth + 10, 25);-->
524
-
525
- <!-- ctx.fillStyle = 'white';-->
526
- <!-- ctx.fillText(label, x1 + 5, y1 - 7);-->
527
- <!-- }-->
528
- <!-- });-->
529
- <!-- }-->
530
- <!-- }-->
531
- </script>
532
  </body>
533
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Squadron</title>
7
+ <link rel="stylesheet" href="/static/style.css">
8
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  </head>
10
  <body>
11
  <div class="container">
12
+ <h1>Squadron v1.0.2</h1>
13
 
14
  <div class="controls">
15
+ <button id="startButton">Start Share Screen</button>
16
+ <button id="stopButton" disabled>Stop Share Screen</button>
17
  <button id="startDetectionButton" disabled>Start Detection</button>
18
  <button id="pauseDetectionButton" disabled>Pause Detection</button>
19
+ <button id="toggleBoundingBoxButton" disabled>Show All Object</button>
20
  </div>
21
 
22
  <div class="status" id="status">Ready to share screen</div>
23
 
 
 
 
 
24
  <div class="video-container">
25
+ <video id="screenVideo" autoplay playsinline muted></video>
26
  <canvas id="detectionCanvas"></canvas>
27
  <div id="fpsCounter" class="fps-counter">0 FPS</div>
28
  </div>
29
 
30
+ <!-- Alarm sound -->
31
+ <div class="alarm" id="humanAlarm">ALERT: Human detected for extended period!</div>
32
+
33
  <div class="detection-info" id="detectionResults">
34
  <h3>Detection Results</h3>
35
  <p>No detection performed yet.</p>
36
  </div>
37
  </div>
38
 
39
+ <script src="/static/share_screen.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  </body>
41
  </html>