程式碼倉庫
https://github.com/Dongvdong/v1_1_slam_tool
主要有兩個庫
1正常的庫 獲取經緯度 但是無法獲取雲臺和飛機姿態
2 2進位制模式讀取可以獲取更多資訊,但是讀取的高度有精度損失。
# -*- coding: utf-8 -*- # conda activate py37gaosi # 伺服器 # activate py38 # 筆記本 import os import numpy as np #改進檢測地區 import os import exifread import re import sys import requests import json # 遍歷資料夾及子資料夾中的所有圖片,逐個檔案讀取exif資訊 ''' def get_pic_GPS(pic_dir): items = os.listdir(pic_dir) for item in items: path = os.path.join(pic_dir, item) if os.path.isdir(path): get_pic_GPS(path) else: imageread(path) ''' def convert_altitude_to_decimal(altitude): #print(altitude) try: # 可能會丟擲異常的程式碼 a, b = altitude.strip().split('/') #print(f"a:{a},b:{b}") return float(a)/float(b) except ValueError as e: #print(f"不存在‘/’") #print(altitude) # 這裡可以繼續執行其他程式碼 return float(altitude) # 將經緯度轉換為小數形式 def convert_to_decimal(*gps): # 度 if '/' in gps[0]: deg = gps[0].split('/') if deg[0] == '0' or deg[1] == '0': gps_d = 0 else: gps_d = float(deg[0]) / float(deg[1]) else: gps_d = float(gps[0]) # 分 if '/' in gps[1]: minu = gps[1].split('/') if minu[0] == '0' or minu[1] == '0': gps_m = 0 else: gps_m = (float(minu[0]) / float(minu[1])) / 60 else: gps_m = float(gps[1]) / 60 # 秒 if '/' in gps[2]: sec = gps[2].split('/') if sec[0] == '0' or sec[1] == '0': gps_s = 0 else: gps_s = (float(sec[0]) / float(sec[1])) / 3600 else: gps_s = float(gps[2]) / 3600 decimal_gps = gps_d + gps_m + gps_s # 如果是南半球或是西半球 if gps[3] == 'W' or gps[3] == 'S' or gps[3] == "83" or gps[3] == "87": return str(decimal_gps * -1) else: return str(decimal_gps) ''' # 如果提取 影像資訊不需要機身自討 精度更高 Image ImageDescription Image Make Image Model Image Orientation Image XResolution Image YResolution Image ResolutionUnit Image Software Image DateTime Image YCbCrPositioning Image ExifOffset GPS GPSVersionID GPS GPSLatitudeRef GPS GPSLatitude GPS GPSLongitudeRef GPS GPSLongitude GPS GPSAltitudeRef GPS GPSAltitude Image GPSInfo Image XPComment Image XPKeywords Thumbnail Compression Thumbnail XResolution Thumbnail YResolution Thumbnail ResolutionUnit Thumbnail JPEGInterchangeFormat Thumbnail JPEGInterchangeFormatLength EXIF ExposureTime EXIF FNumber EXIF ExposureProgram EXIF ISOSpeedRatings EXIF ExifVersion EXIF DateTimeOriginal EXIF DateTimeDigitized EXIF ComponentsConfiguration EXIF CompressedBitsPerPixel EXIF ShutterSpeedValue EXIF ApertureValue EXIF ExposureBiasValue EXIF MaxApertureValue EXIF SubjectDistance EXIF MeteringMode EXIF LightSource EXIF Flash EXIF FocalLength EXIF MakerNote EXIF FlashPixVersion EXIF ColorSpace EXIF ExifImageWidth EXIF ExifImageLength Interoperability InteroperabilityIndex Interoperability InteroperabilityVersion EXIF InteroperabilityOffset EXIF ExposureIndex EXIF FileSource EXIF SceneType EXIF CustomRendered EXIF ExposureMode EXIF WhiteBalance EXIF DigitalZoomRatio EXIF FocalLengthIn35mmFilm EXIF SceneCaptureType EXIF GainControl EXIF Contrast EXIF Saturation EXIF Sharpness EXIF SubjectDistanceRange EXIF BodySerialNumber ''' # 讀取圖片的經緯度和拍攝時間 def Api_1_1Get_ImageGPS(path): f = open(path, 'rb') GPS = {} gps_=[-1,-1,-1] try: tags = exifread.process_file(f) except: return gps_ #print(tags) #for tag in tags: # print(str(tag),str(tags[str(tag)])) # 南北半球標識 if 'GPS GPSLatitudeRef' in tags: GPS['GPSLatitudeRef'] = str(tags['GPS GPSLatitudeRef']) # print(GPS['GPSLatitudeRef']) else: GPS['GPSLatitudeRef'] = 'N' # 預設設定為北半球 # 東西半球標識 if 'GPS GPSLongitudeRef' in tags: GPS['GPSLongitudeRef'] = str(tags['GPS GPSLongitudeRef']) # print(GPS['GPSLongitudeRef']) else: GPS['GPSLongitudeRef'] = 'E' # 預設設定為東半球 # 海拔高度標識 if 'GPS GPSAltitudeRef' in tags: GPS['GPSAltitudeRef'] = str(tags['GPS GPSAltitudeRef']) # 獲取緯度 if 'GPS GPSLatitude' in tags: lat = str(tags['GPS GPSLatitude']) # 處理無效值 if lat == '[0, 0, 0]' or lat == '[0/0, 0/0, 0/0]': GPS['GPSLatitude']=-1 else: deg, minu, sec = [x.replace(' ', '') for x in lat[1:-1].split(',')] # 將緯度轉換為小數形式 GPS['GPSLatitude'] = convert_to_decimal(deg, minu, sec, GPS['GPSLatitudeRef']) # 獲取經度 if 'GPS GPSLongitude' in tags: lng = str(tags['GPS GPSLongitude']) # print(lng) # 處理無效值 if lng == '[0, 0, 0]' or lng == '[0/0, 0/0, 0/0]': GPS['GPSLongitude']=-1 else: deg, minu, sec = [x.replace(' ', '') for x in lng[1:-1].split(',')] # 將經度轉換為小數形式 GPS['GPSLongitude'] = convert_to_decimal(deg, minu, sec, GPS['GPSLongitudeRef']) # 對特殊的經緯度格式進行處理 # 獲取海拔高度 if 'GPS GPSAltitude' in tags: height = str(tags["GPS GPSAltitude"]) GPS['GPSAltitude'] = convert_altitude_to_decimal(height) # if 'GPS RelativeAltitude' in tags: # height = str(tags["GPS RelativeAltitude"]) # GPS['RelativeAltitude'] = convert_altitude_to_decimal(height) # 獲取圖片拍攝時間 # if 'Image DateTime' in tags: # GPS["DateTime"] = str(tags["Image DateTime"]) # print(GPS["DateTime"]) # elif "EXIF DateTimeOriginal" in tags: # GPS["DateTime"] = str(tags["EXIF DateTimeOriginal"]) # print(GPS["DateTime"]) # if 'Image Make' in tags: # print('照相機制造商:', tags['Image Make']) # if 'Image Model' in tags: # print('照相機型號:', tags['Image Model']) # if 'Image ExifImageWidth' in tags: # print('照片尺寸:', tags['EXIF ExifImageWidth'],tags['EXIF ExifImageLength']) gps_=[float(GPS['GPSLatitude']) ,float(GPS['GPSLongitude']), float(GPS['GPSAltitude'])] #print(gps_) return gps_ ''' # 如果需要提取相機字條和機身姿態 讀取資訊檢視 tiff:Make="DJI" tiff:Model="FC6310R" dc:format="image/jpg" drone-dji:AbsoluteAltitude="+514.64" drone-dji:RelativeAltitude="+100.09" drone-dji:GpsLatitude="34.03250565" drone-dji:GpsLongtitude="108.76779926" drone-dji:GimbalRollDegree="+0.00" 拍照時刻雲臺的Roll 尤拉角 drone-dji:GimbalYawDegree="+93.50" drone-dji:GimbalPitchDegree="-89.90" drone-dji:FlightRollDegree="+2.10" 拍照時刻飛行器機體的Roll 尤拉角 drone-dji:FlightYawDegree="+93.50" drone-dji:FlightPitchDegree="+1.00" drone-dji:FlightXSpeed="+0.00" drone-dji:FlightYSpeed="+0.00" drone-dji:FlightZSpeed="+0.00" drone-dji:CamReverse="0" drone-dji:GimbalReverse="0" drone-dji:SelfData="Undefined" drone-dji:CalibratedFocalLength="3666.666504" 焦距 drone-dji:CalibratedOpticalCenterX="2736.000000" drone-dji:CalibratedOpticalCenterY="1824.000000" drone-dji:RtkFlag="50" drone-dji:RtkStdLon="0.01117" drone-dji:RtkStdLat="0.01132" drone-dji:RtkStdHgt="0.02493" drone-dji:DewarpFlag="1" drone-dji:PhotoDiff="" crs:Version="7.0" crs:HasSettings="False" crs:HasCrop="False" crs:AlreadyApplied="False"> </rdf:Description> ''' # 讀取單個照片的 gps資訊 這個方法有問題 讀取高度精度損失 def Api_1_2Get_Image_AllInfo(file_path): b = b"\x3c\x2f\x72\x64\x66\x3a\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e" a = b"\x3c\x72\x64\x66\x3a\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20" aa=["\x3c\x72\x64\x66\x3a\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x20"] bb=["\x3c\x2f\x72\x64\x66\x3a\x44\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e\x3e"] #xml format to save EXIF的資料規範 # aa ['<rdf:Description '] #print("aa",aa) # bb ['</rdf:Description>'] #print("bb",bb) # rb是讀取二進位制檔案 img = open(file_path, 'rb') # bytearray() 方法返回一個新位元組陣列 data = bytearray() #識別符號, flag = False for i in img.readlines(): # 按行讀取二進位制資訊,標籤成對出現 if a in i: flag = True if flag: #把第i行資料複製到新陣列中 data += i if b in i: break #print("======大疆精靈4照片中原始資料 =======\n",data) if len(data) > 0: data = str(data.decode('ascii'))#ascii #print(data) #filter()函式用於過濾序列,過濾掉不符合條件的元素,返回符合條件的元素組成新列表。 #filter(function,iterable) ,function -- 判斷函式。iterable -- 可迭代物件 #python允許用lambda關鍵字創造匿名函式。 # 在 lambda 關鍵字之後、冒號左邊為引數列表,可不帶引數,也可有多個引數。若有多個引數,則引數間用逗號隔開,冒號右邊為 lambda 表示式的返回值。 #left--->right # judge condition 'drone-dji:' in x lines = list(filter(lambda x: 'drone-dji:' in x, data.split("\n"))) #print("lines",lines) dj_data_dict = {} for d in lines: # remove 'drone-dji:' d = d.strip()[10:] # k is name # v is value k, v = d.split("=") v=v.replace('\"','') #print(k, v) dj_data_dict[k] = v return dj_data_dict else: dj_data_dict="error" def Api_2Get_Image_GPS(img_path): #img_name=img_path[img_path.rfind('/')+1:img_path.rfind('.')]# 去掉字尾 #print(img_name) dj_data_dict=Api_1_1Get_ImageGPS(img_path) GPS_lat_lon_h=[-1,-1,-1] if dj_data_dict !="error": #dj_data_dict['GpsLatitude'] # 這種方法會損失高度 #dj_data_dict['GpsLongtitude'] #dj_data_dict['RelativeAltitude'] lat=float(dj_data_dict[0]) lon=float(dj_data_dict[1]) absh=float(dj_data_dict[2]) GPS_lat_lon_h=[lat,lon,absh] #print(GPS) return GPS_lat_lon_h # 遍歷資料夾讀取照片的GPS 高度保留釐米 def API_read_directory(img_path_dir): Gnss_list=[] for filename in os.listdir(img_path_dir): file_dir_name=img_path_dir+filename #print(filename) GPS_lat_lon_h=Api_2Get_Image_GPS(file_dir_name) gnss_temp=[] gnss_temp.append(filename) gnss_temp.append(GPS_lat_lon_h[0]) gnss_temp.append(GPS_lat_lon_h[1]) gnss_temp.append(GPS_lat_lon_h[2]) #print("1 照片讀取到的GNSS",gnss_temp) Gnss_list.append(gnss_temp) return Gnss_list def API_Save2txt(GPS_txt_name,Gnss_list): with open(GPS_txt_name, 'w') as file: for row in Gnss_list: line = ' '.join(map(str, row)) file.write(f"{line}\n") print(GPS_txt_name,"儲存成功") def API_read2txt(GPS_txt_name): print(GPS_txt_name,"讀取txt資料成功") Gnss_list = [] with open(GPS_txt_name, 'r') as file: for line in file: row = list(map(str, line.split())) Gnss_list.append(row) #print(row) return Gnss_list #====================測試======================== ''' if __name__ == "__main__": # 引數 # 0-1 gps照片路徑 img_path_dir="E:/v0_Project/V0_Mybao/v8_slam/python工具/0測試資料/d1_100mRTKColmap/images/gps_images/" # 0-2 txt儲存的名字 GPS_txt_name="GPS.txt" # 1讀取資料 Gnss_list=API_read_directory(img_path_dir) # 2儲存txt 格式: name lat lon h API_Save2txt(GPS_txt_name,Gnss_list) # 3讀取txt 格式: name lat lon h Gnss_list_Read = API_read2txt(GPS_txt_name) '''
資料格式
DJI_0002.JPG 34.03250563926396 108.7677992500047 514.6380141189607 DJI_0005.JPG 34.03267641704175 108.76781155556024 514.4640141184373 DJI_0011.JPG 34.03394725037507 108.76789833333802 514.635014120011 DJI_0015.JPG 34.03487661148619 108.76796561111581 514.6420141205874 DJI_0018.JPG 34.03509530593064 108.76797844444913 514.6150141188659 DJI_0022.JPG 34.035064472597305 108.76773913889359 514.58201412093 DJI_0025.JPG 34.03463080593064 108.76770336111582 514.6600141209601 DJI_0028.JPG 34.03403180593064 108.76765755556025 514.5780141181838 DJI_0031.JPG 34.033382778152856 108.76761005556025 514.5470141202469 DJI_0035.JPG 34.032533167041734 108.7675511111158 514.6610141190716 DJI_0041.JPG 34.03248758370841 108.7671833611158 514.8310141174146 DJI_0042.JPG 34.03248605593062 108.76717719444913 514.781014119351