Compare commits
9 Commits
8bf7d1946c
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
| 904ba30817 | |||
| 21361341f8 | |||
| a273dbd161 | |||
| 0cb9d1e877 | |||
| 8579b26955 | |||
| 7049547815 | |||
| 03cd0b9dd2 | |||
| 2e50f86b57 | |||
| a0e097b877 |
@@ -45,7 +45,7 @@ It also includes :
|
|||||||
|
|
||||||
### Features v1 / v2
|
### Features v1 / v2
|
||||||
|
|
||||||
| Features | v1-beta | v2-alpha9 |
|
| Features | v1-beta | v2-alpha10 |
|
||||||
|-----------------------------|----------------|------------|
|
|-----------------------------|----------------|------------|
|
||||||
| Timelapse video support | ✔️ | ✔️ |
|
| Timelapse video support | ✔️ | ✔️ |
|
||||||
| EXIF tags writing | ✔️ | ✔️ |
|
| EXIF tags writing | ✔️ | ✔️ |
|
||||||
@@ -55,7 +55,7 @@ It also includes :
|
|||||||
| Multilingual TUI 🇺🇳 | 🟡 2 languages | ✔️ |
|
| Multilingual TUI 🇺🇳 | 🟡 2 languages | ✔️ |
|
||||||
| Configuration customization | ❌ | 🟡 partial |
|
| Configuration customization | ❌ | 🟡 partial |
|
||||||
| JPEG quality customization | ❌ | 🔄 planned |
|
| JPEG quality customization | ❌ | 🔄 planned |
|
||||||
| TOML process setting | ❌ | 🔄 planned |
|
| TOML process setting | ❌ | ✔️ |
|
||||||
|
|
||||||
## Languages
|
## Languages
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ Lors de l'export, un sous-dossier nommé selon la vidéo est créé automatiquem
|
|||||||
|
|
||||||
### Fonctionnalités v1 / v2
|
### Fonctionnalités v1 / v2
|
||||||
|
|
||||||
| Fonctionnalité | v1-beta | v2-alpha9 |
|
| Fonctionnalité | v1-beta | v2-alpha10 |
|
||||||
|--------------------------------------|--------------|--------------|
|
|--------------------------------------|--------------|--------------|
|
||||||
| Support des vidéos timelapse | ✔️ | ✔️ |
|
| Support des vidéos timelapse | ✔️ | ✔️ |
|
||||||
| Écriture des tags EXIF | ✔️ | ✔️ |
|
| Écriture des tags EXIF | ✔️ | ✔️ |
|
||||||
@@ -57,7 +57,7 @@ Lors de l'export, un sous-dossier nommé selon la vidéo est créé automatiquem
|
|||||||
| TUI multilingue 🇺🇳 | 🟡 2 langues | ✔️ |
|
| TUI multilingue 🇺🇳 | 🟡 2 langues | ✔️ |
|
||||||
| Personnalisation de la configuration | ❌ | 🟡 partielle |
|
| Personnalisation de la configuration | ❌ | 🟡 partielle |
|
||||||
| Personnalisation qualité JPEG | ❌ | 🔄 planifiée |
|
| Personnalisation qualité JPEG | ❌ | 🔄 planifiée |
|
||||||
| Paramétrage du traitement via TOML | ❌ | 🔄 planifié |
|
| Paramétrage du traitement via TOML | ❌ | ✔️ |
|
||||||
|
|
||||||
## Langues
|
## Langues
|
||||||
|
|
||||||
|
|||||||
32
examples/process_setting.toml
Normal file
32
examples/process_setting.toml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Example process setting file for video2geoframes.py
|
||||||
|
# See documentation for more information
|
||||||
|
#
|
||||||
|
# Last edition : 2024-07-01
|
||||||
|
|
||||||
|
# Mandatory section
|
||||||
|
[paths]
|
||||||
|
video_file = "one/video/file.mp4"
|
||||||
|
gps_track_file = "one/track_gps/file.gpx"
|
||||||
|
output_folder = "one/folder"
|
||||||
|
|
||||||
|
# Mandatory section
|
||||||
|
[video]
|
||||||
|
# For timelapse video, set video.timelapse = [true, x] (x = framerate)
|
||||||
|
# Otherwise, set video.timelapse = [false, 0]
|
||||||
|
timelapse = [false, 0]
|
||||||
|
start_datetime = 2024-06-30T23:32:00.000
|
||||||
|
rec_timezone = "+02:00"
|
||||||
|
|
||||||
|
[process_settings]
|
||||||
|
# Mandatory setting, but ignored if video.timelapse = [true, x]
|
||||||
|
frame_sampling = 1
|
||||||
|
|
||||||
|
# Optional settings, ignored if set to 0
|
||||||
|
frame_height = 0
|
||||||
|
time_offset = +0.0
|
||||||
|
|
||||||
|
# Mandatory section
|
||||||
|
[metadata]
|
||||||
|
author = "Name or alias"
|
||||||
|
camera_maker = "One"
|
||||||
|
camera_model = "Super Cam"
|
||||||
@@ -1,26 +1,40 @@
|
|||||||
# Localization file for video2geoframes.py
|
# Localization file for video2geoframes.py
|
||||||
# English (US / World)
|
# English (US / World)
|
||||||
#
|
#
|
||||||
# Last edition : 2024-06-23
|
# Last edition : 2024-07-01
|
||||||
|
|
||||||
[ui]
|
[ui]
|
||||||
|
|
||||||
[ui.info]
|
[ui.info]
|
||||||
title = "# video2geoframes.py"
|
title = "# video2geoframes.py"
|
||||||
intro = """Welcome in video2geoframes.py script !
|
intro = """Welcome in video2geoframes.py script !
|
||||||
This script is designed to create geotagged frames from video and GPX track."""
|
This script is designed to create geotagged frames from video and GPS track."""
|
||||||
end = "End of program, press Enter to quit."
|
end = "End of program, press Enter to quit."
|
||||||
|
cancel = "Cancelling... empty input on required setting."
|
||||||
paths_title = "## Paths"
|
paths_title = "## Paths"
|
||||||
parameters_title = "## Process parameters"
|
parameters_title = "## Process parameters"
|
||||||
tags_title = "## Additional tags"
|
tags_title = "## Additional tags"
|
||||||
metadata = """{} ({} {}B)\n
|
metadata = """{} ({} {}B)
|
||||||
- Duration : {} s\n
|
- Duration : {} s
|
||||||
- Start time : {}.{}\n
|
- Start time : {}.{}
|
||||||
- Time offset : {}"""
|
- Time offset : {}"""
|
||||||
|
|
||||||
[ui.units]
|
[ui.error]
|
||||||
|
not_implemented = "Sorry, this function is not implemented, work in progress ;)"
|
||||||
|
file_not_found = "'{}' not found."
|
||||||
|
invalid_toml_key = "Error... invalid key found in TOML file, please check values."
|
||||||
|
|
||||||
|
[ui.unit]
|
||||||
cv2_tqdm = 'frame(s)'
|
cv2_tqdm = 'frame(s)'
|
||||||
|
|
||||||
|
[ui.toml_setting]
|
||||||
|
incomplete_err = "{} {} missing in TOML setting file."
|
||||||
|
video_file = "Video file : {}"
|
||||||
|
gps_track_file = "GPS track file : {}"
|
||||||
|
timelapse_mode = "{} fps timelapse"
|
||||||
|
classic_mode = "{} s sampling"
|
||||||
|
resizing = "Resizing from {}p to {}p"
|
||||||
|
|
||||||
[ui.parameters]
|
[ui.parameters]
|
||||||
toml_setting = "Setting with TOML file ({}/{}) ? "
|
toml_setting = "Setting with TOML file ({}/{}) ? "
|
||||||
|
|
||||||
@@ -48,15 +62,16 @@ video_file = "Enter video path : "
|
|||||||
gps_track = "Enter GPS track path : "
|
gps_track = "Enter GPS track path : "
|
||||||
output_folder = "Enter output folder : "
|
output_folder = "Enter output folder : "
|
||||||
|
|
||||||
path_err = "Error... File doesn't exist."
|
path_err = "Error... file doesn't exist."
|
||||||
|
|
||||||
[ui.metadatas]
|
[ui.metadata]
|
||||||
make = "Enter the camera brand : "
|
make = "Enter the camera brand : "
|
||||||
model = "Enter the camera model : "
|
model = "Enter the camera model : "
|
||||||
author = "Enter author name : "
|
author = "Enter author name : "
|
||||||
|
|
||||||
[processing]
|
[processing]
|
||||||
reading_metadatas = "Reading video metadatas..."
|
reading_toml_setting = "Reading TOML setting file..."
|
||||||
|
reading_metadata = "Reading video metadata..."
|
||||||
sampling = "Extracting frames from video..."
|
sampling = "Extracting frames from video..."
|
||||||
timestamping = "Setting timestamp on frames..."
|
timestamping = "Setting timestamp on frames..."
|
||||||
geotagging = "Geotagging frames..."
|
geotagging = "Geotagging frames..."
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Localization file for video2geoframes.py
|
# Localization file for video2geoframes.py
|
||||||
# French (France)
|
# French (France)
|
||||||
#
|
#
|
||||||
# Last edition : 2024-06-23
|
# Last edition : 2024-07-01
|
||||||
|
|
||||||
[ui]
|
[ui]
|
||||||
|
|
||||||
@@ -10,17 +10,31 @@ title = "# video2geoframes.py"
|
|||||||
intro = """Bienvenue dans le script video2geoframes.py !
|
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."
|
||||||
|
cancel = "Annulation... entrée vide sur un paramètre requis."
|
||||||
paths_title = "## Chemins"
|
paths_title = "## Chemins"
|
||||||
tags_title = "## Tags additionnels"
|
tags_title = "## Tags additionnels"
|
||||||
parameters_title = "## Paramètres du traitement"
|
parameters_title = "## Paramètres du traitement"
|
||||||
metadatas = """{} ({} {}B)
|
metadata = """{} ({} {}B)
|
||||||
- Durée : {} s
|
- Durée : {} s
|
||||||
- Heure de début : {}.{}
|
- Heure de début : {}.{}
|
||||||
- Décalage horaire : {}"""
|
- Décalage horaire : {}"""
|
||||||
|
|
||||||
[ui.units]
|
[ui.error]
|
||||||
|
not_implemented = "Désolé, cette fonction n'est pas encore implémentée, chantier en cours ;)"
|
||||||
|
file_not_found = "'{}' non-trouvé."
|
||||||
|
invalid_toml_key = "Erreur... clé invalide trouvée dans le fichier TOML, vérifiez les valeurs."
|
||||||
|
|
||||||
|
[ui.unit]
|
||||||
cv2_tqdm = 'image(s)'
|
cv2_tqdm = 'image(s)'
|
||||||
|
|
||||||
|
[ui.toml_setting]
|
||||||
|
incomplete_err = "{} {} manquant(s) dans le fichier TOML de paramétrage."
|
||||||
|
video_file = "Fichier vidéo : {}"
|
||||||
|
gps_track_file = "Fichier de trace GPS : {}"
|
||||||
|
timelapse_mode = "Timelapse à {} ips"
|
||||||
|
classic_mode = "Échantillonnage de {} s"
|
||||||
|
resizing = "Redimensionnement de {}p vers {}p"
|
||||||
|
|
||||||
[ui.parameters]
|
[ui.parameters]
|
||||||
toml_setting = "Paramétrage avec un fichier TOML ({}/{}) ? "
|
toml_setting = "Paramétrage avec un fichier TOML ({}/{}) ? "
|
||||||
|
|
||||||
@@ -50,13 +64,14 @@ output_folder = "Entrez le dossier de sortie : "
|
|||||||
|
|
||||||
path_err = "Erreur... le fichier n'existe pas."
|
path_err = "Erreur... le fichier n'existe pas."
|
||||||
|
|
||||||
[ui.metadatas]
|
[ui.metadata]
|
||||||
make = "Entrez la marque du capteur : "
|
make = "Entrez la marque du capteur : "
|
||||||
model = "Entrez le modèle du capteur : "
|
model = "Entrez le modèle du capteur : "
|
||||||
author = "Entrez l'auteur : "
|
author = "Entrez l'auteur : "
|
||||||
|
|
||||||
[processing]
|
[processing]
|
||||||
reading_metadatas = "Lecture des métadonnées de la vidéo..."
|
reading_toml_setting = "Lecture du fichier TOML de paramétrage..."
|
||||||
|
reading_metadata = "Lecture des métadonnées de la vidéo..."
|
||||||
sampling = "Extraction des images depuis la vidéo..."
|
sampling = "Extraction des images depuis la vidéo..."
|
||||||
timestamping = "Définition de l'horodatage des images..."
|
timestamping = "Définition de l'horodatage des images..."
|
||||||
geotagging = "Géotaguage des images..."
|
geotagging = "Géotaguage des images..."
|
||||||
|
|||||||
@@ -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__ = "GPL-3.0-or-later"
|
__license__ = "GPL-3.0-or-later"
|
||||||
__version__ = "2.0-alpha9"
|
__version__ = "2.0-alpha12"
|
||||||
__maintainer__ = "Lucas MATHIEU (@campanu)"
|
__maintainer__ = "Lucas MATHIEU (@campanu)"
|
||||||
__email__ = "campanu@luc-geo.fr"
|
__email__ = "campanu@luc-geo.fr"
|
||||||
|
|
||||||
@@ -71,6 +71,7 @@ def existing_items(expected_items: list, items: list):
|
|||||||
|
|
||||||
def list_enumerator(item_list: list, intermediate_separator: str, last_separator: str):
|
def list_enumerator(item_list: list, intermediate_separator: str, last_separator: str):
|
||||||
i = 1
|
i = 1
|
||||||
|
enumerated_list = []
|
||||||
|
|
||||||
for it in item_list:
|
for it in item_list:
|
||||||
if i == 1:
|
if i == 1:
|
||||||
@@ -85,6 +86,19 @@ def list_enumerator(item_list: list, intermediate_separator: str, last_separator
|
|||||||
return enumerated_list
|
return enumerated_list
|
||||||
|
|
||||||
|
|
||||||
|
def video_metadata_reader(video_path: str):
|
||||||
|
video_md = {}
|
||||||
|
|
||||||
|
video = cv2.VideoCapture(video_path)
|
||||||
|
video_md['fps'] = video.get(cv2.CAP_PROP_FPS)
|
||||||
|
video_md['width'] = video.get(cv2.CAP_PROP_FRAME_WIDTH)
|
||||||
|
video_md['height'] = video.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
||||||
|
video_md['frame_number'] = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||||
|
video = None
|
||||||
|
|
||||||
|
return video_md
|
||||||
|
|
||||||
|
|
||||||
# Start
|
# Start
|
||||||
print("# video2geoframes.py (v{})\n".format(__version__))
|
print("# video2geoframes.py (v{})\n".format(__version__))
|
||||||
|
|
||||||
@@ -95,6 +109,11 @@ conf_file_err = False
|
|||||||
|
|
||||||
# Default values
|
# Default values
|
||||||
mandatory_parameters = ['locale', 'exiftool_path']
|
mandatory_parameters = ['locale', 'exiftool_path']
|
||||||
|
mandatory_settings1 = ['video_file', 'gps_track_file', 'output_folder']
|
||||||
|
mandatory_settings2 = ['frame_sampling']
|
||||||
|
mandatory_settings3 = ['timelapse', 'start_datetime', 'rec_timezone']
|
||||||
|
optional_settings = ['frame_height', 'time_offset']
|
||||||
|
minimal_md = ['author', 'camera_maker', 'camera_model']
|
||||||
locale = 'en_us'
|
locale = 'en_us'
|
||||||
min_frame_samp = 0.5
|
min_frame_samp = 0.5
|
||||||
max_frame_samp = 60.0
|
max_frame_samp = 60.0
|
||||||
@@ -135,10 +154,10 @@ try:
|
|||||||
|
|
||||||
print("(!) {} {} missing in configuration file.".format(missing_parameters_list, verb))
|
print("(!) {} {} missing in configuration file.".format(missing_parameters_list, verb))
|
||||||
raise ValueError
|
raise ValueError
|
||||||
|
else:
|
||||||
# Configuration assignment
|
# Configuration assignment
|
||||||
locale = conf_toml['system']['locale']
|
locale = conf_toml['system']['locale']
|
||||||
exiftool_path = unix_path(conf_toml['system']['exiftool_path']).replace('./', '{}/'.format(base_path))
|
exiftool_path = unix_path(conf_toml['system']['exiftool_path']).replace('./', '{}/'.format(base_path))
|
||||||
except (FileNotFoundError, ValueError):
|
except (FileNotFoundError, ValueError):
|
||||||
print("\nError... configuration file doesn't exists or invalid.")
|
print("\nError... configuration file doesn't exists or invalid.")
|
||||||
default_conf = str(input("Use default configuration instead (Y/N) ? ").upper())
|
default_conf = str(input("Use default configuration instead (Y/N) ? ").upper())
|
||||||
@@ -167,28 +186,145 @@ try:
|
|||||||
# User input
|
# User input
|
||||||
# TOML setting file
|
# TOML setting file
|
||||||
toml_setting = input('\n{}'.format(locale_toml['ui']['parameters']['toml_setting'].format(user_agree, user_disagree)))
|
toml_setting = input('\n{}'.format(locale_toml['ui']['parameters']['toml_setting'].format(user_agree, user_disagree)))
|
||||||
i = 0
|
|
||||||
|
|
||||||
if toml_setting.upper() == 'O':
|
if toml_setting.upper() == user_agree:
|
||||||
|
# TOML setting file path input
|
||||||
while True:
|
while True:
|
||||||
try:
|
toml_setting_path = unix_path(input('{}'.format(locale_toml['ui']['paths']['toml_file']))).strip()
|
||||||
i += 1
|
|
||||||
toml_file_path = unix_path(input('{}'.format(locale_toml['ui']['paths']['toml_file']))).strip()
|
|
||||||
|
|
||||||
if os.path.exists(toml_file_path):
|
if os.path.exists(toml_setting_path):
|
||||||
with codecs.open(toml_file_path, mode='r', encoding='utf-8') as f:
|
break
|
||||||
setting_toml = loads(f.read())
|
elif toml_setting_path == '':
|
||||||
f.close()
|
print('{}'.format(locale_toml['ui']['info']['cancel']))
|
||||||
break
|
raise InterruptedError
|
||||||
else:
|
else:
|
||||||
raise FileNotFoundError
|
|
||||||
except (FileNotFoundError, ValueError):
|
|
||||||
print('{}\n'.format(locale_toml['ui']['paths']['path_err']))
|
print('{}\n'.format(locale_toml['ui']['paths']['path_err']))
|
||||||
True
|
True
|
||||||
|
|
||||||
# <--coding in progress-->
|
# TOML file checking
|
||||||
raise NotImplementedError
|
print("\n{}".format(locale_toml['processing']['reading_toml_setting']))
|
||||||
|
|
||||||
|
try:
|
||||||
|
if os.path.exists(toml_setting_path):
|
||||||
|
with codecs.open(toml_setting_path, mode='r', encoding='utf-8') as f:
|
||||||
|
setting_toml = loads(f.read())
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
reading_settings1 = setting_toml['paths'].keys()
|
||||||
|
reading_settings2 = setting_toml['process_settings'].keys()
|
||||||
|
reading_settings3 = setting_toml['video'].keys()
|
||||||
|
reading_metadata = setting_toml['metadata'].keys()
|
||||||
|
|
||||||
|
check_settings1 = existing_items(mandatory_settings1, reading_settings1)
|
||||||
|
check_settings2 = existing_items(mandatory_settings2, reading_settings2)
|
||||||
|
check_settings3 = existing_items(mandatory_settings3, reading_settings3)
|
||||||
|
check_metadata = existing_items(minimal_md, reading_metadata)
|
||||||
|
|
||||||
|
missing_settings = []
|
||||||
|
missing_settings.extend(check_settings1['missing'])
|
||||||
|
missing_settings.extend(check_settings2['missing'])
|
||||||
|
missing_settings.extend(check_settings3['missing'])
|
||||||
|
|
||||||
|
missing_metadata = check_metadata['missing']
|
||||||
|
|
||||||
|
if len(missing_settings) > 0:
|
||||||
|
missing_settings_list = list_enumerator(missing_settings, ', ', ' and ')
|
||||||
|
|
||||||
|
if len(missing_settings) == 1:
|
||||||
|
verb = 'is'
|
||||||
|
else:
|
||||||
|
verb = 'are'
|
||||||
|
|
||||||
|
print("(!) {}".format(locale_toml['ui']['toml_setting']['incomplete_err'].format(missing_settings_list, verb)))
|
||||||
|
raise ValueError
|
||||||
|
else:
|
||||||
|
video_path = unix_path(setting_toml['paths']['video_file'])
|
||||||
|
|
||||||
|
if os.path.exists(video_path):
|
||||||
|
print('> {}'.format(locale_toml['ui']['toml_setting']['video_file'].format(video_path)))
|
||||||
|
video_md = video_metadata_reader(video_path)
|
||||||
|
|
||||||
|
if video_md['frame_number'] > 0:
|
||||||
|
video_fps = video_md['fps']
|
||||||
|
video_width = video_md['width']
|
||||||
|
video_height = video_md['height']
|
||||||
|
video_frame_number = video_md['frame_number']
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
else:
|
||||||
|
current_file = video_path
|
||||||
|
raise FileNotFoundError
|
||||||
|
|
||||||
|
gps_track_path = unix_path(setting_toml['paths']['gps_track_file'])
|
||||||
|
|
||||||
|
if os.path.exists(gps_track_path):
|
||||||
|
print('> {}'.format(locale_toml['ui']['toml_setting']['gps_track_file'].format(gps_track_path)))
|
||||||
|
else:
|
||||||
|
current_file = gps_track_path
|
||||||
|
raise FileNotFoundError
|
||||||
|
|
||||||
|
output_folder = unix_path(setting_toml['paths']['output_folder'])
|
||||||
|
|
||||||
|
timelapse = bool(setting_toml['video']['timelapse'][0])
|
||||||
|
timelapse_fps = int(setting_toml['video']['timelapse'][1])
|
||||||
|
|
||||||
|
if timelapse and min_timelapse_fps <= timelapse_fps <= max_timelapse_fps:
|
||||||
|
print("> {}".format(locale_toml['ui']['toml_setting']['timelapse_mode'].format(timelapse_fps)))
|
||||||
|
elif timelapse and (timelapse_fps < min_timelapse_fps or timelapse_fps > max_timelapse_fps):
|
||||||
|
raise ValueError
|
||||||
|
else:
|
||||||
|
frame_sampling = float(setting_toml['process_settings']['frame_sampling'])
|
||||||
|
|
||||||
|
if min_frame_samp <= frame_sampling <= max_frame_samp:
|
||||||
|
print("> {}".format(locale_toml['ui']['toml_setting']['classic_mode'].format(frame_sampling)))
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
video_start_datetime_obj = setting_toml['video']['start_datetime']
|
||||||
|
video_rec_timezone = str(setting_toml['video']['rec_timezone'])
|
||||||
|
|
||||||
|
if 'time_offset' in setting_toml['process_settings']:
|
||||||
|
time_offset = setting_toml['process_settings']['time_offset']
|
||||||
|
|
||||||
|
if time_offset != 0:
|
||||||
|
if min_time_offset <= time_offset <= max_time_offset:
|
||||||
|
time_offset = float(time_offset)
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
else:
|
||||||
|
time_offset = 0
|
||||||
|
|
||||||
|
if video_height <= max_frame_height:
|
||||||
|
max_frame_height = int(round(video_height, 0))
|
||||||
|
|
||||||
|
if 'frame_height' in setting_toml['process_settings']:
|
||||||
|
frame_height = int(setting_toml['process_settings']['frame_height'])
|
||||||
|
|
||||||
|
if min_frame_height <= frame_height <= max_frame_height:
|
||||||
|
print("> {}".format(locale_toml['ui']['toml_setting']['resizing'].format(video_height, frame_height)))
|
||||||
|
pass
|
||||||
|
elif frame_height == 0 or frame_height > max_frame_height:
|
||||||
|
frame_height = max_frame_height
|
||||||
|
elif frame_height < min_frame_height:
|
||||||
|
print("> {}".format(locale_toml['ui']['toml_setting']['resizing'].format(video_height, min_frame_height)))
|
||||||
|
frame_height = min_frame_height
|
||||||
|
else:
|
||||||
|
print("> {}".format(locale_toml['ui']['toml_setting']['resizing'].format(video_height, max_frame_height)))
|
||||||
|
frame_height = max_frame_height
|
||||||
|
|
||||||
|
if len(missing_metadata) > 0:
|
||||||
|
raise ValueError
|
||||||
|
else:
|
||||||
|
author = setting_toml['metadata']['author']
|
||||||
|
make = setting_toml['metadata']['camera_maker']
|
||||||
|
model = setting_toml['metadata']['camera_model']
|
||||||
|
else:
|
||||||
|
current_file = toml_setting_path
|
||||||
|
raise FileNotFoundError
|
||||||
|
except FileNotFoundError:
|
||||||
|
print('(!) {}'.format(locale_toml['ui']['error']['file_not_found'].format(current_file)))
|
||||||
|
except ValueError:
|
||||||
|
print('{}'.format(locale_toml['ui']['error']['invalid_toml_key']))
|
||||||
# Paths
|
# Paths
|
||||||
else:
|
else:
|
||||||
print('\n{}'.format(locale_toml['ui']['info']['paths_title']))
|
print('\n{}'.format(locale_toml['ui']['info']['paths_title']))
|
||||||
@@ -206,12 +342,12 @@ try:
|
|||||||
print('{}\n'.format(locale_toml['ui']['paths']['path_err']))
|
print('{}\n'.format(locale_toml['ui']['paths']['path_err']))
|
||||||
True
|
True
|
||||||
|
|
||||||
# Video metadatas extraction
|
# Video metadata extraction
|
||||||
video = cv2.VideoCapture(video_path)
|
video_md = video_metadata_reader(video_path)
|
||||||
video_fps = video.get(cv2.CAP_PROP_FPS)
|
video_fps = video_md['fps']
|
||||||
video_width = video.get(cv2.CAP_PROP_FRAME_WIDTH)
|
video_width = video_md['width']
|
||||||
video_height = video.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
video_height = video_md['height']
|
||||||
video_total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
|
video_frame_number = video_md['frame_number']
|
||||||
|
|
||||||
# GPS track file
|
# GPS track file
|
||||||
while True:
|
while True:
|
||||||
@@ -236,13 +372,14 @@ try:
|
|||||||
timelapse = input(locale_toml['ui']['parameters']['timelapse'].format(user_agree, user_disagree)).upper()
|
timelapse = input(locale_toml['ui']['parameters']['timelapse'].format(user_agree, user_disagree)).upper()
|
||||||
|
|
||||||
if timelapse == user_agree:
|
if timelapse == user_agree:
|
||||||
|
timelapse = True
|
||||||
|
|
||||||
# Timelapse framerate parameter
|
# Timelapse framerate parameter
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
timelapse_fps = int(input(locale_toml['ui']['parameters']['timelapse_fps'].format(min_timelapse_fps,
|
timelapse_fps = int(input(locale_toml['ui']['parameters']['timelapse_fps'].format(min_timelapse_fps,
|
||||||
max_timelapse_fps)))
|
max_timelapse_fps)))
|
||||||
if max_timelapse_fps >= timelapse_fps >= min_timelapse_fps:
|
if max_timelapse_fps >= timelapse_fps >= min_timelapse_fps:
|
||||||
frame_sampling = float(1 / timelapse_fps)
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
print('\n{}'.format(locale_toml['ui']['parameters']['timelapse_fps_err'].format(min_timelapse_fps,
|
print('\n{}'.format(locale_toml['ui']['parameters']['timelapse_fps_err'].format(min_timelapse_fps,
|
||||||
@@ -251,7 +388,6 @@ try:
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
print('\n{}'.format(locale_toml['ui']['parameters']['timelapse_fps_err'].format(min_timelapse_fps, max_timelapse_fps)))
|
print('\n{}'.format(locale_toml['ui']['parameters']['timelapse_fps_err'].format(min_timelapse_fps, max_timelapse_fps)))
|
||||||
True
|
True
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Frame sampling parameter
|
# Frame sampling parameter
|
||||||
while True:
|
while True:
|
||||||
@@ -319,23 +455,24 @@ try:
|
|||||||
# User-defined metadata
|
# User-defined metadata
|
||||||
print('\n{}'.format(locale_toml['ui']['info']['tags_title']))
|
print('\n{}'.format(locale_toml['ui']['info']['tags_title']))
|
||||||
|
|
||||||
make = input(locale_toml['ui']['metadatas']['make'])
|
make = input(locale_toml['ui']['metadata']['make'])
|
||||||
model = input(locale_toml['ui']['metadatas']['model'])
|
model = input(locale_toml['ui']['metadata']['model'])
|
||||||
author = input(locale_toml['ui']['metadatas']['author'])
|
author = input(locale_toml['ui']['metadata']['author'])
|
||||||
|
|
||||||
# Video metadatas formatting
|
# Video metadata formatting
|
||||||
print('\n{}'.format(locale_toml['processing']['reading_metadatas']))
|
print('\n{}'.format(locale_toml['processing']['reading_metadata']))
|
||||||
|
video = cv2.VideoCapture(video_path)
|
||||||
|
|
||||||
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_frame_number / 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 = int(int(video_start_datetime_obj.strftime('%f')) / 1000)
|
video_start_subsectime = int(int(video_start_datetime_obj.strftime('%f')) / 1000)
|
||||||
|
|
||||||
# Metadatas recap
|
# metadata recap
|
||||||
print('\n{}'.format(locale_toml['ui']['info']['metadatas'].format(video_file_name,
|
print('\n{}'.format(locale_toml['ui']['info']['metadata'].format(video_file_name,
|
||||||
round(video_file_size[0], 3), video_file_size[1],
|
round(video_file_size[0], 3), video_file_size[1],
|
||||||
video_duration, video_start_datetime,
|
video_duration, video_start_datetime,
|
||||||
'{:03d}'.format(video_start_subsectime),
|
'{:03d}'.format(video_start_subsectime),
|
||||||
@@ -351,24 +488,25 @@ try:
|
|||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
|
|
||||||
if timelapse == user_agree:
|
if timelapse == True:
|
||||||
frame_interval = frame_sampling / video_fps
|
frame_sampling = float(1 / video_fps)
|
||||||
|
frame_interval = float(1 / timelapse_fps)
|
||||||
else:
|
else:
|
||||||
frame_interval = frame_sampling
|
frame_interval = frame_sampling
|
||||||
|
|
||||||
cv2_tqdm_unit = locale_toml['ui']['units']['cv2_tqdm']
|
cv2_tqdm_unit = locale_toml['ui']['unit']['cv2_tqdm']
|
||||||
cv2_tqdm_range = int(video_duration / frame_interval)
|
cv2_tqdm_range = int(video_duration / frame_sampling)
|
||||||
|
|
||||||
for i in tqdm(range(cv2_tqdm_range), unit=cv2_tqdm_unit):
|
for i in tqdm(range(cv2_tqdm_range), unit=cv2_tqdm_unit):
|
||||||
t = frame_interval * i * 1000
|
t = frame_sampling * i * 1000
|
||||||
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
|
# Image resizing
|
||||||
if frame_height != 0:
|
if frame_height != video_height:
|
||||||
resize_factor = video_height / frame_height
|
resize_factor = frame_height / video_height
|
||||||
image_height = frame_height
|
image_height = frame_height
|
||||||
image_width = int(round(video_height * resize_factor, 0))
|
image_width = int(round(video_width * resize_factor, 0))
|
||||||
|
|
||||||
frame = cv2.resize(frame, (image_width, image_height), interpolation=cv2.INTER_LANCZOS4)
|
frame = cv2.resize(frame, (image_width, image_height), interpolation=cv2.INTER_LANCZOS4)
|
||||||
|
|
||||||
@@ -376,10 +514,10 @@ try:
|
|||||||
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, 0x221111])
|
||||||
|
|
||||||
# Time tags formatting
|
# Time tags formatting
|
||||||
time_shift = i * frame_sampling
|
time_shift = i * frame_interval
|
||||||
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')
|
||||||
current_subsec_time = int(int(current_datetime_obj.strftime('%f')) / 1000)
|
current_subsec_time = int(int(current_datetime_obj.strftime('%f')) / 1000)
|
||||||
@@ -397,12 +535,10 @@ try:
|
|||||||
|
|
||||||
exif_tags = {
|
exif_tags = {
|
||||||
piexif.ExifIFD.DateTimeOriginal: current_datetime,
|
piexif.ExifIFD.DateTimeOriginal: current_datetime,
|
||||||
|
piexif.ExifIFD.SubSecTimeOriginal: str(current_subsec_time),
|
||||||
piexif.ExifIFD.OffsetTimeOriginal: video_rec_timezone
|
piexif.ExifIFD.OffsetTimeOriginal: video_rec_timezone
|
||||||
}
|
}
|
||||||
|
|
||||||
if current_subsec_time > 0:
|
|
||||||
exif_tags[piexif.ExifIFD.SubSecTime] = str(current_subsec_time)
|
|
||||||
|
|
||||||
image_exif['0th'] = image_tags
|
image_exif['0th'] = image_tags
|
||||||
image_exif['Exif'] = exif_tags
|
image_exif['Exif'] = exif_tags
|
||||||
|
|
||||||
@@ -421,7 +557,7 @@ try:
|
|||||||
# End
|
# End
|
||||||
input('\n{}'.format(locale_toml['ui']['info']['end']))
|
input('\n{}'.format(locale_toml['ui']['info']['end']))
|
||||||
except NotImplementedError:
|
except NotImplementedError:
|
||||||
print("\nSorry, this function is not implemented, work in progress ;)")
|
print('\n{}'.format(locale_toml['ui']['error']['not_implemented']))
|
||||||
except InterruptedError:
|
except InterruptedError:
|
||||||
input("\nEnd of program, press Enter to quit.")
|
input("\nEnd of program, press Enter to quit.")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user