Title: Measuring size of objects in an image with OpenCV - PyImageSearch

URL Source: https://pyimagesearch.com/2016/03/28/measuring-size-of-objects-in-an-image-with-opencv/

Published Time: 2016-03-28T14:00:29+00:00

Markdown Content:
Last updated on July 8, 2021.

![Image 1: size_of_objects_example_02](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20550%20443'%3E%3C/svg%3E)

**Measuring the size of an object (or objects)** in an image has been a _heavily requested tutorial_ on the PyImageSearch blog for some time now — and it feels _great_ to get this post online and share it with you.

Accurate size measurement requires exposure to objects of various sizes and perspectives. A curated dataset library would provide such diversity, ensuring your algorithms can handle a wide range of scenarios.

[Roboflow](https://roboflow.com/?ref=pyimagesearch) has free tools for each stage of the computer vision pipeline that will streamline your workflows and supercharge your productivity.

Sign up or Log in to your [Roboflow account](https://roboflow.com/?ref=pyimagesearch) to access state of the art dataset libaries and revolutionize your computer vision pipeline.

You can start by choosing your own datasets or using our [PyimageSearch’s assorted library of useful datasets](https://universe.roboflow.com/pyimagesearch?ref=pyimagesearch).

Bring data in any of 40+ formats to [Roboflow](https://roboflow.com/?ref=pyimagesearch), train using any state-of-the-art model architectures, deploy across multiple platforms (API, NVIDIA, browser, iOS, etc), and connect to applications or 3rd party tools.

With a few images, you can train a working computer vision model in an afternoon. For example, bring data into [Roboflow](https://roboflow.com/?ref=pyimagesearch) from anywhere via API, label images with the cloud-hosted image annotation tool, kickoff a hosted model training with one-click, and deploy the model via a hosted API endpoint. This process can be executed in a code-centric way, in the cloud-based UI, or any mix of the two.

Over 250,000 developers and machine learning engineers from companies such as Cardinal Health, Walmart, USG, Rivian, Intel, and Medtronic build computer vision pipelines with Roboflow. Get started today, no credit card required.

Today’s post is the second in a three part series on _measuring the size of objects in an image_ and _computing the distances between them_.

[Last week](https://pyimagesearch.com/2016/03/21/ordering-coordinates-clockwise-with-python-and-opencv/), we learned an important technique: how _reliably_ order a set of rotated bounding box coordinates in a top-left, top-right, bottom-right, and bottom-left arrangement.

Today we are going to utilize this technique to aid us in computing the size of objects in an image. **Be sure to read the entire post to see how it’s done!**

*   **Update July 2021:** Added section on how to improve object size measurement accuracy by performing a proper camera calibration with a checkerboard.

![Image 2](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20128%20128'%3E%3C/svg%3E)

#### Looking for the source code to this post?

[Jump Right To The Downloads Section](#download-the-code)

Measuring the size of objects in an image is similar to [computing the distance from our camera to an object](https://pyimagesearch.com/2015/05/04/target-acquired-finding-targets-in-drone-and-quadcopter-video-streams-using-python-and-opencv/) — in both cases, we need to define a ratio that measures the number of pixels per a given metric.

I call this the “pixels per metric” ratio, which I have more formally defined in the following section.

### The “pixels per metric” ratio

In order to determine the size of an object in an image, we first need to perform a “calibration” (not to be confused with [intrinsic/extrinsic calibration](http://www.cs.rutgers.edu/~elgammal/classes/cs534/lectures/Calibration.pdf)) using a reference object. Our reference object should have two important properties:

*   **Property #1:** We should know the _dimensions of this object_ (in terms of width or height) in a _measurable unit_ (such as millimeters, inches, etc.).
*   **Property #2:** We should be able to easily find this reference object in an image, either based on the _placement_ of the object (such as the reference object always being placed in the top-left corner of an image) or via _appearances_ (like being a distinctive color or shape, unique and different from all other objects in the image). In either case, our reference should should be _uniquely identifiable_ in some manner.

In this example, we’ll be using the United States quarter as our reference object and throughout all examples, ensure it is always the _left-most_ object in our image:

![Image 3: Figure 1: We'll use a United States quarter as our reference object and ensure it is always placed as the left-most object in the image, making it easy for us to extract it by sorting contours based on their location.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20800%20600'%3E%3C/svg%3E)

**Figure 1:** We’ll use a United States quarter as our reference object and ensure it is always placed as the left-most object in the image, making it easy for us to extract it by sorting contours based on their location.

By guaranteeing the quarter is the left-most object, we can sort our object contours from left-to-right, grab the quarter (which will always be the first contour in the sorted list), and use it to define our _pixels\_per\_metric_, which we define as:

_pixels\_per\_metric = object\_width / know\_width_

A US quarter has a _known\_width_ of [0.955 inches](https://en.wikipedia.org/wiki/Quarter_\(United_States_coin\)#Current_design). Now, suppose that our _object\_width_ (measured in pixels) is computed be 150 pixels wide (based on its associated bounding box).

The _pixels\_per\_metric_ is therefore:

_pixels\_per\_metric = 150px / 0.955in = 157px_

Thus implying there are approximately 157 pixels per every 0.955 inches in our image. Using this ratio, we can compute the size of objects in an image.

### Measuring the size of objects with computer vision

Now that we understand the “pixels per metric” ratio, we can implement the Python driver script used to measure the size of objects in an image.

Open up a new file, name it `object_size.py` , and insert the following code:

\# import the necessary packages
from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
import numpy as np
import argparse
import imutils
import cv2

def midpoint(ptA, ptB):
	return ((ptA\[0\] + ptB\[0\]) \* 0.5, (ptA\[1\] + ptB\[1\]) \* 0.5)

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add\_argument("-i", "--image", required=True,
	help="path to the input image")
ap.add\_argument("-w", "--width", type=float, required=True,
	help="width of the left-most object in the image (in inches)")
args = vars(ap.parse\_args())

**Lines 2-8** import our required Python packages. We’ll be making heavy use of the [imutils package](https://github.com/jrosebr1/imutils) in this example, so if you don’t have it installed, make sure you install it before proceeding:

$ pip install imutils

Otherwise, if you _do_ have `imutils` installed, ensure you have the latest version, which is `0.3.6` at the time of this writing:

$ pip install --upgrade imutils

**Lines 10 and 11** defines a helper method called `midpoint` , which as the name suggests, is used to compute the [midpoint](http://www.purplemath.com/modules/midpoint.htm) between two sets of _(x, y)_\-coordinates.

We then parse our command line arguments on **Lines 14-19**. We require two arguments, `--image` , which is the path to our input image containing the objects we want to measure, and `--width` , which is the width (in inches) of our reference object, presumed to be the left-most object in our `--image` .

We can now load our image and preprocess it:

\# load the image, convert it to grayscale, and blur it slightly
image = cv2.imread(args\["image"\])
gray = cv2.cvtColor(image, cv2.COLOR\_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)

# perform edge detection, then perform a dilation + erosion to
# close gaps in between object edges
edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)

# find contours in the edge map
cnts = cv2.findContours(edged.copy(), cv2.RETR\_EXTERNAL,
	cv2.CHAIN\_APPROX\_SIMPLE)
cnts = imutils.grab\_contours(cnts)

# sort the contours from left-to-right and initialize the
# 'pixels per metric' calibration variable
(cnts, \_) = contours.sort\_contours(cnts)
pixelsPerMetric = None

**Lines 22-24** load our image from disk, convert it to grayscale, and then smooth it using a Gaussian filter. We then perform edge detection along with a dilation + erosion to close any gaps in between edges in the edge map (**Lines 28-30**).

**Lines 33-35** find contours (i.e., the outlines) that correspond to the objects in our edge map.

These contours are then sorted from left-to-right (allowing us to extract our reference object) on **Line 39**. We also initialize our `pixelsPerMetric` value on **Line 40**.

The next step is to examine each of the contours:

\# loop over the contours individually
for c in cnts:
	# if the contour is not sufficiently large, ignore it
	if cv2.contourArea(c) < 100:
		continue

	# compute the rotated bounding box of the contour
	orig = image.copy()
	box = cv2.minAreaRect(c)
	box = cv2.cv.BoxPoints(box) if imutils.is\_cv2() else cv2.boxPoints(box)
	box = np.array(box, dtype="int")

	# order the points in the contour such that they appear
	# in top-left, top-right, bottom-right, and bottom-left
	# order, then draw the outline of the rotated bounding
	# box
	box = perspective.order\_points(box)
	cv2.drawContours(orig, \[box.astype("int")\], -1, (0, 255, 0), 2)

	# loop over the original points and draw them
	for (x, y) in box:
		cv2.circle(orig, (int(x), int(y)), 5, (0, 0, 255), -1)

On **Line 43** we start looping over each of the individual contours. If the contour is not sufficiently large, we discard the region, presuming it to be noise left over from the edge detection process (**Lines 45 and 46**).

Provided that the contour region is large enough, we compute the rotated bounding box of the image on **Lines 50-52**, taking special care to use the `cv2.cv.BoxPoints` function for OpenCV 2.4 and the `cv2.boxPoints` method for OpenCV 3.

We then arrange our rotated bounding `box` coordinates in top-left, top-right, bottom-right, and bottom-left order, [as discussed in last week’s blog post](https://pyimagesearch.com/2016/03/21/ordering-coordinates-clockwise-with-python-and-opencv/) (**Line 58**).

Lastly, **Lines 59-63** draw the _outline_ of the object in _green_, followed by drawing the _vertices_ of the bounding box rectangle in as _small, red circles_.

Now that we have our bounding box ordered, we can compute a series of midpoints:

	# unpack the ordered bounding box, then compute the midpoint
	# between the top-left and top-right coordinates, followed by
	# the midpoint between bottom-left and bottom-right coordinates
	(tl, tr, br, bl) = box
	(tltrX, tltrY) = midpoint(tl, tr)
	(blbrX, blbrY) = midpoint(bl, br)

	# compute the midpoint between the top-left and top-right points,
	# followed by the midpoint between the top-righ and bottom-right
	(tlblX, tlblY) = midpoint(tl, bl)
	(trbrX, trbrY) = midpoint(tr, br)

	# draw the midpoints on the image
	cv2.circle(orig, (int(tltrX), int(tltrY)), 5, (255, 0, 0), -1)
	cv2.circle(orig, (int(blbrX), int(blbrY)), 5, (255, 0, 0), -1)
	cv2.circle(orig, (int(tlblX), int(tlblY)), 5, (255, 0, 0), -1)
	cv2.circle(orig, (int(trbrX), int(trbrY)), 5, (255, 0, 0), -1)

	# draw lines between the midpoints
	cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),
		(255, 0, 255), 2)
	cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),
		(255, 0, 255), 2)

**Lines 68-70** unpacks our ordered bounding box, then computes the midpoint between the top-left and top-right points, followed by the midpoint between the bottom-right points.

We’ll also compute the midpoints between the top-left + bottom-left and top-right + bottom-right, respectively (**Lines 74 and 75**).

**Lines 78-81** draw the _blue_ midpoints on our `image` , followed by connecting the midpoints with _purple_ lines.

Next, we need to initialize our `pixelsPerMetric` variable by investigating our reference object:

	# compute the Euclidean distance between the midpoints
	dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))
	dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))

	# if the pixels per metric has not been initialized, then
	# compute it as the ratio of pixels to supplied metric
	# (in this case, inches)
	if pixelsPerMetric is None:
		pixelsPerMetric = dB / args\["width"\]

First, we compute the Euclidean distance between the our sets of midpoints (**Lines 90 and 91**). The `dA` variable will contain the _height_ distance (in pixels) while `dB` will hold our _width_ distance.

We then make a check on **Line 96** to see if our `pixelsPerMetric` variable has been initialized, and if it hasn’t, we divide `dB` by our supplied `--width` , thus giving us our (approximate) pixels per inch.

Now that our `pixelsPerMetric` variable has been defined, we can measure the size of objects in an image:

	# compute the size of the object
	dimA = dA / pixelsPerMetric
	dimB = dB / pixelsPerMetric

	# draw the object sizes on the image
	cv2.putText(orig, "{:.1f}in".format(dimA),
		(int(tltrX - 15), int(tltrY - 10)), cv2.FONT\_HERSHEY\_SIMPLEX,
		0.65, (255, 255, 255), 2)
	cv2.putText(orig, "{:.1f}in".format(dimB),
		(int(trbrX + 10), int(trbrY)), cv2.FONT\_HERSHEY\_SIMPLEX,
		0.65, (255, 255, 255), 2)

	# show the output image
	cv2.imshow("Image", orig)
	cv2.waitKey(0)

**Lines 100 and 101** compute the dimensions of the object (_**in inches**_) by dividing the respective Euclidean distances by the `pixelsPerMetric` value (see the **“Pixels Per Metric”** section above for more information on why this ratio works).

**Lines 104-109** draw the dimensions of the object on our `image` , while **Lines 112 and 113** display the output results.

### Object size measuring results

To test our `object_size.py` script, just issue the following command:

$ python object\_size.py --image images/example\_01.png --width 0.955

Your output should look something like the following:

![Image 4: Figure 2: Measuring the size of objects in an image using OpenCV, Python, and computer vision + image processing techniques.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20550%20424'%3E%3C/svg%3E)

**Figure 2:** Measuring the size of objects in an image using OpenCV, Python, and computer vision + image processing techniques.

As you can see, we have successfully computed the size of each object in an our image — our business card is correctly reported as _3.5in x 2in_. Similarly, our nickel is accurately described as _0.8in x 0.8in_.

However, not all our results are _perfect_.

The Game Boy cartridges are reported as having slightly different dimensions (even though they are the same size). The height of both quarters are also off by _0.1in_.

So why is this? How come the object measurements are not 100% accurate?

The reason is two-fold:

1.  First, I hastily took this photo with my iPhone. The angle is most certainly _not_ a perfect 90-degree angle “looking down” (like a birds-eye-view) at the objects. Without a perfect 90-degree view (or as close to it as possible), the dimensions of the objects can appear distorted.
2.  Second, I did not calibrate my iPhone using the intrinsic and extrinsic parameters of the camera. Without determining these parameters, photos can be prone to radial and tangential lens distortion. Performing an extra calibration step to find these parameters can “un-distort” our image and lead to a better object size approximation (but I’ll leave the discussion of distortion correction as a topic of a future blog post).

In the meantime, strive to obtain as close to a 90-degree viewing angle as possible when taking photos of your objects — this will help increase the accuracy of your object size estimation.

That said, let’s look at a second example of measuring object size, this time measuring the dimensions of pills:

$ python object\_size.py --image images/example\_02.png --width 0.955

![Image 5: Measuring the size of pills in an image with OpenCV.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20550%20443'%3E%3C/svg%3E)

**Figure 3:** Measuring the size of pills in an image with OpenCV.

Nearly 50% of all 20,000+ prescription pills in the United States are _round_ and/or _white_, thus if we can filter pills based on their measurements, we stand a better chance at accurately identification the medication.

Finally, we have a final example, this time using a _3.5in x 2in_ business card to measure the size of two vinyl EPs and an envelope:

$ python object\_size.py --image images/example\_03.png --width 3.5

![Image 6: Figure 4: A final example of measuring the size of objects in an image with Python + OpenCV.](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20550%20451'%3E%3C/svg%3E)

**Figure 4:** A final example of measuring the size of objects in an image with Python + OpenCV.

Again, the results aren’t quite perfect, but this is due to (1) the viewing angle and (2) lens distortion, as mentioned above.

### **Improving object size measurement accuracy with a proper camera calibration**

Before we can measure the size of an object in an image, we first need to calibrate our system. In this post, we used a simple “pixels per metric” technique.

However, better accuracy can be obtained by performing a proper camera calibration by computing the extrinsic and intrinsic parameters:

*   **Extrinsic parameters** are rotation and translation matrices used to convert something from the world frame to the camera frame
*   **Intrinsic parameters** are the internal camera parameters, such as the focal length, to convert that information into a pixel

**Specifically, we are interested in the intrinsic parameters.** But how do we go about obtaining them?

The most common way is to perform a checkerboard camera calibration using OpenCV. Doing so will remove radial distortion and tangential distortion, both of which impact the output image, and therefore the output measurement of objects in the image.

Here are some resources to help you get started with camera calibration:

*   [Understanding Lens Distortion](https://learnopencv.com/understanding-lens-distortion/)
*   [Camera Calibration using OpenCV](https://learnopencv.com/camera-calibration-using-opencv/)
*   [Camera Calibration (official OpenCV documentation)](https://docs.opencv.org/master/dc/dbb/tutorial_py_calibration.html)

### What's next? We recommend [PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend).

**Course information:**  
84 total classes • 114+ hours of on-demand code walkthrough videos • Last updated: February 2024  
★★★★★ 4.84 (128 Ratings) • 16,000+ Students Enrolled

**I strongly believe that if you had the right teacher you could _master_ computer vision and deep learning.**

Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?

That’s _not_ the case.

All you need to master computer vision and deep learning is for someone to explain things to you in _simple, intuitive_ terms. _And that’s exactly what I do_. My mission is to change education and how complex Artificial Intelligence topics are taught.

If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to _successfully_ and _confidently_ apply computer vision to your work, research, and projects. Join me in computer vision mastery.

**Inside PyImageSearch University you'll find:**

*   ✓ **84 courses** on essential computer vision, deep learning, and OpenCV topics
*   ✓ **84 Certificates** of Completion
*   ✓ **114+ hours** of on-demand video
*   ✓ **Brand new courses released _regularly_**, ensuring you can keep up with state-of-the-art techniques
*   ✓ **Pre-configured Jupyter Notebooks in Google Colab**
*   ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
*   ✓ Access to **centralized code repos for _all_ 536+ tutorials** on PyImageSearch
*   ✓ **Easy one-click downloads** for code, datasets, pre-trained models, etc.
*   ✓ **Access** on mobile, laptop, desktop, etc.

[Click here to join PyImageSearch University](https://pyimagesearch.com/pyimagesearch-university/?utm_source=blogPost&utm_medium=bottomBanner&utm_campaign=What%27s%20next%3F%20I%20recommend)

Summary
-------

In this blog post, we learned how to measure the size of objects in an image using Python and OpenCV.

Just like in our tutorial on [measuring the distance from a camera to an object](https://pyimagesearch.com/2015/05/04/target-acquired-finding-targets-in-drone-and-quadcopter-video-streams-using-python-and-opencv/), we need to determine our “pixels per metric” ratio, which describes the number of pixels that can “fit” into a given number of inches, millimeters, meters, etc.

To compute this ratio, we need a reference object with two important properties:

*   **Property #1:** The reference object should have _known dimensions_ (such as width or height) in terms of a _measurable unit_ (inches, millimeters, etc.).
*   **Property #2:** The reference object should be _easy to find_, either in terms of _location_ of the object or in its _appearance_.

Provided that both of these properties can be met, you can utilize your reference object to calibrate your _pixels\_per\_metric_ variable, and from there, compute the size of other objects in an image.

In our next blog post, we’ll take this example a step further and learn how to compute the distance _**between**_ objects in an image.

**Be sure to signup for the PyImageSearch Newsletter using the form below to be notified when the next blog post goes live — _you won’t want to miss it!_**

![Image 7](data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20410%20567'%3E%3C/svg%3E)

#### Download the Source Code and FREE 17-page Resource Guide

Enter your email address below to get a .zip of the code and a **FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning.** Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!