import math import requests import numpy as np import json from io import BytesIO from fastapi import FastAPI from pydantic import BaseModel from ultralytics import YOLO from PIL import Image app = FastAPI() model = YOLO("posmPJSTRIKE_v1.3.pt") with open("base_width.json", "r") as f: base_width = json.load(f) with open("name_conversion.json", "r") as f: name_convert = json.load(f) class ImageRequest(BaseModel): image_url: str def get_image_from_url(image_url): response = requests.get(image_url) image = Image.open(BytesIO(response.content)).convert("RGB") return np.array(image) def name_conversion(actual_distances,object_positions, name_convert): actual_distances_sys = [] object_positions_sys = {} for item in actual_distances: actual_distances_sys.append({'object':(name_convert[list(item.values())[0][0]],name_convert[list(item.values())[0][1]]),'distances': str(list(item.values())[1]) + " cm"}) for item in object_positions: object_positions_sys.update({name_convert[item]:{"top": str(object_positions[item]['top']) + " cm", "bottom": str(object_positions[item]['bottom']) + " cm", "left": str(object_positions[item]['left']) + " cm", "right": str(object_positions[item]['right']) + " cm"}}) return object_positions_sys, actual_distances_sys def find_position(objects_names_points, par_pix_cm, image): object_positions = {} for obj in objects_names_points: name = list(obj.keys())[0] points = list(obj.values())[0] top_distance = round((points[0][1] - 0) * par_pix_cm[name], 2) bottom_distance = round((image.size[1] - points[3][1]) * par_pix_cm[name], 2) left_distance = round((points[0][0] - 0) * par_pix_cm[name], 2) right_distance = round((image.size[0] - points[3][0]) * par_pix_cm[name], 2) object_positions.update({name: {"top": top_distance, "bottom": bottom_distance, "left": left_distance, "right": right_distance}}) return object_positions def get_actual_distance(closest_points, par_pix_cm): actual_results_n_distance = [] for i in closest_points: avg_px_cm = ((par_pix_cm[i[0]] + par_pix_cm[i[1]]) / 2) actual_results_n_distance.append({'object': i, 'distances': round(closest_points[i] * avg_px_cm, 2)}) return actual_results_n_distance def pixel_per_cm(objects_names_width_pix): par_pix_cm = {} for i in objects_names_width_pix: par_pix_cm_width = base_width[i][0] / objects_names_width_pix[i][0] par_pix_cm_height = base_width[i][1] / objects_names_width_pix[i][1] avg_par_pix_cm = (par_pix_cm_width + par_pix_cm_height) / 2 par_pix_cm.update({i: avg_par_pix_cm}) return par_pix_cm def get_points_n_names(results): objects_names_points = [] objects_names_width_pix = {} for box, cls in zip(results[0].boxes.xyxy, results[0].boxes.cls): x1, y1, x2, y2 = map(int, box) class_name = results[0].names[int(cls)] width = x2 - x1 height = y2 - y1 objects_names_points.append({class_name: [(x1, y1), (x2, y1), (x1, y2), (x2, y2)]}) objects_names_width_pix.update({class_name: [width, height]}) return objects_names_points, objects_names_width_pix def euclidean_distance(point1, point2): dist_pixels = math.sqrt((point2[0] - point1[0])**2 + (point2[1] - point1[1])**2) return dist_pixels def find_closest_points(lst): closest_points = {} for i in range(len(lst)): for j in range(i + 1, len(lst)): list1 = lst[i] list2 = lst[j] min_distance = float('inf') closest_objects_pair = None for obj1 in list1.values(): points1 = obj1 for obj2 in list2.values(): points2 = obj2 for point1 in points1: for point2 in points2: distance = euclidean_distance(point1, point2) if distance < min_distance: min_distance = distance closest_objects_pair = (list1.keys(), list2.keys()) closest_points.update({(list(closest_objects_pair[0])[0], list(closest_objects_pair[1])[0]): round(min_distance, 2)}) return closest_points @app.post("/process_image") def process_image(request: ImageRequest): image = get_image_from_url(request.image_url) image = Image.fromarray(image) size = (640, 640) image.thumbnail(size) res = model(image) objects_names_points, objects_names_width_pix = get_points_n_names(res) par_pix_cm = pixel_per_cm(objects_names_width_pix) closest_points = find_closest_points(objects_names_points) actual_distances = get_actual_distance(closest_points, par_pix_cm) object_positions = find_position(objects_names_points, par_pix_cm, image) # Remove the distance between the same object for item in actual_distances[:]: if item['object'][0] == item['object'][1]: actual_distances.remove(item) # Convert the object names to the system names object_positions_sys, actual_distances_sys = name_conversion(actual_distances,object_positions, name_convert) return { "object_positions": object_positions_sys, "actual_distances": actual_distances_sys }