Hi all, I just made this tool in python that given a folder containing videos (with srt files), images or both, extracts georeferenced frames from all the present videos at the specified interval, and updates the present photos with the absolute altitude above sea level if you know the altitude of the starting point.
the tool consists in two files, a gui (made with customtkinter, you can save this gui in a .pyw file to open it on double click) and a image/video processor (must be saved in the same folder of the gui and called alt_changer.py).
For the ones who do not want to install python and the necessary libraries , i've packaged this tool in an one-click exe with gui that is hosted on my website at this link: https://www.miro-rava.com/documents/DJI_GPS_Video_Editor.exe

If you want, you can package the exe by yourself using something like auto-py-to-exe (remember to install all the necessary libraries for the files to work using pip or conda before packaging the exe: customtkinter, opencv-python, exif, piexif, pillow)
this are the two files:
the gui:
and the photo/video processor (needs to be named alt_changer.py in order to work with the gui, otherwise rename it in function start_batch() in the gui):
this code should work with all types of .SRT files from DJI drones, but in case it doesn't work, you can simply change the regex pattern in this line:
using tome online regex editors and testers
let me know if this tool is helpful and any future improvements/features i can add to it
the tool consists in two files, a gui (made with customtkinter, you can save this gui in a .pyw file to open it on double click) and a image/video processor (must be saved in the same folder of the gui and called alt_changer.py).
For the ones who do not want to install python and the necessary libraries , i've packaged this tool in an one-click exe with gui that is hosted on my website at this link: https://www.miro-rava.com/documents/DJI_GPS_Video_Editor.exe

If you want, you can package the exe by yourself using something like auto-py-to-exe (remember to install all the necessary libraries for the files to work using pip or conda before packaging the exe: customtkinter, opencv-python, exif, piexif, pillow)
this are the two files:
the gui:
Python:
import customtkinter
import subprocess
import sys
import os
customtkinter.set_appearance_mode("light") # Modes: "System" (standard), "Dark", "Light"
customtkinter.set_default_color_theme("green") # Themes: "blue" (standard), "green", "dark-blue"
app = customtkinter.CTk()
app.geometry("600x280")
app.title("Altitude Image Processor")
def resource_path(relative_path):
""" Get absolute path to resource, works for dev and for PyInstaller """
try:
# PyInstaller creates a temp folder and stores path in _MEIPASS
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def start_batch():
subprocess.Popen(["python", resource_path("alt_changer.py"), folder_path.get(), entry_1.get(),entry_2.get()])
def browse_folder():
# Allow user to select a directory and store it in global var
# called folder_path
global folder_path
filename = customtkinter.filedialog.askdirectory()
folder_path.set(filename)
frame_1 = customtkinter.CTkFrame(master=app)
frame_1.pack(pady=20, padx=20, fill="both", expand=True)
folder_path = customtkinter.StringVar()
folder_path.set("No Folder Selected")
button_1 = customtkinter.CTkButton(text="Chose Image Folder", master=frame_1, command=browse_folder)
button_1.pack(pady=10, padx=10)
label_2 = customtkinter.CTkLabel(master=frame_1, textvariable=folder_path, justify=customtkinter.LEFT)
label_2.pack(pady=10, padx=10)
entry_1 = customtkinter.CTkEntry(master=frame_1, placeholder_text="Enter Altitude in m")
entry_1.pack(pady=10, padx=10)
entry_2 = customtkinter.CTkEntry(master=frame_1, placeholder_text="Frames Interval in s")
entry_2.pack(pady=10, padx=10)
button_2 = customtkinter.CTkButton(text="Start Batch Processing", master=frame_1, command=start_batch)
button_2.pack(pady=10, padx=10)
if __name__ == '__main__':
app.mainloop()
and the photo/video processor (needs to be named alt_changer.py in order to work with the gui, otherwise rename it in function start_batch() in the gui):
Python:
import os
import sys
import cv2
import piexif
import re
import exif
from PIL import Image
from fractions import Fraction
files = os.listdir(sys.argv[1])
percentage = 0
def progress_bar(current, total, bar_length=20):
fraction = current / total
arrow = int(fraction * bar_length - 1) * '-' + '>'
padding = int(bar_length - len(arrow)) * ' '
ending = '\n' if current == total else '\r'
print(f'Progress: [{arrow}{padding}] {int(fraction*100)}%', end=ending)
def parse_srt_file(srt_path):
latitudes = []
longitudes = []
altitudes = []
with open(srt_path, 'r') as file:
srt_content = file.read()
pattern = r'\[latitude: ([\d.-]+)\] \[longitude: ([\d.-]+)\] \[altitude: ([\d.-]+)\]'
matches = re.findall(pattern, srt_content)
for match in matches:
latitude = float(match[0])
longitude = float(match[1])
altitude = float(match[2])
altitudes.append(altitude)
latitudes.append(latitude)
longitudes.append(longitude)
return latitudes, longitudes, altitudes
# Convert the latitude and longitude to the required format (Rational)
def degrees_to_rational(number):
degrees = int(abs(number))
minutes = int((abs(number) - degrees) * 60)
seconds = int(((abs(number) - degrees - minutes / 60) * 3600) * 100)
return [(degrees, 1), (minutes, 1), (seconds, 100)]
print("Elaborating Videos with Timestamps if present:")
for videoPath in files:
if videoPath[-4:] in [".MOV", ".mov", ".MP4", ".mp4"]:
video_path = os.path.join(sys.argv[1], videoPath)
video_name = os.path.splitext(os.path.basename(video_path))[0]
srt_path = os.path.join(sys.argv[1], video_name + ".srt")
capture = cv2.VideoCapture(video_path)
frame_count = int(capture.get(cv2.CAP_PROP_FRAME_COUNT))
frames_to_extract = range(0, frame_count-1, int(float(sys.argv[3]) * capture.get(cv2.CAP_PROP_FPS)))
try:
latitudes, longitudes, altitudes = parse_srt_file(srt_path)
except FileNotFoundError:
print(f"WARNING ---> Skipping Video: {video_name} ---> NO .SRT File found")
continue
for frame_index in frames_to_extract:
capture.set(cv2.CAP_PROP_POS_FRAMES, frame_index)
success, frame = capture.read()
if success:
frame_path = os.path.normpath(os.path.join(sys.argv[1], f'{video_name}_frame_{frame_index}.jpg'))
cv2.imwrite(frame_path, frame)
try:
frame_latitude = latitudes[frame_index]
frame_longitude = longitudes[frame_index]
frame_altitude = altitudes[frame_index]+int(sys.argv[2])
except Exception as e:
print(f"Image not processed because ---> {e}")
continue
if frame_latitude is not None and frame_longitude is not None and frame_altitude is not None:
try:
# Open the image file
image = Image.open(frame_path)
# Get the existing EXIF data
exif_dict = piexif.load(frame_path)
# Preserve existing GPS data
gps_info = exif_dict.get("GPS", {})
altitude_ref = gps_info.get(piexif.GPSIFD.GPSAltitudeRef, 0)
gps_version = exif_dict.get(piexif.GPSIFD.GPSVersionID, (2, 3, 0, 0))
new_lat_rational = degrees_to_rational(frame_latitude)
new_lon_rational = degrees_to_rational(frame_longitude)
# Update the GPS data in the EXIF metadata
exif_dict["GPS"] = {
piexif.GPSIFD.GPSLatitudeRef: 'N' if frame_latitude >= 0 else 'S',
piexif.GPSIFD.GPSLatitude: new_lat_rational,
piexif.GPSIFD.GPSLongitudeRef: 'E' if frame_longitude >= 0 else 'W',
piexif.GPSIFD.GPSLongitude: new_lon_rational,
piexif.GPSIFD.GPSVersionID: (2, 3, 0, 0),
piexif.GPSIFD.GPSAltitude: Fraction.from_float(frame_altitude).limit_denominator().as_integer_ratio(),
piexif.GPSIFD.GPSAltitudeRef: 0,
}
# Encode the EXIF data and save it back to the image
exif_bytes = piexif.dump(exif_dict)
image.save(frame_path, exif=exif_bytes)
print(f'{video_name}_frame_{frame_index}.jpg')
except Exception as e:
print(f"Image not processed because ---> {e}")
else:
print(f"No GPS data found for frame: {frame_path}")
else:
print(f"Error extracting frame at index {frame_index}")
capture.release()
print("Elaborating Images:")
for imagePath in files:
if imagePath[-4:] in [".JPG", ".PNG", ".jpg", ".png"]:
percentage += 100/len(files)
full_imagePath = os.path.join(sys.argv[1], imagePath)
with open(full_imagePath, 'rb') as image_file:
img = exif.Image(image_file)
img.gps_altitude = img.gps_altitude + int(sys.argv[2])
ext = full_imagePath[-4:]
tempPath = full_imagePath[:-4]
with open(f"{tempPath}_mod{ext}", 'wb') as test_image_file:
test_image_file.write(img.get_file())
os.remove(full_imagePath)
print(f"{percentage:.2f}% --> Changed GPS Altitude of: {full_imagePath[-12:-4]}")
print("100.00% --> Done!!")
this code should work with all types of .SRT files from DJI drones, but in case it doesn't work, you can simply change the regex pattern in this line:
Python:
pattern = r'\[latitude: ([\d.-]+)\] \[longitude: ([\d.-]+)\] \[altitude: ([\d.-]+)\]'
let me know if this tool is helpful and any future improvements/features i can add to it