Spaces:
Sleeping
Sleeping
| import cv2 | |
| import numpy as np | |
| import pandas as pd | |
| import pkg_resources as pkg | |
| import torch | |
| import math | |
| from typing import Tuple | |
| from data_utils.image_utils import _get_width_and_height | |
| def points_to_xyxy(coords: np.ndarray) -> list: | |
| x_coords = [coord[0] for coord in coords] | |
| y_coords = [coord[1] for coord in coords] | |
| x1 = min(x_coords) | |
| y1 = min(y_coords) | |
| x2 = max(x_coords) | |
| y2 = max(y_coords) | |
| return [x1, y1, x2, y2] | |
| def xyxy2xywh(x): | |
| # Convert nx4 boxes from [x1, y1, x2, y2] to [x, y, w, h] where xy1=top-left, xy2=bottom-right | |
| y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) | |
| y[..., 0] = (x[..., 0] + x[..., 2]) / 2 # x center | |
| y[..., 1] = (x[..., 1] + x[..., 3]) / 2 # y center | |
| y[..., 2] = x[..., 2] - x[..., 0] # width | |
| y[..., 3] = x[..., 3] - x[..., 1] # height | |
| return y | |
| def xywh2xyxy(x): | |
| # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right | |
| y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x) | |
| y[..., 0] = x[..., 0] - x[..., 2] / 2 # top left x | |
| y[..., 1] = x[..., 1] - x[..., 3] / 2 # top left y | |
| y[..., 2] = x[..., 0] + x[..., 2] / 2 # bottom right x | |
| y[..., 3] = x[..., 1] + x[..., 3] / 2 # bottom right y | |
| return y | |
| def is_abox_in_bbox(abox_coords, bbox_coords): | |
| # aboxκ° bboxμμ μλμ§ νμΈνλ ν¨μ. μ’ννμ. (x1,y1,x2,y2) | |
| if ( | |
| bbox_coords[0] <= abox_coords[0] | |
| and bbox_coords[1] <= abox_coords[1] | |
| and abox_coords[2] <= bbox_coords[2] | |
| and abox_coords[3] <= bbox_coords[3] | |
| ): | |
| return True | |
| else: | |
| return False | |
| def calculate_aspect_ratio(box): | |
| width = box[2] - box[0] | |
| height = box[3] - box[1] | |
| aspect_ratio = width / (height + 1e-8) | |
| return aspect_ratio | |
| def get_box_shape(box, threshold=0.1): | |
| """ | |
| Check if a box is close to a square. | |
| - threshold (float): The threshold for considering the box as close to a square. | |
| Default is 0.1. | |
| Returns: | |
| - str: "square" or "horizontal" or "vertical" | |
| """ | |
| aspect_ratio = calculate_aspect_ratio(box) | |
| if abs(1 - aspect_ratio) < threshold: | |
| return "square" | |
| elif aspect_ratio > 1: | |
| return "horizontal" | |
| elif aspect_ratio < 1: | |
| return "vertical" | |
| def calculate_aspect_ratio_loss(predicted_box, gt_box): | |
| """predicted_boxμ gt_boxκ°μ κ°λ‘μΈλ‘ λΉμ¨μ λν μ°¨μ΄λλ₯Ό λ°ν range:0~1. ν΄μλ‘ μ°¨μ΄κ° ν¬λ€λ λ».""" | |
| gt_aspect_ratio = calculate_aspect_ratio(gt_box) | |
| pred_aspect_ratio = calculate_aspect_ratio(predicted_box) | |
| ratio_difference = abs(gt_aspect_ratio - pred_aspect_ratio) | |
| loss = 2 * math.atan(ratio_difference) / math.pi | |
| return loss | |
| def clip_boxes(boxes, shape): | |
| # Clip boxes (xyxy) to image shape (height, width) | |
| if isinstance(boxes, torch.Tensor): # faster individually | |
| boxes[..., 0].clamp_(0, shape[1]) # x1 | |
| boxes[..., 1].clamp_(0, shape[0]) # y1 | |
| boxes[..., 2].clamp_(0, shape[1]) # x2 | |
| boxes[..., 3].clamp_(0, shape[0]) # y2 | |
| else: # np.array (faster grouped) | |
| boxes[..., [0, 2]] = boxes[..., [0, 2]].clip(0, shape[1]) # x1, x2 | |
| boxes[..., [1, 3]] = boxes[..., [1, 3]].clip(0, shape[0]) # y1, y2 | |
| def is_box_overlap(box1, box2): | |
| # Box overlap checking logic | |
| if box1[0] > box2[2] or box1[2] < box2[0] or box1[1] > box2[3] or box1[3] < box2[1]: | |
| return False | |
| else: | |
| return True | |
| def intersection_area(box1, box2): | |
| """ | |
| Calculate the intersection area between two bounding boxes. | |
| Parameters: | |
| - box1, box2: Tuple or list representing the bounding box in the format (x1, y1, x2, y2). | |
| Returns: | |
| - area: Intersection area between the two boxes. | |
| """ | |
| x1_box1, y1_box1, x2_box1, y2_box1 = box1 | |
| x1_box2, y1_box2, x2_box2, y2_box2 = box2 | |
| # Calculate intersection coordinates | |
| x_intersection = max(x1_box1, x1_box2) | |
| y_intersection = max(y1_box1, y1_box2) | |
| x_intersection_end = min(x2_box1, x2_box2) | |
| y_intersection_end = min(y2_box1, y2_box2) | |
| # Calculate intersection area | |
| width_intersection = max(0, x_intersection_end - x_intersection) | |
| height_intersection = max(0, y_intersection_end - y_intersection) | |
| area = width_intersection * height_intersection | |
| return area | |
| def bbox_iou(box1, box2, GIoU=False, DIoU=False, CIoU=False, CIoU2=False, eps=1e-7): | |
| """ | |
| Caclulate IoUs(GIoU,DIoU,CIoU,CIoU2) | |
| Parameters: | |
| - box1, box2: Tuple or list representing the bounding box in the format (x1, y1, x2, y2). | |
| Returns: | |
| - IoU or GIoU or DIoU or CIoU or CIoU2 | |
| """ | |
| # Returns Intersection over Union (IoU) | |
| # Get the coordinates of bounding boxes | |
| # x1, y1, x2, y2 = box1 | |
| b1_x1, b1_y1, b1_x2, b1_y2 = box1 | |
| b2_x1, b2_y1, b2_x2, b2_y2 = box2 | |
| w1, h1 = b1_x2 - b1_x1, b1_y2 - b1_y1 + eps | |
| w2, h2 = b2_x2 - b2_x1, b2_y2 - b2_y1 + eps | |
| # Intersection area | |
| inter = intersection_area(box1, box2) | |
| # Union Area | |
| union = w1 * h1 + w2 * h2 - inter + eps | |
| iou = inter / union | |
| if CIoU or DIoU or GIoU or CIoU2: | |
| cw = max(b1_x2, b2_x2) - min( | |
| b1_x1, b2_x1 | |
| ) # convex (smallest enclosing box) width | |
| ch = max(b1_y2, b2_y2) - min(b1_y1, b2_y1) # convex height | |
| c_area = cw * ch + eps # convex area | |
| giou_penalty = (c_area - union) / c_area | |
| if GIoU: # GIoU https://arxiv.org/pdf/1902.09630.pdf | |
| return round(iou - giou_penalty, 4) # GIoU | |
| elif ( | |
| DIoU or CIoU | |
| ): # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1 | |
| rho2 = ( | |
| (b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 | |
| + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2 | |
| ) / 4 # center dist ** 2 | |
| c2 = cw**2 + ch**2 + eps # convex diagonal squared | |
| diou_penalty = rho2 / c2 | |
| if DIoU: | |
| return round(iou - diou_penalty, 4) # DIoU | |
| if CIoU or CIoU2: | |
| v = (4 / math.pi**2) * ( | |
| (np.arctan((w2 / h2)) - np.arctan(w1 / h1)) ** 2 | |
| ) | |
| alpha = v / (v - iou + (1 + eps)) | |
| ciou_penalty = diou_penalty + alpha * v | |
| if CIoU2: | |
| ciou2_penalty = giou_penalty + diou_penalty + alpha * v | |
| return round(iou - ciou2_penalty) # CIoU2 | |
| return round(iou - ciou_penalty, 4) # CIoU | |
| return round(iou, 4) # IoU | |
| def rotate_around_point(x, y, pivot_x, pivot_y, degrees) -> Tuple[int, int]: | |
| """μ£Όμ΄μ§ μ’ν (x,y)λ₯Ό μΆ μ’ν(pivot_x,pivot_y_λ₯Ό κΈ°μ€μΌλ‘ λ°μκ³ λ°©ν₯μΌλ‘ νμ . return new_x,new_y""" | |
| # κ°λλ₯Ό λΌλμμΌλ‘ λ³ν | |
| angle_radians = np.radians(degrees) | |
| # νμ λ³ν μ μ© | |
| x_new = ( | |
| pivot_x | |
| + np.cos(angle_radians) * (x - pivot_x) | |
| - np.sin(angle_radians) * (y - pivot_y) | |
| ) | |
| y_new = ( | |
| pivot_y | |
| + np.sin(angle_radians) * (x - pivot_x) | |
| + np.cos(angle_radians) * (y - pivot_y) | |
| ) | |
| return int(x_new), int(y_new) | |
| def rotate_box_coordinates_on_pivot(x1, y1, x2, y2, degrees, pivot_x, pivot_y): | |
| """μ£Όμ΄μ§ box μ’ν(x1,y1,x2,y2)λ₯Ό μ£Όμ΄μ§ μΆ μ’ν(pivot_x,pivot_y)μ λν΄ μκ³ λ°©ν₯μΌλ‘ νμ """ | |
| radians = np.radians(degrees) | |
| rotation_matrix = np.array( | |
| [[np.cos(radians), -np.sin(radians)], [np.sin(radians), np.cos(radians)]] | |
| ) | |
| # μμ μ’νλ₯Ό μ€μ¬μ κΈ°μ€μΌλ‘ νμ | |
| box_coordinates = np.array( | |
| [ | |
| [x1 - pivot_x, y1 - pivot_y], | |
| [x2 - pivot_x, y1 - pivot_y], | |
| [x2 - pivot_x, y2 - pivot_y], | |
| [x1 - pivot_x, y2 - pivot_y], | |
| ] | |
| ) | |
| rotated_box_coordinates = np.dot(box_coordinates, rotation_matrix.T) | |
| # νμ ν μ’νμ μ€μ¬ μ’νλ₯Ό λν΄ μλ μ’νλ‘ λ³ν | |
| rotated_box_coordinates += np.array([pivot_y, pivot_x]) | |
| # λ³νλ μ’νλ₯Ό μλ‘μ΄ μμ μ’νλ‘ λ°ν | |
| new_x1, new_y1 = rotated_box_coordinates.min(axis=0) | |
| new_x2, new_y2 = rotated_box_coordinates.max(axis=0) | |
| return int(new_x1), int(new_y1), int(new_x2), int(new_y2) | |
| def bbox_iou_torch( | |
| box1, box2, xywh=False, GIoU=False, DIoU=False, CIoU=False, eps=1e-7 | |
| ): | |
| # Returns Intersection over Union (IoU) of box1(1,4) to box2(n,4) | |
| # Get the coordinates of bounding boxes | |
| if xywh: # transform from xywh to xyxy | |
| (x1, y1, w1, h1), (x2, y2, w2, h2) = box1.chunk(4, -1), box2.chunk(4, -1) | |
| w1_, h1_, w2_, h2_ = w1 / 2, h1 / 2, w2 / 2, h2 / 2 | |
| b1_x1, b1_x2, b1_y1, b1_y2 = x1 - w1_, x1 + w1_, y1 - h1_, y1 + h1_ | |
| b2_x1, b2_x2, b2_y1, b2_y2 = x2 - w2_, x2 + w2_, y2 - h2_, y2 + h2_ | |
| else: # x1, y1, x2, y2 = box1 | |
| b1_x1, b1_y1, b1_x2, b1_y2 = box1.chunk(4, -1) | |
| b2_x1, b2_y1, b2_x2, b2_y2 = box2.chunk(4, -1) | |
| w1, h1 = b1_x2 - b1_x1, (b1_y2 - b1_y1).clamp(eps) | |
| w2, h2 = b2_x2 - b2_x1, (b2_y2 - b2_y1).clamp(eps) | |
| # Intersection area | |
| inter = (b1_x2.minimum(b2_x2) - b1_x1.maximum(b2_x1)).clamp(0) * ( | |
| b1_y2.minimum(b2_y2) - b1_y1.maximum(b2_y1) | |
| ).clamp(0) | |
| # Union Area | |
| union = w1 * h1 + w2 * h2 - inter + eps | |
| # IoU | |
| iou = inter / union | |
| if CIoU or DIoU or GIoU: | |
| cw = b1_x2.maximum(b2_x2) - b1_x1.minimum( | |
| b2_x1 | |
| ) # convex (smallest enclosing box) width | |
| ch = b1_y2.maximum(b2_y2) - b1_y1.minimum(b2_y1) # convex height | |
| if CIoU or DIoU: # Distance or Complete IoU https://arxiv.org/abs/1911.08287v1 | |
| c2 = cw**2 + ch**2 + eps # convex diagonal squared | |
| rho2 = ( | |
| (b2_x1 + b2_x2 - b1_x1 - b1_x2) ** 2 | |
| + (b2_y1 + b2_y2 - b1_y1 - b1_y2) ** 2 | |
| ) / 4 # center dist ** 2 | |
| if ( | |
| CIoU | |
| ): # https://github.com/Zzh-tju/DIoU-SSD-pytorch/blob/master/utils/box/box_utils.py#L47 | |
| v = (4 / math.pi**2) * ( | |
| torch.atan(w2 / h2) - torch.atan(w1 / h1) | |
| ).pow(2) | |
| with torch.no_grad(): | |
| alpha = v / (v - iou + (1 + eps)) | |
| return iou - (rho2 / c2 + v * alpha) # CIoU | |
| return iou - rho2 / c2 # DIoU | |
| c_area = cw * ch + eps # convex area | |
| return ( | |
| iou - (c_area - union) / c_area | |
| ) # GIoU https://arxiv.org/pdf/1902.09630.pdf | |
| return iou # IoU | |
| def generate_random_box(width_range, height_range): | |
| """ | |
| Generate random bounding box coordinates (x1, y1, x2, y2) with random width and height. | |
| Parameters: | |
| - width_range: Tuple representing the range of width values (min_width, max_width). | |
| - height_range: Tuple representing the range of height values (min_height, max_height). | |
| Returns: | |
| - box: Tuple representing the bounding box in the format (x1, y1, x2, y2). | |
| """ | |
| min_width, max_width = width_range | |
| min_height, max_height = height_range | |
| width = np.random.randint(min_width, max_width) | |
| height = np.random.randint(min_height, max_height) | |
| x1 = np.random.randint(0, 100 - width) | |
| y1 = np.random.randint(0, 100 - height) | |
| x2 = x1 + width | |
| y2 = y1 + height | |
| return x1, y1, x2, y2 | |
| def mask_to_bboxes(mask, margin_rate=2, pixel_thresh=300) -> pd.DataFrame: | |
| nlabels, segmap, stats, centroids = cv2.connectedComponentsWithStats( | |
| image=mask, connectivity=4 | |
| ) | |
| bboxes = pd.DataFrame( | |
| stats[1:, :], columns=["bbox_x1", "bbox_y1", "width", "height", "pixel_count"] | |
| ) | |
| img_width, img_height = _get_width_and_height(mask) | |
| bboxes = bboxes[bboxes["pixel_count"].ge(pixel_thresh)] | |
| bboxes["bbox_x2"] = bboxes["bbox_x1"] + bboxes["width"] | |
| bboxes["bbox_y2"] = bboxes["bbox_y1"] + bboxes["height"] | |
| bboxes["margin"] = bboxes.apply( | |
| lambda x: int( | |
| math.sqrt( | |
| x["pixel_count"] | |
| * min(x["width"], x["height"]) | |
| / (x["width"] * x["height"]) | |
| ) | |
| * margin_rate | |
| ), | |
| axis=1, | |
| ) | |
| bboxes["bbox_x1"] = bboxes.apply( | |
| lambda x: max(0, x["bbox_x1"] - x["margin"]), axis=1 | |
| ) | |
| bboxes["bbox_y1"] = bboxes.apply( | |
| lambda x: max(0, x["bbox_y1"] - x["margin"]), axis=1 | |
| ) | |
| bboxes["bbox_x2"] = bboxes.apply( | |
| lambda x: min(img_width, x["bbox_x2"] + x["margin"]), axis=1 | |
| ) | |
| bboxes["bbox_y2"] = bboxes.apply( | |
| lambda x: min(img_height, x["bbox_y2"] + x["margin"]), axis=1 | |
| ) | |
| bboxes = bboxes[["bbox_x1", "bbox_y1", "bbox_x2", "bbox_y2"]] | |
| img_width, img_height = _get_width_and_height(mask) | |
| if img_width >= img_height: | |
| bboxes.sort_values(by=["bbox_x1", "bbox_y1"], inplace=True) | |
| else: | |
| bboxes.sort_values(by=["bbox_y1", "bbox_x1"], inplace=True) | |
| return bboxes | |
| def bbox_to_mask(bboxes: list, mask_size): | |
| """ | |
| Creates a mask image based on bounding box coordinates. | |
| Args: | |
| - bboxes: list (x_min, y_min, x_max, y_max) representing the bounding box coordinates. | |
| - mask_size: Tuple (height, width) representing the size of the mask image to be created. | |
| Returns: | |
| - Mask image with the specified bounding box area filled with white. | |
| """ | |
| # Initialize a black mask image with the specified size | |
| mask = np.zeros(mask_size, dtype=np.uint8) | |
| # mask = np.zeros_like(img).astype("uint8") | |
| for bbox in bboxes: | |
| # Extract bounding box coordinates | |
| x_min, y_min, x_max, y_max = bbox | |
| # Ensure bbox coordinates are within mask bounds | |
| x_min = max(0, x_min) | |
| y_min = max(0, y_min) | |
| x_max = min(mask_size[1], x_max) | |
| y_max = min(mask_size[0], y_max) | |
| # Fill the bounding box area with white color in the mask image | |
| mask[y_min:y_max, x_min:x_max] = 255 | |
| return mask | |
| def move_box_a_to_center_of_box_b(A, B): | |
| # Aμ Bμ μ’ν (l, t, r, b) | |
| lA, tA, rA, bA = A | |
| lB, tB, rB, bB = B | |
| # λ°μ€ Aμ λλΉμ λμ΄ | |
| width_A = rA - lA | |
| height_A = bA - tA | |
| # λ°μ€ Bμ μ€μ¬ μ’ν | |
| center_x_B = (lB + rB) / 2 | |
| center_y_B = (tB + bB) / 2 | |
| # λ°μ€ Aμ μλ‘μ΄ μ’ν (μ€μ¬μ Bμ μ€μ¬μΌλ‘ μ΄λ) | |
| new_lA = center_x_B - width_A / 2 | |
| new_tA = center_y_B - height_A / 2 | |
| new_rA = center_x_B + width_A / 2 | |
| new_bA = center_y_B + height_A / 2 | |
| # μλ‘μ΄ A λ°μ€μ μ’ν λ°ν | |
| return (new_lA, new_tA, new_rA, new_bA) | |
| def scale_bboxes(bboxes, max_x, max_y, x_scale_factor=1.2, y_scale_factor=1.05): | |
| # κΈ°μ‘΄ μ’νμμ κ° λ°μ€μ μ€μ¬ μ’ν, λλΉ, λμ΄ κ³μ° | |
| bboxes["cx"] = (bboxes["bbox_x1"] + bboxes["bbox_x2"]) / 2 | |
| bboxes["cy"] = (bboxes["bbox_y1"] + bboxes["bbox_y2"]) / 2 | |
| bboxes["width"] = bboxes["bbox_x2"] - bboxes["bbox_x1"] | |
| bboxes["height"] = bboxes["bbox_y2"] - bboxes["bbox_y1"] | |
| # κ° λ°μ€μ ν¬κΈ°λ₯Ό 1.2λ°°λ‘ λλ¦Ό | |
| bboxes["new_width"] = bboxes["width"] * x_scale_factor | |
| bboxes["new_height"] = bboxes["height"] * y_scale_factor | |
| # μλ‘μ΄ μ’ν κ³μ° | |
| bboxes["new_x1"] = bboxes["cx"] - bboxes["new_width"] / 2 | |
| bboxes["new_y1"] = bboxes["cy"] - bboxes["new_height"] / 2 | |
| bboxes["new_x2"] = bboxes["cx"] + bboxes["new_width"] / 2 | |
| bboxes["new_y2"] = bboxes["cy"] + bboxes["new_height"] / 2 | |
| # box λ²μ μ ν | |
| bboxes["new_x1"] = bboxes["new_x1"].clip(lower=0).astype(int) | |
| bboxes["new_y1"] = bboxes["new_y1"].clip(lower=0).astype(int) | |
| bboxes["new_x2"] = bboxes["new_x2"].clip(upper=max_x).astype(int) | |
| bboxes["new_y2"] = bboxes["new_y2"].clip(upper=max_y).astype(int) | |
| # κ²°κ³Ό λ°μ΄ν°νλ μ μμ± | |
| new_bboxes = bboxes[ | |
| ["ori_content", "new_x1", "new_y1", "new_x2", "new_y2", "predicted_lang"] | |
| ].copy() | |
| new_bboxes.columns = [ | |
| "ori_content", | |
| "bbox_x1", | |
| "bbox_y1", | |
| "bbox_x2", | |
| "bbox_y2", | |
| "predicted_lang", | |
| ] | |
| return new_bboxes | |
| if __name__ == "__main__": | |
| w_range = (100, 200) | |
| h_range = (100, 200) | |
| box1 = generate_random_box(w_range, h_range) | |
| box2 = generate_random_box(w_range, h_range) | |
| print(f"box1 coors : {box1}") | |
| print(f"box2 coors : {box2}") | |
| print(f"intersection area : {intersection_area(box1,box2)}") | |
| iou = bbox_iou(box1, box2) | |
| giou = bbox_iou(box1, box2, GIoU=True) | |
| diou = bbox_iou(box1, box2, DIoU=True) | |
| ciou = bbox_iou(box1, box2, CIoU=True) | |
| print(iou, giou, diou, ciou) | |