Have you ever wondered what distance the ball of a pen travels when you take your notes? Well, if so, you don’t have to wonder anymore, because OpenCV will give you a very precise answer. A light and fast script will recognize the contours (with a given cutoff accuracy), count how many pixels they occupy, and then convert the pixels to the actual length. An observation: OpenCV prefers to recognize contours from bottom to top. Any practical application of this script?
Hint: You need to know the exact pixel/mm ratio
Hint2: The animation snippet is only for visualizing purpose, the script could work faster without it.
import time
import cv2
import numpy as np
def preprocess_image(image_path):
# Load the image and convert to grayscale
image = cv2.imread(image_path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Smoothing and binarizing the image
blur = cv2.GaussianBlur(gray, (5, 5), 0)
_, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
return image, thresh
def get_text_contours(thresh_image):
# Finding contours on the image
contours, _ = cv2.findContours(thresh_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return contours
def calculate_length(contours):
# Calculating the total length of contour lines
total_length = 0
for contour in contours:
total_length += cv2.arcLength(contour, True)
return total_length
def display_contours_and_length(image, contours, total_length_mm):
# Displaying contours one by one
for i, contour in enumerate(contours):
# Drawing a single contour in red
cv2.drawContours(image, [contour], -1, (0, 0, 255), 1)
# Displaying the image
cv2.imshow('Contours Animation', image)
cv2.waitKey(30) # Time to display the image in milliseconds before moving to the next contour
# Delay for smooth animation
time.sleep(0.05)
# Text to be displayed
text = f"Total length: {total_length_mm/1000:.2f} m"
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 1
font_thickness = 2
# Calculating the size of the text
text_size = cv2.getTextSize(text, font, font_scale, font_thickness)[0]
# Position of the text
text_x = 50
text_y = 250
# Drawing a white rectangle as a background for the text
cv2.rectangle(image, (text_x, text_y - text_size[1] - 10), (text_x + text_size[0], text_y + 10), (255, 255, 255), cv2.FILLED)
# Drawing the text
cv2.putText(image, text, (text_x, text_y), font, font_scale, (255, 0, 255), font_thickness)
# Displaying the image with text
cv2.imshow('Contours Animation', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Image path
image_path = 'example.jpg'
# Image processing
image, thresh = preprocess_image(image_path)
# Extracting contours
contours = get_text_contours(thresh)
# Calculating the length of lines
total_length = calculate_length(contours)
# Calculating the scale
actual_length_mm = 5.5 # Actual length of the ruler in mm
length_in_pixels = 24 # Length of the ruler in the image in pixels
scale = actual_length_mm / length_in_pixels # Scale in mm per pixel
# Assuming `total_length` is the total length of contours in pixels
total_length_mm = total_length * scale
print("Total line length in millimeters:", total_length_mm)
# Optionally: Display image with contours
# Sequentially displaying contours
display_contours_and_length(image.copy(), contours, total_length_mm)