Skip to content
Snippets Groups Projects
Commit 89beed00 authored by Antoine Gaudron-Desjardins's avatar Antoine Gaudron-Desjardins
Browse files

Merge branch 'documentation' into 'main'

Documentation

See merge request !52
parents 5ded1cae 8d81c710
No related branches found
No related tags found
1 merge request!52Documentation
Pipeline #44469 failed
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
========== ==========
# Run the server-side FastAPI app # Run the server-side FastAPI app
## In development mode ## In development mode
Using the template files, you first need to create backend/.env and backend/cameras.py.
You can start a virtual environment with the following instructions. You can start a virtual environment with the following instructions.
```sh ```sh
...@@ -34,6 +35,8 @@ Run the server, `docker-compose up -d` ...@@ -34,6 +35,8 @@ Run the server, `docker-compose up -d`
# Run the client-side React app in a different terminal window: # Run the client-side React app in a different terminal window:
Using the template file, you first need to create frontend/.env.
```sh ```sh
$ cd frontend $ cd frontend
$ npm install $ npm install
...@@ -44,6 +47,18 @@ Navigate to [http://localhost:3000](http://localhost:3000) ...@@ -44,6 +47,18 @@ Navigate to [http://localhost:3000](http://localhost:3000)
<br/> <br/>
# Documentation
## Algorithm
The crowd-counting AI model used is based on this repository:
https://github.com/ZhengPeng7/W-Net-Keras
The dataset used is ShanghaiTech Part B.
The model is given a 3-channel image and generates a density map of half the size of the input image.
The estimated number of people is obtained by summing on all pixels of the density map.
<br/>
# TODO # TODO
## Coté algo ## Coté algo
- 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) - 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)
......
restaurants = [ restaurants = [
{ {
"restaurant": "local", "restaurant": "local",
"a_factor": 30, # the linear function estimating the waiting time t based on the number of people n and the number c of open checkouts is:
"b_factor": 120, # t = a * n / c + b
"a_factor": 30, # slope
"b_factor": 120, # y-intercept
# list of all the cameras that correspond to a same restaurant
"cameras": "cameras":
[ [
{ {
# the RTSP url will be: rtsp://user:password@IP:stream
"IP": "", "IP": "",
"user": "", "user": "",
"password": "", "password": "",
"stream": "stream1", "stream": "stream1",
# list of the coordinates of the points which constitue the region that should be masked on the picture
"mask_points": "mask_points":
[ [
# polygon which should be part of the mask
[ [
# [x, y] coordinates of each vertex of the polygon (on a 1280*720 picture)
[70, 370], [70, 370],
[420, 720], [420, 720],
[1280, 720], [1280, 720],
...@@ -22,8 +29,10 @@ restaurants = [ ...@@ -22,8 +29,10 @@ restaurants = [
[130, 350] [130, 350]
] ]
], ],
"caisses": # list of the coordinates of each checkout
"checkouts":
[ [
# each checkout corresponds to a rectangle, indicated by two x and two y coordinates
{ {
"x1": 380, "x1": 380,
"x2": 435, "x2": 435,
......
...@@ -17,6 +17,7 @@ async def handle_cameras(): ...@@ -17,6 +17,7 @@ async def handle_cameras():
db = SessionLocal() db = SessionLocal()
for restaurant in restaurants: for restaurant in restaurants:
for camera in restaurant["cameras"]: for camera in restaurant["cameras"]:
# each camera masked is constructed based on the given coordiantes and saved
mask = np.zeros((720, 1280, 3), dtype=np.float32) mask = np.zeros((720, 1280, 3), dtype=np.float32)
cv2.fillPoly(mask, np.array(camera["mask_points"]), (255, 255, 255)) cv2.fillPoly(mask, np.array(camera["mask_points"]), (255, 255, 255))
camera["mask"] = mask camera["mask"] = mask
...@@ -28,6 +29,7 @@ async def handle_cameras(): ...@@ -28,6 +29,7 @@ async def handle_cameras():
for restaurant in restaurants: for restaurant in restaurants:
# we check whether or not the restaurant is opened
is_open = db.query( is_open = db.query(
models.OpeningHours).filter( models.OpeningHours).filter(
models.OpeningHours.place == restaurant["restaurant"], models.OpeningHours.place == restaurant["restaurant"],
...@@ -36,13 +38,15 @@ async def handle_cameras(): ...@@ -36,13 +38,15 @@ async def handle_cameras():
models.OpeningHours.close_time >= current_time).first() is not None models.OpeningHours.close_time >= current_time).first() is not None
if is_open: if is_open:
count_prediction = 0 count_prediction = 0 # estimated number of people in the restaurant
open_checkouts = 0 open_checkouts = 0 # estimated number of open checkouts in the restaurant
cams_working = True cams_working = True # boolean indicating whether or not all cams are working in the restaurant
for camera in restaurant["cameras"]: for camera in restaurant["cameras"]:
# connection to the rtsp stream
cap = cv2.VideoCapture(f'rtsp://{camera["user"]}:{camera["password"]}@{camera["IP"]}/{camera["stream"]}') cap = cv2.VideoCapture(f'rtsp://{camera["user"]}:{camera["password"]}@{camera["IP"]}/{camera["stream"]}')
if cams_working and cap.isOpened(): if cams_working and cap.isOpened():
# extraction and preoprocessing of the first frame
_, frame = cap.read() _, frame = cap.read()
masked_img = cv2.bitwise_and( masked_img = cv2.bitwise_and(
frame.astype(np.float32), camera["mask"]) frame.astype(np.float32), camera["mask"])
...@@ -53,16 +57,19 @@ async def handle_cameras(): ...@@ -53,16 +57,19 @@ async def handle_cameras():
np.array( np.array(
[treated_img]))), [treated_img]))),
axis=0) axis=0)
# getting the density map from the model and the number of people
pred_map = np.squeeze(model.predict(input_image, verbose=0)) pred_map = np.squeeze(model.predict(input_image, verbose=0))
count_prediction += np.sum(pred_map) count_prediction += np.sum(pred_map)
for caisse in camera["caisses"]: for checkout in camera["checkouts"]:
if np.sum(pred_map[caisse["x1"] // 2:caisse["x2"] // 2, caisse["y1"] // 2:caisse["y2"] // 2]) > 0.5: # we check whether or not the density in the checkout area is high enough to determine if it is open or not
if np.sum(pred_map[checkout["x1"] // 2:checkout["x2"] // 2, checkout["y1"] // 2:checkout["y2"] // 2]) > 0.5:
open_checkouts += 1 open_checkouts += 1
else: else:
cams_working = False cams_working = False
cap.release() cap.release()
if cams_working and open_checkouts: if cams_working and open_checkouts:
# the estimated waiting time is calculated and put in the database
waiting_time = timedelta( waiting_time = timedelta(
seconds=restaurant['b_factor'] + seconds=restaurant['b_factor'] +
int(count_prediction * int(count_prediction *
...@@ -75,4 +82,5 @@ async def handle_cameras(): ...@@ -75,4 +82,5 @@ async def handle_cameras():
db.add(db_record) db.add(db_record)
db.commit() db.commit()
await manager.broadcast(json.dumps({"type": "data"})) await manager.broadcast(json.dumps({"type": "data"}))
# sleeping enough time so that there is a 60 second delay between the start of two consecutive loops
time.sleep(max(0, 60 - (datetime.now() - current_date).total_seconds())) time.sleep(max(0, 60 - (datetime.now() - current_date).total_seconds()))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment