例如我们要求这张拍摄到的无人机图片中红色线框出的足球场的面积。假设我们使用的是大疆精灵4RTK飞行H米高度拍摄到的这张图片。
step1:首先计算足球场在这张图片中所占的像素面积,这里有两种方法可以获得:
法1:我们可以使用photoshop对目标足球场使用多边形套索工具进行框选,可以直接在photoshop中读出这个球场在这张图片中所占的像素面积(如果没有显示这个直方图可以点击“”窗口”、“直方图”,注意高速缓存级别为1,如果不是1就点击“导航器”右边的四条横杠,点击“不使用高速缓存的刷新”)。可以从直方图里读出这个球场的像素面积为26372
法2:使用如下代码,将object_img_path替换成你的图片路径,对目标足球场进行顺时针选点,当最后一个点与第一个点闭合时完成选点,将自动计算圈出的足球场的像素面积。
#输入图片以及参照物面积,选点后可以在控制台上输出目标的像素面积
import cv2
class get_scale_of_real_mod_pixel:
def __init__(self, img_path):
self.object_pixel_area = None
self.image = None
self.points = []
self.img_path = img_path
self._read_img()
def _read_img(self):
self.image, self.resize_scale = self.resize_img(cv2.imread(self.img_path))
def resize_img(self, image, max_height=1000, max_width=1500):
"""调整图像大小以适应屏幕,并返回缩放因子"""
height, width = image.shape[:2]
resize_scale = 1 # 默认缩放因子为1(即无缩放)
if height > max_height or width > max_width:
resize_scale = min(max_width / float(width), max_height / float(height))
image = cv2.resize(image, None, fx=resize_scale, fy=resize_scale, interpolation=cv2.INTER_AREA)
return image, resize_scale
def click_event(self, event, x, y, flags, params):
if event == cv2.EVENT_LBUTTONDOWN:
self.points.append((x, y))
cv2.circle(self.image, (x, y), 3, (0, 255, 0), -1)
if len(self.points) > 1:
# 画出连接当前点和上一个点的边界线
cv2.line(self.image, self.points[-2], self.points[-1], (0, 255, 0), 2)
cv2.imshow('image', self.image)
def calculate_scale(self):
cv2.imshow('image', self.image)
cv2.setMouseCallback('image', self.click_event)
# 定义接近第一个点的范围(像素)
close_distance = 10
# 等待用户选择足够多的点,或者点击到第一个点附近时结束
while len(self.points) < 4 or (len(self.points) > 1 and self.distance(self.points[-1], self.points[0]) > close_distance):
cv2.waitKey(1)
# 闭合边界线
if len(self.points) > 1:
cv2.line(self.image, self.points[-1], self.points[0], (0, 255, 0), 2)
cv2.destroyAllWindows()
# 计算边界线围出的图形的像素面积
polygon = [(int(p[0] / self.resize_scale), int(p[1] / self.resize_scale)) for p in self.points]
polygon_area = self.calculate_polygon_area(polygon)
print(f"边界线围出的图像像素面积为{polygon_area}")
def calculate_polygon_area(self, polygon):
n = len(polygon)
area = 0
for i in range(n):
x1, y1 = polygon[i]
x2, y2 = polygon[(i + 1) % n]
area += x1 * y2 - x2 * y1
return abs(area) / 2.0
def distance(self, point1, point2):
return ((point1[0] - point2[0]) ** 2 + (point1[1] - point2[1]) ** 2) ** 0.5
# 使用示例
if __name__ == '__main__':
object_img_path = r"D:\High and pixel\89.JPG" # 指定图片路径
scaleFunc = get_scale_of_real_mod_pixel(object_img_path)
scaleFunc.calculate_scale() # 启动计算过程
总之,我们假设目标足球场的像素面积为A。
step2:当我们已经获得在图片中目标足球场的像素面积A之后,我们可以利用GSD去计算他的实际面积。首先我们要了解什么是GSD。
GSD(地面采样间隔):GSD表示一个像素对应现实生活中的实际距离,一般单位为:米每像素或者厘米每像素。例如:GSD为5cm/像素:表示图片中一个像素代表真实世界中实际距离为5cm。
每台无人机的GSD不太相同,可以在官网上查找或者打电话无人机咨询客服,他们会告诉你你的无人机GSD信息。或者通过以下公式自己手动计算你的无人机的GSD:
其中H为飞行高度,f为相机镜头焦距,a为像元尺寸(单位为mm)。
例如:这里我们使用的是大疆精灵4RTK,可以再官网上找到他的GSD计算公式为:
单位:cm/像素
由我们的飞行高度为H,带入可以得到GSD为 xx cm/像素,再换算成m/像素,这里指一个像素代表实际长度为多少米,我们可以将这个结果平方,得到一个像素代表的实际面积B(这里的单位是平方米)。
step3:由step1中求得的目标足球场的像素面积A与step2中求到的一个像素代表的实际面积B,我们可以得到目标足球场的实际面积为A*B