From b0f42c9e8f06533d27b95b54bda8fe2e6b7e8b71 Mon Sep 17 00:00:00 2001 From: AlexsandrSnytkin Date: Mon, 7 Oct 2024 09:51:47 +0700 Subject: [PATCH] Init --- .idea/VideoCapture.iml | 15 +++ .idea/inspectionProfiles/Project_Default.xml | 32 +++++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 10 ++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 7 ++ .idea/workspace.xml | 109 ++++++++++++++++++ MotionDetector/motion_detection | 1 + Simple_video_register/README.md | 15 +++ Simple_video_register/backend/Dockerfile | 10 ++ Simple_video_register/backend/app/__init__.py | 1 + .../app/__pycache__/__init__.cpython-311.pyc | Bin 0 -> 129 bytes .../app/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 111 bytes .../app/__pycache__/main.cpython-311.pyc | Bin 0 -> 1181 bytes .../app/__pycache__/main.cpython-38.pyc | Bin 0 -> 688 bytes Simple_video_register/backend/app/main.py | 20 ++++ .../routes/__pycache__/camera.cpython-311.pyc | Bin 0 -> 2108 bytes .../routes/__pycache__/camera.cpython-38.pyc | Bin 0 -> 1209 bytes .../__pycache__/detection.cpython-311.pyc | Bin 0 -> 640 bytes .../__pycache__/detection.cpython-38.pyc | Bin 0 -> 487 bytes .../backend/app/routes/camera.py | 25 ++++ .../backend/app/routes/detection.py | 6 + .../backend/app/routes/motion_detector.py | 22 ++++ .../backend/app/video_stream.py | 12 ++ .../backend/requirements.txt | 8 ++ .../test_app.cpython-311-pytest-8.2.2.pyc | Bin 0 -> 10961 bytes .../backend/tests/test_app.py | 60 ++++++++++ .../docker-compose-prod.yaml | 11 ++ Simple_video_register/docker-compose.yaml | 17 +++ Simple_video_register/frontend/.dockerignore | 3 + Simple_video_register/frontend/Dockerfile | 16 +++ .../frontend/Dockerfile-prod | 15 +++ Simple_video_register/frontend/package.json | 18 +++ Simple_video_register/frontend/src/App.vue | 83 +++++++++++++ .../frontend/src/components/CameraGrid.vue | 40 +++++++ .../frontend/src/components/CameraView.vue | 31 +++++ .../frontend/src/components/Settings.vue | 71 ++++++++++++ Simple_video_register/frontend/src/main.js | 8 ++ Simple_video_register/nginx/nginx.conf | 17 +++ Stream_System/.github/workflows/ci.yml | 44 +++++++ Stream_System/backend/Dockerfile | 6 + Stream_System/backend/app.py | 45 ++++++++ Stream_System/backend/camera.py | 36 ++++++ Stream_System/backend/database.py | 19 +++ Stream_System/backend/motion_detection.py | 15 +++ Stream_System/backend/requirements.txt | 2 + Stream_System/docker-compose.yml | 30 +++++ Stream_System/frontend/Dockerfile | 28 +++++ Stream_System/frontend/package.json | 12 ++ Stream_System/frontend/src/App.vue | 60 ++++++++++ .../frontend/src/components/CameraBlock.vue | 48 ++++++++ .../frontend/src/components/CameraControl.vue | 23 ++++ .../frontend/src/components/CameraGrid.vue | 57 +++++++++ .../frontend/src/components/CameraView.vue | 13 +++ Stream_System/frontend/src/main.js | 6 + VideoRegister | 1 + 56 files changed, 1142 insertions(+) create mode 100644 .idea/VideoCapture.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 160000 MotionDetector/motion_detection create mode 100644 Simple_video_register/README.md create mode 100644 Simple_video_register/backend/Dockerfile create mode 100644 Simple_video_register/backend/app/__init__.py create mode 100644 Simple_video_register/backend/app/__pycache__/__init__.cpython-311.pyc create mode 100644 Simple_video_register/backend/app/__pycache__/__init__.cpython-38.pyc create mode 100644 Simple_video_register/backend/app/__pycache__/main.cpython-311.pyc create mode 100644 Simple_video_register/backend/app/__pycache__/main.cpython-38.pyc create mode 100644 Simple_video_register/backend/app/main.py create mode 100644 Simple_video_register/backend/app/routes/__pycache__/camera.cpython-311.pyc create mode 100644 Simple_video_register/backend/app/routes/__pycache__/camera.cpython-38.pyc create mode 100644 Simple_video_register/backend/app/routes/__pycache__/detection.cpython-311.pyc create mode 100644 Simple_video_register/backend/app/routes/__pycache__/detection.cpython-38.pyc create mode 100644 Simple_video_register/backend/app/routes/camera.py create mode 100644 Simple_video_register/backend/app/routes/detection.py create mode 100644 Simple_video_register/backend/app/routes/motion_detector.py create mode 100644 Simple_video_register/backend/app/video_stream.py create mode 100644 Simple_video_register/backend/requirements.txt create mode 100644 Simple_video_register/backend/tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc create mode 100644 Simple_video_register/backend/tests/test_app.py create mode 100644 Simple_video_register/docker-compose-prod.yaml create mode 100644 Simple_video_register/docker-compose.yaml create mode 100644 Simple_video_register/frontend/.dockerignore create mode 100644 Simple_video_register/frontend/Dockerfile create mode 100644 Simple_video_register/frontend/Dockerfile-prod create mode 100644 Simple_video_register/frontend/package.json create mode 100644 Simple_video_register/frontend/src/App.vue create mode 100644 Simple_video_register/frontend/src/components/CameraGrid.vue create mode 100644 Simple_video_register/frontend/src/components/CameraView.vue create mode 100644 Simple_video_register/frontend/src/components/Settings.vue create mode 100644 Simple_video_register/frontend/src/main.js create mode 100644 Simple_video_register/nginx/nginx.conf create mode 100644 Stream_System/.github/workflows/ci.yml create mode 100644 Stream_System/backend/Dockerfile create mode 100644 Stream_System/backend/app.py create mode 100644 Stream_System/backend/camera.py create mode 100644 Stream_System/backend/database.py create mode 100644 Stream_System/backend/motion_detection.py create mode 100644 Stream_System/backend/requirements.txt create mode 100644 Stream_System/docker-compose.yml create mode 100644 Stream_System/frontend/Dockerfile create mode 100644 Stream_System/frontend/package.json create mode 100644 Stream_System/frontend/src/App.vue create mode 100644 Stream_System/frontend/src/components/CameraBlock.vue create mode 100644 Stream_System/frontend/src/components/CameraControl.vue create mode 100644 Stream_System/frontend/src/components/CameraGrid.vue create mode 100644 Stream_System/frontend/src/components/CameraView.vue create mode 100644 Stream_System/frontend/src/main.js create mode 160000 VideoRegister diff --git a/.idea/VideoCapture.iml b/.idea/VideoCapture.iml new file mode 100644 index 0000000..0a88826 --- /dev/null +++ b/.idea/VideoCapture.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..fc6ca88 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,32 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..b2d336e --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..741b5f0 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..4610cf8 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..4da83fb --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1727670817034 + + + + \ No newline at end of file diff --git a/MotionDetector/motion_detection b/MotionDetector/motion_detection new file mode 160000 index 0000000..2ff71b1 --- /dev/null +++ b/MotionDetector/motion_detection @@ -0,0 +1 @@ +Subproject commit 2ff71b19839d5b0dd14c43d0fd55bd1a37abee38 diff --git a/Simple_video_register/README.md b/Simple_video_register/README.md new file mode 100644 index 0000000..2da1d9b --- /dev/null +++ b/Simple_video_register/README.md @@ -0,0 +1,15 @@ +# Multi-Camera Surveillance Project + +## Overview +This project enables you to connect up to 24 cameras, choose the grid size for video feeds dynamically, and manage the camera settings through a Vue.js frontend with a Flask backend. + +### How to Run +1. Install Docker and Docker Compose. +2. Run `docker-compose up --build` in the project directory. +3. Access the frontend at `http://localhost:8080`. +4. Access the backend API documentation at `http://localhost:5000/swagger`. + +### Features +- Add and remove cameras. +- Dynamic grid size based on the number of cameras. +- Simple motion detection placeholder (to be implemented). diff --git a/Simple_video_register/backend/Dockerfile b/Simple_video_register/backend/Dockerfile new file mode 100644 index 0000000..da4395d --- /dev/null +++ b/Simple_video_register/backend/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt ./ +RUN pip install -r requirements.txt + +COPY . . + +CMD ["gunicorn", "-b", "0.0.0.0:5000", "app.main:app"] diff --git a/Simple_video_register/backend/app/__init__.py b/Simple_video_register/backend/app/__init__.py new file mode 100644 index 0000000..1508d48 --- /dev/null +++ b/Simple_video_register/backend/app/__init__.py @@ -0,0 +1 @@ +# Empty file to initialize the module diff --git a/Simple_video_register/backend/app/__pycache__/__init__.cpython-311.pyc b/Simple_video_register/backend/app/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b26c633ea060ba05e2d339ded55ceafb04d5b893 GIT binary patch literal 129 zcmZ3^%ge<81a0lF($s^MY(U0zh7^Wi22Do4l?+8pK>lZt)=dU}j`w{J;PsikN|706U)g`kg0}WoY3e}wF^Gc<7=auIATDMB5-AM944RC7D;bJF!U*D*h<;*00T{=} dXXa&=#K-FuRNmsS$<0qG%}KQbss0Sa3;?f66$AhP literal 0 HcmV?d00001 diff --git a/Simple_video_register/backend/app/__pycache__/main.cpython-311.pyc b/Simple_video_register/backend/app/__pycache__/main.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b912953c2f8977595f10404c26c7ddbde7528576 GIT binary patch literal 1181 zcmZ`%y=&V*6o02LTbArNG&s0T$k5W@(%6|glu{@uO{WwZ=%7Ui>zs&4mO06kcv6BP z9s?cH(NjuE{}?$KA~Xd`DedHvEmQC053H1)-n)D6_ujpG_d9*b=W{^Er<31}p9tVP zbEZR>7&jkiM*@| zMyE6I=VJZYlgxqivC{<5X1Se?I;mYfG#tCa#k|pK#YGP{P2KYi->T~k%f?^fhLyT4 z($-puI)>G(whrUmWWV0F2;+T$s#l^@06&z%m9loJtn~{6r8ZP*p->|#4aHeqXZv-% zli92$pVhlZS>s}1-}U@hXt~7SsVv4~gONzsJ>z_0>jZm#qiqxRGsn3xR(FX=(YxzHnT#ZR3|@g}RJ1zF z4p*KIU}FdyA#7YD;iPd**@Mr ZE%%p#?Sb%kC_E0QGU7qFPjeEs{sJz*52pYC literal 0 HcmV?d00001 diff --git a/Simple_video_register/backend/app/__pycache__/main.cpython-38.pyc b/Simple_video_register/backend/app/__pycache__/main.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0070ae93fae4bc21e658d1affd5ff9bd171eef28 GIT binary patch literal 688 zcmYjPy^a$x5cbb*_CLu9D&8Q?h6g|h6hwJQoM_xw*4jCi^Zv-*6}q&CbTm{@P$ng> zv@HdKN1$TtL`OXG_?sEe%;(vCR}?9N_4WSG_7_Izr!&r10LBa0_B$wvB-W_G3}c6h zCJklOW2%EDWTD4_o;4ARJPvi-BrL&5VU^xtmZ?x>caX2BJ6W#seW4;bI}q?WoXE)I zY(JOr0n0?DpAg(!s8nV*#C%iPho#JAA?IHpM=q|=YWW-3p%tFO*IL*c0Q6;F!x3!W zyuE^Rwh@go0#Ncw4XPUIuH9v8(bwzBM|N{>$cJFOwjagywKA0}eZD9d8`#!OZa{l1 z@c{z~a=FV-IpwWr^omSW^gUZz)k{2QCWch6{w|?!?{_2Ww(M`G^i+h8 z^}@9wpb#;x+Z1!|u5rGLo|azf&yV2cWD4GQc9?>`GsETTf`wZ*2;*{D;W0PL4%<;1 z$DgG~U)33F+&5+yK%+F>I4D~lM`A+fe|+YDn2wo2$dPGbc$}Na-Gs;1EEiI`PTgoK r#X?{3NS&Io{r~PGN_?4TO(#dKp2Po#>=RHqJ_(622n7pJN@M&Fvl6FP literal 0 HcmV?d00001 diff --git a/Simple_video_register/backend/app/main.py b/Simple_video_register/backend/app/main.py new file mode 100644 index 0000000..e27aa4a --- /dev/null +++ b/Simple_video_register/backend/app/main.py @@ -0,0 +1,20 @@ +from flask import Flask +from flask_restful import Api +from flask_cors import CORS +from app.routes.camera import Camera +from app.routes.detection import Detection + +app = Flask(__name__) +CORS(app) +api = Api(app) + +# Add routes +api.add_resource(Camera, '/api/camera') +api.add_resource(Detection, '/api/detection') + +@app.route('/swagger') +def swagger_ui(): + return app.send_static_file('swagger.yaml') + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) diff --git a/Simple_video_register/backend/app/routes/__pycache__/camera.cpython-311.pyc b/Simple_video_register/backend/app/routes/__pycache__/camera.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e3fc0e8a92f35be10e389a236562423c7bf2e22 GIT binary patch literal 2108 zcmb_d&1)M+6rcUjm$kOstP?x2AnP_D_FyM&1C@hu3N|EhUDrtn*fK>{H7iH9zB0Qi ziBO9Vf%;&I2?gtrf)8ohlKugqr(Oy^^e}4{Di(YyJ-8~H72 zdGqG&d%qcd*V);Dp#AjapSg7jq2HO%Y5{++KL&#hgb@}Bs3@d_qL>l|CW{5BD5qor ziRc!>(o=+GLw*TA>!Dp)PESE&b6q7G8qYt3ylC3LVbZ(URPfj@w~znNN_}qc)qZq_!6E z_t`wwMJzoRkMANNtMkwmme1x12Wn7u96ad_hjQZm;~v8}l0T6F+`+8$=(HOqmRU(x zNny7Sf`ICZc}yZOtSMe0v<$`u-et*SwJ#c`nOQX4NS2o=oyWUdFh$rW)CNAvJkA%Z zMXgjVergb{oYOorQzJ$u`_RDob#SDI2&<+mGyb^JqG7pG!6>;Q-`EXiDix!I39E}P zxw2^#a;}UsR)&XWvelZVy}5W54EaOKRm(81dJcvvPh7cDHmzE8oJTpt1XgnMIlv0q ziCtK$ugXoecO&^M*_gD|grg=_!#mNgRcozoN8?U3-XPo20U8}>D&1>SzbTiumCN6n z-(UZ6)J`OwMDpi{RQnjZt;{&e3{_@!)JvOR{4%~J|Eky%51feybau|3n4|qE=(alV zsPpub1*$GkW#JF?QsWLyOw#@jZFR~~r?#?Gy-St5`w$;G#(kCp&iycC?*HIE7VBbG zd@L^W^FaE}#y9^YTr0t~Q5yX3^HBeHmP=S}<7A+Xxe^YxS#f^r;c3zZi6QK~d)({t zN)1FSmOnEviNQ=w^_6!fv{Kp9a^-3X=f6IrAn9i7-(ql%fePSCm2$6+$UK@~0UXk-r*Gr-v)he%yGM6=^wmf+s;oz7bP)PB$8W|r z>q>2LI&7~ ym4XNPCOqnW-59w7I+i*!3$P~%g3v^7)6;speosCxK$PvUI{O#X{(Z_658@xOH@15K literal 0 HcmV?d00001 diff --git a/Simple_video_register/backend/app/routes/__pycache__/camera.cpython-38.pyc b/Simple_video_register/backend/app/routes/__pycache__/camera.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7efcbfcf990280119c2daf3e88f0d1486bb183e GIT binary patch literal 1209 zcmZWp&2H2%5Vjp}l5TfPMFAD5Ld%VCXn6pHDisNV#2#94$t4OqPT4j;TRTB?wLP^G z*B&_b&>ndOo`7fYl~Z{HPRuyFw16d#X2vs~&)-b)uHWx6T;D$b%+5W=evol{Ni?=` zn`bD10nb^%V_t|@aI!`26*88by<*^jJYyi0JoVxZboN-}Ut<=FgblPhYLuyMaA2x( zoE=BphP%pCjUK5H1>If(jcwfK2@1tx4lEY?EcSqdIAgJFOHmQ<(e@$0VxjeS(n9HU z#4&BL@KLOTdk444P}J;zpYR#K6u__eiKx99JCLV%^C8o_q+fOxzL@~;LR@jqY9Hjn z3!QZa{)NDtAQO9Z7Q)h)qunJN7@}Do+ECYKk~BKMUdOt~f*6DLQJwZ$po0`Z!8O4p zjtXT=y07fg$UP$&!}WJO($t~pM8Hu1XusgRP%VCaMHYyhNUrM&+56kk+Y#_Lb>-c=GGv7RI@)tEN_FtIgk> z99uc5OdVj5>7iiU=Uu+S*ZGE+F7G(xn-uY17pS7^LX674T>vo?l6mZq>!%Bzu^(?r z9IbMr?aFM?2^=)Xc4yAM2fnjiH==N;m$8B-T3hZ`T3AR_QGHaP3HNku?tJ}nP*(LI ztC|wVUv4PWeac@Uafiez3hPa($(%ZUm%M8vTsfl6ai{N7qz@`rwW6$BJU!w)zRIWl zIZtiOUrZ6|KTJ{e8b3OGKc^SsT#2DW5(x*R)p?8MZv+lH4W6NaV0_x07sZ(vMu82J zq{MC|iR~syQ9+ZF-b<3BCe7Ouce{Ro*_tkD$+)TFGH!ut_pn#o!hsw3q;- zvH`Soq2T~MEg#89I#DO+I@5MyY3Q`uXb}NbF`;#s&|M-K&2-L}O4)6BwK=2LyrA82 zYTY2Jb^M+uec6qq7(OHafg~DAdaj~9YU7O^yVuJ-4B|+t9WfQr=v}6ITSiybY;Xfp z18Hgnp%3p%YsvaAhC4-v1GBL_)M4}GtSu^(?ZJ)(-Qcr z%!G~}*z{zJx7d`2_#Rx_+X4zYI1YhifpF)TfMr}W6mu;$ER$NINfbu6;GytU+Y}sc z?Su9tzd^VOCX!WL%TkbR8O~~OE?>d=@?emfc2yN=)B3ckI|DT|9`yDD)$cTDSSN=*GpsCe^jN7TuR*CWR;q4wZ)iVJ z>bcL&u3|^~ZvTC4OlJ}kVX%++z?V}QlG5bvNm+2d?2V=7L+waEynswXe6?$PP2V** GO~fA(pK1&M literal 0 HcmV?d00001 diff --git a/Simple_video_register/backend/app/routes/camera.py b/Simple_video_register/backend/app/routes/camera.py new file mode 100644 index 0000000..0e8cc93 --- /dev/null +++ b/Simple_video_register/backend/app/routes/camera.py @@ -0,0 +1,25 @@ +from flask import request, jsonify +from flask_restful import Resource + +class Camera(Resource): + cameras = [] + + def post(self): + data = request.json + rtsp_url = data.get('rtsp_url') + if len(self.cameras) < 24: + self.cameras.append({'id': len(self.cameras), 'rtsp_url': rtsp_url}) + return jsonify({"message": "Camera added", "camera_id": len(self.cameras) - 1}), 200 + else: + return jsonify({"message": "Maximum number of cameras reached"}), 400 + + def delete(self): + data = request.json + camera_id = data.get('camera_id') + if 0 <= camera_id < len(self.cameras): + self.cameras.pop(camera_id) + return jsonify({"message": "Camera removed"}), 200 + return jsonify({"message": "Camera ID not found"}), 404 + + def get(self): + return jsonify({"cameras": self.cameras}) diff --git a/Simple_video_register/backend/app/routes/detection.py b/Simple_video_register/backend/app/routes/detection.py new file mode 100644 index 0000000..5813caa --- /dev/null +++ b/Simple_video_register/backend/app/routes/detection.py @@ -0,0 +1,6 @@ +from flask_restful import Resource + +class Detection(Resource): + def get(self): + # Placeholder for motion and abandoned object detection + return {"message": "Detection logic not implemented"}, 200 diff --git a/Simple_video_register/backend/app/routes/motion_detector.py b/Simple_video_register/backend/app/routes/motion_detector.py new file mode 100644 index 0000000..cb1d7b8 --- /dev/null +++ b/Simple_video_register/backend/app/routes/motion_detector.py @@ -0,0 +1,22 @@ +import cv2 + +class MotionDetector: + def __init__(self): + self.previous_frame = None + + def detect_motion(self, frame): + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + gray = cv2.GaussianBlur(gray, (21, 21), 0) + + if self.previous_frame is None: + self.previous_frame = gray + return False + + frame_diff = cv2.absdiff(self.previous_frame, gray) + thresh = cv2.threshold(frame_diff, 25, 255, cv2.THRESH_BINARY)[1] + thresh = cv2.dilate(thresh, None, iterations=2) + + contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + self.previous_frame = gray + + return len(contours) > 0 diff --git a/Simple_video_register/backend/app/video_stream.py b/Simple_video_register/backend/app/video_stream.py new file mode 100644 index 0000000..2d7003b --- /dev/null +++ b/Simple_video_register/backend/app/video_stream.py @@ -0,0 +1,12 @@ +import cv2 + +class VideoStream: + def __init__(self, rtsp_url): + self.rtsp_url = rtsp_url + self.stream = cv2.VideoCapture(rtsp_url) + + def get_frame(self): + ret, frame = self.stream.read() + if not ret: + return None + return frame diff --git a/Simple_video_register/backend/requirements.txt b/Simple_video_register/backend/requirements.txt new file mode 100644 index 0000000..3693255 --- /dev/null +++ b/Simple_video_register/backend/requirements.txt @@ -0,0 +1,8 @@ +Flask +flask-restful +opencv-python-headless +numpy +flask-cors +gunicorn +sqlalchemy +pymysql diff --git a/Simple_video_register/backend/tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc b/Simple_video_register/backend/tests/__pycache__/test_app.cpython-311-pytest-8.2.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c38e39f4f02ffa5bd93ddfd2a7b97d9dbf3ba4b7 GIT binary patch literal 10961 zcmeHN&2JmW72oA9ze!P&B`dOBm#rHoOl*Bva-`UC99fZTS+ay$N>Zz;7c_SzQ{q>5 zSGFXRfM7U?69{b&1)M_6`K;{ZoFPQ(SWa)37Yu;O;=? zuBqUrg0gsnn`)4qAVabXWTWf`*(7^FHp~137x8|F__2u75Tcok;gByGo)6=)k{(UZ z8vb)>J)TYtT~SiH5gb)!^kH2|l5C7M0}1FPcL08m{>*dS2*+c`c-?}S13f)t$2*Xa zg*v4oDRtHf7YFfk;3 zaDMo-vENcMd46DUL^?PA{_yGH!GX!)@v(`BTg5IJu2?!X6NkB_E1E9F5^O^!$}a{92q<|{eh;a+O(Eh(&wki%pHto z^aWL!o`@$i2}QbsvsqG=*|?@F>hz^(Y+gyp)7ZR5qy&w3WR}z>Fi8YX0_{4q@GtnR za1Zu{KOVes_U2MSY+pb09;Wl5*9&4~y{DgMItyagKfQs~9~QiOirzgNoZxKzeCB?z zaaAe=_ZNfvH#pwAkBIw$hShVQj<1g29N!R3B`JA`nC}^|T*a1p{P?j=L$VJVT5bmL zv*Cu;i3>zLL}$K7QP$N;5wq2(|M)mxu-q};xf@z@?!NEwt@=L=t_E)gp&KP7DS>V{ zcSARTs-g`5E={|AbxcA<10n_jDm_F%-^jFQ| zson0c+If^+ErU(M*G=MJtNZI$c#tv6%0bpFoRE+DLEeHZ_BLdy51E%8vJmJ0BvxMg zIX=smSGAl&cIJevBP-04)gdQlMeUC|TTNhPVb`_+sA;||RByEx;?A$zv6 z8eQSEmI~W!4ao9$ppSQ1Kh;VcuG8eO<5E1dZ1uK`bW~cbgAyuHviJAGGI*79%0Ahj zb;fy;bLn`Em5vHaYz@x3DlJv!t^-jn@;L|m#GD`pa&9;agly3$oB?-MxWeT;Sx?>3 z4`w|u`VEM*kA8(mdK`tTB-Bm3xwwsrK~F!~-CedLSAvCX}mWQFb2^r)&!nhxuB zvNIM>UN|ePYF3TOUbP}o{c#Sv2IW3*~?yqnT*Z57xi4$x5 za?h);XO@mcG~cySB77o@sji6D0Zr)(8W4u8_;TSBwBTq&gXR=1lTK;MnqUZkMR5eh z(anaa21KC~8+`x;WuMjnEm9}K#C=5uXB_bqw0{U2>yBvQc#6~k^VAhclw9cnSLCQ3 z_2p=D-DtE&{SkJbts1+enY5aW z>XNdUNkmgoyp1zNbau^4iS7sBrQydG>A-YbGriVK56w`6Z&@?_R#=xcb40@{I&2bszCfuHtoShH5R^-$|?u6=ebzqkKOUw(8pfAaFpYlTp}7>ci)Sr-o$ z#e@0E?>Cn0x3vHiUoeX(hGn6Y)ndwOO*O101}xazqZ$C)@T@Hx9>Od z`K@@kUQvM*F#yd%MXF(0rhx=gR@bTtqg6m!zhH!dZAO@JE7+QT1gD;>P0N>_iI(x! z`!*V_I%H?%GG0aCt}PR|n-I9CCV_ij7=e51(ATpOxQ|hYUk+>=fd`*~z#9mGhn|DL zU%qo>C-BB?9epc-H;t*d{;I7gcB5zmvAll#d!P-^_0Eta%PPo4Yo)CSXj9eedHy-|CDnBrt z@BQVWs+an#>tM4*#&~p|+ReDL{Osj3bStn~F6^JvfIJPNi$3dLwt%I~Jfpt(~ zC5g)>O}S*GDOV}ZbnAOfPhr2%>KWeflOt=3>MHG);!FV|9cNCIgAh?V=9nEg1D>o1 zai%xxtvmV+Suc!!2$A;DukffmR-Q^9Ucz%^AAOUiwrBJ`F#28?eT*}k#tgR^Q@WC5 zS9-H46V)$w>S;<<9mz~@L@>M*CB`W%(x*^Oex_Ssfhf#E(=tR+lq8r5#6g?<< zQ5*#k@sUu?5Wq)Dvwc|9kK%0<$50$cfg9uM2@o1y%VyXW4%5IZMEKOw-*Et8n5w-> z==8p?`SS~3^ykm~BJZ0n_@;}#>6O9zqU*-lPsXl~{kEqdwiU&;yx2x`e-~T75nGw= z0mN*}pDpM294>@9ilL7GBf2exd(~kn4eMCYR~_Zi0FVJ#JV-Sx3l-~Opy21-=SNWW928h*%wK->LB>C9HI|^!?L0)F&(0~ zA<#wy^)&n$ep%6CYCMB`iz+^+S#BvYDE4w)fPQ3{~?I7jUK-=v|{_V5*kLL1$`9fg67?@ufDwFC_ zc$!!g+w)@k6G^qJ5b7?5x_3y`+*@VDRZUZr#2A2a8r85YB|u8rOj%v4CJdBN#nv>{ zfCZZoX55Bn*&R}a>t%;jOPhT=qzbbEkGbvKXL#|X>cy9+!U=ers)9}%;Z>WYRK=-n zB~C@WL?F*gs~E}t4{23B5B6$?ovnl=s~rCb@EG2j!DpcT_VAbf{Lkm}fkYvYC__NP;W8RyTg+vJ25U|Y{L?iml%N26xFaS;Ok+a=*ch&U@g)FRDlH8w3)S +
+
+ +
+
+ +
+
+ + + + + diff --git a/Simple_video_register/frontend/src/components/CameraGrid.vue b/Simple_video_register/frontend/src/components/CameraGrid.vue new file mode 100644 index 0000000..ab94acc --- /dev/null +++ b/Simple_video_register/frontend/src/components/CameraGrid.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/Simple_video_register/frontend/src/components/CameraView.vue b/Simple_video_register/frontend/src/components/CameraView.vue new file mode 100644 index 0000000..90a62f6 --- /dev/null +++ b/Simple_video_register/frontend/src/components/CameraView.vue @@ -0,0 +1,31 @@ + + + + + + \ No newline at end of file diff --git a/Simple_video_register/frontend/src/components/Settings.vue b/Simple_video_register/frontend/src/components/Settings.vue new file mode 100644 index 0000000..1ba253e --- /dev/null +++ b/Simple_video_register/frontend/src/components/Settings.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/Simple_video_register/frontend/src/main.js b/Simple_video_register/frontend/src/main.js new file mode 100644 index 0000000..77f35b6 --- /dev/null +++ b/Simple_video_register/frontend/src/main.js @@ -0,0 +1,8 @@ +import Vue from 'vue'; +import App from './App.vue'; + +Vue.config.productionTip = false; + +new Vue({ + render: h => h(App), +}).$mount('#app'); diff --git a/Simple_video_register/nginx/nginx.conf b/Simple_video_register/nginx/nginx.conf new file mode 100644 index 0000000..758a646 --- /dev/null +++ b/Simple_video_register/nginx/nginx.conf @@ -0,0 +1,17 @@ +server { + + listen 80; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + error_page 500 502 503 504 /50x.html; + + location = /50x.html { + root /usr/share/nginx/html; + } + +} \ No newline at end of file diff --git a/Stream_System/.github/workflows/ci.yml b/Stream_System/.github/workflows/ci.yml new file mode 100644 index 0000000..bf89f44 --- /dev/null +++ b/Stream_System/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +name: CI Pipeline + +on: + push: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install Python dependencies + run: | + cd backend + pip install -r requirements.txt + + - name: Run backend tests + run: | + cd backend + pytest + + - name: Set up Node.js + uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: Install frontend dependencies + run: | + cd frontend + npm install + + - name: Run frontend tests + run: | + cd frontend + npm run test diff --git a/Stream_System/backend/Dockerfile b/Stream_System/backend/Dockerfile new file mode 100644 index 0000000..083fc0c --- /dev/null +++ b/Stream_System/backend/Dockerfile @@ -0,0 +1,6 @@ +FROM python:3.11-slim +WORKDIR /app +COPY . . +RUN pip install -r requirements.txt +EXPOSE 5000 +CMD ["python", "app.py"] diff --git a/Stream_System/backend/app.py b/Stream_System/backend/app.py new file mode 100644 index 0000000..2b623fc --- /dev/null +++ b/Stream_System/backend/app.py @@ -0,0 +1,45 @@ +from flask import Flask, Response, request, jsonify +from camera import Camera +from database import log_event, get_all_events +import threading + +app = Flask(__name__) + +# Хранилище камер и событий детекции +cameras = {} +motion_detected = [] + +@app.route('/add_camera', methods=['POST']) +def add_camera(): + camera_id = request.json['id'] + if camera_id not in cameras: + cameras[camera_id] = Camera(camera_id) + threading.Thread(target=cameras[camera_id].start_stream, args=(motion_detected,)).start() + return jsonify({"message": "Camera added"}), 200 + return jsonify({"message": "Camera already exists"}), 400 + +@app.route('/remove_camera', methods=['POST']) +def remove_camera(): + camera_id = request.json['id'] + if camera_id in cameras: + cameras[camera_id].stop_stream() + del cameras[camera_id] + return jsonify({"message": "Camera removed"}), 200 + return jsonify({"message": "Camera not found"}), 404 + +@app.route('/stream/') +def stream(camera_id): + if camera_id in cameras: + return Response(cameras[camera_id].get_frame(), mimetype='multipart/x-mixed-replace; boundary=frame') + return "Camera not found", 404 + +@app.route('/events', methods=['GET']) +def get_events(): + return jsonify(get_all_events()), 200 + +@app.route('/motion', methods=['GET']) +def motion(): + return jsonify(motion_detected), 200 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5000) diff --git a/Stream_System/backend/camera.py b/Stream_System/backend/camera.py new file mode 100644 index 0000000..8d9b0e7 --- /dev/null +++ b/Stream_System/backend/camera.py @@ -0,0 +1,36 @@ +import cv2 +from motion_detection import detect_motion +from database import log_event + +class Camera: + def __init__(self, camera_id, source=0): + self.camera_id = camera_id + self.capture = cv2.VideoCapture(source) + self.is_running = True + + def start_stream(self): + background_frame = None + while self.is_running: + ret, frame = self.capture.read() + if not ret: + break + + if background_frame is None: + background_frame = frame + + moving_objects = detect_motion(frame, background_frame) + if moving_objects: + log_event(self.camera_id, moving_objects) + + def get_frame(self): + while self.is_running: + ret, frame = self.capture.read() + if ret: + _, buffer = cv2.imencode('.jpg', frame) + frame = buffer.tobytes() + yield (b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n') + + def stop_stream(self): + self.is_running = False + self.capture.release() diff --git a/Stream_System/backend/database.py b/Stream_System/backend/database.py new file mode 100644 index 0000000..106167e --- /dev/null +++ b/Stream_System/backend/database.py @@ -0,0 +1,19 @@ +import sqlite3 +from datetime import datetime + +def log_event(camera_id, objects): + conn = sqlite3.connect('events.db') + c = conn.cursor() + for obj in objects: + c.execute("INSERT INTO events (camera_id, timestamp, object) VALUES (?, ?, ?)", + (camera_id, datetime.now(), str(obj))) + conn.commit() + conn.close() + +def get_all_events(): + conn = sqlite3.connect('events.db') + c = conn.cursor() + c.execute("SELECT * FROM events") + events = c.fetchall() + conn.close() + return events diff --git a/Stream_System/backend/motion_detection.py b/Stream_System/backend/motion_detection.py new file mode 100644 index 0000000..ff5eec3 --- /dev/null +++ b/Stream_System/backend/motion_detection.py @@ -0,0 +1,15 @@ +import cv2 + +def detect_motion(frame, background_frame): + diff = cv2.absdiff(background_frame, frame) + gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) + blur = cv2.GaussianBlur(gray, (5, 5), 0) + _, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY) + contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + moving_objects = [] + for contour in contours: + if cv2.contourArea(contour) > 500: + x, y, w, h = cv2.boundingRect(contour) + moving_objects.append((x, y, w, h)) + return moving_objects diff --git a/Stream_System/backend/requirements.txt b/Stream_System/backend/requirements.txt new file mode 100644 index 0000000..5867b6d --- /dev/null +++ b/Stream_System/backend/requirements.txt @@ -0,0 +1,2 @@ +Flask +opencv-python \ No newline at end of file diff --git a/Stream_System/docker-compose.yml b/Stream_System/docker-compose.yml new file mode 100644 index 0000000..cdd5f6e --- /dev/null +++ b/Stream_System/docker-compose.yml @@ -0,0 +1,30 @@ +version: '3' +services: + backend: + build: ./backend + ports: + - "5000:5000" + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + volumes: + - './frontend:/src' + - '/src/node_modules' + ports: + - "80:80" + db: + image: postgres + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: password + ports: + - "5432:5432" + prometheus: + image: prom/prometheus + ports: + - "9090:9090" + grafana: + image: grafana/grafana + ports: + - "3000:3000" diff --git a/Stream_System/frontend/Dockerfile b/Stream_System/frontend/Dockerfile new file mode 100644 index 0000000..2dcef51 --- /dev/null +++ b/Stream_System/frontend/Dockerfile @@ -0,0 +1,28 @@ +# FROM node:14-alpine +# WORKDIR /app +# COPY . . +# RUN npm install +# RUN npm run build +# EXPOSE 8080 +# CMD ["npm", "run", "serve"] + + +# base image +FROM node:12.2.0-alpine + +# set working directory +WORKDIR /src + +# add `/app/node_modules/.bin` to $PATH +ENV key=/src/node_modules/.bin:$PATH + +# install and cache app dependencies +COPY package.json /src/package.json +RUN npm install +RUN npm install @vue/cli@3.7.0 -g +RUN npm run build + +EXPOSE 80 + +# start app +CMD ["npm", "run", "serve"] \ No newline at end of file diff --git a/Stream_System/frontend/package.json b/Stream_System/frontend/package.json new file mode 100644 index 0000000..3debfc1 --- /dev/null +++ b/Stream_System/frontend/package.json @@ -0,0 +1,12 @@ +{ + "name": "camera-stream", + "version": "1.0.0", + "scripts": { + "serve": "vue-cli-service serve", + "build": "vue-cli-service build" + }, + "dependencies": { + "vue": "^2.6.12" + } + } + \ No newline at end of file diff --git a/Stream_System/frontend/src/App.vue b/Stream_System/frontend/src/App.vue new file mode 100644 index 0000000..aad989f --- /dev/null +++ b/Stream_System/frontend/src/App.vue @@ -0,0 +1,60 @@ + + + + + + \ No newline at end of file diff --git a/Stream_System/frontend/src/components/CameraBlock.vue b/Stream_System/frontend/src/components/CameraBlock.vue new file mode 100644 index 0000000..0800e1d --- /dev/null +++ b/Stream_System/frontend/src/components/CameraBlock.vue @@ -0,0 +1,48 @@ + + + + + + \ No newline at end of file diff --git a/Stream_System/frontend/src/components/CameraControl.vue b/Stream_System/frontend/src/components/CameraControl.vue new file mode 100644 index 0000000..50b1038 --- /dev/null +++ b/Stream_System/frontend/src/components/CameraControl.vue @@ -0,0 +1,23 @@ + + + + \ No newline at end of file diff --git a/Stream_System/frontend/src/components/CameraGrid.vue b/Stream_System/frontend/src/components/CameraGrid.vue new file mode 100644 index 0000000..0e10fae --- /dev/null +++ b/Stream_System/frontend/src/components/CameraGrid.vue @@ -0,0 +1,57 @@ + + + + + + \ No newline at end of file diff --git a/Stream_System/frontend/src/components/CameraView.vue b/Stream_System/frontend/src/components/CameraView.vue new file mode 100644 index 0000000..2d8fb1f --- /dev/null +++ b/Stream_System/frontend/src/components/CameraView.vue @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/Stream_System/frontend/src/main.js b/Stream_System/frontend/src/main.js new file mode 100644 index 0000000..dcd0325 --- /dev/null +++ b/Stream_System/frontend/src/main.js @@ -0,0 +1,6 @@ +import Vue from 'vue' +import App from './App.vue' + +new Vue({ + render: h => h(App), +}).$mount('#app') diff --git a/VideoRegister b/VideoRegister new file mode 160000 index 0000000..7c0d239 --- /dev/null +++ b/VideoRegister @@ -0,0 +1 @@ +Subproject commit 7c0d239e48a11cb620fe18eb10e9750c181a6134