| | import copy |
| | import ast |
| | from PIL import Image, ImageDraw, ImageFont, ImageColor |
| |
|
| | additional_colors = [colorname for (colorname, colorcode) in ImageColor.colormap.items()] |
| |
|
| | def plot_bounding_boxes(im, bounding_boxes, input_width, input_height): |
| | """ |
| | Plots bounding boxes on an image with markers for each a name, using PIL, normalized coordinates, and different colors. |
| | |
| | Args: |
| | img_path: The path to the image file. |
| | bounding_boxes: A list of bounding boxes containing the name of the object |
| | and their positions in normalized [y1 x1 y2 x2] format. |
| | """ |
| | |
| |
|
| | |
| | img = im |
| | width, height = img.size |
| | |
| | |
| | draw = ImageDraw.Draw(img) |
| |
|
| | |
| | colors = [ |
| | 'red', |
| | 'green', |
| | 'blue', |
| | 'yellow', |
| | 'orange', |
| | 'pink', |
| | 'purple', |
| | 'brown', |
| | 'gray', |
| | 'beige', |
| | 'turquoise', |
| | 'cyan', |
| | 'magenta', |
| | 'lime', |
| | 'navy', |
| | 'maroon', |
| | 'teal', |
| | 'olive', |
| | 'coral', |
| | 'lavender', |
| | 'violet', |
| | 'gold', |
| | 'silver', |
| | ] + additional_colors |
| |
|
| | if bounding_boxes is None: |
| | |
| | return |
| | |
| | |
| | for i, bounding_box in enumerate(bounding_boxes): |
| | |
| | if len(bounding_box["bbox_2d"]) != 4: |
| | continue |
| | color = colors[i % len(colors)] |
| |
|
| | |
| | |
| |
|
| | |
| | abs_x1 = int(bounding_box["bbox_2d"][0]/input_width * width) |
| | abs_y1 = int(bounding_box["bbox_2d"][1]/input_height * height) |
| | abs_x2 = int(bounding_box["bbox_2d"][2]/input_width * width) |
| | abs_y2 = int(bounding_box["bbox_2d"][3]/input_height * height) |
| | |
| |
|
| | if abs_x1 > abs_x2: |
| | abs_x1, abs_x2 = abs_x2, abs_x1 |
| |
|
| | if abs_y1 > abs_y2: |
| | abs_y1, abs_y2 = abs_y2, abs_y1 |
| | |
| | |
| | |
| | draw.rectangle( |
| | ((abs_x1, abs_y1), (abs_x2, abs_y2)), outline=color, width=4 |
| | ) |
| |
|
| | |
| | if "label" in bounding_box: |
| | draw.text((abs_x1 + 8, abs_y1 + 6), bounding_box["label"], fill=color) |
| |
|
| | |
| |
|
| |
|
| | def plot_movement(im, data, input_width, input_height): |
| |
|
| | img = im |
| | width, height = img.size |
| | draw = ImageDraw.Draw(img) |
| | colors = ['red', 'blue'] |
| | line_width = 4 |
| |
|
| | if data is None: |
| | |
| | return |
| | |
| |
|
| | for line in data: |
| | |
| | start_point = line.get("start_point_2d", None) |
| | end_point = line.get("end_point_2d", None) |
| | if start_point is None or end_point is None: |
| | |
| | return |
| |
|
| | |
| | |
| |
|
| | |
| | abs_x_start = int(start_point[0]) / input_width * width |
| | abs_y_start = int(start_point[1]) / input_height * height |
| |
|
| | abs_x_end = int(end_point[0]) / input_width * width |
| | abs_y_end = int(end_point[1]) / input_height * height |
| |
|
| | |
| | draw.line((abs_x_start, abs_y_start, abs_x_end, abs_y_end), fill='black', width=line_width) |
| |
|
| | |
| | for i, point in enumerate([start_point, end_point]): |
| | color = colors[i % len(colors)] |
| | abs_x = int(point[0]) / input_width * width |
| | abs_y = int(point[1]) / input_height * height |
| | radius = 4 |
| | |
| |
|
| | draw.ellipse([(abs_x - radius, abs_y - radius), (abs_x + radius, abs_y + radius)], fill=color) |
| | |
| |
|
| |
|
| | def safe_eval(item): |
| | try: |
| | |
| | return ast.literal_eval(item) |
| | except (ValueError, SyntaxError) as e: |
| | |
| | |
| | return None |
| |
|
| |
|
| | def parse_json(json_output): |
| | |
| | json_output_list = [] |
| | lines = json_output.splitlines() |
| | for i, line in enumerate(lines): |
| | |
| | if line.strip() == "```json": |
| | tmp = "\n".join(lines[i+1:]) |
| | if "```" in tmp: |
| | tmp = tmp.split("```")[0] |
| | else: |
| | tmp = tmp.split("</think>")[0].strip() |
| | json_output_list.append(tmp) |
| | return json_output_list |
| |
|
| |
|
| | def parse_bbox_and_movement(response): |
| | parsed_list = parse_json(response) |
| | parsed_list = [safe_eval(item) for item in parsed_list] |
| | parsed_list = [item for item in parsed_list if item is not None] |
| | bbox_list, movement_list = [], [] |
| | for item_list in parsed_list: |
| | for item in item_list: |
| | if "bbox_2d" in item and "label" in item: |
| | bbox_list.append(item) |
| | elif "start_point_2d" in item and "end_point_2d" in item and "label" in item: |
| | movement_list.append(item) |
| | |
| | |
| | |
| | |
| | |
| | |
| | return bbox_list, movement_list |
| |
|
| |
|
| |
|
| | def merge_bbox_list(bbox_list_origin, bbox_list_new, image_index_new): |
| | """ |
| | 合并两个边界框列表,确保每个标签的边界框唯一,优先使用 bbox_list_new 中的数据。 |
| | |
| | :param bbox_list_origin: 原始边界框列表 |
| | :param bbox_list_new: 新的边界框列表 |
| | :return: 合并后的边界框列表 |
| | """ |
| | image_index = -1 |
| | if len(bbox_list_new): |
| | |
| | new_label_dict = {bbox['label']: bbox for bbox in bbox_list_new} |
| | merged_bbox_list = [] |
| | try: |
| | image_index = [bb['index']-1 for bb in bbox_list_new] |
| | except Exception as e: |
| | print('bbox_list_new:', bbox_list_new) |
| | raise e |
| | assert len(set(image_index)) == 1, f"bbox_list_new: {bbox_list_new}" |
| | image_index = image_index[0] |
| | if image_index not in bbox_list_origin: |
| | bbox_list_origin[image_index] = [] |
| |
|
| | |
| | for bbox in bbox_list_origin[image_index]: |
| | label = bbox['label'] |
| | if label in new_label_dict: |
| | |
| | merged_bbox_list.append(new_label_dict.pop(label)) |
| | else: |
| | |
| | merged_bbox_list.append(bbox) |
| |
|
| | |
| | merged_bbox_list.extend(new_label_dict.values()) |
| | bbox_list_origin[image_index_new] = copy.deepcopy(merged_bbox_list) |
| | return image_index, bbox_list_origin |
| |
|
| |
|
| | def merge_movement_list(movement_list_origin, movement_list_new, image_index_new): |
| | """ |
| | 合并两个移动方向列表,确保每个标签的移动方向唯一,优先使用 movement_list_new 中的数据。 |
| | |
| | :param movement_list_origin: 原始移动方向列表 |
| | :param movement_list_new: 新的移动方向列表 |
| | :return: 合并后的移动方向列表 |
| | """ |
| | image_index = -1 |
| | if len(movement_list_new): |
| | |
| | new_label_dict = {movement['label']: movement for movement in movement_list_new} |
| | merged_movement_list = [] |
| |
|
| | image_index = [mv['index'] - 1 for mv in movement_list_new] |
| | assert len(set(image_index)) == 1 |
| | image_index = image_index[0] |
| | if image_index not in movement_list_origin: |
| | movement_list_origin[image_index] = [] |
| |
|
| | |
| | for movement in movement_list_origin[image_index]: |
| | label = movement['label'] |
| | if label in new_label_dict: |
| | |
| | merged_movement_list.append(new_label_dict.pop(label)) |
| | else: |
| | |
| | merged_movement_list.append(movement) |
| |
|
| | |
| | merged_movement_list.extend(new_label_dict.values()) |
| | movement_list_origin[image_index_new] = copy.deepcopy(merged_movement_list) |
| | return image_index, movement_list_origin |
| |
|
| |
|
| | def merge_bbox_movement(bbox_list_origin, movement_list_origin, bbox_list_new, movement_list_new, image_index_new): |
| | idx1, merged_bbox_list = merge_bbox_list(bbox_list_origin, bbox_list_new, image_index_new) |
| | idx2, merged_movement_list = merge_movement_list(movement_list_origin, movement_list_new, image_index_new) |
| | if idx1 >= 0 and idx2 == -1: |
| | merged_movement_list[image_index_new] = copy.deepcopy(merged_movement_list[idx1]) |
| | |
| | |
| |
|
| | if idx2 >= 0 and idx1 == -1: |
| | merged_bbox_list[image_index_new] = copy.deepcopy(merged_bbox_list[idx2]) |
| | |
| | |
| |
|
| | if image_index_new not in merged_bbox_list: |
| | merged_bbox_list[image_index_new] = [] |
| | if image_index_new not in merged_movement_list: |
| | merged_movement_list[image_index_new] = [] |
| |
|
| | return max([idx1, idx2]), merged_bbox_list, merged_movement_list |
| |
|