当前位置: 首页 > news >正文

航测建模篇-正射影像生产-持续连载中

技术栈

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

image

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')
http://www.sczhlp.com/news/12498/

相关文章:

  • 电容层析成像Tikhonov算法
  • 2025 -- 云智计划 -- 【CSP-S】模拟赛 #202123_总结+题解
  • 极简工作任务排期管理工具,小团队、小项目工作任务管理工具。
  • Tenable Nessus 10.9.3 (macOS, Linux, Windows) - 漏洞评估解决方案
  • MassAI-使用ZoneShape让Actor动起来
  • MacOS安装brew
  • mysql 主从架构详解
  • AI促进研发管理案例
  • Java面向对象基础——11.class版本
  • jr数学的退休生活-2
  • inode节点扩容
  • AI机器人有贪嗔痴吗?
  • 抖音中国移动发布动感地带联名卡,火山引擎数智平台提供技术支持
  • 手把手搭建自动化测试环境:10分钟搞定Python/Java双环境
  • 正确理解EFCore导航属性与Include
  • 飞算JavaAI开发助手到底有多强
  • HTTP状态码
  • CF490F 题解
  • 来火山引擎「算子广场」,一键处理多模态数据
  • 《JMeter核心技术、性能测试与性能分析》 教学大纲及标准
  • AT_arc066_d [ARC066F] Contest with Drinks Hard 题解
  • linux根目录挂载物理分区,导致根目录无法扩容,已部署的人大金仓数据库,所在的目录拷贝到新磁盘后,重新挂载到原目录,导致无法授权问题
  • CF1408E Avoid Rainbow Cycles
  • 飞算JavaAI开发助手:用“说人话”生成完整Java工程,效率飙升500%
  • rsbuild中使用svelte模板报错(未解决)
  • mysql5.7版本部署
  • 泛型哲学
  • 从零开始学MCP(1)| MCP 协议核心原理解析
  • Uniapp 之renderjs解决swiper+多个video卡顿问题
  • 新增两大模型!TRAE 中国版支持 GLM-4.5、国际版支持 o3