Skip to content

ROI statistics - Entry / Exits #879

@SairajLoke

Description

@SairajLoke

Is your feature request related to a problem? Please describe.

I'll like to raise PRs for adding ROI statistics which are currently lacking in movement, starting with function for computing entries/exits.

What : Event Statistics for each region and individual which may include(non-exhaustively)

  • entry and exits between regions
  • dwell time
  • co-occupancy, etc

Why : Used in research like 1, and as discussed in
#418 conditional arrays proposal

Describe the solution you'd like

For now, i've considered implementing entry/exit count first, then move to other events
You can find the demo use of the compute_entry_exits here - github-gist

Expected Output (visualized):
image

image Image

notice how using more keypoints with mode = 'all', decrease entires/exits

Describe solution you've considered

Possible Usage

entries_and_exits = compute_entry_exits(
    data=positions_filtered.isel(time=slice(2000, 2500)),
    keypoints=["centre", "snout", "tail_base"],
    mode="all",
    regions=[
        center_region,
        open_left_region,
        open_right_region,
        closed_top_region,
        closed_bottom_region,
    ],
    min_frames_transition=10,
)

See
github-gist OR
github-gist in interactive binder env for more detailed demo:

Planned implementation: compute_entry_exits
#compute_entry_exits:  (see below discussion for location of this function)

def compute_entry_exits(
    data: xr.DataArray,
    keypoints: str,
    mode: Literal["centroid", "all", "any", "majority"],
    regions: ROICollection,
    min_frames_transition: int,
) -> xr.DataArray:

   if isinstance(keypoints, str):
      keypoints = [keypoints]

   data_sel = data.sel(keypoints=keypoints) #('time', 'space', 'keypoints', 'individuals') 

   assert not np.isnan(data_sel).any(), (
      "Expecting NaN values handled beforehand"
   )
   
   region_names = []
   entry_list = []
   exit_list = []
   #temporal_occupancies = {} #single array maybe useful for dwelltime, en/ex, co-occupancy

   for r in regions:
      
      name = r.name
      region_names.append(name)
      
      if mode == "centroid":
         centroid = data_sel.mean(dim="keypoints")
         occupancies = r.contains_point(centroid)

      else:
         raw_occ = r.contains_point(data_sel)

         if mode == "all":
               occupancies = raw_occ.all(dim="keypoints")

         elif mode == "any":
               occupancies = raw_occ.any(dim="keypoints")

         elif mode == "majority":
               occupancies = raw_occ.mean(dim="keypoints") > 0.5

         else:
               raise ValueError(
                  "mode must be one of: centroid, all, any, majority" #or maybe top-k out of n-keypoints
               )

      occupancies = occupancies.astype(int)

      # temporal filtering to suppress noisy transitions check 
      occupancies = (
         occupancies
         .rolling(time=min_frames_transition)
         .sum()
         >= min_frames_transition
      ).astype(int)

      diff_occupancies = occupancies.diff("time").astype(int)  #('time', 'individuals')

      # print(np.unique(diff_occupancies.values))# values be -1,0,1
      entry_counts = (diff_occupancies == 1).sum(dim="time") #( individuals)
      exit_counts = (diff_occupancies == -1).sum(dim="time")

      entry_list.append(entry_counts)
      exit_list.append(exit_counts)


   # stack regions
   entries = xr.concat(entry_list, dim="region")
   exits = xr.concat(exit_list, dim="region")

   entries = entries.assign_coords(region=region_names)
   exits = exits.assign_coords(region=region_names)

   # add event dimension
   entries = entries.expand_dims(event=["entry"])
   exits = exits.expand_dims(event=["exit"])

   result = xr.concat([entries, exits], dim="event")
   result.name = "entry_exit_counts"

   return result

Additional questions / Discussion

Design choice:
Case 1: roi.conditions.compute_entry_exits:

  1. Putting compute_entry_exits in roi.conditions implies compute_entry_exits returns some kinda boolean array as implied from the name conditions but it is rather a data array involving integer value information about some aspect of the ROI and not like 'compute_region_occupancy'....should I create a new module named 'events' withing roi?

Case 2: separate roi.events file, with roi.events.compute_entry_exits or rather an roi.event class :

  1. isolated module concerned with events happening with ROIs
  2. class might help to share common data array ( ex, temporal region occupancies req to calculate other aspects like : en/ex events (diff along time, min_transition_window)
  • dwell time(sum occupancy along 'time'),
  • co-occupancy(AND operation btw occupancies of selected individuals, and sum result along'time'), etc)
  1. may help in adding more events mentioned above.
    Also thought of instead storing entry/exit events instead of occupancy but occupancy seem to be more useful for different event rather than just entry/exit times

Challenges:

  1. multiple keypoints transition scenario, as mentioned by @niksirbi here- zulip discussion

soln

  • to condense the keypoints dim to single value based on mode provided by user(all/any/majority/centroid)
  • implementation similar to above single keypoint, with just 1 diff, i do an additionl step using over the multi-keypoint -> single value (see the code for compute_entry_exits above)

  1. Sensitivity to noisy detections at borders

soln

  • add a window check to filter noisy transitions
  • should we do a window check like in above code or assume(and mention) that user does approp. filtering/smoothening?

Would like to know @niksirbi , if there are any suggestions before raising a PR. Thanks !

Related Issues

#377: on defining ROIs |

#418: conditional arrays proposal |

#421: region occupancy |

Implementation Plan
.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    🤔 Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions