kopia lustrzana https://github.com/OpenDroneMap/ODM
Added black frame detection, srt parser and exif tags population
rodzic
d4846ad7d0
commit
9efea5a966
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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))
|
||||
|
||||
|
|
@ -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
|
Ładowanie…
Reference in New Issue