Skip to content

Commit

Permalink
Merge pull request #470 from catalystneuro/enhance_alignment_method_n…
Browse files Browse the repository at this point in the history
…ames

[Sync VIIc] Final alignment cleanup and name changes
  • Loading branch information
CodyCBakerPhD authored Jun 7, 2023
2 parents ac52bbc + eb035e0 commit 42c2690
Show file tree
Hide file tree
Showing 22 changed files with 382 additions and 363 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
Changed `get_default_ophys_metadata()` to add `Device` and `ImagingPlane` metadata which are both used by imaging and segmentation.
Added `photon_series_type` to `get_nwb_imaging_metadata()` to fill metadata for `OnePhotonSeries` or `TwoPhotonSeries`. [PR #462](https://github.com/catalystneuro/neuroconv/pull/462)
* Split `align_timestamps` and `align_starting_times` into `align_segment_timestamps` and `align_segment_starting_times` for API consistency for multi-segment `RecordingInterface`s. [PR #463](https://github.com/catalystneuro/neuroconv/pull/463)
* Rename `align_timestamps` and `align_segmentt_timestamps` into `set_aligned_timestamps` and `set_aligned_segment_timestamps` to more clearly indicate their usage and behavior. [PR #470](https://github.com/catalystneuro/neuroconv/pull/470)

### Testing
* The tests for `automatic_dandi_upload` now follow up-to-date DANDI validation rules for file name conventions. [PR #310](https://github.com/catalystneuro/neuroconv/pull/310)
Expand Down
10 changes: 5 additions & 5 deletions src/neuroconv/basetemporalalignmentinterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def get_timestamps(self) -> np.ndarray:
)

@abstractmethod
def align_timestamps(self, aligned_timestamps: np.ndarray):
def set_aligned_timestamps(self, aligned_timestamps: np.ndarray):
"""
Replace all timestamps for this interface with those aligned to the common session start time.
Expand All @@ -55,18 +55,18 @@ def align_timestamps(self, aligned_timestamps: np.ndarray):
"The protocol for synchronizing the timestamps of this interface has not been specified!"
)

def align_starting_time(self, starting_time: float):
def set_aligned_starting_time(self, aligned_starting_time: float):
"""
Align the starting time for this interface relative to the common session start time.
Must be in units seconds relative to the common 'session_start_time'.
Parameters
----------
starting_time : float
aligned_starting_time : float
The starting time for all temporal data in this interface.
"""
self.align_timestamps(aligned_timestamps=self.get_timestamps() + starting_time)
self.set_aligned_timestamps(aligned_timestamps=self.get_timestamps() + aligned_starting_time)

def align_by_interpolation(self, unaligned_timestamps: np.ndarray, aligned_timestamps: np.ndarray):
"""
Expand All @@ -89,6 +89,6 @@ def align_by_interpolation(self, unaligned_timestamps: np.ndarray, aligned_times
aligned_timestamps : numpy.ndarray
The timestamps aligned to the primary time basis.
"""
self.align_timestamps(
self.set_aligned_timestamps(
aligned_timestamps=np.interp(x=self.get_timestamps(), xp=unaligned_timestamps, fp=aligned_timestamps)
)
34 changes: 17 additions & 17 deletions src/neuroconv/datainterfaces/behavior/audio/audiointerface.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,54 +95,54 @@ def get_original_timestamps(self) -> np.ndarray:
def get_timestamps(self) -> Optional[np.ndarray]:
raise NotImplementedError("The AudioInterface does not yet support timestamps.")

def align_timestamps(self, aligned_timestamps: List[np.ndarray]):
def set_aligned_timestamps(self, aligned_timestamps: List[np.ndarray]):
raise NotImplementedError("The AudioInterface does not yet support timestamps.")

def align_starting_time(self, starting_time: float):
def set_aligned_starting_time(self, aligned_starting_time: float):
"""
Align all starting times for all audio files in this interface relative to the common session start time.
Must be in units seconds relative to the common 'session_start_time'.
Parameters
----------
starting_time : float
aligned_starting_time : float
The common starting time for all temporal data in this interface.
Applies to all segments if there are multiple file paths used by the interface.
"""
if self._segment_starting_times is None and self._number_of_audio_files == 1:
self._segment_starting_times = [starting_time]
self._segment_starting_times = [aligned_starting_time]
elif self._segment_starting_times is not None and self._number_of_audio_files > 1:
self._segment_starting_times = [
segment_starting_time + starting_time for segment_starting_time in self._segment_starting_times
segment_starting_time + aligned_starting_time for segment_starting_time in self._segment_starting_times
]
else:
raise ValueError(
"There are no segment starting times to shift by a common value! "
"Please set them using 'align_segment_starting_times'."
"Please set them using 'set_aligned_segment_starting_times'."
)

def align_segment_starting_times(self, segment_starting_times: List[float]):
def set_aligned_segment_starting_times(self, aligned_segment_starting_times: List[float]):
"""
Align the individual starting time for each audio file in this interface relative to the common session start time.
Must be in units seconds relative to the common 'session_start_time'.
Parameters
----------
segment_starting_times : list of floats
aligned_segment_starting_times : list of floats
The relative starting times of each audio file (segment).
"""
segment_starting_times_length = len(segment_starting_times)
assert isinstance(segment_starting_times, list) and all(
[isinstance(x, float) for x in segment_starting_times]
), "Argument 'segment_starting_times' must be a list of floats."
assert segment_starting_times_length == self._number_of_audio_files, (
f"The number of entries in 'segment_starting_times' ({segment_starting_times_length}) must be equal to the number of "
f"audio file paths ({self._number_of_audio_files})."
aligned_segment_starting_times_length = len(aligned_segment_starting_times)
assert isinstance(aligned_segment_starting_times, list) and all(
[isinstance(x, float) for x in aligned_segment_starting_times]
), "Argument 'aligned_segment_starting_times' must be a list of floats."
assert aligned_segment_starting_times_length == self._number_of_audio_files, (
f"The number of entries in 'aligned_segment_starting_times' ({aligned_segment_starting_times_length}) "
f"must be equal to the number of audio file paths ({self._number_of_audio_files})."
)

self._segment_starting_times = segment_starting_times
self._segment_starting_times = aligned_segment_starting_times

def align_by_interpolation(self, unaligned_timestamps: np.ndarray, aligned_timestamps: np.ndarray):
raise NotImplementedError("The AudioInterface does not yet support timestamps.")
Expand Down Expand Up @@ -200,7 +200,7 @@ def run_conversion(
if self._number_of_audio_files > 1 and self._segment_starting_times is None:
raise ValueError(
"If you have multiple audio files, then you must specify each starting time by calling "
"'.align_segment_starting_times(segment_starting_times=...)'!"
"'.set_aligned_segment_starting_times(...)'!"
)
starting_times = self._segment_starting_times or [0.0]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def get_timestamps(self) -> np.ndarray:
"Unable to retrieve timestamps for this interface! Define the `get_timestamps` method for this interface."
)

def align_timestamps(self, aligned_timestamps: np.ndarray):
def set_aligned_timestamps(self, aligned_timestamps: np.ndarray):
raise NotImplementedError(
"The protocol for synchronizing the timestamps of this interface has not been specified!"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def get_timestamps(self) -> np.ndarray:
timestamps = self._timestamps if self._timestamps is not None else self.get_original_timestamps()
return timestamps

def align_timestamps(self, aligned_timestamps: np.ndarray):
def set_aligned_timestamps(self, aligned_timestamps: np.ndarray):
self._timestamps = aligned_timestamps

def run_conversion(
Expand Down
31 changes: 15 additions & 16 deletions src/neuroconv/datainterfaces/behavior/video/videodatainterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def get_timestamps(self, stub_test: bool = False) -> List[np.ndarray]:
"""
return self._timestamps or self.get_original_timestamps(stub_test=stub_test)

def align_timestamps(self, aligned_timestamps: List[np.ndarray]):
def set_aligned_timestamps(self, aligned_timestamps: List[np.ndarray]):
"""
Replace all timestamps for this interface with those aligned to the common session start time.
Expand All @@ -157,15 +157,15 @@ def align_timestamps(self, aligned_timestamps: List[np.ndarray]):
), "If setting both timestamps and starting times, please set the timestamps first so they can be shifted by the starting times."
self._timestamps = aligned_timestamps

def align_starting_time(self, starting_time: float, stub_test: bool = False):
def set_aligned_starting_time(self, aligned_starting_time: float, stub_test: bool = False):
"""
Align all starting times for all videos in this interface relative to the common session start time.
Must be in units seconds relative to the common 'session_start_time'.
Parameters
----------
starting_time : float
aligned_starting_time : float
The common starting time for all segments of temporal data in this interface.
stub_test : bool, default: False
If timestamps have not been set to this interface, it will attempt to retrieve them
Expand All @@ -175,19 +175,18 @@ def align_starting_time(self, starting_time: float, stub_test: bool = False):
To limit that scan to a small number of frames, set `stub_test=True`.
"""
if self._timestamps is not None:
self.align_timestamps(
aligned_timestamps=[
timestamps + starting_time for timestamps in self.get_timestamps(stub_test=stub_test)
]
)
aligned_timestamps = [
timestamps + aligned_starting_time for timestamps in self.get_timestamps(stub_test=stub_test)
]
self.set_aligned_timestamps(aligned_timestamps=aligned_timestamps)
elif self._segment_starting_times is not None:
self._segment_starting_times = [
starting_time + starting_time for starting_time in self._segment_starting_times
segment_starting_time + aligned_starting_time for segment_starting_time in self._segment_starting_times
]
else:
raise ValueError("There are no timestamps or starting times set to shift by a common value!")

def align_segment_starting_times(self, segment_starting_times: List[float], stub_test: bool = False):
def set_aligned_segment_starting_times(self, aligned_segment_starting_times: List[float], stub_test: bool = False):
"""
Align the individual starting time for each video (segment) in this interface relative to the common session start time.
Expand All @@ -204,22 +203,22 @@ def align_segment_starting_times(self, segment_starting_times: List[float], stub
To limit that scan to a small number of frames, set `stub_test=True`.
"""
segment_starting_times_length = len(segment_starting_times)
assert segment_starting_times_length == self._number_of_files, (
f"The length of the 'segment_starting_times' list ({segment_starting_times_length}) does not match the "
aligned_segment_starting_times_length = len(aligned_segment_starting_times)
assert aligned_segment_starting_times_length == self._number_of_files, (
f"The length of the 'aligned_segment_starting_times' list ({aligned_segment_starting_times_length}) does not match the "
"number of video files ({self._number_of_files})!"
)
if self._timestamps is not None:
self.align_timestamps(
self.set_aligned_timestamps(
aligned_timestamps=[
timestamps + segment_starting_time
for timestamps, segment_starting_time in zip(
self.get_timestamps(stub_test=stub_test), segment_starting_times
self.get_timestamps(stub_test=stub_test), aligned_segment_starting_times
)
]
)
else:
self._segment_starting_times = segment_starting_times
self._segment_starting_times = aligned_segment_starting_times

def align_by_interpolation(self, unaligned_timestamps: np.ndarray, aligned_timestamps: np.ndarray):
raise NotImplementedError("The `align_by_interpolation` method has not been developed for this interface yet.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,14 @@ def get_timestamps(self) -> Union[np.ndarray, List[np.ndarray]]:
for segment_index in range(self._number_of_segments)
]

def align_timestamps(self, aligned_timestamps: np.ndarray):
def set_aligned_timestamps(self, aligned_timestamps: np.ndarray):
assert (
self._number_of_segments == 1
), "This recording has multiple segments; please use 'align_segment_timestamps' instead."

self.recording_extractor.set_times(times=aligned_timestamps)

def align_segment_timestamps(self, aligned_segment_timestamps: List[np.ndarray]):
def set_aligned_segment_timestamps(self, aligned_segment_timestamps: List[np.ndarray]):
"""
Replace all timestamps for all segments in this interface with those aligned to the common session start time.
Expand All @@ -186,54 +186,57 @@ def align_segment_timestamps(self, aligned_segment_timestamps: List[np.ndarray])
), "Recording has multiple segment! Please pass a list of timestamps to align each segment."
assert (
len(aligned_segment_timestamps) == self._number_of_segments
), f"The number of timestamp vectors ({len(aligned_timestamps)}) does not match the number of segments ({self._number_of_segments})!"
), f"The number of timestamp vectors ({len(aligned_segment_timestamps)}) does not match the number of segments ({self._number_of_segments})!"

for segment_index in range(self._number_of_segments):
self.recording_extractor.set_times(
times=aligned_segment_timestamps[segment_index], segment_index=segment_index
)

def align_starting_time(self, starting_time: float):
def set_aligned_starting_time(self, aligned_starting_time: float):
if self._number_of_segments == 1:
self.align_timestamps(aligned_timestamps=self.get_timestamps() + starting_time)
self.set_aligned_timestamps(aligned_timestamps=self.get_timestamps() + aligned_starting_time)
else:
self.align_segment_timestamps(
self.set_aligned_segment_timestamps(
aligned_segment_timestamps=[
segment_timestamps + starting_time for segment_timestamps in self.get_timestamps()
segment_timestamps + aligned_starting_time for segment_timestamps in self.get_timestamps()
]
)

def align_segment_starting_times(self, segment_starting_times: List[float]):
def set_aligned_segment_starting_times(self, aligned_segment_starting_times: List[float]):
"""
Align the starting time for each segment in this interface relative to the common session start time.
Must be in units seconds relative to the common 'session_start_time'.
Parameters
----------
segment_starting_times : list of floats
aligned_segment_starting_times : list of floats
The starting time for each segment of data in this interface.
"""
assert (
len(segment_starting_times) == self._number_of_segments
), f"The length of the starting_times ({len(starting_times)}) does not match the number of segments ({self._number_of_segments})!"
assert len(aligned_segment_starting_times) == self._number_of_segments, (
f"The length of the starting_times ({len(aligned_segment_starting_times)}) does not match the "
"number of segments ({self._number_of_segments})!"
)

if self._number_of_segments == 1:
self.align_starting_time(starting_time=segment_starting_times[0])
self.set_aligned_starting_time(aligned_starting_time=aligned_segment_starting_times[0])
else:
aligned_segment_timestamps = [
segment_timestamps + segment_starting_time
for segment_timestamps, segment_starting_time in zip(self.get_timestamps(), segment_starting_times)
segment_timestamps + aligned_segment_starting_time
for segment_timestamps, aligned_segment_starting_time in zip(
self.get_timestamps(), aligned_segment_starting_times
)
]
self.align_segment_timestamps(aligned_segment_timestamps=aligned_segment_timestamps)
self.set_aligned_segment_timestamps(aligned_segment_timestamps=aligned_segment_timestamps)

def align_by_interpolation(
self,
unaligned_timestamps: np.ndarray,
aligned_timestamps: np.ndarray,
):
if self._number_of_segments == 1:
self.align_timestamps(
self.set_aligned_timestamps(
aligned_timestamps=np.interp(x=self.get_timestamps(), xp=unaligned_timestamps, fp=aligned_timestamps)
)
else:
Expand Down
Loading

0 comments on commit 42c2690

Please sign in to comment.