Bounding boxes displaced in the image.
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 AND ONE LABEL IN PYTHON
My labels in python (at least one example)
Environment (please complete the following information):
- OS: [e.g. iOS]
- Label Studio Version [e.g. 0.8.0]
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.
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
Script output result (using nodejs/sharp to do the drawing and compositing with svg).
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.