Skip to content

Commit

Permalink
implemented functionality for copying ROIs from another video
Browse files Browse the repository at this point in the history
  • Loading branch information
niksirbi committed Nov 8, 2023
1 parent 9ac22d0 commit dbba2e6
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 1 deletion.
78 changes: 77 additions & 1 deletion wazp/callbacks/roi.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,53 @@ def update_roi_select_options(
else:
return dash.no_update, dash.no_update, dash.no_update

@app.callback(
[
Output("copy-rois-video-select", "options"),
Output("copy-rois-video-select", "value"),
],
[
Input("roi-storage", "data"),
Input("video-select", "value"),
],
)
def update_copy_rois_from_video_select_options(
roi_storage, video_path
) -> Optional[tuple[list[dict], str]]:
"""Update the options of the video select dropdown for copying ROIs.
Parameters
----------
roi_storage : dict
Dictionary storing ROI data for each video.
video_path : str
Path to the current video file.
Returns
-------
list[dict]
list of dictionaries with keys 'label' and 'value'
str
value of the first video in the list
"""
current_video_name = pl.Path(video_path).name
# Find all videos with ROIs in the storage that are not the current video
videos_with_rois = [
video_name
for video_name in roi_storage.keys()
if (len(roi_storage[video_name]["shapes"]) > 0)
and (video_name != current_video_name)
]
# Create the options for the dropdown
options = [{"label": v, "value": v} for v in videos_with_rois]
# If there are other videos with ROIs
if len(options) == 0:
# Display a message in the dropdown
options = [{"label": "No other videos with defined ROIs.", "value": "none"}]
# Set the value to the first video in the list
value = options[0]["value"]
return options, value

@app.callback(
[
Output("frame-slider", "max"),
Expand Down Expand Up @@ -309,6 +356,7 @@ def set_roi_color_in_table(roi_table: list, roi_color_mapping: dict) -> list:
Input("frame-graph", "relayoutData"),
Input("load-rois-button", "n_clicks"),
Input("delete-rois-button", "n_clicks"),
Input("copy-rois-button", "n_clicks"),
],
[
State("video-select", "value"),
Expand All @@ -317,25 +365,29 @@ def set_roi_color_in_table(roi_table: list, roi_color_mapping: dict) -> list:
State("roi-colors-storage", "data"),
State("roi-table", "data"),
State("roi-table", "selected_rows"),
State("copy-rois-video-select", "value"),
],
)
def update_roi_storage(
graph_relayout: dict,
load_clicks: int,
delete_clicks: int,
copy_clicks: int,
video_path: str,
frame_num: int,
roi_storage: dict,
roi_color_mapping: dict,
roi_table_rows: list,
roi_table_selected_rows: list,
video_to_copy_from: str,
) -> tuple[dict, list]:
"""
Update the ROI storage, when:
- Shapes are added/removed from the frame graph
- Shapes are edited on the frame graph
- Shapes are loaded from file
- Shapes are deleted from the ROI table
- Shapes are copied from another video
Parameters
----------
Expand All @@ -346,8 +398,10 @@ def update_roi_storage(
Number of times the load ROIs button has been clicked.
delete_clicks : int
Number of times the delete ROIs button has been clicked.
copy_clicks : int
Number of times the copy ROIs button has been clicked.
video_path : str
Path to the video file.
Path to the current video file.
frame_num : int
Frame number.
roi_storage : dict
Expand All @@ -360,6 +414,8 @@ def update_roi_storage(
List of dictionaries with ROI table data.
roi_table_selected_rows : list
List of indices for the selected rows in the ROI table.
video_to_copy_from : str
Name of the video to copy ROIs from.
Returns
-------
Expand Down Expand Up @@ -462,6 +518,14 @@ def update_roi_storage(
# Clear the row selection
roi_table_selected_rows = []

# If triggered by the copy ROIs button click
# Copy the ROIs from the selected video
elif trigger == "copy-rois-button.n_clicks":
if copy_clicks > 0 and video_to_copy_from != "none":
roi_storage[video_name]["shapes"] = roi_storage[video_to_copy_from][
"shapes"
]

return roi_storage, roi_table_selected_rows

@app.callback(
Expand Down Expand Up @@ -837,3 +901,15 @@ def disable_delete_rois_button(
Whether to enable the delete ROIs button.
"""
return len(selected_rows) == 0

@app.callback(
Output("copy-rois-button", "disabled"),
Input("copy-rois-video-select", "value"),
)
def disable_copy_rois_button(
video_to_copy_from: str,
) -> bool:
"""If there are no videos to copy ROIs from,
disable the 'Copy ROIs from' button
"""
return video_to_copy_from == "none"
25 changes: 25 additions & 0 deletions wazp/pages/02_ROI.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
init_videos = ["No videos found yet"]
# Get initial set of ROIs to initialize dropdown
init_roi_names = ["No ROIs defined yet"]
# Initial value for copy ROIs dropdown
init_copy_rois_video = ["No videos with defined ROIs yet"]
# Default color for ROI drawing
init_roi_color = px.colors.qualitative.Dark2[0]
# Initialize the frame slider parameters for each video
Expand Down Expand Up @@ -134,6 +136,15 @@
clearable=False,
)

# Dropdown for selecting the video to copy ROIs from
copy_rois_dropdown = dcc.Dropdown(
id="copy-rois-video-select",
placeholder="Select video to copy ROIs from",
options=[{"label": v, "value": v} for v in init_copy_rois_video],
value=init_copy_rois_video[0],
clearable=False,
)

# Buttons for saving/loading ROIs
disabled_button_style = {
"n_clicks": 0,
Expand All @@ -143,6 +154,13 @@
"class_name": "w-100",
}

# Button for copying ROIs
copy_rois_button = dbc.Button(
"Copy from",
id="copy-rois-button",
**disabled_button_style,
)

save_rois_button = dbc.Button(
"Save all",
id="save-rois-button",
Expand Down Expand Up @@ -221,6 +239,13 @@
[
dbc.CardHeader(
[
dbc.Row(
[
dbc.Col(copy_rois_button, width=3),
dbc.Col(dcc.Loading(copy_rois_dropdown), width=9),
]
),
html.Br(),
dbc.Row(
[
dbc.Col(delete_rois_button, width={"size": "auto"}),
Expand Down

0 comments on commit dbba2e6

Please sign in to comment.