假设有一张含有地理坐标信息的tif格式的图片及其对应的jpg或者png格式的普通图片
如下图所示:
使用rasterio计算tif文件中两点之间的物理实际距离-LMLPHP

使用rasterio计算tif文件中两点之间的物理实际距离-LMLPHP
其中第一张为tif格式的地理信息图,第二张为按照一定比例下采样之后转换得到的普通jpg图片

如何计算jpg图片中任意两点之间的实际距离呢?
比如,计算图中间位置的桥的长度,也就是桥两个端点之间的距离

在tif图片中,如果我们知道了点的经纬度就可以使用geopy包中的geodesic函数计算两点间的距离
代码如下

from geopy.distance import geodesic
distance = geodesic((lat1,lon1), (lat2,lon2)).meters

其中lat1,lon1为第一个点的纬度和经度

所以问题的关键在于如何找到jpg图片中的点对应的经纬度
为了达到该目标需要经过3个步骤

步骤一

找到jpg中的点在图片中所在的相对图片宽和高的比例
代码如下:

import cv2 as cv
image = cv.imread('example.jpg')
h, w, c = image.shape
# 选取两个点
pick1_x, pick1_y = 2022, 2160
pick2_x, pick2_y = 1902, 2160
pick1_x_ratio, pick1_y_ratio = pick1_x/w, pick1_y/h
pick2_x_ratio, pick2_y_ratio = pick2_x/w, pick2_y/h

步骤二

按照比例找到两个点对应的tif图中的行与列
代码如下:

import rasterio
with rasterio.open(tif_file) as src:
	tmp_height = src.height  # 数据高度
    tmp_width = src.width  # 数据宽度
    pick1_col, pick1_row = int(pick1_x_ratio*tmp_width), int(pick1_y_ratio*tmp_height)
    pick2_col, pick2_row = int(pick2_x_ratio*tmp_width), int(pick2_y_ratio*tmp_height)

步骤三

将行列号转换为以地图单位(Map units)表示的坐标
代码如下:

with rasterio.open(tif_file) as src:
	pick1_lon_map, pick1_lat_map = src.xy(pick1_row, pick1_col)
    pick2_lon_map, pick2_lat_map = src.xy(pick2_row, pick2_col)

步骤四

将地图单位坐标转换为十进制度数(Decimal Degrees)坐标,
此处应注意使用的坐标系
代码如下:

import pyproj
import rasterio
with rasterio.open(tif_file) as src:
	# 定义源坐标系统和目标坐标系统(这里以 EPSG:4326 作为目标坐标系统,即 WGS84 经纬度坐标系统)
	src_proj = pyproj.Proj(src.crs)  # src.crs为获取到的tif图中的源坐标系统
    dst_proj = pyproj.Proj(init='EPSG:4326')
    print("picked points 坐标转换")
    pick1_long, pick1_latt = pyproj.transform(src_proj, dst_proj, pick1_lon_map, pick1_lat_map)
    pick2_long, pick2_latt = pyproj.transform(src_proj, dst_proj, pick2_lon_map, pick2_lat_map)

步骤五

根据十进制坐标,计算两点之间的距离
代码如下:

from geopy.distance import geodesic
# 纬度在前,经度在后
distance = geodesic((pick1_latt, pick1_long), (pick2_latt, pick2_long)).meters
print(f"选取的两点之间的距离:{distance} meters")

选取的两点为桥的两个端点,因此最终计算得到桥长为15.57米
使用rasterio计算tif文件中两点之间的物理实际距离-LMLPHP

整体代码如下:

import cv2 as cv
import rasterio
from geopy.distance import geodesic
import pyproj

# 打开图像文件
tif_file_path = 'path_to_tif_file'
jpg_file_path = 'path_to_jpg_file'

image = cv.imread(jpg_file_path)
h, w, c = image.shape

# 打开TIFF文件
with rasterio.open(tif_file_path) as src:
    # 选取桥头两个点
    pick1_x, pick1_y = 2022, 2160
    pick2_x, pick2_y = 1902, 2160

    tmp_height = src.height  # 数据高度,行数
    tmp_width = src.width  # 数据宽度,列数

    # step1 and step2: 计算两点所占比例及其在tif中对应的行列号
    pick1_col, pick1_row = int(pick1_x*tmp_width/w), int(pick1_y*tmp_height/h)
    pick2_col, pick2_row = int(pick2_x*tmp_width/w), int(pick2_y*tmp_height/h)
    
    # step3: 计算对应的地图单位坐标
    print("picked point 经纬度")
    pick1_lon, pick1_lat = src.xy(pick1_row, pick1_col)
    pick2_lon, pick2_lat = src.xy(pick2_row, pick2_col)
    print("pick1经纬度:", pick1_lon, ' ', pick1_lat)
    print("pick2经纬度:", pick2_lon, ' ', pick2_lat)


    # 定义源坐标与目标坐标系统(这里以 EPSG:4326 作为目标坐标系统,即 WGS84 经纬度坐标系统)
    src_proj = pyproj.Proj(src.crs)
    dst_proj = pyproj.Proj(init='EPSG:4326')

    # step 4: 将地图单位坐标转为十进制经纬度坐标
    print("picked points 坐标转换")
    pick1_long, pick1_latt = pyproj.transform(src_proj, dst_proj, pick1_lon, pick1_lat)
    pick2_long, pick2_latt = pyproj.transform(src_proj, dst_proj, pick2_lon, pick2_lat)
    print(f"第一个点经纬度:{pick1_long}  {pick1_latt}")
    print(f"第二个点经纬度:{pick2_long}  {pick2_latt}")

    # step5: 计算两点之间的距离
    distance = geodesic((pick1_latt, pick1_long), (pick2_latt, pick2_long)).meters
    print(f"选取的两点之间的距离:{distance} meters")

还有一个转换值得关注,
即行列号与地图单位坐标之间的互相转换
代码如下:

import rasterio
with rasterio.open(tif_file) as src:
	# 行列号转地图单位坐标
	lon_map, lat_map = src.xy(row, col)
	# 地图单位坐标转行列号
	row, col = src.index(lon_map, lat_map)
06-16 01:35