在计算机视觉和图形学应用中,准确测量图像上的点之间距离是一项常见且重要的任务。本篇技术博客将详细介绍如何利用Python编程语言和OpenCV库构建一个交互式的图像距离测量工具。我们将通过编写一个名为ImageProcessor的类,让用户能够在图像上点击选取点,并实时显示两点间的实际距离(以毫米为单位)。下面让我们一起深入探讨代码实现及其核心功能。
一、代码结构概览
首先,我们来看看整个程序的主要组成部分:
python
import cv2
import numpy as np
class ImageProcessor:
def __init__(self, image_path, pixel_to_mm=0.3):
self.image_path = image_path
self.pixel_to_mm = pixel_to_mm
self.image = None
self.points = []
self.distances = []
self.load_image()
def load_image(self):
try:
self.image = cv2.imread(self.image_path)
except FileNotFoundError:
print("Image file not found.")
exit()
def calculate_distance(self, point1, point2):
pixel_distance = np.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
actual_distance = pixel_distance * self.pixel_to_mm
return pixel_distance, actual_distance
def draw_on_image(self):
img_copy = self.image.copy()
for i in range(len(self.points)):
cv2.circle(img_copy, self.points[i], 3, (0, 0, 255), -1) # Display Point
if i % 2 == 1:
cv2.line(img_copy, self.points[i - 1], self.points[i], (255, 0, 0), 2) # Draw line between points
pixel_distance, actual_distance = self.calculate_distance(self.points[i - 1], self.points[i])
self.distances.append(actual_distance)
text = "AD: {:.2f} mm".format(actual_distance)
cv2.putText(img_copy, text, (
(self.points[i - 1][0] + self.points[i][0]) // 2, (self.points[i - 1][1] + self.points[i][1]) // 2),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
return img_copy
def mouse_event(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
self.points.append((x, y))
def process(self):
cv2.namedWindow('Image')
cv2.setMouseCallback('Image', self.mouse_event)
while True:
img_copy = self.draw_on_image()
cv2.imshow('Image', img_copy)
key = cv2.waitKey(1) & 0xFF
if key == 27: # Press 'Esc' to exit
break
cv2.destroyAllWindows()
if __name__ == "__main__":
processor = ImageProcessor('mask/img.png', pixel_to_mm=0.3)
processor.process()
该程序主要由以下几部分组成:
-
标题引入必要的库:cv2(OpenCV库)用于图像处理和显示,numpy(NumPy库)用于数值计算。
-
定义ImageProcessor类:封装图像加载、点选、距离计算、结果显示等逻辑。
-
主程序入口:创建ImageProcessor实例,传入图像路径和像素与毫米的比例,默认为0.3,然后调用process方法启动交互过程。
接下来,我们将逐一解析ImageProcessor类中的关键方法。
二、ImageProcessor类详解
- __init__方法
python
def __init__(self, image_path, pixel_to_mm=0.3):
self.image_path = image_path
self.pixel_to_mm = pixel_to_mm
self.image = None
self.points = []
self.distances = []
self.load_image()
__init__方法是类的构造器,负责初始化对象属性。这里定义了以下几个属性:
image_path: 存储待处理图像的路径。
pixel_to_mm: 像素与毫米之间的比例,默认值为0.3,表示每个像素代表0.3毫米。
image: 用于存放加载的图像数据。
points: 存储用户点击的点坐标,按点击顺序排列。
distances: 存储已计算的实际距离值。
最后,load_image方法被调用,用于加载指定路径下的图像
- load_image方法
python
def load_image(self):
try:
self.image = cv2.imread(self.image_path)
except FileNotFoundError:
print("Image file not found.")
exit()
load_image方法负责从指定路径加载图像。它使用cv2.imread函数尝试读取图像,如果文件不存在,则捕获FileNotFoundError异常,打印错误消息并退出程序,确保程序在遇到无效文件时能优雅终止。
- calculate_distance方法
python
def calculate_distance(self, point1, point2):
pixel_distance = np.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2)
actual_distance = pixel_distance * self.pixel_to_mm
return pixel_distance, actual_distance
calculate_distance方法接收两个点坐标作为参数,计算它们之间的像素距离(欧氏距离)并转换为实际距离(毫米)。结果以元组形式返回,包含像素距离和实际距离。
- draw_on_image方法
python
def draw_on_image(self):
img_copy = self.image.copy()
for i in range(len(self.points)):
cv2.circle(img_copy, self.points[i], 3, (0, 0, 255), -1) # Display Point
if i % 2 == 1:
cv2.line(img_copy, self.points[i - 1], self.points[i], (255, 0, 0), 2) # Draw line between points
pixel_distance, actual_distance = self.calculate_distance(self.points[i - 1], self.points[i])
self.distances.append(actual_distance)
text = "AD: {:.2f} mm".format(actual_distance)
cv2.putText(img_copy, text, (
(self.points[i - 1][0] + self.points[i][0]) // 2, (self.points[i - 1][1] + self.points[i][1]) // 2),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
return img_copy
draw_on_image方法负责在图像副本上绘制用户选择的点、连线及距离文字。具体步骤如下:
- 创建图像副本,防止直接修改原始图像。
- 遍历points列表,绘制每个点为红色圆圈。
- 当索引i为奇数时,表示当前点与前一个点构成一对,于是:
绘制连接这两个点的线。
调用calculate_distance计算它们之间的实际距离,并将结果添加到distances列表。
在两点之间合适的位置绘制包含实际距离的文字。
最后返回处理过的图像副本。
- mouse_event
python
def mouse_event(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
self.points.append((x, y))
mouse_event方法作为鼠标回调函数,响应用户的鼠标操作。当用户按下左键(cv2.EVENT_LBUTTONDOWN)时,将当前鼠标位置((x, y))添加到points列表中。