label-studio icon indicating copy to clipboard operation
label-studio copied to clipboard

Bounding boxes displaced in the image.

Open luforestal opened this issue 1 year ago • 2 comments

Hi, I have exported my annotations in JSON format. But when I try to view them in python, they appear offset almost 100 pixels. Here is an example of how they look in LabelStudio and how I see them in my code.

The code that I'm using:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image

def get_rotated_bounding_box_corners_and_extremes(x_center_pct, y_center_pct, width_pct, height_pct, rotation_deg, image_width, image_height):
    """
    Calculate the four corner coordinates of a rotated bounding box and its extreme values.

    Args:
    x_center_pct (float): x-coordinate of the center of the box in percentage.
    y_center_pct (float): y-coordinate of the center of the box in percentage.
    width_pct (float): Width of the box in percentage.
    height_pct (float): Height of the box in percentage.
    rotation_deg (float): Rotation of the box in degrees.
    image_width (int): Width of the image in pixels.
    image_height (int): Height of the image in pixels.

    Returns:
    corners (list of tuples): List with the (x, y) coordinates of the four corners of the rotated bounding box.
    extremes (tuple): Tuple with (xmin, xmax, ymin, ymax) of the rotated bounding box.
    """

    x_center = x_center_pct / 100 * image_width
    y_center = y_center_pct / 100 * image_height
    width = width_pct / 100 * image_width
    height = height_pct / 100 * image_height

    rotation_rad = np.deg2rad(rotation_deg)

    corners = np.array([
        [x_center - width / 2, y_center - height / 2],
        [x_center + width / 2, y_center - height / 2],
        [x_center + width / 2, y_center + height / 2],
        [x_center - width / 2, y_center + height / 2]
    ])


    rotation_matrix = np.array([
        [np.cos(rotation_rad), -np.sin(rotation_rad)],
        [np.sin(rotation_rad), np.cos(rotation_rad)]
    ])


    rotated_corners = np.dot(corners - np.array([x_center, y_center]), rotation_matrix.T) + np.array([x_center, y_center])


    rotated_corners = rotated_corners.astype(int)

    # Obtener extremos
    x_coords = rotated_corners[:, 0]
    y_coords = rotated_corners[:, 1]
    xmin = x_coords.min()
    xmax = x_coords.max()
    ymin = y_coords.min()
    ymax = y_coords.max()

    return rotated_corners.tolist(), (xmin, xmax, ymin, ymax)

def plot_rotated_bounding_box(image_path, corners):
    """
    Plot the rotated bounding box on the original image.

    Args:
    image_path (str): Path to the original image.
    corners (list of tuples): List with the (x, y) coordinates of the four corners of the rotated bounding box.
    """

    img = Image.open(image_path)
    img_width, img_height = img.size
    print('width', img_width, 'h', img_height)

    fig, ax = plt.subplots(1)
    ax.imshow(img)

    for i in range(len(corners)):
        next_i = (i + 1) % len(corners)
        x_values = [corners[i][0], corners[next_i][0]]
        y_values = [corners[i][1], corners[next_i][1]]
        ax.plot(x_values, y_values, 'r-')

    plt.gca().set_aspect('equal', adjustable='box')
    plt.show()

# Example
image_path = 'save_frame_test.jpg'  
image_width = 640
image_height = 352
x_center_pct =  72.26388590829315
y_center_pct = 87.85889758341253
width_pct = 14.273870065050815  
height_pct = 9.12891897312828
rotation_deg = -25.40638952513592

Screenshots IMAGE AND LABELS IN LABELSTUDIO image

IMAGE AND ONE LABEL IN PYTHON My labels in python (at least one example) image

Environment (please complete the following information):

  • OS: [e.g. iOS]
  • Label Studio Version [e.g. 0.8.0]

luforestal avatar May 27 '24 12:05 luforestal

hey @luforestal thanks for the bug report. As a sane first step, can we try upgrading Label Studio to the latest? We just want to make sure that this bug is present on the latest version.

sajarin avatar Jun 03 '24 16:06 sajarin

Hey, chiming in to say that I am encountering a similar issue with rotated bounding boxes when trying to work from the JSON export.

I get inconsistencies with the rotation that are visible for angles > 5°.

My example:

LabelStudio Screenshot 2024-06-16 at 23 02 33

Script output result (using nodejs/sharp to do the drawing and compositing with svg).

Screenshot 2024-06-16 at 23 02 46

It looks like the angle value used in labelstudio does not have the center of the rectangle (cx, cy) for origin (as expected) but the top-left point of the rectangle instead, which is quite strange as the UI actually rotates around the center!

So for me the fix looks like to patch all rects with:

const patchLabelStudioRect = <T extends Rect>(rect: T): T => {
  const { x, y } = rotatePoint({ x: rect.x, y: rect.y }, rect.rotation, getRectCenter(rect));
  rect.x = x;
  rect.y = y;
  return rect;
};

Interesting to dig this up a bit more, do you know where I can find the code responsible for drawing the boxes?

I'm using label-studio 1.2.0.

mgcrea avatar Jun 16 '24 21:06 mgcrea