Today we will look at the simplest possible implementation for measuring objects with Aruco Marker.
For example this USB stick

depending on the proximity to the camera and the perspective changes its size. Its measurement is still possible with a complicated camera calibration but it is not the goal of this tutorial, how can we then easily solve the problem?
For this there is a solution already used in augmented reality, it is the Aruco Marker standard.

aruco tag

You can see more details about the standard and the integration with OpenCV also in the official documentation. Detection of ArUco Markers

We’re going to:
Identify the object in space
Calculate pixels and centimeters with Aruco Marker

Installations required to measure objects with Aruco Marker

Fundamental before proceeding with the installation of the libraries that allow the use of Aruco Marker. In this case, you have to make sure you have installed Contrib OpenCV, attention is not enough just installing OpenCV, it has to be specifically with Contrib otherwise you may have lost file errors or something similar.

If you have not installed it, run this command from the terminal, it does not matter which operating system you are using.

pip install opencv-contrib-python

Identify the object in space

Before proceeding with the measurement, we need to find the object in space and obtain its coordinates. For this project, it is not a fundamental part of understanding the topic so I have already prepared a file called object_detector.py that you can download and import.

from object_detector import *

# Load Object Detector
detector = HomogeneousBgDetector()

...

contours = detector.detect_objects(img)

...

I want to clarify that this is a very simple object detection method. For more precise identification, I suggest you evaluate my course Object Detection Course to identify objects with deep learning.

We use a for loop to extract the arrays with all points delimiting the identified objects. Not having perfect geometric shapes we have to reduce the delimitation of the object to a simple rectangle, as in this case.

# Draw objects boundaries
for cnt in contours:
    # Get rect
    rect = cv2.minAreaRect(cnt)
    (x, y), (w, h), angle = rect

    # Display rectangle
    box = cv2.boxPoints(rect)
    box = np.int0(box)

    cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
    cv2.polylines(img, [box], True, (255, 0, 0), 2)

In the picture you can see the result before and after

Calculate pixels and centimeters with Aruco Marker

Now we have the coordinates of the object and thanks to the rectangle we can already show the dimensions in pixels but by converting this measurement to centimeters we would not get real data because we need a reference on the dimensions.
For this purpose we use Aruco Marker. It has the huge advantage of not having to calibrate the camera because we know it is an exact square of 5cm X 5cm. Beyond this OpenCV recognizes it and does not need complex operations for integration. The marker only needs to appear in the video along with the objects.

Aruco Marker reference

Now we have to load aruco detector but also the dictionary. For this tutorial I used DICT_5X5_50

# Load Aruco detector
parameters = cv2.aruco.DetectorParameters_create()
aruco_dict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_5X5_50)

We need to detect the aruco marker in the photo and only obtain the corners.

...
# Get Aruco marker
corners, _, _ = cv2.aruco.detectMarkers(img, aruco_dict, parameters=parameters)

We draw a polygon around the marker to make sure we have identified it correctly.

# Draw polygon around the marker
int_corners = np.int0(corners)
cv2.polylines(img, int_corners, True, (0, 255, 0), 5)
Aruco Marker Polygone

We know that the Aruco marker used by me is a square with 5 cm per side so we have a perimeter of 20 cm which corresponds to about 560 px. We then derive the perimeter from the points identified and with a simple division we obtain the ratio that will also be used for the conversion on objects.

# Aruco Perimeter
aruco_perimeter = cv2.arcLength(corners[0], True)

# Pixel to cm ratio
pixel_cm_ratio = aruco_perimeter / 20

Convert pixels to cm of objects

Within the for loop relating to the contours of the objects, among other things, also two parameters h and w that correspond to the height and width are extracted.
Applying the calculation of the ratio to these two variables we obtain the centimeters.

# Draw objects boundaries
for cnt in contours:
    # Get rect
    rect = cv2.minAreaRect(cnt)
    (x, y), (w, h), angle = rect

    # Get Width and Height of the Objects by applying the Ratio pixel to cm
    object_width = w / pixel_cm_ratio
    object_height = h / pixel_cm_ratio

we just have to print the result on the screen

    
    cv2.putText(img, "Width {} cm".format(round(object_width, 1)), (int(x - 100), int(y - 20)), cv2.FONT_HERSHEY_PLAIN, 2, (100, 200, 0), 2)
    cv2.putText(img, "Height {} cm".format(round(object_height, 1)), (int(x - 100), int(y + 15)), cv2.FONT_HERSHEY_PLAIN, 2, (100, 200, 0), 2)

This is the result

aruco marker cm