(slam工具)2 從照片讀取GNSS資訊

MKT-porter發表於2024-06-17

程式碼倉庫

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

  

相關文章