diff --git a/.gitignore b/.gitignore index de88a2b3adcdd604db49c8ff544b4427e4f935e9..a17152a7e7ce9d3f563aa4472d25aa817a7d29ef 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules/ build/ env/ .env -__pycache__ \ No newline at end of file +__pycache__ +cameras.py \ No newline at end of file diff --git a/README.md b/README.md index 6be0232a3f8b924143ae4d7b56a5d0aef8aae9c7..c8107378dada63bf4e092718fbe13b49905a3a2f 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,6 @@ Navigate to [http://localhost:3000](http://localhost:3000) # TODO ## Coté algo -- Faire tournez le script de comptage que pendant les créneaux d'ouvertures du RU associé -- Mettre en place le script pour compter les caisses ouvertes -- Mettre en place le couplage des caméras (implique de pouvoir définir les masques proprement) dans le script de comptage de personnes - Accéder à d'autre infos telle que l'API des cours sur demande à la DISI pour intégrer ça aux prédictions (ex: cours en promo complète juste implique plus d'attente) ## Coté dev diff --git a/backend/cameras.py.template b/backend/cameras.py.template new file mode 100644 index 0000000000000000000000000000000000000000..5d197e572134b2e9d63518bcbcd5a227ee42bde0 --- /dev/null +++ b/backend/cameras.py.template @@ -0,0 +1,43 @@ +restaurants = [ + { + "restaurant": "local", + "a_factor": 30, + "b_factor": 120, + "cameras": + [ + { + "IP": "", + "user": "", + "password": "", + "stream": "stream1", + "mask_points": + [ + [ + [70, 370], + [420, 720], + [1280, 720], + [1280, 250], + [930, 215], + [450, 550], + [130, 350] + ] + ], + "caisses": + [ + { + "x1": 380, + "x2": 435, + "y1": 740, + "y2": 780 + }, + { + "x1": 300, + "x2": 350, + "y1": 830, + "y2": 880 + } + ] + } + ] + } +] diff --git a/backend/db/crud.py b/backend/db/crud.py index df872ffc41042fd3a602dcdb7dcd8fccb57b2130..a856d45a090291fc14ce11865323dec349e15d31 100644 --- a/backend/db/crud.py +++ b/backend/db/crud.py @@ -231,7 +231,7 @@ def get_news(place: str, db: Session): next_timetable = None for time_slot in opening_hours: if current_date.time() < time_slot.open_time: - next_timetable=time_slot.open_time + next_timetable = time_slot.open_time break if not next_timetable: closure = db.query( diff --git a/backend/main.py b/backend/main.py index d6c986ac7a837ba1f558c9d386bfb257ded706f8..ba7a7273e7fbcd7de70ec586c1bd11c248187224 100644 --- a/backend/main.py +++ b/backend/main.py @@ -2,6 +2,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from dotenv import load_dotenv from threading import Thread +from asyncio import run import os from db import database, models @@ -30,7 +31,7 @@ app.add_middleware( async def on_startup(): # Database creation models.Base.metadata.create_all(bind=database.engine) - t = Thread(target=handle_cameras) + t = Thread(target=run, args=(handle_cameras(),)) t.start() diff --git a/backend/video_capture.py b/backend/video_capture.py index 9806146f1aad3258b8e71d350cf91b4c6093d04f..08cf8533470937c824551fdf2c7e28441e7858e8 100644 --- a/backend/video_capture.py +++ b/backend/video_capture.py @@ -3,75 +3,76 @@ from datetime import datetime, timedelta import numpy as np import keras from utils.preprocessing import fix_singular_shape, norm_by_imagenet -from dotenv import load_dotenv import json -import os +import time +from cameras import restaurants from db import models from db.database import SessionLocal from routers.websocket import manager -def handle_cameras(): +async def handle_cameras(): model = keras.models.load_model('assets', compile=False) db = SessionLocal() - load_dotenv() - camera_number = int(os.getenv('CAM_NUMBER')) - cameras = [] - for i in range(camera_number): - camera = {} - camera["place"] = os.getenv(f'CAM_{i}_PLACE') - camera["IP"] = os.getenv(f'CAM_{i}_IP') - camera["user"] = os.getenv(f'CAM_{i}_USER') - camera["password"] = os.getenv(f'CAM_{i}_PASSWORD') - camera["stream"] = os.getenv(f'CAM_{i}_STREAM') - camera["a_factor"] = int(os.getenv(f'CAM_{i}_A_FACTOR')) - camera["b_factor"] = int(os.getenv(f'CAM_{i}_B_FACTOR')) - camera["framegap"] = int(os.getenv(f'CAM_{i}_FRAMEGAP')) - camera["count"] = 0 - camera["cap"] = cv2.VideoCapture( - f'rtsp://{camera["user"]}:{camera["password"]}@{camera["IP"]}/{camera["stream"]}') - mask_length = int(os.getenv(f'CAM_{i}_POINTS_NB')) - mask_points = [] - for j in range(mask_length): - point = os.getenv(f'CAM_{i}_POINT_{j}') - mask_points.append(list(map(int, point.split(',')))) - mask = np.zeros((720, 1280, 3), dtype=np.float32) - cv2.fillPoly(mask, [np.array(mask_points)], (255, 255, 255)) - camera["mask"] = mask - cameras.append(camera) + for restaurant in restaurants: + for camera in restaurant["cameras"]: + mask = np.zeros((720, 1280, 3), dtype=np.float32) + cv2.fillPoly(mask, np.array(camera["mask_points"]), (255, 255, 255)) + camera["mask"] = mask while True: - for camera in cameras: - if camera['cap'].isOpened(): - ret, frame = camera['cap'].read() - if ret and camera['count'] % camera['framegap'] == 0: - current_time = datetime.now() - masked_img = cv2.bitwise_and( - frame.astype(np.float32), camera["mask"]) - treated_img = fix_singular_shape(masked_img, 16) - input_image = np.expand_dims( - np.squeeze( - norm_by_imagenet( - np.array( - [treated_img]))), - axis=0) - pred_map = np.squeeze(model.predict(input_image)) - count_prediction = np.sum(pred_map) + + current_date = datetime.now() + weekday, current_time = current_date.weekday(), current_date.time() + + for restaurant in restaurants: + + is_open = db.query( + models.OpeningHours).filter( + models.OpeningHours.place == restaurant["restaurant"], + models.OpeningHours.day == weekday, + models.OpeningHours.open_time <= current_time, + models.OpeningHours.close_time >= current_time).first() is not None + + if is_open: + count_prediction = 0 + open_checkouts = 0 + cams_working = True + + for camera in restaurant["cameras"]: + cap = cv2.VideoCapture(f'rtsp://{camera["user"]}:{camera["password"]}@{camera["IP"]}/{camera["stream"]}') + if cams_working and cap.isOpened(): + _, frame = cap.read() + masked_img = cv2.bitwise_and( + frame.astype(np.float32), camera["mask"]) + treated_img = fix_singular_shape(masked_img, 16) + input_image = np.expand_dims( + np.squeeze( + norm_by_imagenet( + np.array( + [treated_img]))), + axis=0) + pred_map = np.squeeze(model.predict(input_image, verbose=0)) + count_prediction += np.sum(pred_map) + for caisse in camera["caisses"]: + if np.sum(pred_map[caisse["x1"] // 2:caisse["x2"] // 2, caisse["y1"] // 2:caisse["y2"] // 2]) > 0.5: + open_checkouts += 1 + else: + cams_working = False + cap.release() + + if cams_working and open_checkouts: waiting_time = timedelta( - seconds=camera['b_factor'] + - int(count_prediction) * - camera['a_factor']) + seconds=restaurant['b_factor'] + + int(count_prediction * + restaurant['a_factor'] / open_checkouts)) db_record = models.Records( - place=camera['place'], - date=current_time, + place=restaurant['restaurant'], + date=current_date, density=int(count_prediction), waiting_time=waiting_time) db.add(db_record) db.commit() - manager.broadcast(json.dumps({"type": "data"})) - camera['count'] += 1 - else: - camera["cap"] = cv2.VideoCapture( - f"rtsp://{camera['user']}:{camera['password']}@{camera['IP']}/{camera['stream']}") - print("tentative de reconnexion") + await manager.broadcast(json.dumps({"type": "data"})) + time.sleep(max(0, 60 - (datetime.now() - current_date).total_seconds()))