diff --git a/Dockerfile b/Dockerfile index d4e7cf4..016d441 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM python:3.10 RUN apt-get update COPY requirements.txt . -RUN pip install -r requirements.txt +RUN pip --default-timeout=100 install -r requirements.txt RUN apt-get install ffmpeg libsm6 libxext6 -y WORKDIR /var/www/5scontrol diff --git a/confs/settings.env b/confs/settings.env index 7fe0bb6..2a3c702 100644 --- a/confs/settings.env +++ b/confs/settings.env @@ -1,222 +1,28 @@ username = 'admin' password = 'just4Taqtile' -task = 'bottles' -camera_url = 'http://192.168.1.163/onvif-http/snapshot?Profile_1' +camera_url = 'http://192.168.1.162/onvif-http/snapshot?Profile_1' server_url = 'http://127.0.0.1' extra = ' [ { "areas": [ - { - "itemId": 51, - "itemName": "Savisriegis 4.8*90/50", - "multiRow": True, - "coords": [ - { - "x1": 670.5552672548002, - "x2": 702.439024390244, - "y1": 777.6878534798535, - "y2": 916.178695970696 - } - ] - }, - { - "itemId": 52, - "itemName": "M645 107Р (RAL7035)", - "multiRow": True, - "coords": [ - { - "x1": 896.2807100591715, - "x2": 941.72449704142, - "y1": 420.2308155844156, - "y2": 592.6152311688312 - } - ] - }, - { - "itemId": 36, - "itemName": "Savisriegis 4.8*100", - "multiRow": True, - "coords": [ - { - "x1": 705.503355704698, - "x2": 734.1387024608501, - "y1": 778.1279162303664, - "y2": 914.8399581151832 - } - ] - }, { "itemId": 39, - "itemName": "643370", - "multiRow": False, - "coords": [ - { - "x1": 834.3624161073826, - "x2": 886.1918210290828, - "y1": 412.13838743455494, - "y2": 604.7741989528795 - } - ] - }, - { - "itemId": 43, - "itemName": "М175.1 107Р", - "multiRow": True, - "coords": [ - { - "x1": 622.4608501118569, - "x2": 669.7091722595079, - "y1": 400.7457172774869, - "y2": 584.4525235602094 - } - ] - }, - { - "itemId": 21, - "itemName": "Savisriegis 4.8*120/72", - "multiRow": True, - "coords": [ - { - "x1": 733.9296644295302, - "x2": 764.5746398210291, - "y1": 775.4805445026178, - "y2": 919.7046282722513 - } - ] - }, - { - "itemId": 26, - "itemName": "M620 107P", - "multiRow": True, - "coords": [ - { - "x1": 944.6886800894856, - "x2": 992.5864519015662, - "y1": 419.7686282722513, - "y2": 608.0453193717277 - } - ] - }, - { - "itemId": 46, - "itemName": "М104 anthrazitgrau (176Р)", - "multiRow": False, - "coords": [ - { - "x1": 477.8523489932886, - "x2": 523.668903803132, - "y1": 397.8975497382199, - "y2": 563.0912670157068 - } - ] - }, - { - "itemId": 41, - "itemName": "М162", - "multiRow": False, - "coords": [ - { - "x1": 725.5480984340045, - "x2": 771.6581655480984, - "y1": 402.1698010471204, - "y2": 587.7236439790576 - } - ] - }, - { - "itemId": 42, - "itemName": "М261.2", - "multiRow": False, - "coords": [ - { - "x1": 674.0044742729307, - "x2": 721.4536733780761, - "y1": 399.3216335078534, - "y2": 582.0429738219896 - } - ] - }, - { - "itemId": 40, - "itemName": "М177", - "multiRow": False, - "coords": [ - { - "x1": 775.6599552572708, - "x2": 830.3838210290828, - "y1": 409.290219895288, - "y2": 597.6765654450262 - } - ] - }, - { - "itemId": 25, - "itemName": "M687 107 P RAL 7035", - "multiRow": True, - "coords": [ - { - "x1": 1278.527562642369, - "x2": 1327.8070523917995, - "y1": 639.3769769769771, - "y2": 770.5239959959961 - } - ] - }, - { - "itemId": 44, - "itemName": "М104 cremeweib (607)", - "multiRow": False, - "coords": [ - { - "x1": 575.2125279642058, - "x2": 621.0290827740492, - "y1": 399.3216335078534, - "y2": 573.0598534031413 - } - ] - }, - { - "itemId": 45, - "itemName": "М104 schwarzbraun (925)", - "multiRow": False, + "itemName": "tea", + "multiRow": false, + "task": "bottles", + "lowStockLevel": 20, "coords": [ { - "x1": 527.9642058165548, - "x2": 572.3489932885906, - "y1": 396.47346596858637, - "y2": 571.6357696335078 - } - ] - }, - { - "itemId": 20, - "itemName": "Savisriegis 4.2*55", - "multiRow": True, - "coords": [ - { - "x1": 764.8933333333332, - "x2": 803.9107999999999, - "y1": 784.2821463414635, - "y2": 930.5133268292684 + "x1": 495.8731636363636, + "x2": 1417.4731636363636, + "y1": 186.89133014354067, + "y2": 464.53088357256775 } ] } ], - "zones": [ - { - "zoneId": 5, - "zoneName": "STELAG 1.1", - "coords": [ - { - "x1": 355, - "x2": 1416, - "y1": 234.82086001829828, - "y2": 972.4311070448308 - } - ] - } - ] + "zones": [] } ] ' diff --git a/get_predictions.py b/get_predictions.py index 78cc3d0..65e824f 100644 --- a/get_predictions.py +++ b/get_predictions.py @@ -5,12 +5,18 @@ PORT = 5000 def predict_human(img: np.array, server_url: str, logger: Logger): - response = requests.post( - f"{server_url}:{PORT}/predict_human", - json={ - "image": img.tolist() - } + try: + response = requests.post( + f"{server_url}:{PORT}/predict_human", + json={ + "image": img.tolist() + } + ) + except Exception as exc: + logger.critical( + "Cannot send request. Error - {}".format(exc) ) + return [None, None] status_code = response.status_code if status_code == 200: n_boxes = response.json().get('n_boxes') @@ -24,31 +30,43 @@ def predict_human(img: np.array, server_url: str, logger: Logger): return [n_boxes, coordinates] def predict_bottles(img: np.array, server_url: str, logger: Logger): - response = requests.post( - f"{server_url}:{PORT}/predict_bottles", - json={ - "image": img.tolist() - } + try: + response = requests.post( + f"{server_url}:{PORT}/predict_bottles", + json={ + "image": img.tolist() + } + ) + except Exception as exc: + logger.critical( + "Cannot send request. Error - {}".format(exc) ) + return [None, None] status_code = response.status_code if status_code == 200: n_bottles = response.json().get('n_items') coordinates = np.array(response.json().get("coordinates")) else: logger.warning( - "Response code = {}.\n JSON = {}".format(status_code, response.json()) + "Response code = {}.\n response = {}".format(status_code, response) ) n_bottles = None coordinates = None return [n_bottles, coordinates] def predict_boxes(img: np.array, server_url: str, logger: Logger): - response = requests.post( - f"{server_url}:{PORT}/predict_boxes", - json={ - "image": img.tolist() - } - ) + try: + response = requests.post( + f"{server_url}:{PORT}/predict_boxes", + json={ + "image": img.tolist() + } + ) + except Exception as exc: + logger.critical( + "Cannot send request. Error - {}".format(exc) + ) + return [None, None] status_code = response.status_code if status_code == 200: n_boxes = response.json().get('n_items') diff --git a/main.py b/main.py index 53da252..bbdc3e7 100644 --- a/main.py +++ b/main.py @@ -12,8 +12,8 @@ if os.environ.get("extra") is None: load_dotenv("confs/settings.env") -extra = os.environ.get("extra") -extra = ast.literal_eval(extra)[0] +extra: str = os.environ.get("extra") +extra = json.loads(extra)[0] areas = extra.get("areas") zones = extra.get("zones") @@ -26,6 +26,5 @@ logger = create_logger() dataset = HTTPLIB2Capture(source, username=username, password=password, logger=logger) -target = os.environ.get("task") if os.environ.get("task") is not None else "boxes" -run_min_max(dataset, logger, areas, folder, DEBUG_FOLDER, server_url, zones, target) +run_min_max(dataset, logger, areas, folder, DEBUG_FOLDER, server_url, zones) diff --git a/min_max_utils/MinMaxReporter.py b/min_max_utils/MinMaxReporter.py index 1ea766a..673086e 100644 --- a/min_max_utils/MinMaxReporter.py +++ b/min_max_utils/MinMaxReporter.py @@ -73,8 +73,18 @@ def create_report(self, n_boxes_history: list, img: np.array, areas: list, boxes debug_user_image = draw_line(debug_user_image, line, area_coords, thickness=4) is_red_line_in_subarea = is_red_line_in_item = True - text_item = f"{item_name}: {n_boxes_history[item_index][subarr_idx] if not is_red_line_in_subarea else 'low stock level'}" - + if multi_row: + text_item = f"{item_name}: {n_boxes_history[item_index][subarr_idx] if not is_red_line_in_subarea else 'low stock level'}" + else: + text_item = f"{item_name}: " + if len(boxes_coords[item_index][subarr_idx]) == 0: + text_item += 'out of stock' + elif len(boxes_coords[item_index][subarr_idx]) <= item["lowStockLevel"]: + text_item += 'low stock level' + else: + text_item += 'in stock' + + rectangle_color = (0, 255, 0) if "in stock" in text_item else (51, 51, 255) debug_user_image = draw_rect(debug_user_image, area_coords, rectangle_color, thickness=2) if not multi_row: @@ -84,7 +94,7 @@ def create_report(self, n_boxes_history: list, img: np.array, areas: list, boxes debug_user_image = draw_rect( debug_user_image, bbox_coords[:4], - (51, 51, 255) if is_red_line_in_item else (0, 255, 0), + rectangle_color, thickness=2 ) debug_user_image = draw_text( @@ -105,15 +115,27 @@ def create_report(self, n_boxes_history: list, img: np.array, areas: list, boxes (255, 255, 255) ) - status_text = 'Out of stock' if is_red_line_in_item else 'In stock' - tot_number = f"\n{item_name} total number: {sum(n_boxes_history[item_index])}" if not multi_row else "" + total_number = sum(n_boxes_history[item_index]) + + + if multi_row: + status_text = 'Out of stock' if is_red_line_in_item else 'In stock' + else: + if total_number == 0: + status_text = 'Out of stock' + elif total_number <= item["lowStockLevel"]: + status_text = "Low stock" + else: + status_text = "In stock" + + tot_number = f"\n{item_name} total number: {total_number}" if not multi_row else "" text_all_img = f"{status_text}" + tot_number debug_user_image = draw_text( debug_user_image, (10, 1000), text_all_img, 1920, - (255, 0, 0) if is_red_line_in_item else (0, 255, 0), + (255, 0, 0) if status_text in ('Out of stock', 'Low stock') else (0, 255, 0), min_font_size=30, img_fraction=None ) diff --git a/min_max_utils/visualization_utils.py b/min_max_utils/visualization_utils.py index 47940e0..23436b8 100644 --- a/min_max_utils/visualization_utils.py +++ b/min_max_utils/visualization_utils.py @@ -18,7 +18,7 @@ def draw_text(img, coords: list, text: str, area_size:int, text_color: tuple, mi def get_scaled_font(text: str, area_size: int, img_fraction: float = 0.5, min_font_size: int = 14) -> ImageFont: font = ImageFont.truetype("fonts/Inter-Bold.ttf", min_font_size) fontsize = min_font_size - while font.getsize(text)[0] < img_fraction * area_size: + while font.getlength(text) < img_fraction * area_size: fontsize += 1 font = ImageFont.truetype("fonts/Inter-Bold.ttf", fontsize) return font diff --git a/model_image/Dockerfile b/model_image/Dockerfile index 7aed65a..1f99571 100644 --- a/model_image/Dockerfile +++ b/model_image/Dockerfile @@ -2,7 +2,7 @@ FROM python:3.10 RUN apt-get update RUN pip install torch==1.13.1+cpu torchvision==0.14.1+cpu --extra-index-url https://download.pytorch.org/whl/cpu COPY requirements.txt . -RUN pip install -r requirements.txt +RUN pip --default-timeout=100 install -r requirements.txt RUN apt-get install ffmpeg libsm6 libxext6 -y WORKDIR /var/www/5scontrol diff --git a/model_image/app.py b/model_image/app.py index 3fdffb0..1afd82a 100644 --- a/model_image/app.py +++ b/model_image/app.py @@ -5,7 +5,6 @@ import numpy as np import colorlog import logging -import os app = Flask(__name__) @@ -55,7 +54,7 @@ def predict_boxes(): @app.route('/predict_bottles', methods=['POST']) def predict_bottles(): - if request.methos == 'POST': + if request.method == 'POST': image = np.array(request.json['image']).astype(np.float32) n_bottles, coords = human_model(image, classes=[39]) logger.info("Request to predict_bottles: " + str(n_bottles)) diff --git a/model_image/flask_configs/flask_confs.json b/model_image/flask_configs/flask_confs.json index c944acb..9efdaac 100644 --- a/model_image/flask_configs/flask_confs.json +++ b/model_image/flask_configs/flask_confs.json @@ -3,7 +3,7 @@ 0 ], "iou_thres": 0.5, - "conf_thres": 0.475, + "conf_thres": 0.46, "box_detect_model": "min_max_v0.3.10.pt", "human_detect_model": "min_max_v1.0h.pt", "img_size": 640, diff --git a/run.py b/run.py index 7b15cee..4b67b6a 100644 --- a/run.py +++ b/run.py @@ -4,20 +4,16 @@ from min_max_utils.min_max_utils import filter_boxes, check_box_in_area, convert_coords_from_dict_to_list, drop_area, \ most_common from confs.load_configs import N_STEPS -from min_max_utils.visualization_utils import draw_rect from min_max_utils.MinMaxReporter import Reporter import time -import uuid -import cv2 def run_min_max(dataset: HTTPLIB2Capture, logger: Logger, areas: list[dict], - folder: str, debug_folder: str, server_url: str, zones: list, target: str): + folder: str, debug_folder: str, server_url: str, zones: list): stat_history = [] is_human_was_detected = True n_iters = 0 reporter = Reporter(logger, server_url, folder, debug_folder) - send_request_func = predict_boxes if target == 'boxes' else predict_bottles while True: img = dataset.get_snapshot() @@ -45,8 +41,13 @@ def run_min_max(dataset: HTTPLIB2Capture, logger: Logger, areas: list[dict], cropped_images = [] for zone in zones: x1, y1, x2, y2 = convert_coords_from_dict_to_list(zone.get("coords")[0]) - cropped_images.append(img[y1:y2, x1:x2]) - model_preds = [send_request_func(crop_img, server_url, logger) for crop_img in cropped_images] + cropped_images.append(img[y1:y2, x1:x2]) + model_preds_boxes = [predict_boxes(crop_img, server_url, logger) for crop_img in cropped_images] + if any(elem is None for elem in model_preds_boxes): + continue + model_preds_bottles = [predict_bottles(crop_img, server_url, logger) for crop_img in cropped_images] + if any(elem[0] is None for elem in model_preds_bottles): + continue areas_stat = [] @@ -69,16 +70,22 @@ def run_min_max(dataset: HTTPLIB2Capture, logger: Logger, areas: list[dict], (not is_human_was_detected and not is_human_in_image_now and len(stat_history)): boxes_preds = None if zones: + model_preds_to_use = model_preds_boxes if item["task"] == "boxes" else model_preds_bottles for idx, zone in enumerate(zones): zone_coords = convert_coords_from_dict_to_list(zone.get("coords")[0]) if check_box_in_area(area_coord, zone_coords): - boxes_preds = filter_boxes(zone_coords, *model_preds[idx], area_coord) + boxes_preds = filter_boxes( + zone_coords, + *model_preds_to_use[idx], + area_coord + ) item["zoneId"] = zone.get("zoneId") break if boxes_preds is None: logger.critical("Area is not in zone") exit(1) else: + send_request_func = predict_boxes if item["task"] == "boxes" else predict_bottles model_preds = send_request_func(img[area_coord[1]:area_coord[3], area_coord[0]:area_coord[2]], server_url, logger) if model_preds[0] is None: