Skip to content

Commit

Permalink
roi_nb
Browse files Browse the repository at this point in the history
  • Loading branch information
sronilsson committed Jan 19, 2025
1 parent 2c438be commit acf6211
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 134 deletions.
9 changes: 3 additions & 6 deletions simba/mixins/pop_up_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@
from PIL import ImageTk

from simba.mixins.config_reader import ConfigReader
from simba.ui.tkinter_functions import (DropDownMenu, Entry_Box, FileSelect,
SimbaButton, hxtScrollbar)
from simba.utils.checks import (check_float, check_instance, check_int,
check_valid_lst)
from simba.ui.tkinter_functions import (DropDownMenu, Entry_Box, FileSelect, SimbaButton, hxtScrollbar)
from simba.utils.checks import (check_float, check_instance, check_int, check_valid_lst)
from simba.utils.enums import Formats, Options
from simba.utils.errors import CountError, NoFilesFoundError
from simba.utils.lookups import (get_color_dict, get_icons_paths,
get_named_colors)
from simba.utils.lookups import (get_color_dict, get_icons_paths, get_named_colors)
from simba.utils.read_write import find_core_cnt


Expand Down
2 changes: 1 addition & 1 deletion simba/roi_tools/ROI_multiply.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def multiply_ROIs(config_path: Union[str, os.PathLike],
:param Union[str, os.PathLike] config_path: Path to SimBA project config file.
:param Union[str, os.PathLike] filename: Path to video in project for which ROIs should be duplicated in the other videos in the project
:return: None
:return: None. The results are stored in the ``/project_folder/logs/measures\ROI_definitions.h5`` of the SimBA project
:example:
>>> multiply_ROIs(config_path=r"C:\troubleshooting\mitra\project_folder\project_config.ini", filename=r"C:\troubleshooting\mitra\project_folder\videos\501_MA142_Gi_CNO_0514.mp4")
Expand Down
160 changes: 73 additions & 87 deletions simba/roi_tools/ROI_reset.py
Original file line number Diff line number Diff line change
@@ -1,101 +1,87 @@
from typing import Union
import os
from configparser import ConfigParser
from tkinter import *

import pandas as pd

from simba.utils.enums import ConfigKey, Formats, Keys, Paths
from simba.utils.enums import ConfigKey, Keys, Paths, Links
from simba.utils.errors import NoROIDataError
from simba.utils.printing import stdout_trash
from simba.utils.read_write import get_fn_ext, read_config_file


def reset_video_ROIs(config_path, filename):
_, file_name_wo_ext, VideoExtension = get_fn_ext(filename)
config = ConfigParser()
configFile = str(config_path)
config.read(configFile)
vidInfPath = config.get(
ConfigKey.GENERAL_SETTINGS.value, ConfigKey.PROJECT_PATH.value
)
logFolderPath = os.path.join(vidInfPath, "logs")
ROIcoordinatesPath = os.path.join(logFolderPath, Paths.ROI_DEFINITIONS.value)
if not os.path.isfile(ROIcoordinatesPath):
raise NoROIDataError(
msg="Cannot delete ROI definitions: no definitions exist to delete"
)

else:
rectanglesInfo = pd.read_hdf(ROIcoordinatesPath, key=Keys.ROI_RECTANGLES.value)
circleInfo = pd.read_hdf(ROIcoordinatesPath, key=Keys.ROI_CIRCLES.value)
polygonInfo = pd.read_hdf(ROIcoordinatesPath, key=Keys.ROI_POLYGONS.value)
store = pd.HDFStore(ROIcoordinatesPath, mode="w")

try:
rectanglesInfo = rectanglesInfo[rectanglesInfo["Video"] != file_name_wo_ext]
except KeyError:
pass
store["rectangles"] = rectanglesInfo

try:
circleInfo = circleInfo[circleInfo["Video"] != file_name_wo_ext]
except KeyError:
pass
store["circleDf"] = circleInfo
from simba.utils.read_write import get_fn_ext, read_config_file, remove_files
from simba.utils.checks import check_file_exist_and_readable,check_valid_dataframe
from simba.ui.tkinter_functions import TwoOptionQuestionPopUp


def reset_video_ROIs(config_path: Union[str, os.PathLike],
filename: Union[str, os.PathLike]) -> None:

"""
Delete drawn ROIs for a single video in a SimBA project.
:param Union[str, os.PathLike] config_path: Path to SimBA project config file.
:param Union[str, os.PathLike] filename: Path to video in project for which ROIs should be duplicated in the other videos in the project
:return: None. The results are stored in the ``/project_folder/logs/measures\ROI_definitions.h5`` of the SimBA project
:example:
>>> reset_video_ROIs(config_path=r"C:\troubleshooting\mitra\project_folder\project_config.ini", filename=r"C:\troubleshooting\mitra\project_folder\videos\501_MA142_Gi_CNO_0514.mp4")
"""

check_file_exist_and_readable(file_path=config_path)
check_file_exist_and_readable(file_path=filename)
_, video_name, video_ext = get_fn_ext(filename)
config = read_config_file(config_path=config_path)
project_path = config.get(ConfigKey.GENERAL_SETTINGS.value, ConfigKey.PROJECT_PATH.value)
roi_coordinates_path = os.path.join(project_path, "logs", Paths.ROI_DEFINITIONS.value)
if not os.path.isfile(roi_coordinates_path):
raise NoROIDataError(msg=f"Cannot reset/delete ROI definitions: no ROI definitions exist in SimBA project. Could find find a file at expected location {roi_coordinates_path}. Create ROIs before deleting ROIs.", source=reset_video_ROIs.__name__)
with pd.HDFStore(roi_coordinates_path) as hdf: roi_data_keys = [x[1:] for x in hdf.keys()]
missing_keys = [x for x in roi_data_keys if x not in [Keys.ROI_RECTANGLES.value, Keys.ROI_CIRCLES.value, Keys.ROI_POLYGONS.value]]
if len(missing_keys) > 0:
raise NoROIDataError(msg=f'The ROI data file {roi_coordinates_path} is corrupted. Missing the following keys: {missing_keys}', source=reset_video_ROIs.__name__)

rectangles_df = pd.read_hdf(path_or_buf=roi_coordinates_path, key=Keys.ROI_RECTANGLES.value)
circles_df = pd.read_hdf(path_or_buf=roi_coordinates_path, key=Keys.ROI_CIRCLES.value)
polygon_df = pd.read_hdf(path_or_buf=roi_coordinates_path, key=Keys.ROI_POLYGONS.value)
check_valid_dataframe(df=rectangles_df, source=f'{reset_video_ROIs.__name__} rectangles_df', required_fields=['Video'])
check_valid_dataframe(df=circles_df, source=f'{reset_video_ROIs.__name__} circles_df', required_fields=['Video'])
check_valid_dataframe(df=polygon_df, source=f'{reset_video_ROIs.__name__} polygon_df', required_fields=['Video'])
video_rectangle_roi_records = rectangles_df[rectangles_df["Video"] == video_name]
video_circle_roi_records = circles_df[circles_df["Video"] == video_name]
video_polygon_roi_records = polygon_df[polygon_df["Video"] == video_name]
video_roi_cnt = len(video_rectangle_roi_records) + len(video_circle_roi_records) + len(video_polygon_roi_records)
if video_roi_cnt == 0:
raise NoROIDataError(msg=f"Cannot delete ROIs for video {video_name}: no ROI records exist for {video_name}. Create ROIs for for video {video_name} first", source=reset_video_ROIs.__name__)

store = pd.HDFStore(roi_coordinates_path, mode="w")
store[Keys.ROI_RECTANGLES.value] = rectangles_df[rectangles_df["Video"] != video_name]
store[Keys.ROI_CIRCLES.value] = circles_df[circles_df["Video"] != video_name]
store[Keys.ROI_POLYGONS.value] = polygon_df[polygon_df["Video"] != video_name]
store.close()
stdout_trash(msg=f"Deleted ROI records for video {video_name}. Deleted rectangle count: {len(video_rectangle_roi_records)}, circles: {len(video_circle_roi_records)}, polygons: {len(video_polygon_roi_records)}.")

try:
polygonInfo = polygonInfo[polygonInfo["Video"] != file_name_wo_ext]
except KeyError:
pass
store["polygons"] = polygonInfo
def delete_all_ROIs(config_path: Union[str, os.PathLike]) -> None:
"""
Launches a pop-up asking if to delete all SimBA roi definitions. If click yes, then the ``/project_folder/logs/measures\ROI_definitions.h5`` of the SimBA project is deleted.
print("Deleted ROI record: " + str(file_name_wo_ext))
store.close()
:param config_path: Path to SimBA project config file.
:return: None
:example:
>>> delete_all_ROIs(config_path=r"C:\troubleshooting\ROI_movement_test\project_folder\project_config.ini")
"""

def delete_all_ROIs(config_path: str):
def delete_file(config_path):
question = TwoOptionQuestionPopUp(title="WARNING!", question="Do you want to delete all defined ROIs in the project?", option_one="YES", option_two="NO", link=Links.ROI.value)
if question.selected_option == "YES":
check_file_exist_and_readable(file_path=config_path)
config = read_config_file(config_path=config_path)
project_path = config.get(
ConfigKey.GENERAL_SETTINGS.value, ConfigKey.PROJECT_PATH.value
)
roi_data_path = os.path.join(project_path, "logs", Paths.ROI_DEFINITIONS.value)

if not os.path.isfile(roi_data_path):
raise NoROIDataError(
msg=f"No ROI definitions exist in this SimBA project. Expected file at path {roi_data_path}"
)
project_path = config.get(ConfigKey.GENERAL_SETTINGS.value, ConfigKey.PROJECT_PATH.value)
roi_coordinates_path = os.path.join(project_path, "logs", Paths.ROI_DEFINITIONS.value)
if not os.path.isfile(roi_coordinates_path):
raise NoROIDataError(msg=f"Cannot delete ROI definitions: no ROI definitions exist in SimBA project. Could find find a file at expected location {roi_coordinates_path}. Create ROIs before deleting ROIs.", source=reset_video_ROIs.__name__)
else:
os.remove(roi_data_path)
close_window()
stdout_trash(
msg=f"SIMBA COMPLETE: All ROI definitions deleted in this SimBA project ({roi_data_path})"
)

def close_window():
delete_confirm_win.destroy()
delete_confirm_win.update()

delete_confirm_win = Toplevel()
delete_confirm_win.minsize(200, 200)

question_frame = LabelFrame(
delete_confirm_win, text="Confirm", font=("Arial", 16, "bold"), padx=5, pady=5
)
question_lbl = Label(
question_frame,
text="Do you want to delete all defined ROIs in the project?",
font=Formats.LABELFRAME_HEADER_FORMAT.value,
)
remove_files(file_paths=[roi_coordinates_path], raise_error=True)
stdout_trash(msg=f"Deleted all ROI records for video for the SimBA project (Deleted file {roi_coordinates_path}). USe the Define ROIs menu to create new ROIs.")
else:
pass

yes_button = Button(
question_frame, text="YES", fg="black", command=lambda: delete_file(config_path)
)
no_button = Button(
question_frame, text="NO", fg="black", command=lambda: close_window()
)

question_frame.grid(row=0, sticky=W)
question_lbl.grid(row=1, column=0, sticky=W, pady=10, padx=10)
yes_button.grid(row=2, column=1, sticky=W, pady=10, padx=10)
no_button.grid(row=2, column=2, sticky=W, pady=10, padx=10)
#delete_all_ROIs(config_path=r"C:\troubleshooting\ROI_movement_test\project_folder\project_config.ini")
42 changes: 13 additions & 29 deletions simba/ui/tkinter_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,43 +363,27 @@ class TwoOptionQuestionPopUp(object):
:parameter Optional[str] link: If not None, then a link to documentation presenting background info about the user choices.
"""

def __init__(
self,
question: str,
option_one: str,
option_two: str,
title: str,
link: Optional[str] = None,
):
def __init__(self,
question: str,
option_one: str,
option_two: str,
title: str,
link: Optional[str] = None):

self.main_frm = Toplevel()
self.main_frm.geometry("600x200")
self.main_frm.title(title)

question_frm = Frame(self.main_frm)
question_frm.pack(expand=True, fill="both")
Label(
question_frm, text=question, font=Formats.LABELFRAME_HEADER_FORMAT.value
).pack()
button_one = Button(
question_frm,
text=option_one,
fg="blue",
command=lambda: self.run(option_one),
)
button_two = Button(
question_frm,
text=option_two,
fg="red",
command=lambda: self.run(option_two),
)
Label(question_frm, text=question, font=Formats.LABELFRAME_HEADER_FORMAT.value).pack()

button_one = SimbaButton(parent=question_frm, txt=option_one, txt_clr="blue", bg_clr="lightgrey", img='check_blue', cmd=self.run, cmd_kwargs={'selected_option': lambda: option_one}, font=Formats.FONT_LARGE.value)
button_two = SimbaButton(parent=question_frm, txt=option_two, txt_clr="red", bg_clr="lightgrey", img='close', cmd=self.run, cmd_kwargs={'selected_option': lambda: option_two}, font=Formats.FONT_LARGE.value)
#button_one = Button(question_frm, text=option_one, fg="blue", command=lambda: self.run(option_one))
#button_two = Button(question_frm, text=option_two, fg="red", command=lambda: self.run(option_two))
if link:
link_lbl = Label(
question_frm,
text="Click here for more information.",
cursor="hand2",
fg="blue",
)
link_lbl = Label(question_frm, text="Click here for more information.", cursor="hand2", fg="blue")
link_lbl.bind("<Button-1>", lambda e: callback(link))
link_lbl.place(relx=0.5, rely=0.30, anchor=CENTER)
button_one.place(relx=0.5, rely=0.50, anchor=CENTER)
Expand Down
14 changes: 3 additions & 11 deletions simba/utils/read_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -1702,9 +1702,7 @@ def copy_files_in_directory(in_dir: Union[str, os.PathLike],
shutil.copy(file_path, out_dir)


def remove_files(
file_paths: List[Union[str, os.PathLike]], raise_error: Optional[bool] = False
) -> None:
def remove_files(file_paths: List[Union[str, os.PathLike]], raise_error: Optional[bool] = False) -> None:
"""
Delete (remove) the files specified within a list of filepaths.
Expand All @@ -1718,21 +1716,15 @@ def remove_files(

for file_path in file_paths:
if not os.path.isfile(file_path) and raise_error:
raise NoFilesFoundError(
msg=f"Cannot delete {file_path}. File does not exist",
source=remove_files.__name__,
)
raise NoFilesFoundError(msg=f"Cannot delete {file_path}. File does not exist", source=remove_files.__name__)
elif not os.path.isfile(file_path):
pass
else:
try:
os.remove(file_path)
except:
if raise_error:
raise PermissionError(
msg=f"Cannot read {file_path}. Is the file open in an alternative app?",
source=remove_files.__name__,
)
raise PermissionError(msg=f"Cannot read {file_path}. Is the file open in an alternative app?", source=remove_files.__name__)
else:
pass

Expand Down

0 comments on commit acf6211

Please sign in to comment.