kopia lustrzana https://github.com/OpenDroneMap/ODM
Switch to pillow + timezone fixes
rodzic
a74337f8fe
commit
02257a62cd
|
@ -1,18 +1,21 @@
|
||||||
|
from opendm.video.parameters import Parameters
|
||||||
import datetime
|
import datetime
|
||||||
|
from fractions import Fraction
|
||||||
|
import io
|
||||||
from math import floor
|
from math import floor
|
||||||
import time
|
import time
|
||||||
import cv2
|
import cv2
|
||||||
import os
|
import os
|
||||||
from opendm.video.parameters import Parameters
|
|
||||||
import pymediainfo
|
import pymediainfo
|
||||||
import collections
|
import collections
|
||||||
from exif import Image, Orientation
|
from PIL import Image
|
||||||
from checkers import BlackFrameChecker, PercentageBlurChecker, SimilarityChecker, ThresholdBlurChecker
|
from checkers import BlackFrameChecker, PercentageBlurChecker, SimilarityChecker, ThresholdBlurChecker
|
||||||
from srtparser import SrtFileParser
|
from srtparser import SrtFileParser
|
||||||
|
import piexif
|
||||||
|
|
||||||
class Video2Dataset:
|
class Video2Dataset:
|
||||||
|
|
||||||
def __init__(self, parameters: Parameters):
|
def __init__(self, parameters : Parameters):
|
||||||
self.parameters = parameters
|
self.parameters = parameters
|
||||||
|
|
||||||
# We prioritize blur threshold over blur percentage.
|
# We prioritize blur threshold over blur percentage.
|
||||||
|
@ -46,20 +49,25 @@ class Video2Dataset:
|
||||||
|
|
||||||
# get file name
|
# get file name
|
||||||
file_name = os.path.basename(input_file)
|
file_name = os.path.basename(input_file)
|
||||||
|
|
||||||
print("Processing video: {}".format(input_file))
|
print("Processing video: {}".format(input_file))
|
||||||
|
|
||||||
|
# get video info
|
||||||
|
video_info = self.GetVideoInfo(input_file)
|
||||||
|
print(video_info)
|
||||||
|
|
||||||
|
utc_offset = self.parameters.utc_offset if self.parameters.utc_offset is not None else video_info.utc_offset
|
||||||
|
|
||||||
if self.parameters.use_srt:
|
if self.parameters.use_srt:
|
||||||
srt_file = os.path.splitext(input_file)[0] + ".srt"
|
srt_file = os.path.splitext(input_file)[0] + ".srt"
|
||||||
if os.path.exists(srt_file):
|
if os.path.exists(srt_file):
|
||||||
print("Loading SRT file: {}".format(srt_file))
|
print("Loading SRT file: {}".format(srt_file))
|
||||||
srt_parser = SrtFileParser(srt_file, self.parameters.utc_offset)
|
srt_parser = SrtFileParser(srt_file, utc_offset)
|
||||||
srt_parser.parse()
|
srt_parser.parse()
|
||||||
else:
|
else:
|
||||||
srt_file = os.path.splitext(input_file)[0] + ".SRT"
|
srt_file = os.path.splitext(input_file)[0] + ".SRT"
|
||||||
if os.path.exists(srt_file):
|
if os.path.exists(srt_file):
|
||||||
print("Loading SRT file: {}".format(srt_file))
|
print("Loading SRT file: {}".format(srt_file))
|
||||||
srt_parser = SrtFileParser(srt_file, self.parameters.utc_offset)
|
srt_parser = SrtFileParser(srt_file, utc_offset)
|
||||||
srt_parser.parse()
|
srt_parser.parse()
|
||||||
else:
|
else:
|
||||||
print("SRT file not found: {}".format(srt_file))
|
print("SRT file not found: {}".format(srt_file))
|
||||||
|
@ -67,11 +75,6 @@ class Video2Dataset:
|
||||||
else:
|
else:
|
||||||
srt_parser = None
|
srt_parser = None
|
||||||
|
|
||||||
# get video info
|
|
||||||
video_info = self.GetVideoInfo(input_file)
|
|
||||||
|
|
||||||
print(video_info)
|
|
||||||
|
|
||||||
if (self.blur_checker is not None and self.blur_checker.NeedPreProcess()):
|
if (self.blur_checker is not None and self.blur_checker.NeedPreProcess()):
|
||||||
print("Preprocessing for blur checker...")
|
print("Preprocessing for blur checker...")
|
||||||
self.blur_checker.PreProcess(input_file, self.parameters.start, self.parameters.end, self.parameters.internal_width, self.parameters.internal_height)
|
self.blur_checker.PreProcess(input_file, self.parameters.start, self.parameters.end, self.parameters.internal_width, self.parameters.internal_height)
|
||||||
|
@ -189,40 +192,65 @@ class Video2Dataset:
|
||||||
|
|
||||||
_, buf = cv2.imencode('.' + self.parameters.frame_format, frame)
|
_, buf = cv2.imencode('.' + self.parameters.frame_format, frame)
|
||||||
|
|
||||||
img = Image(buf.tobytes())
|
start_time_utc = video_info.start_time_utc if video_info.start_time_utc is not None \
|
||||||
|
else srt_parser.data[0].timestamp if srt_parser is not None \
|
||||||
|
else datetime.datetime.now()
|
||||||
|
|
||||||
start_time = (video_info.start_time if video_info.start_time is not None \
|
elapsed_time_utc = start_time_utc + datetime.timedelta(seconds=(self.frame_index / video_info.frame_rate))
|
||||||
else srt_parser.data[0].timestamp if srt_parser is not None \
|
elapsed_time = elapsed_time_utc + srt_parser.utc_offset
|
||||||
else datetime.datetime.now()) + self.parameters.utc_offset
|
|
||||||
|
|
||||||
elapsed_time = start_time + datetime.timedelta(seconds=(self.frame_index / video_info.frame_rate))
|
img = Image.open(io.BytesIO(buf))
|
||||||
|
|
||||||
# Set datetime_original
|
# Exif dict contains the following keys: '0th', 'Exif', 'GPS', '1st', 'thumbnail'
|
||||||
img.datetime_original = elapsed_time.strftime('%Y:%m:%d %H:%M:%S')
|
|
||||||
img.datetime_digitized = elapsed_time.strftime('%Y:%m:%d %H:%M:%S')
|
# Set the EXIF metadata
|
||||||
img.datetime = elapsed_time.strftime('%Y:%m:%d %H:%M:%S')
|
exif_dict = {
|
||||||
img.pixel_x_dimension = frame.shape[1]
|
"0th": {
|
||||||
img.pixel_y_dimension = frame.shape[0]
|
piexif.ImageIFD.Software: "ODM",
|
||||||
img.orientation = video_info.orientation if video_info.orientation is not None else Orientation.TOP_LEFT
|
piexif.ImageIFD.DateTime: elapsed_time.strftime('%Y:%m:%d %H:%M:%S'),
|
||||||
img.software = "Video2Dataset"
|
piexif.ImageIFD.XResolution: (frame.shape[1], 1),
|
||||||
|
piexif.ImageIFD.YResolution: (frame.shape[0], 1),
|
||||||
|
},
|
||||||
|
"Exif": {
|
||||||
|
piexif.ExifIFD.DateTimeOriginal: elapsed_time.strftime('%Y:%m:%d %H:%M:%S'),
|
||||||
|
piexif.ExifIFD.DateTimeDigitized: elapsed_time.strftime('%Y:%m:%d %H:%M:%S'),
|
||||||
|
piexif.ExifIFD.PixelXDimension: (frame.shape[1], 1),
|
||||||
|
piexif.ExifIFD.PixelYDimension: (frame.shape[0], 1),
|
||||||
|
}}
|
||||||
|
|
||||||
|
if video_info.orientation is not None:
|
||||||
|
exif_dict["0th"][piexif.ImageIFD.Orientation] = video_info.orientation
|
||||||
|
|
||||||
if video_info.model is not None:
|
if video_info.model is not None:
|
||||||
img.model = video_info.model
|
exif_dict["Exif"][piexif.ImageIFD.Model] = video_info.model
|
||||||
|
|
||||||
entry = srt_parser.get_entry(elapsed_time) if srt_parser is not None else None
|
entry = srt_parser.get_entry(elapsed_time_utc) if srt_parser is not None else None
|
||||||
|
|
||||||
if (entry is not None):
|
if entry is not None:
|
||||||
|
segs = entry["shutter"].split("/")
|
||||||
|
exif_dict["Exif"][piexif.ExifIFD.ExposureTime] = (int(float(segs[0])), int(float(segs[1])))
|
||||||
|
exif_dict["Exif"][piexif.ExifIFD.FocalLength] = (entry["focal_len"], 1)
|
||||||
|
exif_dict["Exif"][piexif.ExifIFD.FNumber] = (entry["fnum"], 1)
|
||||||
|
exif_dict["Exif"][piexif.ExifIFD.ISOSpeedRatings] = (entry["iso"], 1)
|
||||||
|
|
||||||
img.exposure_time = 1 / float(entry["shutter"].split("/")[1])
|
exif_dict["GPS"] = {
|
||||||
img.focal_length = entry["focal_len"]
|
piexif.GPSIFD.GPSMapDatum: "WGS-84",
|
||||||
img.f_number = entry["fnum"]
|
piexif.GPSIFD.GPSDateStamp: elapsed_time.strftime('%Y:%m:%d %H:%M:%S')
|
||||||
img.latitude = entry["latitude"]
|
}
|
||||||
img.longitude = entry["longitude"]
|
|
||||||
img.altitude = entry["altitude"]
|
if (entry["latitude"] is not None):
|
||||||
img.photographic_sensitivity = entry["iso"]
|
exif_dict["GPS"][piexif.GPSIFD.GPSLatitude] = FloatToRational(entry["latitude"])
|
||||||
|
|
||||||
|
if (entry["longitude"] is not None):
|
||||||
|
exif_dict["GPS"][piexif.GPSIFD.GPSLongitude] = FloatToRational(entry["longitude"])
|
||||||
|
|
||||||
|
if (entry["altitude"] is not None):
|
||||||
|
exif_dict["GPS"][piexif.GPSIFD.GPSAltitude] = FloatToRational(entry["altitude"])
|
||||||
|
|
||||||
|
|
||||||
|
exif_bytes = piexif.dump(exif_dict)
|
||||||
|
img.save(path, exif=exif_bytes)
|
||||||
|
|
||||||
with open(path, "wb") as f:
|
|
||||||
f.write(img.get_file())
|
|
||||||
|
|
||||||
def WriteStats(self, input_file, stats):
|
def WriteStats(self, input_file, stats):
|
||||||
self.f.write("{};{};{};{};{};{};{};{};{};{}\n".format(
|
self.f.write("{};{};{};{};{};{};{};{};{};{}\n".format(
|
||||||
|
@ -237,17 +265,19 @@ class Video2Dataset:
|
||||||
stats["is_similar"] if "is_similar" in stats else "",
|
stats["is_similar"] if "is_similar" in stats else "",
|
||||||
stats["written"] if "written" in stats else "").replace(".", ","))
|
stats["written"] if "written" in stats else "").replace(".", ","))
|
||||||
|
|
||||||
|
|
||||||
def GetVideoInfo(self, input_file):
|
def GetVideoInfo(self, input_file):
|
||||||
|
|
||||||
video = cv2.VideoCapture(input_file)
|
video = cv2.VideoCapture(input_file)
|
||||||
|
|
||||||
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
|
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
|
||||||
frame_rate = video.get(cv2.CAP_PROP_FPS)
|
frame_rate = video.get(cv2.CAP_PROP_FPS)
|
||||||
start_time, orientation, model = self.GetVideoMetadata(input_file)
|
start_time_utc, utc_offset, orientation, model = self.GetVideoMetadata(input_file)
|
||||||
|
|
||||||
video.release()
|
video.release()
|
||||||
|
|
||||||
return collections.namedtuple("VideoInfo", ["total_frames", "frame_rate", "start_time", "orientation", "model"])(total_frames, frame_rate, start_time, orientation, model)
|
return collections.namedtuple("VideoInfo", ["total_frames", "frame_rate", "start_time_utc", "utc_offset", "orientation", "model"])(total_frames, frame_rate, start_time_utc, utc_offset, orientation, model)
|
||||||
|
|
||||||
|
|
||||||
def GetVideoMetadata(self, input_file):
|
def GetVideoMetadata(self, input_file):
|
||||||
|
|
||||||
|
@ -255,19 +285,28 @@ class Video2Dataset:
|
||||||
|
|
||||||
metadata = pymediainfo.MediaInfo.parse(input_file).to_data()
|
metadata = pymediainfo.MediaInfo.parse(input_file).to_data()
|
||||||
|
|
||||||
start_time = None
|
start_time_utc = None
|
||||||
orientation = Orientation.TOP_LEFT
|
orientation = 1
|
||||||
performer = None
|
performer = None
|
||||||
|
utc_offset = None
|
||||||
|
|
||||||
if metadata is not None and 'tracks' in metadata:
|
if metadata is not None and 'tracks' in metadata:
|
||||||
# Check if it is safe to access the first element of the tracks list
|
# Check if it is safe to access the first element of the tracks list
|
||||||
if len(metadata['tracks']) > 0:
|
if len(metadata['tracks']) > 0:
|
||||||
|
|
||||||
start_time = metadata['tracks'][0].get('encoded_date') or \
|
start_time_utc = metadata['tracks'][0].get('encoded_date') or \
|
||||||
metadata['tracks'][0].get('tagged_date') or \
|
metadata['tracks'][0].get('tagged_date')
|
||||||
metadata['tracks'][0].get('file_creation_date')
|
|
||||||
|
|
||||||
start_time = datetime.datetime.strptime(start_time, '%Z %Y-%m-%d %H:%M:%S')
|
start_time_utc = datetime.datetime.strptime(start_time_utc, '%Z %Y-%m-%d %H:%M:%S') if start_time_utc is not None else None
|
||||||
|
|
||||||
|
file_creation_date_utc = metadata['tracks'][0].get('file_creation_date')
|
||||||
|
file_creation_date_utc = datetime.datetime.strptime(file_creation_date_utc, '%Z %Y-%m-%d %H:%M:%S.%f') if file_creation_date_utc is not None else None
|
||||||
|
|
||||||
|
file_creation_date = metadata['tracks'][0].get('file_creation_date__local')
|
||||||
|
file_creation_date = datetime.datetime.strptime(file_creation_date, '%Y-%m-%d %H:%M:%S.%f') if file_creation_date is not None else None
|
||||||
|
|
||||||
|
if file_creation_date_utc is not None and file_creation_date is not None:
|
||||||
|
utc_offset = file_creation_date - file_creation_date_utc
|
||||||
|
|
||||||
performer = metadata['tracks'][0].get('performer')
|
performer = metadata['tracks'][0].get('performer')
|
||||||
|
|
||||||
|
@ -279,17 +318,32 @@ class Video2Dataset:
|
||||||
if orientation is not None:
|
if orientation is not None:
|
||||||
orientation = int(float(orientation))
|
orientation = int(float(orientation))
|
||||||
|
|
||||||
if orientation == 0:
|
# The 8 EXIF orientation values are numbered 1 to 8.
|
||||||
orientation = Orientation.TOP_LEFT
|
# 1 = 0°: the correct orientation, no adjustment is required.
|
||||||
elif orientation == 90:
|
# 2 = 0°, mirrored: image has been flipped back-to-front.
|
||||||
orientation = Orientation.LEFT_BOTTOM
|
# 3 = 180°: image is upside down.
|
||||||
elif orientation == 180:
|
# 4 = 180°, mirrored: image has been flipped back-to-front and is upside down.
|
||||||
orientation = Orientation.BOTTOM_RIGHT
|
# 5 = 90°: image has been flipped back-to-front and is on its side.
|
||||||
elif orientation == 270:
|
# 6 = 90°, mirrored: image is on its side.
|
||||||
orientation = Orientation.RIGHT_TOP
|
# 7 = 270°: image has been flipped back-to-front and is on its far side.
|
||||||
|
# 8 = 270°, mirrored: image is on its far side.
|
||||||
|
|
||||||
return start_time, orientation, performer
|
if orientation == 0:
|
||||||
|
orientation = 1
|
||||||
|
elif orientation == 90:
|
||||||
|
orientation = 8
|
||||||
|
elif orientation == 180:
|
||||||
|
orientation = 3
|
||||||
|
elif orientation == 270:
|
||||||
|
orientation = 6
|
||||||
|
|
||||||
|
return start_time_utc, utc_offset, orientation, performer
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
||||||
return start_time, orientation, performer
|
return start_time_utc, utc_offset, orientation, performer
|
||||||
|
|
||||||
|
|
||||||
|
def FloatToRational(f):
|
||||||
|
f = Fraction(f).limit_denominator()
|
||||||
|
return (f.numerator, f.denominator)
|
Ładowanie…
Reference in New Issue