Added black frame detection, srt parser and exif tags population

pull/1567/head
Luca Di Leo 2023-01-09 11:45:22 +01:00
rodzic d4846ad7d0
commit 9efea5a966
4 zmienionych plików z 555 dodań i 138 usunięć

Wyświetl plik

@ -5,7 +5,6 @@ class PercentageBlurChecker:
def __init__(self, percentage):
self.percentage = percentage
self.cache = None
self.threshold = None
def NeedPreProcess(self):
return True
@ -15,7 +14,8 @@ class PercentageBlurChecker:
# Open video file
cap = cv2.VideoCapture(video_path)
if (cap.isOpened() == False):
raise Exception("Error opening video stream or file")
print("Error opening video stream or file")
return
if start_frame is not None:
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
@ -107,3 +107,83 @@ class SimilarityChecker:
self.last_image_features = cv2.goodFeaturesToTrack(image_bw, self.max_features, 0.01, 10)
return distance, res, self.last_image_id
class NaiveBlackFrameChecker:
def __init__(self, threshold):
self.threshold = threshold
def PreProcess(self, video_path, start_frame, end_frame, width=800, height=600):
return
def NeedPreProcess(self):
return False
def IsBlack(self, image_bw, id):
return np.average(image_bw) < self.threshold
class BlackFrameChecker:
def __init__(self, picture_black_ratio_th=0.98, pixel_black_th=0.30):
self.picture_black_ratio_th = picture_black_ratio_th if picture_black_ratio_th is not None else 0.98
self.pixel_black_th = pixel_black_th if pixel_black_th is not None else 0.30
self.luminance_minimum_value = None
self.luminance_range_size = None
self.absolute_threshold = None
def NeedPreProcess(self):
return True
def PreProcess(self, video_path, start_frame, end_frame, width=800, height=600):
# Open video file
cap = cv2.VideoCapture(video_path)
# Set frame start and end indices
cap.set(cv2.CAP_PROP_POS_FRAMES, start_frame)
frame_end = end_frame
if end_frame == -1:
frame_end = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
# Initialize luminance range size and minimum value
self.luminance_range_size = 0
self.luminance_minimum_value = 255
frame_index = start_frame if start_frame is not None else 0
# Read and process frames from video file
while (cap.isOpened() and (end_frame is None or frame_index <= end_frame)):
ret, frame = cap.read()
if not ret:
break
# Resize frame to specified width and height
frame = cv2.resize(frame, (width, height))
# Convert frame to grayscale
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray_frame_min = gray_frame.min()
gray_frame_max = gray_frame.max()
# Update luminance range size and minimum value
self.luminance_range_size = max(self.luminance_range_size, gray_frame_max - gray_frame_min)
self.luminance_minimum_value = min(self.luminance_minimum_value, gray_frame_min)
frame_index += 1
# Calculate absolute threshold for considering a pixel "black"
self.absolute_threshold = self.luminance_minimum_value + self.pixel_black_th * self.luminance_range_size
# Close video file
cap.release()
def IsBlack(self, image_bw, id):
# Count number of pixels < self.absolute_threshold
nb_black_pixels = np.sum(image_bw < self.absolute_threshold)
# Calculate ratio of black pixels
ratio_black_pixels = nb_black_pixels / (image_bw.shape[0] * image_bw.shape[1])
# Check if ratio of black pixels is above threshold
return ratio_black_pixels >= self.picture_black_ratio_th

Wyświetl plik

@ -0,0 +1,141 @@
import argparse
import datetime
import os
class Parameters:
input = None
output = None
start = None
end = None
output_resolution = None
blur_percentage = None
blur_threshold = None
distance_threshold = None
black_ratio_threshold = None
pixel_black_threshold = None
use_srt = None
timezone = None
frame_format = None
stats_file = None
def __init__(self, args):
# "input" -> path to input video file(s), use ',' to separate multiple files")
# "output" -> path to output directory")
# "start" -> start frame index")
# "end" -> end frame index")
# "output-resolution" -> Override output resolution (ex. 640x480)")
# "blur-percentage" -> discard the lowest X percent of frames based on blur score (allowed values from 0.0 to 1.0)")
# "blur-threshold" -> blur measures that fall below this value will be considered 'blurry' (to be used in exclusion with --blur-percentage)")
# "distance-threshold" -> distance measures that fall below this value will be considered 'similar'")
# "black-ratio-threshold" -> Set the threshold for considering a frame 'black'. Express the minimum value for the ratio: nb_black_pixels / nb_pixels. Default value is 0.98")
# "pixel-black-threshold" -> Set the threshold for considering a pixel 'black'. The threshold expresses the maximum pixel luminance value for which a pixel is considered 'black'. Good value is 0.30 (30%)")
# "use-srt" -> Use SRT files for extracting metadata (same name as video file with .srt extension)")
# "timezone" -> UTC timezone offset (ex. -5 for EST). Default to local timezone")
# "frame-format" -> frame format (jpg, png, tiff, etc.)")
# "stats-file" -> Save statistics to csv file")
if (not self.ValidateParameters(args)):
print("Invalid parameters")
exit()
if not os.path.exists(args["output"]):
os.makedirs(args["output"])
self.input = args["input"].split(",")
self.output = args["output"]
self.start = args["start"] if args["start"] else 0
self.end = args["end"] if args["end"] else None
self.blur_percentage = args["blur_percentage"] if args["blur_percentage"] else None
self.blur_threshold = args["blur_threshold"] if args["blur_threshold"] else None
self.distance_threshold = args["distance_threshold"] if args["distance_threshold"] else None
self.black_ratio_threshold = args["black_ratio_threshold"] if args["black_ratio_threshold"] else None
self.pixel_black_threshold = args["pixel_black_threshold"] if args["pixel_black_threshold"] else None
self.use_srt = args["use_srt"]
self.utc_offset = datetime.timedelta(hours=int(args["timezone"])) if args["timezone"] != "local" else datetime.datetime.now() - datetime.datetime.utcnow()
self.frame_format = args["frame_format"]
self.output_resolution = tuple(map(int, args["output_resolution"].split("x"))) if args["output_resolution"] else None
self.stats_file = args["stats_file"] if args["stats_file"] else None
# We will resize the image to this size before processing
self.internal_width = 800
self.internal_height = 600
# Validate parameters
def ValidateParameters(self, args):
# input can be a list of files comma separated, check for each file
files = args["input"].split(",")
for file in files:
if not os.path.exists(file):
print("Input file does not exist: " + file)
return False
if not os.path.exists(args["output"]):
os.makedirs(args["output"])
if args["start"] and args["start"] < 0:
print("Start frame index must be greater than 0")
return False
if args["end"]:
if args["end"] < 0:
print("End frame index must be greater than 0")
return False
if args["end"] < args["start"]:
print("End frame index must be greater than start frame index")
return False
if args["timezone"] and args["timezone"] != "local":
try:
val = int(args["timezone"])
if val < -12 or val > 14:
print("Timezone must be in the range -12 to 14")
return False
except ValueError:
print("Timezone must be a valid integer")
return False
if args["blur_percentage"] and (args["blur_percentage"] < 0 or args["blur_percentage"] > 1):
print("Blur percentage must be in the range 0.0 to 1.0")
return False
if args["blur_threshold"] and args["blur_threshold"] < 0:
print("Blur threshold must be greater than 0")
return False
if args["distance_threshold"] and args["distance_threshold"] < 0:
print("Distance threshold must be greater than 0")
return False
if args["black_ratio_threshold"] and (args["black_ratio_threshold"] < 0 or args["black_ratio_threshold"] > 1):
print("Black ratio threshold must be in the range 0.0 to 1.0")
return False
if args["pixel_black_threshold"] and (args["pixel_black_threshold"] < 0 or args["pixel_black_threshold"] > 1):
print("Pixel black threshold must be in the range 0.0 to 1.0")
return False
if args["output_resolution"]:
segs = args["output_resolution"].split("x")
if (len(segs) != 2):
print("Output resolution must be in the format WxH")
return False
if (int(segs[0]) <= 0 or int(segs[1]) <= 0):
print("Output resolution must be in the format WxH")
return False
return True

Wyświetl plik

@ -0,0 +1,124 @@
from datetime import datetime
import re
class SrtFileParser:
def __init__(self, filename, utc_offset):
self.filename = filename
self.data = []
self.utc_offset = utc_offset
def get_entry(self, timestamp):
if not self.data:
self.parse()
for entry in self.data:
if entry["timestamp"] <= timestamp:
return entry
return None
def parse(self):
# The SRT file is formatted this way:
# 1
# 00:00:00,000 --> 00:00:00,016
# <font size="36">SrtCnt : 1, DiffTime : 16ms
# 2023-01-06 18:56:48,380,821
# [iso : 3200] [shutter : 1/60.0] [fnum : 280] [ev : 0] [ct : 3925] [color_md : default] [focal_len : 240] [latitude: 0.000000] [longitude: 0.000000] [altitude: 0.000000] </font>
with open(self.filename, 'r') as f:
for line in f:
# Check if line is empty
if not line.strip():
if srtcnt is not None:
self.data.append({
"srtcnt": srtcnt,
"difftime": difftime,
"timestamp": timestamp,
"iso": iso,
"shutter": shutter,
"fnum": fnum,
"ev": ev,
"ct": ct,
"color_md": color_md,
"focal_len": focal_len,
"latitude": latitude,
"longitude": longitude,
"altitude": altitude
})
srtcnt = None
difftime = None
timestamp = None
iso = None
shutter = None
fnum = None
ev = None
ct = None
color_md = None
focal_len = None
latitude = None
longitude = None
altitude = None
continue
# Remove the html font tag
line = re.sub('<[^<]+?>', '', line)
match = re.search("SrtCnt : (\d+)", line)
if match:
srtcnt = int(match.group(1))
match = re.search("DiffTime : (\d+)ms", line)
if match:
difftime = int(match.group(1))
match = re.search("(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},\d{3})", line)
if match:
timestamp = match.group(1)
timestamp = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S,%f")
# The timestamp is in local time, so we need to subtract the UTC offset
timestamp = timestamp - self.utc_offset
match = re.search("iso : (\d+)", line)
if match:
iso = int(match.group(1))
match = re.search("shutter : (\d+/\d+.\d+)", line)
if match:
shutter = match.group(1)
match = re.search("fnum : (\d+)", line)
if match:
fnum = int(match.group(1))
match = re.search("ev : (\d+)", line)
if match:
ev = int(match.group(1))
match = re.search("ct : (\d+)", line)
if match:
ct = int(match.group(1))
match = re.search("color_md : (\w+)", line)
if match:
color_md = match.group(1)
match = re.search("focal_len : (\d+)", line)
if match:
focal_len = int(match.group(1))
match = re.search("latitude: (\d+.\d+)", line)
if match:
latitude = float(match.group(1))
match = re.search("longitude: (\d+.\d+)", line)
if match:
longitude = float(match.group(1))
match = re.search("altitude: (\d+.\d+)", line)
if match:
altitude = float(match.group(1))

Wyświetl plik

@ -1,51 +1,18 @@
import datetime
from math import floor
import time
import cv2
import os
from .checkers import PercentageBlurChecker, SimilarityChecker, ThresholdBlurChecker
# Input parameters:
# input = path to input video file
# output = path to output directory
# start = start frame index
# end = end frame index
# output_resolution = Override output resolution (ex. 640x480)
# blur_percentage = discard the lowest X percent of frames based on blur score (allowed values from 0.0 to 1.0)
# blur_threshold = blur measures that fall below this value will be considered 'blurry' (to be used in exclusion with -bp)
# distance_threshold = distance measures that fall below this value will be considered 'similar'
# frame_format = frame format (jpg, png, tiff, etc.)
# stats_file = Save statistics to csv file
# internal_width = We will resize the image to this width before processing
# internal_height = We will resize the image to this height before processing
class Parameters:
def __init__(self):
self.input = None
self.output = None
self.start = 0
self.end = None
self.blur_percentage = None
self.blur_threshold = None
self.distance_threshold = None
self.frame_format = "jpg"
self.output_resolution = None
self.stats_file = None
# We will resize the image to this size before processing
self.internal_width = 800
self.internal_height = 600
from opendm.video.parameters import Parameters
import pymediainfo
import collections
from exif import Image, Orientation
from checkers import BlackFrameChecker, PercentageBlurChecker, SimilarityChecker, ThresholdBlurChecker
from srtparser import SrtFileParser
class Video2Dataset:
def __init__(self, parameters):
if not self.ValidateParameters(parameters):
raise Exception("Invalid parameters")
def __init__(self, parameters: Parameters):
self.parameters = parameters
# We prioritize blur threshold over blur percentage.
@ -58,120 +25,117 @@ class Video2Dataset:
self.blur_checker = None
self.similarity_checker = SimilarityChecker(parameters.distance_threshold) if parameters.distance_threshold is not None else None
self.black_checker = BlackFrameChecker(parameters.black_ratio_threshold, parameters.pixel_black_threshold) if parameters.black_ratio_threshold is not None or parameters.pixel_black_threshold is not None else None
self.frame_index = parameters.start
self.orientation = None
self.f = None
# Validate parameters
def ValidateParameters(self, args):
if not os.path.exists(args.input):
print("Input file does not exist")
return False
if not os.path.exists(args.output):
os.makedirs(args.output)
if args.start and args.start < 0:
print("Start frame index must be greater than 0")
return False
if args.end:
if args.end < 0:
print("End frame index must be greater than 0")
return False
if args.end < args.start:
print("End frame index must be greater than start frame index")
return False
if args.blur_percentage and (args.blur_percentage < 0 or args.blur_percentage > 1):
print("Blur percentage must be in the range 0.0 to 1.0")
return False
if args.blur_threshold and args.blur_threshold < 0:
print("Blur threshold must be greater than 0")
return False
if args.distance_threshold and args.distance_threshold < 0:
print("Distance threshold must be greater than 0")
return False
if args.output_resolution:
segs = args.output_resolution.split("x")
if (len(segs) != 2):
print("Output resolution must be in the format WxH")
return False
if (int(segs[0]) <= 0 or int(segs[1]) <= 0):
print("Output resolution must be in the format WxH")
return False
return True
def ProcessVideo(self):
start = time.time()
if (self.blur_checker is not None and self.blur_checker.NeedPreProcess()):
print("Preprocessing video...")
self.blur_checker.PreProcess(self.parameters.input, self.parameters.start, self.parameters.end, self.parameters.internal_width, self.parameters.internal_height)
end = time.time()
print("Preprocessing time: {:.2f}s".format(end - start))
print("Calculated threshold is {}".format(self.blur_checker.threshold))
# open video file
cap = cv2.VideoCapture(self.parameters.input)
if (cap.isOpened() == False):
print("Error opening video stream or file")
return
frames_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if (self.parameters.stats_file is not None):
self.f = open(self.parameters.stats_file, "w")
if (self.blur_checker is not None):
self.f.write("blur_threshold;{}\n\n".format(self.blur_checker.threshold).replace(".", ","))
self.f.write("frame_index;blur_score;is_blurry;last_frame_index;similarity_score;is_similar;written\n")
self.f.write("global_idx;file_name;frame_index;blur_score;is_blurry;is_black;last_frame_index;similarity_score;is_similar;written\n")
if (self.parameters.start is not None):
cap.set(cv2.CAP_PROP_POS_FRAMES, self.parameters.start)
self.frame_index = self.parameters.start
start_frame = self.parameters.start
else:
start_frame = 0
self.global_idx = 0
frames_to_process = self.parameters.end - start_frame + 1 if (self.parameters.end is not None) else frames_count - start_frame
# foreach input file
for input_file in self.parameters.input:
while (cap.isOpened()):
ret, frame = cap.read()
# get file name
file_name = os.path.basename(input_file)
if not ret:
break
print("Processing video: {}".format(input_file))
if (self.parameters.end is not None and self.frame_index > self.parameters.end):
break
if self.parameters.use_srt:
srt_file = os.path.splitext(input_file)[0] + ".srt"
if os.path.exists(srt_file):
print("Loading SRT file: {}".format(srt_file))
srt_parser = SrtFileParser(srt_file, self.parameters.utc_offset)
srt_parser.parse()
else:
srt_file = os.path.splitext(input_file)[0] + ".SRT"
if os.path.exists(srt_file):
print("Loading SRT file: {}".format(srt_file))
srt_parser = SrtFileParser(srt_file, self.parameters.utc_offset)
srt_parser.parse()
else:
print("SRT file not found: {}".format(srt_file))
srt_parser = None
else:
srt_parser = None
# Calculate progress percentage
progress = floor((self.frame_index - start_frame + 1) / frames_to_process * 100)
print("[{:3d}%] Processing frame {}/{}: ".format(progress, self.frame_index - start_frame + 1, frames_to_process), end="")
# get video info
video_info = self.GetVideoInfo(input_file)
stats = self.ProcessFrame(frame)
print(video_info)
if stats is not None and self.parameters.stats_file is not None:
self.WriteStats(stats)
if (self.blur_checker is not None and self.blur_checker.NeedPreProcess()):
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)
end = time.time()
print("Preprocessing time: {:.2f}s".format(end - start))
print("Calculated threshold is {}".format(self.blur_checker.threshold))
cap.release()
if (self.f is not None):
if (self.black_checker is not None and self.black_checker.NeedPreProcess()):
start2 = time.time()
print("Preprocessing for black checker...")
self.black_checker.PreProcess(input_file, self.parameters.start, self.parameters.end, self.parameters.internal_width, self.parameters.internal_height)
end = time.time()
print("Preprocessing time: {:.2f}s".format(end - start2))
print("Calculated luminance_range_size is {}".format(self.black_checker.luminance_range_size))
print("Calculated luminance_minimum_value is {}".format(self.black_checker.luminance_minimum_value))
print("Calculated absolute_threshold is {}".format(self.black_checker.absolute_threshold))
# open video file
cap = cv2.VideoCapture(input_file)
if (not cap.isOpened()):
print("Error opening video stream or file")
return
frames_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
if (self.parameters.start is not None):
cap.set(cv2.CAP_PROP_POS_FRAMES, self.parameters.start)
self.frame_index = self.parameters.start
start_frame = self.parameters.start
else:
start_frame = 0
frames_to_process = self.parameters.end - start_frame + 1 if (self.parameters.end is not None) else frames_count - start_frame
while (cap.isOpened()):
ret, frame = cap.read()
if not ret:
break
if (self.parameters.end is not None and self.frame_index > self.parameters.end):
break
# Calculate progress percentage
progress = floor((self.frame_index - start_frame + 1) / frames_to_process * 100)
print("[{}][{:3d}%] Processing frame {}/{}: ".format(file_name, progress, self.frame_index - start_frame + 1, frames_to_process), end="")
stats = self.ProcessFrame(frame, video_info, srt_parser)
if stats is not None and self.parameters.stats_file is not None:
self.WriteStats(input_file, stats)
cap.release()
if self.f is not None:
self.f.close()
end = time.time()
print("Total processing time: {:.2f}s".format(end - start))
def ProcessFrame(self, frame):
def ProcessFrame(self, frame, video_info, srt_parser):
res = {"frame_index": self.frame_index}
res = {"frame_index": self.frame_index, "global_idx": self.global_idx}
frame_bw = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
frame_bw = cv2.resize(frame_bw, (self.parameters.internal_width, self.parameters.internal_height))
@ -186,6 +150,15 @@ class Video2Dataset:
self.frame_index += 1
return res
if (self.black_checker is not None):
is_black = self.black_checker.IsBlack(frame_bw, self.frame_index)
res["is_black"] = is_black
if is_black:
print ("black, skipping")
self.frame_index += 1
return res
if (self.similarity_checker is not None):
similarity_score, is_similar, last_frame_index = self.similarity_checker.IsSimilar(frame_bw, self.frame_index)
res["similarity_score"] = similarity_score
@ -197,27 +170,126 @@ class Video2Dataset:
self.frame_index += 1
return res
self.SaveFrame(frame)
self.SaveFrame(frame, video_info, srt_parser)
res["written"] = True
self.frame_index += 1
self.global_idx += 1
print ("saved")
return res
def SaveFrame(self, frame):
def SaveFrame(self, frame, video_info, srt_parser: SrtFileParser):
if (self.parameters.output_resolution is not None):
frame = cv2.resize(frame, self.parameters.output_resolution)
cv2.imwrite(os.path.join(self.parameters.output,
"frame_{}.{}".format(self.frame_index, self.parameters.frame_format)), frame)
path = os.path.join(self.parameters.output,
"frame_{}_{}.{}".format(self.global_idx, self.frame_index, self.parameters.frame_format))
def WriteStats(self, stats):
self.f.write("{};{};{};{};{};{};{}\n".format(stats["frame_index"],
_, buf = cv2.imencode('.' + self.parameters.frame_format, frame)
img = Image(buf.tobytes())
start_time = (video_info.start_time if video_info.start_time is not None \
else srt_parser.data[0].timestamp if srt_parser is not None \
else datetime.datetime.now()) + self.parameters.utc_offset
elapsed_time = start_time + datetime.timedelta(seconds=(self.frame_index / video_info.frame_rate))
# Set datetime_original
img.datetime_original = elapsed_time.strftime('%Y:%m:%d %H:%M:%S')
img.datetime_digitized = elapsed_time.strftime('%Y:%m:%d %H:%M:%S')
img.datetime = elapsed_time.strftime('%Y:%m:%d %H:%M:%S')
img.pixel_x_dimension = frame.shape[1]
img.pixel_y_dimension = frame.shape[0]
img.orientation = video_info.orientation if video_info.orientation is not None else Orientation.TOP_LEFT
img.software = "Video2Dataset"
if video_info.model is not None:
img.model = video_info.model
entry = srt_parser.get_entry(elapsed_time) if srt_parser is not None else None
if (entry is not None):
img.exposure_time = 1 / float(entry["shutter"].split("/")[1])
img.focal_length = entry["focal_len"]
img.f_number = entry["fnum"]
img.latitude = entry["latitude"]
img.longitude = entry["longitude"]
img.altitude = entry["altitude"]
img.photographic_sensitivity = entry["iso"]
with open(path, "wb") as f:
f.write(img.get_file())
def WriteStats(self, input_file, stats):
self.f.write("{};{};{};{};{};{};{};{};{};{}\n".format(
stats["global_idx"],
input_file,
stats["frame_index"],
stats["blur_score"] if "blur_score" in stats else "",
stats["is_blurry"] if "is_blurry" in stats else "",
stats["is_black"] if "is_black" in stats else "",
stats["last_frame_index"] if "last_frame_index" in stats else "",
stats["similarity_score"] if "similarity_score" 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):
video = cv2.VideoCapture(input_file)
total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
frame_rate = video.get(cv2.CAP_PROP_FPS)
start_time, orientation, model = self.GetVideoMetadata(input_file)
video.release()
return collections.namedtuple("VideoInfo", ["total_frames", "frame_rate", "start_time", "orientation", "model"])(total_frames, frame_rate, start_time, orientation, model)
def GetVideoMetadata(self, input_file):
try:
metadata = pymediainfo.MediaInfo.parse(input_file).to_data()
start_time = None
orientation = Orientation.TOP_LEFT
performer = None
if metadata is not None and 'tracks' in metadata:
# Check if it is safe to access the first element of the tracks list
if len(metadata['tracks']) > 0:
start_time = metadata['tracks'][0].get('encoded_date') or \
metadata['tracks'][0].get('tagged_date') or \
metadata['tracks'][0].get('file_creation_date')
start_time = datetime.datetime.strptime(start_time, '%Z %Y-%m-%d %H:%M:%S')
performer = metadata['tracks'][0].get('performer')
# Check if it is safe to access the second element of the tracks list
if len(metadata['tracks']) > 1:
orientation = metadata['tracks'][1].get('rotation')
if orientation is not None:
orientation = int(float(orientation))
if orientation == 0:
orientation = Orientation.TOP_LEFT
elif orientation == 90:
orientation = Orientation.LEFT_BOTTOM
elif orientation == 180:
orientation = Orientation.BOTTOM_RIGHT
elif orientation == 270:
orientation = Orientation.RIGHT_TOP
return start_time, orientation, performer
except Exception as e:
return start_time, orientation, performer