技术栈
编写时间:2025-07-10
更新时间:2025-07-30
选项:iTwinModelerCapture(原cc) 、Webodm(开源)

iTwinModelerCapture
背景
实操路线
Webodm
背景
实操路线
将航飞的pos写入图片
# -*- coding: utf-8 -*-
import os
import csv
from PIL import Image
import piexifclass ExifWriter:"""将 txt/CSV 中的 GPS、姿态信息写入 JPG/TIF 影像的 EXIF。输入格式:Name,Lat,Lng,Alt,Roll,Pitch,Yaw"""def __init__(self, txt_path: str, img_root: str, save_root: str):""":param txt_path: 包含 Name,Lat,Lng,... 的 txt 文件路径:param img_root: 原影像目录:param save_root: 写入后的影像保存目录(自动创建)"""self.txt_path = txt_pathself.img_root = img_rootself.save_root = save_rootos.makedirs(self.save_root, exist_ok=True)# ------------------ 公共 API ------------------def run(self):"""主入口:遍历 txt → 逐图写入"""for row in self._read_txt(self.txt_path):name, lat, lng, alt, roll, pitch, yaw = rowimg_name = name.strip()img_path = os.path.join(self.img_root, img_name)out_path = os.path.join(self.save_root, img_name)if not os.path.isfile(img_path):print(f"⚠️ 找不到文件: {img_path}")continuegps_dict = self._build_gps_dict(float(lat), float(lng), float(alt))self._write_exif(img_path, out_path, gps_dict)print(f"✅ {img_name} 已写入 EXIF")def read_exif_and_write_txt(self,img_root):"""读取指定路径下所有照片的经纬度和高程信息,写入到图片文件夹中的 txt 文件"""txt_file_path = os.path.join(img_root, "exif_info.txt")with open(txt_file_path, "w", newline='') as txt_file:writer = csv.writer(txt_file)writer.writerow(["Name", "Lat", "Lng", "Alt"])for img_name in os.listdir(img_root):if img_name.lower().endswith(('.jpg', '.jpeg', '.png')):img_path = os.path.join(img_root, img_name)lat, lng, alt = self._read_exif(img_path)if lat is not None and lng is not None and alt is not None:writer.writerow([img_name, lat, lng, alt])print(f"✅ 所有照片的经纬度和高程信息已写入到 {txt_file_path}")# ------------------ 内部工具 ------------------@staticmethoddef _read_txt(txt_path: str):"""读取 txt/CSV,跳过表头"""with open(txt_path, newline='') as f:reader = csv.reader(f, skipinitialspace=True)next(reader, None) # 跳过标题for row in reader:if len(row) < 7:continueyield row@staticmethoddef _format_latlng(latlng: float):degree = int(latlng)minute = int((abs(latlng) - abs(degree)) * 60)second_float = (((abs(latlng) - abs(degree)) * 60) - minute) * 60# 截断小数部分,保留6位小数(对应1000000分母)second = int(second_float * 1000000) # 直接截断,不四舍五入return ((degree, 1), (minute, 1), (second, 1000000))@staticmethoddef _build_gps_dict(lat: float, lng: float, alt: float):"""构造 EXIF 所需 GPS 字典"""return {"lat": ExifWriter._format_latlng(lat),"lng": ExifWriter._format_latlng(lng),"alt": (int(round(alt * 100)), 100),"lat_ref": "N" if lat >= 0 else "S","lng_ref": "E" if lng >= 0 else "W"}@staticmethoddef _write_exif(src: str, dst: str, gps_dict: dict):"""把 GPS 信息写进图片 EXIF"""img = Image.open(src)exif_dict = piexif.load(img.info.get("exif", b""))# 覆盖或新建 GPS IFDexif_dict["GPS"] = {piexif.GPSIFD.GPSLatitudeRef: gps_dict["lat_ref"],piexif.GPSIFD.GPSLatitude: gps_dict["lat"],piexif.GPSIFD.GPSLongitudeRef: gps_dict["lng_ref"],piexif.GPSIFD.GPSLongitude: gps_dict["lng"],piexif.GPSIFD.GPSAltitudeRef: 0, # 0 表示海平面以上piexif.GPSIFD.GPSAltitude: gps_dict["alt"]}exif_bytes = piexif.dump(exif_dict)img.save(dst, "JPEG", exif=exif_bytes)@staticmethoddef _read_exif(img_path: str):"""读取图片的经纬度和高程信息"""try:img = Image.open(img_path)exif_dict = piexif.load(img.info.get("exif", b""))gps_dict = exif_dict.get("GPS", {})lat = ExifWriter._convert_to_decimal(gps_dict.get(piexif.GPSIFD.GPSLatitude, None), gps_dict.get(piexif.GPSIFD.GPSLatitudeRef, None))lng = ExifWriter._convert_to_decimal(gps_dict.get(piexif.GPSIFD.GPSLongitude, None), gps_dict.get(piexif.GPSIFD.GPSLongitudeRef, None))alt = gps_dict.get(piexif.GPSIFD.GPSAltitude, None)if alt is not None:alt = alt[0] / alt[1] # 将分数转换为十进制return lat, lng, altexcept Exception as e:print(f"⚠️ 读取 {img_path} 的 EXIF 信息时出错: {e}")return None, None, None@staticmethoddef _convert_to_decimal(value, ref):if value is None or ref is None:return Noned, m, s = valuedecimal_value = d[0]/d[1] + (m[0]/m[1])/60 + (s[0]/s[1])/3600if ref in ["S", "W"]:decimal_value = -decimal_valuereturn round(decimal_value, 10) # 保留10位小数,避免计算误差# ------------------ 一键调用 ------------------
if __name__ == "__main__":writer = ExifWriter(txt_path=r'E:\work\1\20250714-04h-44m-18s.txt',img_root=r'E:\work\1\pictures',save_root=r'E:\work\1\pictures1')writer.run()writer.read_exif_and_write_txt(r'E:\work\1\pictures1')