fmdt-python
Advanced tools
| import fmdt | ||
| vid = "demo.mp4" | ||
| tracks = "tracks.txt" | ||
| bb = "bb.txt" | ||
| fmdt.detect(vid_in_path=vid, trk_out_path=tracks, trk_bb_path=bb).split() |
+1
-1
@@ -80,4 +80,4 @@ from fmdt.core import ( | ||
| MINOR_VERSION = 0 | ||
| PATCH = 35 | ||
| PATCH = 36 | ||
| VERSION = str(MAJOR_VERSION) + "." + str(MINOR_VERSION) + "." + str(PATCH) |
+62
-4
@@ -12,4 +12,6 @@ """Module dealing with the database stuff""" | ||
| import pandas as pd | ||
| import numpy as np | ||
| import sqlite3 | ||
| from enum import Enum | ||
| from termcolor import colored | ||
| import os | ||
@@ -253,3 +255,2 @@ | ||
| # Lookup the id in our default database file. | ||
@@ -295,6 +296,36 @@ def id(self) -> int: | ||
| def frame_rate(self) -> float: | ||
| return fmdt.utils.get_avg_frame_rate(self.full_path()) | ||
| def nb_frames(self) -> int: | ||
| return fmdt.utils.get_video_nb_frames(self.full_path()) | ||
| def duration(self) -> float: | ||
| return fmdt.utils.get_video_duration(self.full_path()) | ||
| def get_intervals(self, dur_s: float) -> list[tuple[int, int]]: | ||
| """Compute a list of (start_frame, end_frame) intervals whose length is dur_s""" | ||
| fps = self.frame_rate() | ||
| total_dur = self.duration() | ||
| is_cfr = fmdt.utils.video_has_cfr(self.full_path()) | ||
| if not is_cfr: | ||
| print(colored(f"\tWARNING: {self} doesnt have constant frame rate (CFR), computed intervals may not return desired length of {dur_s} s", "red")) | ||
| # let's first get a list of intervals in seconds | ||
| start_points = np.arange(0, total_dur + dur_s, dur_s) | ||
| ints = [] | ||
| for i in range(len(start_points) - 1): | ||
| f_start = int(start_points[i] * fps) | ||
| f_end = int(start_points[i + 1] * fps) | ||
| ints.append((f_start, f_end)) | ||
| return ints | ||
| def partition(self, dur_s: float) -> None: | ||
| """Partition a video into smaller segments of size dur_s""" | ||
| ints = self.get_intervals(dur_s) | ||
| fmdt.split_video_at_intervals(self.full_path(), ints, 0, 0, condense=False) | ||
| def evaluate_args( | ||
@@ -385,8 +416,16 @@ self, | ||
| def load_draco6(filename: str = "videos.db", db_dir = fmdt.download.__DATA_DIR, require_gt = False, require_exist = False) -> list[Video]: | ||
| def load_draco6( | ||
| filename: str = "videos.db", | ||
| db_dir = fmdt.download.__DATA_DIR, | ||
| require_gt = False, | ||
| require_exist = False | ||
| ) -> list[Video]: | ||
| """Load draco6 `Video` objects that are stored in the `db_dir`/`filename` .db file""" | ||
| vids = load_in_videos(filename, db_dir) | ||
| d6 = [v for v in vids if v.is_draco6()] | ||
| if require_gt: | ||
| d6 = [v for v in d6 if v.has_meteors()] | ||
| if require_exist: | ||
@@ -397,7 +436,15 @@ d6 = [v for v in d6 if v.exists()] | ||
| def load_draco12(db_filename: str = "videos.db", db_dir = fmdt.download.__DATA_DIR, require_gt = False, require_exist = False) -> list[Video]: | ||
| def load_draco12( | ||
| db_filename: str = "videos.db", | ||
| db_dir = fmdt.download.__DATA_DIR, | ||
| require_gt = False, | ||
| require_exist = False | ||
| ) -> list[Video]: | ||
| vids = load_in_videos(db_filename, db_dir) | ||
| d12 = [v for v in vids if v.is_draco12()] | ||
| if require_gt: | ||
| d12 = [v for v in d12 if v.has_meteors()] | ||
| if require_exist: | ||
@@ -408,7 +455,15 @@ d12 = [v for v in d12 if v.exists()] | ||
| def load_window(db_filename: str = "videos.db", db_dir = fmdt.download.__DATA_DIR, require_gt = False, require_exist = False) -> list[Video]: | ||
| def load_window( | ||
| db_filename: str = "videos.db", | ||
| db_dir = fmdt.download.__DATA_DIR, | ||
| require_gt = False, | ||
| require_exist = False | ||
| ) -> list[Video]: | ||
| vids = load_in_videos(db_filename, db_dir) | ||
| win = [v for v in vids if v.is_window()] | ||
| if require_gt: | ||
| win = [v for v in win if v.has_meteors()] | ||
| if require_exist: | ||
@@ -420,2 +475,3 @@ win = [v for v in win if v.exists()] | ||
| def load_demo(db_filename: str = "videos.db", db_dir = fmdt.download.__DATA_DIR) -> list[Video]: | ||
| """Load video 2022_05_31_tauh_34_meteors.mp4 from our database""" | ||
| vids = load_in_videos(db_filename, db_dir) | ||
@@ -429,2 +485,4 @@ win = [v for v in vids if v.name == "2022_05_31_tauh_34_meteors.mp4"] | ||
| """Query all of the ground truths in our database""" | ||
| if not os.path.exists(dir + "/" + db_filename): | ||
@@ -431,0 +489,0 @@ fmdt.download.download_videos_db(db_filename, log=False, overwrite=False, dir=dir) |
+59
-6
@@ -56,2 +56,13 @@ import ffmpeg | ||
| def get_nominal_frame_rate(filename: str) -> float: | ||
| """ | ||
| Get the average framerate of a video | ||
| Adapted from https://github.com/kkroening/ffmpeg-python/blob/master/examples/video_info.py#L15 | ||
| """ | ||
| probe = ffmpeg.probe(filename) | ||
| video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None) | ||
| frame_rates = video_stream['r_frame_rate'].split('/') | ||
| return float(frame_rates[0]) / float(frame_rates[1]) | ||
| def get_video_width(filename: str) -> int: | ||
@@ -68,3 +79,20 @@ probe = ffmpeg.probe(filename) | ||
| def get_video_nb_frames(filename: str) -> int: | ||
| probe = ffmpeg.probe(filename) | ||
| video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None) | ||
| return int(video_stream['nb_frames']) | ||
| def get_video_duration(filename: str) -> float: | ||
| """Get the duration of video in seconds""" | ||
| probe = ffmpeg.probe(filename) | ||
| video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None) | ||
| return float(video_stream['duration']) | ||
| def video_has_cfr(filename: str) -> bool: | ||
| """Check whether a video is encoded with a constant frame rate (CFR) | ||
| This amounts to checking if the nominal frame rate and the average frame rate are identical""" | ||
| return get_avg_frame_rate(filename) == get_nominal_frame_rate(filename) | ||
| def decompose_video_filename(filename: str) -> tuple[str, str]: | ||
@@ -118,4 +146,5 @@ """Separate the name of a video from its extension | ||
| quiet = True, | ||
| overwrite = False | ||
| overwrite = False, | ||
| ) -> None: | ||
| """Extract videos frames using ffmpeg""" | ||
@@ -130,3 +159,3 @@ assert_file_exists(filename) | ||
| fps = get_avg_frame_rate(filename) | ||
| start_time = (start_frame / fps) - 1 # offset by at most one second | ||
| start_time = (start_frame / fps) | ||
| start_time = time_s_to_ffmpeg_format(start_time) | ||
@@ -136,6 +165,4 @@ | ||
| duration = (end_frame - start_frame) / fps | ||
| duration = time_s_to_ffmpeg_format(duration + 1) # Add one second to the duration | ||
| duration = time_s_to_ffmpeg_format(duration) | ||
| # end_time = frame_number_to_ffmpeg_format(filename, start_frame, seconds_offset=1) | ||
| args = ["ffmpeg", "-ss", start_time, "-i", filename, "-t", duration, out_file] | ||
@@ -233,2 +260,28 @@ | ||
| process.stderr.close() | ||
| process.wait() | ||
| process.wait() | ||
| def video_partition(length_video: float, length_sequence: float, fps: float) -> list[tuple[int, int]]: | ||
| """ | ||
| Determine the frame intervals on which to split a video so that the sequences do not exceed a certain duration | ||
| Parameters | ||
| ---------- | ||
| length_video (float): duration of the video in seconds | ||
| length_sequence (float): max duration of a sequence in seconds | ||
| fps (float): average number of frames per second | ||
| """ | ||
| nb_frame = length_video * fps | ||
| nb_frame_seq = length_sequence * fps | ||
| x=np.linspace(0,nb_frame,int(nb_frame/nb_frame_seq)+1,dtype='int64') #contains bounds of intervals of frames | ||
| intervals=[] | ||
| for i in range(x.shape[0]-1): | ||
| if(i==0): | ||
| intervals.append((x[i],x[i+1]+10)) | ||
| elif(i==x.shape[0]-2): | ||
| intervals.append((x[i]-10,x[i+1])) | ||
| else: | ||
| intervals.append((x[i]-10,x[i+1]+10)) | ||
| #on prend un "voisinage" de chaque borne car le découpage avec ffmpeg n'est pas précis à la frame près. On ne peut donc garantir que des séquence autour de length_sequence | ||
| return(intervals) |
+15
-1
@@ -78,2 +78,16 @@ video_name,start_frame,end_frame,start_x,start_y,end_x,end_y | ||
| Draconids-12mm2.00.21-1250-205500.avi,20,27,222,174,228,207 | ||
| Draconids-12mm2.00.09-1450-202900.avi,10,19,87,337,93,379 | ||
| Draconids-12mm2.00.09-1450-202900.avi,10,19,87,337,93,379 | ||
| window_3_sony_0405-0410UTC.mp4,273,278,1768,1107,1774,1155 | ||
| window_3_sony_0405-0410UTC.mp4,318,337,3238,554,3244,818 | ||
| window_3_sony_0405-0410UTC.mp4,1466,1477,2400,1508,2403,1660 | ||
| window_3_sony_0405-0410UTC.mp4,2417,2429,1108,1678,1102,1821 | ||
| window_3_sony_0405-0410UTC.mp4,3058,3064,534,1241,534,1313 | ||
| window_3_sony_0405-0410UTC.mp4,3792,3802,3566,445,3572,581 | ||
| window_3_sony_0405-0410UTC.mp4,4613,4616,2545,1429,2548,1465 | ||
| window_3_sony_0405-0410UTC.mp4,4624,4633,1740,505,1743,660 | ||
| window_3_sony_0405-0410UTC.mp4,5580,5588,312,1171,312,1262 | ||
| window_3_sony_0405-0410UTC.mp4,6854,6862,2931,1034,2943,1107 | ||
| window_3_sony_0405-0410UTC.mp4,6918,6936,2472,192,2491,560 | ||
| window_3_sony_0405-0410UTC.mp4,7081,7108,3697,1687,3691,1915 | ||
| window_3_sony_0405-0410UTC.mp4,7487,7500,1449,1727,1440,1827 | ||
| window_3_sony_0405-0410UTC.mp4,7494,7500,3359,1289,3356,1341 |
+1
-1
| Metadata-Version: 2.1 | ||
| Name: fmdt-python | ||
| Version: 0.0.35 | ||
| Version: 0.0.36 | ||
| Summary: utlity functions for fast meteor detection toolbox | ||
@@ -5,0 +5,0 @@ Project-URL: fmdt, https://github.com/alsoc/fmdt |
+1
-1
@@ -11,3 +11,3 @@ [build-system] | ||
| ] | ||
| version = "0.0.35" | ||
| version = "0.0.36" | ||
| authors = [ | ||
@@ -14,0 +14,0 @@ { name="Evan Voyles", email="ejovo13@yahoo.com" }, |
Sorry, the diff of this file is not supported yet
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
551929
0.9%60
1.69%4128
2.3%