Skip to content

Commit

Permalink
video tools
Browse files Browse the repository at this point in the history
  • Loading branch information
sronilsson committed May 8, 2024
1 parent fe0ead3 commit 2ac5308
Show file tree
Hide file tree
Showing 11 changed files with 343 additions and 202 deletions.
Binary file added docs/_static/img/change_single_video_fps.mp4
Binary file not shown.
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
#'sphinx_autodoc_typehints',
'sphinx_togglebutton',
'nbsphinx',
'sphinx.ext.intersphinx']
'sphinx.ext.intersphinx',
'sphinxcontrib.video']
intersphinx_mapping = {
'python': ('https://docs.python.org/3', None),
}
Expand Down
1 change: 1 addition & 0 deletions docs/environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ dependencies:
- pip:
- pip==21.2.2
- sphinx-autodoc-typehints
- sphinxcontrib-video
- sphinx-togglebutton==0.3.2
- sphinx_rtd_theme
- simba-uw-tf-dev
Expand Down
63 changes: 18 additions & 45 deletions simba/SimBA.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,12 @@
InitiateClipMultipleVideosByFrameNumbersPopUp,
InitiateClipMultipleVideosByTimestampsPopUp, InteractiveClahePopUp,
MergeFrames2VideoPopUp, MultiCropPopUp, MultiShortenPopUp,
SuperImposeFrameCountPopUp, VideoRotatorPopUp, VideoTemporalJoinPopUp)
SuperImposeFrameCountPopUp, VideoRotatorPopUp, VideoTemporalJoinPopUp, DownsampleMultipleVideosPopUp, DownsampleSingleVideoPopUp)
from simba.ui.pop_ups.visualize_pose_in_dir_pop_up import \
VisualizePoseInFolderPopUp
from simba.ui.tkinter_functions import DropDownMenu, Entry_Box, FileSelect
from simba.ui.video_info_ui import VideoInfoTable
from simba.utils.checks import (check_ffmpeg_available,
check_file_exist_and_readable, check_int)
from simba.utils.checks import (check_ffmpeg_available, check_file_exist_and_readable, check_int)
from simba.utils.custom_feature_extractor import CustomFeatureExtractor
from simba.utils.enums import OS, Defaults, Formats, Paths, TagNames
from simba.utils.errors import InvalidInputError
Expand Down Expand Up @@ -1613,12 +1612,7 @@ def __init__(self):
)

menu.add_cascade(label="Tools", menu=video_process_menu)
video_process_menu.add_cascade(
label="Change fps...",
compound="left",
image=self.menu_icons["fps"]["img"],
menu=fps_menu,
)
video_process_menu.add_cascade(label="Change fps...", compound="left", image=self.menu_icons["fps"]["img"], menu=fps_menu)

clip_video_menu = Menu(menu)
clip_video_menu.add_command(label="Clip single video", command=ClipVideoPopUp)
Expand Down Expand Up @@ -1647,36 +1641,11 @@ def __init__(self):
)

crop_video_menu = Menu(menu)
crop_video_menu.add_command(
label="Crop videos",
compound="left",
image=self.menu_icons["crop"]["img"],
command=CropVideoPopUp,
)
crop_video_menu.add_command(
label="Crop videos (circles)",
compound="left",
image=self.menu_icons["circle"]["img"],
command=CropVideoCirclesPopUp,
)
crop_video_menu.add_command(
label="Crop videos (polygons)",
compound="left",
image=self.menu_icons["polygon"]["img"],
command=CropVideoPolygonsPopUp,
)
crop_video_menu.add_command(
label="Multi-crop",
compound="left",
image=self.menu_icons["crop"]["img"],
command=MultiCropPopUp,
)
video_process_menu.add_cascade(
label="Crop videos...",
compound="left",
image=self.menu_icons["crop"]["img"],
menu=crop_video_menu,
)
crop_video_menu.add_command(label="Crop videos", compound="left", image=self.menu_icons["crop"]["img"], command=CropVideoPopUp)
crop_video_menu.add_command(label="Crop videos (circles)", compound="left", image=self.menu_icons["circle"]["img"], command=CropVideoCirclesPopUp)
crop_video_menu.add_command(label="Crop videos (polygons)", compound="left", image=self.menu_icons["polygon"]["img"], command=CropVideoPolygonsPopUp)
crop_video_menu.add_command(label="Multi-crop", compound="left", image=self.menu_icons["crop"]["img"], command=MultiCropPopUp)
video_process_menu.add_cascade(label="Crop videos...", compound="left", image=self.menu_icons["crop"]["img"], menu=crop_video_menu)

format_menu = Menu(video_process_menu)
format_menu.add_command(label="Change image file formats", command=ChangeImageFormatPopUp)
Expand Down Expand Up @@ -1725,12 +1694,16 @@ def __init__(self):
command=MakePathPlotPopUp,
)

video_process_menu.add_command(
label="Down-sample videos",
compound="left",
image=self.menu_icons["sample"]["img"],
command=DownsampleVideoPopUp,
)
downsample_video_menu = Menu(video_process_menu)
downsample_video_menu.add_command(label="Down-sample single video", command=DownsampleSingleVideoPopUp)
downsample_video_menu.add_command(label="Down-sample multiple videos", command=DownsampleMultipleVideosPopUp)
video_process_menu.add_cascade(label="Down-sample video...", compound="left", image=self.menu_icons["sample"]["img"], menu=downsample_video_menu)


#video_process_menu.add_command(label="Down-sample videos",compound="left",image=self.menu_icons["sample"]["img"],command=DownsampleVideoPopUp)



video_process_menu.add_cascade(
label="Drop body-parts from tracking data",
compound="left",
Expand Down
49 changes: 11 additions & 38 deletions simba/plotting/ROI_feature_visualizer_mp.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,34 +64,10 @@ def __insert_texts(shape_info: dict, img: np.ndarray):
for cnt, animal_data in bp_lk.items():
animal, animal_bp, _ = animal_data
animal_name = f"{animal} {animal_bp}"
cv2.putText(
img,
text_locations[animal_name][shape_name]["in_zone_text"],
text_locations[animal_name][shape_name]["in_zone_text_loc"],
font,
font_size,
shape_color,
1,
)
cv2.putText(
img,
text_locations[animal_name][shape_name]["distance_text"],
text_locations[animal_name][shape_name]["distance_text_loc"],
font,
font_size,
shape_color,
1,
)
cv2.putText(img, text_locations[animal_name][shape_name]["in_zone_text"], text_locations[animal_name][shape_name]["in_zone_text_loc"], font, font_size, shape_color, 1)
cv2.putText(img, text_locations[animal_name][shape_name]["distance_text"], text_locations[animal_name][shape_name]["distance_text_loc"], font, font_size, shape_color, 1)
if directing_data is not None and style_attr[DIRECTIONALITY]:
cv2.putText(
img,
text_locations[animal][shape_name]["directing_text"],
text_locations[animal][shape_name]["directing_text_loc"],
font,
font_size,
shape_color,
1,
)
cv2.putText(img, text_locations[animal][shape_name]["directing_text"], text_locations[animal][shape_name]["directing_text_loc"], font, font_size, shape_color, 1)
return img

fourcc = cv2.VideoWriter_fourcc(*Formats.MP4_CODEC.value)
Expand All @@ -101,7 +77,7 @@ def __insert_texts(shape_info: dict, img: np.ndarray):
save_path = os.path.join(save_temp_dir, f"{group_cnt}.mp4")
writer = cv2.VideoWriter(save_path, fourcc, video_meta_data["fps"], (video_meta_data["width"] * 2, video_meta_data["height"]))
cap = cv2.VideoCapture(video_path)
cap.set(1, start_frm)
cap.set(1, current_frm)
while current_frm <= end_frm:
ret, img = cap.read()
if ret:
Expand Down Expand Up @@ -141,9 +117,9 @@ def __insert_texts(shape_info: dict, img: np.ndarray):
color=shape_info[shape_name]["Color BGR"],
thickness=shape_info[shape_name]["Thickness"],
style=style_attr[DIRECTIONALITY_STYLE])
current_frm += 1
writer.write(np.uint8(img))
print(f"Multiprocessing frame: {current_frm} / {video_meta_data['frame_count']} on core {group_cnt}...")
current_frm += 1
else:
break
writer.release()
Expand Down Expand Up @@ -191,7 +167,8 @@ def __init__(self,
if platform.system() == "Darwin":
multiprocessing.set_start_method("spawn", force=True)
check_int(name=f"{self.__class__.__name__} core_cnt",value=core_cnt,min_value=-1,max_value=find_core_cnt()[0])
if core_cnt == -1: core_cnt = find_core_cnt()[0]
if core_cnt == -1:
core_cnt = find_core_cnt()[0]
check_file_exist_and_readable(file_path=video_path)
ConfigReader.__init__(self, config_path=config_path)
PlottingMixin.__init__(self)
Expand All @@ -206,18 +183,14 @@ def __init__(self,
data=self.roi_dict, video_name=self.video_name
)
self.core_cnt, self.style_attr = core_cnt, style_attr
self.save_path = os.path.join(
self.roi_features_save_dir, f"{self.video_name}.mp4"
)
self.save_path = os.path.join(self.roi_features_save_dir, f"{self.video_name}.mp4")
if not os.path.exists(self.roi_features_save_dir):
os.makedirs(self.roi_features_save_dir)
self.save_temp_dir = os.path.join(self.roi_features_save_dir, "temp")
if os.path.exists(self.save_temp_dir):
remove_a_folder(folder_dir=self.save_temp_dir)
os.makedirs(self.save_temp_dir)
self.data_path = os.path.join(
self.outlier_corrected_dir, f"{self.video_name}.{self.file_type}"
)
self.data_path = os.path.join(self.outlier_corrected_dir, f"{self.video_name}.{self.file_type}")
if not os.path.isfile(self.data_path):
raise NoFilesFoundError(
msg=f"SIMBA ERROR: Could not find the file at path {self.data_path}. Make sure the data file exist to create ROI visualizations",
Expand Down Expand Up @@ -379,7 +352,7 @@ def __get_border_img_size(self, video_path: Union[str, os.PathLike]):
def run(self):
self.img_w_border_h, self.img_w_border_w = self.__get_border_img_size(video_path=self.video_path)
self.__calc_text_locs()
frm_lst = np.arange(0, len(self.data_df)+1)
frm_lst = np.arange(0, len(self.data_df) + 1)
frm_lst = np.array_split(frm_lst, self.core_cnt)
frame_range = []
for i in range(len(frm_lst)): frame_range.append((i, frm_lst[i]))
Expand All @@ -405,7 +378,7 @@ def run(self):
roi_features_df=self.roi_features_df,
animal_bps=self.animal_bp_dict)
for cnt, result in enumerate(pool.imap(constants, frame_range, chunksize=self.multiprocess_chunksize)):
print(f"Batch core {result+1}/{self.core_cnt} complete...")
print(f"Batch core {result+1}/{self.core_cnt} complete...")
print(f"Joining {self.video_name} multi-processed video...")
concatenate_videos_in_folder(in_folder=self.save_temp_dir, save_path=self.save_path, video_format="mp4", remove_splits=True)
self.timer.stop_timer()
Expand Down
1 change: 0 additions & 1 deletion simba/roi_tools/ROI_feature_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class ROIFeatureCreator(ConfigReader, FeatureExtractionMixin):
.. note::
`ROI tutorials <https://github.com/sgoldenlab/simba/blob/master/docs/ROI_tutorial_new.md>`__.
:param Union[str, os.PathLike] config_path: Path to SimBA project config file in Configparser format.
:param List[str] body_parts: List of the body-parts to use as proxy for animal location(s).
:param Optional[Union[str, os.PathLike]] data_path: Path to folder or file holding the data used to calculate ROI aggregate statistics. If None, then defaults to the `project_folder/csv/outlier_corrected_movement_location` directory of the SimBA project. Default: None.
Expand Down
Loading

0 comments on commit 2ac5308

Please sign in to comment.