4 Commits

3 changed files with 45 additions and 30 deletions

View File

@@ -1,7 +1,7 @@
# Localization file for video2geoframes.py script # Localization file for video2geoframes.py script
# English (US / World) # English (US / World)
# #
# Last edition : 2024-06-22 # Last edition : 2024-06-23
[ui] [ui]
@@ -12,6 +12,7 @@ This script is designed to create geotagged frames from video and GPX track."""
end = "End of program, press Enter to quit." end = "End of program, press Enter to quit."
paths_title = "## Paths" paths_title = "## Paths"
parameters_title = "## Process parameters" parameters_title = "## Process parameters"
tags_title = "## Additional tags"
metadata = """{} ({} {}B)\n metadata = """{} ({} {}B)\n
- Duration : {} s\n - Duration : {} s\n
- Start time : {}.{}\n - Start time : {}.{}\n
@@ -21,16 +22,16 @@ metadata = """{} ({} {}B)\n
cv2_tqdm = 'frame(s)' cv2_tqdm = 'frame(s)'
[ui.parameters] [ui.parameters]
toml_setting² = "Setting with TOML file ({}/{}) ? " toml_setting = "Setting with TOML file ({}/{}) ? "
timelapse = "Timelapse video ({}/{}) ? " timelapse = "Timelapse video ({}/{}) ? "
timelapse_fps = "Timelapse framerate (frame/s) [{}-{}] : " timelapse_fps = "Timelapse framerate (frame/s) [{}-{}] : "
timelapse_fps_err = "Error... please enter a decimal between {} et {}." timelapse_fps_err = "Error... please enter a decimal between {} et {}."
frame_samp = "Enter the frame sampling in seconds [{}-{}] : " frame_samp = "Enter frame sampling in seconds [{}-{}] : "
frame_samp_err = "Error... please enter a decimal between {} and {}." frame_samp_err = "Error... please enter a decimal between {} and {}."
frame_height = "Enter frame height in pixels (ratio unchanged) [{}-{}] : " frame_height = "Enter output frame height in pixels (ratio unchanged) [{}-{}] : "
frame_height_err = "Error... please enter an integer between {} and {}." frame_height_err = "Error... please enter an integer between {} and {}."
video_start_datetime = "Enter video start datetime following ISO format (exemple : 2023-09-18T22:00:02.000) : " video_start_datetime = "Enter video start datetime following ISO format (exemple : 2023-09-18T22:00:02.000) : "
@@ -38,7 +39,7 @@ video_start_datetime_err = "Error... entered datetime is not valid."
rec_timezone = "Enter time offset related to UTC (example for CEST : +02:00) : " rec_timezone = "Enter time offset related to UTC (example for CEST : +02:00) : "
time_offset = "Enter time offset between video and GPX in seconds [{}-{}] : " time_offset = "Enter time offset with GPS track in seconds [{}-{}] : "
time_offset_err = "Error... please enter a decimal between {} and {}." time_offset_err = "Error... please enter a decimal between {} and {}."
[ui.paths] [ui.paths]

View File

@@ -1,7 +1,7 @@
# Localization file for video2geoframes.py script # Localization file for video2geoframes.py script
# French (France) # French (France)
# #
# Last edition : 2024-06-22 # Last edition : 2024-06-23
[ui] [ui]
@@ -11,6 +11,7 @@ intro = """Bienvenue dans le script video2geoframes.py !
Ce script permet, à partir d'une vidéo et d'une trace GPS, de créer un ensemble de photos géotaguées.""" Ce script permet, à partir d'une vidéo et d'une trace GPS, de créer un ensemble de photos géotaguées."""
end = "Fin du programme, appuyez sur Entrée pour fermer." end = "Fin du programme, appuyez sur Entrée pour fermer."
paths_title = "## Chemins" paths_title = "## Chemins"
tags_title = "## Tags additionnels"
parameters_title = "## Paramètres du traitement" parameters_title = "## Paramètres du traitement"
metadatas = """{} ({} {}B) metadatas = """{} ({} {}B)
- Durée : {} s - Durée : {} s
@@ -27,10 +28,10 @@ timelapse = "Vidéo timelapse ({}/{}) ? "
timelapse_fps = "Débit d'image du timelapse (image/s) [{}-{}] : " timelapse_fps = "Débit d'image du timelapse (image/s) [{}-{}] : "
timelapse_fps_err = "Erreur... entrez un entier entre {} et {}." timelapse_fps_err = "Erreur... entrez un entier entre {} et {}."
frame_samp = "Entrez l'espacement temporel des images en secondes [{}-{}] : " frame_samp = "Entrez l'espacement temporel en secondes entre les images [{}-{}] : "
frame_samp_err = "'Erreur... veuillez entrer un nombre décimal entre {} et {}." frame_samp_err = "'Erreur... veuillez entrer un nombre décimal entre {} et {}."
frame_height = "Entrez la hauteur des images en pixels (ratio inchangé) [{}-{}] : " frame_height = "Entrez la hauteur en pixels des images en sortie (ratio inchangé) [{}-{}] : "
frame_height_err = "Erreur... veuillez entrer un nombre entier entre {} et {}." frame_height_err = "Erreur... veuillez entrer un nombre entier entre {} et {}."
video_start_datetime = "Entrez l'horodatage du début de la vidéo au format ISO (exemple : 2023-09-18T22:00:02.000) : " video_start_datetime = "Entrez l'horodatage du début de la vidéo au format ISO (exemple : 2023-09-18T22:00:02.000) : "
@@ -38,7 +39,7 @@ video_start_datetime_err = "Erreur... l'horodatage entré est invalide."
rec_timezone = "Entrez le décalage horaire par rapport à UTC (exemple pour CEST : +02:00) : " rec_timezone = "Entrez le décalage horaire par rapport à UTC (exemple pour CEST : +02:00) : "
time_offset = "Entrez le décalage temporel entre la vidéo et le GPX en secondes [{}-{}] : " time_offset = "Entrez le décalage temporel avec la trace GPS en secondes [{}-{}] : "
time_offset_err = "Erreur... veuillez entrer un nombre décimal entre {} et {}." time_offset_err = "Erreur... veuillez entrer un nombre décimal entre {} et {}."
[ui.paths] [ui.paths]

View File

@@ -8,7 +8,7 @@ Designed for contribution to street-level imagery projects like Mapillary or Pan
__author__ = "Lucas MATHIEU (@campanu)" __author__ = "Lucas MATHIEU (@campanu)"
__license__ = "AGPL-3.0-or-later" __license__ = "AGPL-3.0-or-later"
__version__ = "2.0-alpha3" __version__ = "2.0-alpha6"
__maintainer__ = "Lucas MATHIEU (@campanu)" __maintainer__ = "Lucas MATHIEU (@campanu)"
__email__ = "campanu@luc-geo.fr" __email__ = "campanu@luc-geo.fr"
@@ -60,9 +60,13 @@ ini_file_err = False
## Default values ## Default values
locale = 'en_us' locale = 'en_us'
min_frame_samp = 0.5 min_frame_samp = 0.5
max_frame_samp = float(60) max_frame_samp = 60.0
min_timelapse_fps = 1 min_timelapse_fps = 1
max_timelapse_fps = 15 max_timelapse_fps = 15
min_frame_height = 480
max_frame_height = 9000
min_time_offset = -10.0
max_time_offset = 10.0
## Platform-dependent commands ## Platform-dependent commands
if platform.system() == 'Windows': if platform.system() == 'Windows':
@@ -154,6 +158,13 @@ else:
print('{}\n'.format(locale_toml['ui']['paths']['path_err'])) print('{}\n'.format(locale_toml['ui']['paths']['path_err']))
True True
### Video metadatas extraction
video = cv2.VideoCapture(video_path)
video_fps = video.get(cv2.CAP_PROP_FPS)
video_width = video.get(cv2.CAP_PROP_FRAME_WIDTH)
video_height = video.get(cv2.CAP_PROP_FRAME_HEIGHT)
video_total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
### GPS track file ### GPS track file
while True: while True:
try: try:
@@ -195,7 +206,6 @@ else:
else: else:
### Frame sampling parameter ### Frame sampling parameter
while True: while True:
try: try:
frame_sampling = float(input(locale_toml['ui']['parameters']['frame_samp'].format(min_frame_samp, frame_sampling = float(input(locale_toml['ui']['parameters']['frame_samp'].format(min_frame_samp,
@@ -211,8 +221,8 @@ else:
True True
## Frame height parameter ## Frame height parameter
min_frame_height = 480 if video_height <= max_frame_height:
max_frame_height = 6000 max_frame_height = int(round(video_height, 0))
while True: while True:
try: try:
@@ -221,6 +231,8 @@ else:
if max_frame_height >= frame_height >= min_frame_height: if max_frame_height >= frame_height >= min_frame_height:
break break
elif frame_height == 0:
break
else: else:
print(locale_toml['ui']['parameters']['frame_height_err'].format(min_frame_height, max_frame_height)) print(locale_toml['ui']['parameters']['frame_height_err'].format(min_frame_height, max_frame_height))
True True
@@ -242,9 +254,6 @@ else:
video_rec_timezone = input(locale_toml['ui']['parameters']['rec_timezone']) video_rec_timezone = input(locale_toml['ui']['parameters']['rec_timezone'])
### Time offset parameter ### Time offset parameter
min_time_offset = -10.0
max_time_offset = 10.0
while True: while True:
try: try:
time_offset = float(input(locale_toml['ui']['parameters']['time_offset'].format(min_time_offset, max_time_offset))) time_offset = float(input(locale_toml['ui']['parameters']['time_offset'].format(min_time_offset, max_time_offset)))
@@ -258,26 +267,22 @@ else:
print(locale_toml['ui']['parameters']['time_offset_err'].format(min_time_offset, max_time_offset)) print(locale_toml['ui']['parameters']['time_offset_err'].format(min_time_offset, max_time_offset))
True True
### User-defined metadata ## User-defined metadata
print('\n{}'.format(locale_toml['ui']['info']['tags_title']))
make = input(locale_toml['ui']['metadatas']['make']) make = input(locale_toml['ui']['metadatas']['make'])
model = input(locale_toml['ui']['metadatas']['model']) model = input(locale_toml['ui']['metadatas']['model'])
author = input(locale_toml['ui']['metadatas']['author']) author = input(locale_toml['ui']['metadatas']['author'])
# Video metadatas extraction # Video metadatas formatting
print('\n{}'.format(locale_toml['processing']['reading_metadatas'])) print('\n{}'.format(locale_toml['processing']['reading_metadatas']))
video = cv2.VideoCapture(video_path)
video_fps = video.get(cv2.CAP_PROP_FPS)
video_width = video.get(cv2.CAP_PROP_FRAME_WIDTH)
video_height = video.get(cv2.CAP_PROP_FRAME_HEIGHT)
video_total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
video_file_name = os.path.basename(video_path) video_file_name = os.path.basename(video_path)
video_file_size = byte_multiple(os.stat(video_path).st_size) video_file_size = byte_multiple(os.stat(video_path).st_size)
video_duration = video_total_frames / video_fps video_duration = video_total_frames / video_fps
video_start_datetime_obj = video_start_datetime_obj + timedelta(seconds=time_offset) video_start_datetime_obj = video_start_datetime_obj + timedelta(seconds=time_offset)
video_start_datetime = video_start_datetime_obj.strftime('%Y:%m:%d %H:%M:%S') video_start_datetime = video_start_datetime_obj.strftime('%Y-%m-%d %H:%M:%S')
video_start_subsectime = video_start_datetime_obj.strftime('%f') video_start_subsectime = video_start_datetime_obj.strftime('%f')
# Metadata recap # Metadata recap
@@ -302,7 +307,7 @@ if timelapse == user_agree:
else: else:
frame_interval = frame_sampling frame_interval = frame_sampling
cv2_tqdm_unit = " {}".format(locale_toml['ui']['units']['cv2_tqdm']) cv2_tqdm_unit = locale_toml['ui']['units']['cv2_tqdm']
cv2_tqdm_range = int(video_duration / frame_interval) cv2_tqdm_range = int(video_duration / frame_interval)
for i in tqdm(range(cv2_tqdm_range), unit=cv2_tqdm_unit): for i in tqdm(range(cv2_tqdm_range), unit=cv2_tqdm_unit):
@@ -310,13 +315,21 @@ for i in tqdm(range(cv2_tqdm_range), unit=cv2_tqdm_unit):
video.set(cv2.CAP_PROP_POS_MSEC, t) video.set(cv2.CAP_PROP_POS_MSEC, t)
ret, frame = video.read() ret, frame = video.read()
### Image resizing
if frame_height != 0:
resize_factor = video_height / frame_height
image_height = frame_height
image_width = int(round(video_height * resize_factor), 0)
frame = cv2.resize(frame, (image_width, image_height), interpolation=cv2.INTER_LANCZOS4)
frame_name = '{:05d}'.format(i) frame_name = '{:05d}'.format(i)
image_name = "{}_f{}.jpg".format(video_file_name.split('.')[0], frame_name) image_name = "{}_f{}.jpg".format(video_file_name.split('.')[0], frame_name)
image_path = "{}/{}".format(output_folder, image_name) image_path = "{}/{}".format(output_folder, image_name)
cv2.imwrite(image_path, frame, [cv2.IMWRITE_JPEG_QUALITY, 88, cv2.IMWRITE_JPEG_PROGRESSIVE, 1, cv2.IMWRITE_JPEG_SAMPLING_FACTOR, 0x411111]) cv2.imwrite(image_path, frame, [cv2.IMWRITE_JPEG_QUALITY, 88, cv2.IMWRITE_JPEG_PROGRESSIVE, 1, cv2.IMWRITE_JPEG_SAMPLING_FACTOR, 0x411111])
## Time tags preparation ## Time tags formatting
time_shift = i * frame_sampling time_shift = i * frame_sampling
current_datetime_obj = video_start_datetime_obj + timedelta(seconds=time_shift) current_datetime_obj = video_start_datetime_obj + timedelta(seconds=time_shift)
current_datetime = current_datetime_obj.strftime('%Y:%m:%d %H:%M:%S') current_datetime = current_datetime_obj.strftime('%Y:%m:%d %H:%M:%S')
@@ -346,7 +359,7 @@ for i in tqdm(range(cv2_tqdm_range), unit=cv2_tqdm_unit):
piexif.ImageIFD.Model: model, piexif.ImageIFD.Model: model,
piexif.ImageIFD.Artist: author, piexif.ImageIFD.Artist: author,
piexif.ImageIFD.Copyright: "{}, {}".format(author, video_start_datetime_obj.strftime('%Y')), piexif.ImageIFD.Copyright: "{}, {}".format(author, video_start_datetime_obj.strftime('%Y')),
piexif.ImageIFD.Software : 'video2geoframes.py' piexif.ImageIFD.Software: 'video2geoframes.py (v{})'.format(__version__)
} }
exif_tags = { exif_tags = {