From 125a09c659fe90ee7ca0a048ab61a58e18ede190 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 17 Jan 2022 18:13:38 +0530 Subject: [PATCH 01/70] add video capture context --- .../behavior/movie/movie_utils.py | 175 +++++++++++++----- 1 file changed, 126 insertions(+), 49 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index bbf92595c..1fb5d332f 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -1,7 +1,9 @@ -"""Authors: Cody Baker.""" +"""Authors: Saksham Sharda, Cody Baker.""" from pathlib import Path +from typing import Union, Tuple + import numpy as np -from typing import Union +from tqdm import tqdm try: import cv2 @@ -13,50 +15,125 @@ PathType = Union[str, Path] -def get_movie_timestamps(movie_file: PathType): - """ - Return numpy array of the timestamps for a movie file. - - Parameters - ---------- - movie_file : PathType - """ - cap = cv2.VideoCapture(str(movie_file)) - timestamps = [cap.get(cv2.CAP_PROP_POS_MSEC)] - success, frame = cap.read() - while success: - timestamps.append(cap.get(cv2.CAP_PROP_POS_MSEC)) - success, frame = cap.read() - cap.release() - return np.array(timestamps) - - -def get_movie_fps(movie_file: PathType): - """ - Return the internal frames per second (fps) for a movie file. - - Parameters - ---------- - movie_file : PathType - """ - cap = cv2.VideoCapture(str(movie_file)) - if int((cv2.__version__).split(".")[0]) < 3: - fps = cap.get(cv2.cv.CV_CAP_PROP_FPS) - else: - fps = cap.get(cv2.CAP_PROP_FPS) - cap.release() - return fps - - -def get_frame_shape(movie_file: PathType): - """ - Return the shape of frames from a movie file. - - Parameters - ---------- - movie_file : PathType - """ - cap = cv2.VideoCapture(str(movie_file)) - success, frame = cap.read() - cap.release() - return frame.shape +class VideoCaptureContext(cv2.VideoCapture): + def __init__(self, *args, stub=False, **kwargs): + self._args = args + self._kwargs = kwargs + super().__init__(*args, **kwargs) + self.stub = stub + self._current_frame = 0 + self.frame_count = self.get_movie_frame_count() + self.fps = self.get_movie_fps() + self.frame = self.get_movie_frame(0) + assert self.frame is not None, "unable to read the movie file provided" + + def get_movie_timestamps(self): + """ + Return numpy array of the timestamps for a movie file. + + """ + if not self.isOpened(): + raise ValueError("movie file is not open") + ts = [self.get(cv2.CAP_PROP_POS_MSEC)] + for i in tqdm(range(1, self.get_movie_frame_count()), desc="retrieving video timestamps"): + self.current_frame = i + ts.append(self.get(cv2.CAP_PROP_POS_MSEC)) + self.current_frame = 0 + return np.array(ts) + + def get_movie_fps(self): + """ + Return the internal frames per second (fps) for a movie file. + + """ + if int(cv2.__version__.split(".")[0]) < 3: + return self.get(cv2.cv.CV_CAP_PROP_FPS) + return self.get(cv2.CAP_PROP_FPS) + + def get_frame_shape(self) -> Tuple: + """ + Return the shape of frames from a movie file. + """ + return self.frame.shape + + def get_movie_frame_count(self): + """ + Return the total number of frames for a movie file. + + """ + if self.stub: + # if stub the assume a max frame count of 10 + return 10 + if int(cv2.__version__.split(".")[0]) < 3: + count = self.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT) + else: + count = self.get(cv2.CAP_PROP_FRAME_COUNT) + return int(count) + + @property + def current_frame(self): + return self._current_frame + + @current_frame.setter + def current_frame(self, frame_no): + if int(cv2.__version__.split(".")[0]) < 3: + set_arg = cv2.cv.CV_CAP_PROP_POS_FRAMES + else: + set_arg = cv2.CAP_PROP_POS_FRAMES + set_value = self.set(set_arg, frame_no) + if set_value: + self._current_frame = frame_no + else: + raise ValueError(f"could not set frame no {frame_no}") + + def get_movie_frame(self, frame_no: int): + """ + Return the specific frame from a movie. + """ + if not self.isOpened(): + raise ValueError("movie file is not open") + assert frame_no < self.get_movie_frame_count(), "frame number is greater than length of movie" + self.current_frame = frame_no + success, frame = self.read() + self.current_frame = 0 + if success: + return frame + elif frame_no > 0: + return np.nan*np.ones(self.get_frame_shape()) + + def get_movie_frame_dtype(self): + """ + Return the dtype for frame in a movie file. + """ + return self.frame.dtype + + def __iter__(self): + return self + + def __next__(self): + if not self.isOpened(): + raise StopIteration + try: + if self.current_frame < self.frame_count: + success, frame = self.read() + self.current_frame += 1 + if success: + return frame + else: + return np.nan*np.ones(self.get_frame_shape()) + else: + self.current_frame = 0 + raise StopIteration + except Exception: + raise StopIteration + + def __enter__(self): + if not self.isOpened(): + super().__init__(*self._args, **self._kwargs) + return self + + def __exit__(self, *args): + self.release() + + def __del__(self): + self.release() From dbf31cfbc1aa4794ecb56182b9b060f20c15533d Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 17 Jan 2022 18:15:19 +0530 Subject: [PATCH 02/70] use videocapture context, allow multiple video files for external mode, --- .../behavior/movie/moviedatainterface.py | 120 +++++++++--------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 4c9a8d7bb..c7c6a47d2 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -11,12 +11,10 @@ from hdmf.backends.hdf5.h5_utils import H5DataIO from hdmf.data_utils import DataChunkIterator -from .movie_utils import get_movie_timestamps, get_movie_fps, get_frame_shape from ....basedatainterface import BaseDataInterface from ....utils.conversion_tools import check_regular_timestamps, get_module from ....utils.json_schema import get_schema_from_hdmf_class, get_base_schema - try: import cv2 @@ -83,6 +81,7 @@ def run_conversion( chunk_data: bool = True, module_name: Optional[str] = None, module_description: Optional[str] = None, + compression: str = "gzip" ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -105,6 +104,8 @@ def run_conversion( and may contain most keywords normally accepted by an ImageSeries (https://pynwb.readthedocs.io/en/stable/pynwb.image.html#pynwb.image.ImageSeries). The list for the 'Movies' key should correspond one to one to the movie files in the file_paths list. + If multiple movies need to be in the same ImageSeries, then supply the same value for "name" key. + Multiple movies in same ImageSeries is only supported if 'external_mode'=True. stub_test : bool, optional If True, truncates the write operation for fast testing. The default is False. external_mode : bool, optional @@ -128,6 +129,8 @@ def run_conversion( If the processing module specified by module_name does not exist, it will be created with this description. The default description is the same as used by the conversion_tools.get_module function. """ + from .movie_utils import VideoCaptureContext + file_paths = self.source_data["file_paths"] if stub_test: @@ -151,88 +154,83 @@ def run_conversion( f"({len(image_series_kwargs_list)}) vs. file_paths ({len(self.source_data['file_paths'])})!" ) - for j, file in enumerate(file_paths): - timestamps = starting_times[j] + get_movie_timestamps(movie_file=file) - - if len(starting_times) != len(file_paths): - starting_times.append(timestamps[-1]) + # check for duplicates in image_series_kwargs list keys: + def _check_duplicates(image_series_kwargs_list): + image_series_kwargs_list_keys = [i["name"] for i in image_series_kwargs_list] + if len(set(image_series_kwargs_list_keys)) < len(image_series_kwargs_list_keys): + assert external_mode, "for multiple video files under the same ImageSeries name, use exernal_mode=True" + keys_set = [] + image_series_kwargs_list_unique = [] + for no, image_series_kwargs in enumerate(image_series_kwargs_list): + if image_series_kwargs["name"] not in keys_set: + keys_set.append(image_series_kwargs["name"]) + image_series_kwargs_list_unique.append(dict(image_series_kwargs, data=[file_paths[no]])) + else: + idx = keys_set.index(image_series_kwargs["name"]) + image_series_kwargs_list_unique[idx]["data"].append(file_paths[no]) + return image_series_kwargs_list_unique - image_series_kwargs = dict(image_series_kwargs_list[j]) - if check_regular_timestamps(ts=timestamps): - fps = get_movie_fps(movie_file=file) - image_series_kwargs.update(starting_time=starting_times[j], rate=fps) - else: - image_series_kwargs.update(timestamps=H5DataIO(timestamps, compression="gzip")) + image_series_kwargs_list_updated = _check_duplicates(image_series_kwargs_list) + for j, image_series_kwargs in enumerate(image_series_kwargs_list_updated): + file_list = image_series_kwargs.pop("data") if external_mode: - image_series_kwargs.update(format="external", external_file=[file]) + image_series_kwargs.update(format="external", external_file=file_list) + with VideoCaptureContext(str(file_list[0]), stub=stub_test) as vc: + fps = vc.get_movie_fps() + image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: + file = file_list[0] uncompressed_estimate = Path(file).stat().st_size * 70 available_memory = psutil.virtual_memory().available - if not chunk_data and uncompressed_estimate >= available_memory: + if not chunk_data and not stub_test and uncompressed_estimate >= available_memory: warn( f"Not enough memory (estimated {round(uncompressed_estimate/1e9, 2)} GB) to load movie file as " f"array ({round(available_memory/1e9, 2)} GB available)! Forcing chunk_data to True." ) chunk_data = True - - total_frames = len(timestamps) - frame_shape = get_frame_shape(movie_file=file) - maxshape = [total_frames] - maxshape.extend(frame_shape) - best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) - tqdm_pos, tqdm_mininterval = (0, 10) + video_capture_ob = VideoCaptureContext(str(file), stub=stub_test) + total_frames = video_capture_ob.get_movie_frame_count() if chunk_data: - - def data_generator(file, count_max): - cap = cv2.VideoCapture(str(file)) - for _ in range(min(count_max, total_frames)): - success, frame = cap.read() - yield frame - cap.release() - - mov = DataChunkIterator( - data=tqdm( - iterable=data_generator(file=file, count_max=count_max), - desc=f"Copying movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, - ), + frame_shape = video_capture_ob.get_frame_shape() + maxshape = (total_frames, *frame_shape) + best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) + iterable = DataChunkIterator.from_iterable( + data=video_capture_ob, iter_axis=0, # nwb standard is time as zero axis maxshape=tuple(maxshape), + dtype=video_capture_ob.get_movie_frame_dtype(), ) - image_series_kwargs.update(data=H5DataIO(mov, compression="gzip", chunks=best_gzip_chunk)) + data = H5DataIO(iterable, compression=compression, chunks=best_gzip_chunk) else: - cap = cv2.VideoCapture(str(file)) - mov = [] + iterable = [] + tqdm_pos, tqdm_mininterval = (0, 10) with tqdm( desc=f"Reading movie data for {Path(file).name}", position=tqdm_pos, total=total_frames, mininterval=tqdm_mininterval, ) as pbar: - for _ in range(min(count_max, total_frames)): - success, frame = cap.read() - mov.append(frame) + for frame in video_capture_ob: + iterable.append(frame) pbar.update(1) - cap.release() - image_series_kwargs.update( - data=H5DataIO( - DataChunkIterator( - tqdm( - iterable=np.array(mov), - desc=f"Writing movie data for {Path(file).name}", - position=tqdm_pos, - mininterval=tqdm_mininterval, - ), - iter_axis=0, # nwb standard is time as zero axis - maxshape=tuple(maxshape), - ), - compression="gzip", - chunks=best_gzip_chunk, - ) - ) + iterable = np.array(iterable) + + data = H5DataIO(iterable, compression=compression) + + # capture data in kwargs: + image_series_kwargs.update(data=data) + # capture time info in kwargs: + with VideoCaptureContext(str(file), stub=stub_test) as vc: + timestamps = starting_times[j] + vc.get_movie_timestamps() + if len(starting_times) != len(file_paths): + starting_times.append(timestamps[-1]) + if check_regular_timestamps(ts=timestamps): + fps = vc.get_movie_fps() + image_series_kwargs.update(starting_time=starting_times[j], rate=fps) + else: + image_series_kwargs.update(timestamps=timestamps) + if module_name is None: nwbfile.add_acquisition(ImageSeries(**image_series_kwargs)) else: From 098143c464785e6350f034e58c60b7dfdce8ff1e Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 17 Jan 2022 18:18:18 +0530 Subject: [PATCH 03/70] add separate file for testing movie interface --- tests/test_internals/test_interfaces.py | 114 +-------------- tests/test_internals/test_movie_interface.py | 141 +++++++++++++++++++ 2 files changed, 144 insertions(+), 111 deletions(-) create mode 100644 tests/test_internals/test_movie_interface.py diff --git a/tests/test_internals/test_interfaces.py b/tests/test_internals/test_interfaces.py index 68ef2ab1d..8ab12a5ea 100644 --- a/tests/test_internals/test_interfaces.py +++ b/tests/test_internals/test_interfaces.py @@ -1,21 +1,16 @@ -import numpy as np +from pathlib import Path from platform import python_version from sys import platform -from packaging import version from tempfile import mkdtemp -from shutil import rmtree -from pathlib import Path -from itertools import product import pytest import spikeextractors as se -from spikeextractors.testing import check_recordings_equal, check_sortings_equal -from pynwb import NWBHDF5IO from hdmf.testing import TestCase +from packaging import version +from spikeextractors.testing import check_recordings_equal, check_sortings_equal from nwb_conversion_tools import ( NWBConverter, - MovieInterface, RecordingTutorialInterface, SortingTutorialInterface, SIPickleRecordingExtractorInterface, @@ -117,106 +112,3 @@ class SpikeInterfaceTestNWBConverter(NWBConverter): check_recordings_equal(RX1=toy_data[0], RX2=nwb_recording) check_recordings_equal(RX1=toy_data[0], RX2=nwb_recording, return_scaled=False) check_sortings_equal(SX1=toy_data[1], SX2=nwb_sorting) - - -def test_movie_interface(): - if HAVE_OPENCV: - test_dir = Path(mkdtemp()) - movie_file = test_dir / "test1.avi" - nwbfile_path = str(test_dir / "test1.nwb") - (nf, nx, ny) = (50, 640, 480) - writer = cv2.VideoWriter( - filename=str(movie_file), - apiPreference=None, - fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), - fps=25, - frameSize=(ny, nx), - params=None, - ) - for k in range(nf): - writer.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) - writer.release() - - class MovieTestNWBConverter(NWBConverter): - data_interface_classes = dict(Movie=MovieInterface) - - source_data = dict(Movie=dict(file_paths=[movie_file])) - converter = MovieTestNWBConverter(source_data) - metadata = converter.get_metadata() - - # Default usage - converter.run_conversion(metadata=metadata, nwbfile_path=nwbfile_path, overwrite=True) - - # This conversion option operates independently of all others - converter.run_conversion( - metadata=metadata, - nwbfile_path=nwbfile_path, - overwrite=True, - conversion_options=dict(Movie=dict(starting_times=[123.0])), - ) - - # These conversion options do not operate independently, so test them jointly - conversion_options_testing_matrix = [ - dict(Movie=dict(external_mode=False, stub_test=x, chunk_data=y)) - for x, y in product([True, False], repeat=2) - ] - for conversion_options in conversion_options_testing_matrix: - converter.run_conversion( - metadata=metadata, nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_options - ) - - module_name = "TestModule" - module_description = "This is a test module." - nwbfile = converter.run_conversion(metadata=metadata, save_to_file=False) - assert f"Video: {Path(movie_file).stem}" in nwbfile.acquisition - nwbfile = converter.run_conversion( - metadata=metadata, - save_to_file=False, - nwbfile=nwbfile, - conversion_options=dict(Movie=dict(module_name=module_name)), - ) - assert module_name in nwbfile.modules - nwbfile = converter.run_conversion( - metadata=metadata, - save_to_file=False, - conversion_options=dict(Movie=dict(module_name=module_name, module_description=module_description)), - ) - assert module_name in nwbfile.modules and nwbfile.modules[module_name].description == module_description - - metadata.update( - Behavior=dict( - Movies=[ - dict( - name="CustomName", - description="CustomDescription", - unit="CustomUnit", - resolution=12.3, - comments="CustomComments", - ) - ] - ) - ) - converter.run_conversion(metadata=metadata, nwbfile_path=nwbfile_path, overwrite=True) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - custom_name = metadata["Behavior"]["Movies"][0]["name"] - assert custom_name in nwbfile.acquisition - assert metadata["Behavior"]["Movies"][0]["description"] == nwbfile.acquisition[custom_name].description - assert metadata["Behavior"]["Movies"][0]["comments"] == nwbfile.acquisition[custom_name].comments - - converter.run_conversion( - metadata=metadata, - nwbfile_path=nwbfile_path, - overwrite=True, - conversion_options=dict(Movie=dict(external_mode=False, stub_test=True)), - ) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - custom_name = metadata["Behavior"]["Movies"][0]["name"] - assert custom_name in nwbfile.acquisition - assert metadata["Behavior"]["Movies"][0]["description"] == nwbfile.acquisition[custom_name].description - assert metadata["Behavior"]["Movies"][0]["unit"] == nwbfile.acquisition[custom_name].unit - assert metadata["Behavior"]["Movies"][0]["resolution"] == nwbfile.acquisition[custom_name].resolution - assert metadata["Behavior"]["Movies"][0]["comments"] == nwbfile.acquisition[custom_name].comments - - rmtree(test_dir) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py new file mode 100644 index 000000000..e430a6827 --- /dev/null +++ b/tests/test_internals/test_movie_interface.py @@ -0,0 +1,141 @@ +from itertools import product + +import numpy as np +import pytest +from pynwb import NWBHDF5IO + +from nwb_conversion_tools import NWBConverter, MovieInterface + +try: + import cv2 + + HAVE_OPENCV = True +except ImportError: + HAVE_OPENCV = False + + +@pytest.fixture(scope="module") +def create_movies(tmp_path_factory): + if HAVE_OPENCV: + base_path = tmp_path_factory.mktemp("movie_tests") + movie_file1 = base_path / "test1.avi" + movie_file2 = base_path / "test2.avi" + (nf, nx, ny) = (50, 640, 480) + writer1 = cv2.VideoWriter( + filename=str(movie_file1), + apiPreference=None, + fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), + fps=25, + frameSize=(ny, nx), + params=None, + ) + writer2 = cv2.VideoWriter( + filename=str(movie_file2), + apiPreference=None, + fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), + fps=25, + frameSize=(ny, nx), + params=None, + ) + for k in range(nf): + writer1.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) + writer2.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) + writer1.release() + writer2.release() + return [str(movie_file1), str(movie_file2)] + + +@pytest.fixture(scope="module") +def movie_converter(create_movies): + if HAVE_OPENCV: + + class MovieTestNWBConverter(NWBConverter): + data_interface_classes = dict(Movie=MovieInterface) + + source_data = dict(Movie=dict(file_paths=create_movies)) + converter = MovieTestNWBConverter(source_data) + return converter + + +def assert_nwbfile_conversion( + converter, + nwbfile_path, + starting_times: list = None, + module_name: str = None, + module_description: str = None, + metadata: dict = None, + iterator_type: str = "v2", + stub_test: bool = False, + external_mode: bool = False, + chunk_data: bool = True, +): + metadata = converter.get_metadata() if metadata is None else metadata + custom_names = [metadata["Behavior"]["Movies"][i]["name"] for i in range(len(metadata["Behavior"]["Movies"]))] + conversion_opts = dict( + starting_times=starting_times, + iterator_type=iterator_type, + stub_test=stub_test, + external_mode=external_mode, + chunk_data=chunk_data, + ) + if module_name: + conversion_opts.update(module_name=module_name) + if module_description: + conversion_opts.update(module_description=module_description) + converter.run_conversion( + metadata=metadata, nwbfile_path=nwbfile_path, overwrite=True, conversion_options=dict(Movie=conversion_opts) + ) + module_name = module_name if module_name is not None else "acquisition" + with NWBHDF5IO(path=nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.modules[module_name] + assert module_name in mod + if module_description: + assert module_description == mod.description + for no, name in enumerate(custom_names): + if external_mode: + for file_name in converter.data_interfaces["Movie"].source_data["file_paths"]: + assert file_name in mod[name].external_file + else: + assert name in mod + if starting_times: + assert starting_times[no] == mod[name].starting_time + + +@pytest.fixture(scope="module") +def nwbfile_path(tmp_path_factory): + nwbfile_path = str(tmp_path_factory.mktemp("movie_tests") / "test.nwb") + return nwbfile_path + + +def test_conversion_default(movie_converter, create_movies, nwbfile_path): + if HAVE_OPENCV: + starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] + assert_nwbfile_conversion(converter=movie_converter, nwbfile_path=nwbfile_path, starting_times=starting_times) + + +def test_conversion_custom(movie_converter, nwbfile_path): + if HAVE_OPENCV: + module_name = "TestModule" + module_description = "This is a test module." + assert_nwbfile_conversion( + converter=movie_converter, + nwbfile_path=nwbfile_path, + module_description=module_description, + module_name=module_name, + ) + + +def test_conversion_options(movie_converter, nwbfile_path): + if HAVE_OPENCV: + conversion_options_testing_matrix = [ + dict(external_mode=False, stub_test=True, chunk_data=i, iterator_type=j) + for i, j in product([True, False], ["v1", "v2"]) + ] + for conv_ops in conversion_options_testing_matrix: + assert_nwbfile_conversion(converter=movie_converter, nwbfile_path=nwbfile_path, **conv_ops) + + +def test_conversion_external_mode(movie_converter, nwbfile_path): + if HAVE_OPENCV: + assert_nwbfile_conversion(converter=movie_converter, nwbfile_path=nwbfile_path, external_mode=True) From 952b7f4a530bd8b62ab7185569060d8d1878087d Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 17 Jan 2022 18:47:40 +0530 Subject: [PATCH 04/70] fixing test --- .../behavior/movie/moviedatainterface.py | 4 ++-- tests/test_internals/test_movie_interface.py | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index c7c6a47d2..cbc96176a 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -218,8 +218,8 @@ def _check_duplicates(image_series_kwargs_list): data = H5DataIO(iterable, compression=compression) - # capture data in kwargs: - image_series_kwargs.update(data=data) + # capture data in kwargs: + image_series_kwargs.update(data=data) # capture time info in kwargs: with VideoCaptureContext(str(file), stub=stub_test) as vc: timestamps = starting_times[j] + vc.get_movie_timestamps() diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index e430a6827..68b2b96c7 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -64,16 +64,14 @@ def assert_nwbfile_conversion( module_name: str = None, module_description: str = None, metadata: dict = None, - iterator_type: str = "v2", stub_test: bool = False, external_mode: bool = False, - chunk_data: bool = True, + chunk_data: bool = False, ): metadata = converter.get_metadata() if metadata is None else metadata custom_names = [metadata["Behavior"]["Movies"][i]["name"] for i in range(len(metadata["Behavior"]["Movies"]))] conversion_opts = dict( starting_times=starting_times, - iterator_type=iterator_type, stub_test=stub_test, external_mode=external_mode, chunk_data=chunk_data, @@ -116,6 +114,7 @@ def test_conversion_default(movie_converter, create_movies, nwbfile_path): def test_conversion_custom(movie_converter, nwbfile_path): if HAVE_OPENCV: + starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] module_name = "TestModule" module_description = "This is a test module." assert_nwbfile_conversion( @@ -123,17 +122,22 @@ def test_conversion_custom(movie_converter, nwbfile_path): nwbfile_path=nwbfile_path, module_description=module_description, module_name=module_name, + starting_times=starting_times ) def test_conversion_options(movie_converter, nwbfile_path): if HAVE_OPENCV: + starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_options_testing_matrix = [ - dict(external_mode=False, stub_test=True, chunk_data=i, iterator_type=j) - for i, j in product([True, False], ["v1", "v2"]) + dict(external_mode=False, stub_test=True, chunk_data=i) + for i in [True, False] ] for conv_ops in conversion_options_testing_matrix: - assert_nwbfile_conversion(converter=movie_converter, nwbfile_path=nwbfile_path, **conv_ops) + assert_nwbfile_conversion(converter=movie_converter, + nwbfile_path=nwbfile_path, + starting_times=starting_times, + **conv_ops) def test_conversion_external_mode(movie_converter, nwbfile_path): From 5dbe1e1e22c62675cf475ee48c6f5552aec3f7dd Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 17 Jan 2022 13:23:57 +0000 Subject: [PATCH 05/70] Automated changes --- .../datainterfaces/behavior/movie/movie_utils.py | 4 ++-- .../behavior/movie/moviedatainterface.py | 2 +- tests/test_internals/test_movie_interface.py | 12 +++++------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index 1fb5d332f..54bf45b57 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -99,7 +99,7 @@ def get_movie_frame(self, frame_no: int): if success: return frame elif frame_no > 0: - return np.nan*np.ones(self.get_frame_shape()) + return np.nan * np.ones(self.get_frame_shape()) def get_movie_frame_dtype(self): """ @@ -120,7 +120,7 @@ def __next__(self): if success: return frame else: - return np.nan*np.ones(self.get_frame_shape()) + return np.nan * np.ones(self.get_frame_shape()) else: self.current_frame = 0 raise StopIteration diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index cbc96176a..b1a600e5b 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -81,7 +81,7 @@ def run_conversion( chunk_data: bool = True, module_name: Optional[str] = None, module_description: Optional[str] = None, - compression: str = "gzip" + compression: str = "gzip", ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 68b2b96c7..ba840414b 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -122,7 +122,7 @@ def test_conversion_custom(movie_converter, nwbfile_path): nwbfile_path=nwbfile_path, module_description=module_description, module_name=module_name, - starting_times=starting_times + starting_times=starting_times, ) @@ -130,14 +130,12 @@ def test_conversion_options(movie_converter, nwbfile_path): if HAVE_OPENCV: starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_options_testing_matrix = [ - dict(external_mode=False, stub_test=True, chunk_data=i) - for i in [True, False] + dict(external_mode=False, stub_test=True, chunk_data=i) for i in [True, False] ] for conv_ops in conversion_options_testing_matrix: - assert_nwbfile_conversion(converter=movie_converter, - nwbfile_path=nwbfile_path, - starting_times=starting_times, - **conv_ops) + assert_nwbfile_conversion( + converter=movie_converter, nwbfile_path=nwbfile_path, starting_times=starting_times, **conv_ops + ) def test_conversion_external_mode(movie_converter, nwbfile_path): From d7f7a64ce6ab598ae9a3fbae97cfea8ca88d566d Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 19 Jan 2022 20:12:55 +0530 Subject: [PATCH 06/70] keeping videocapture context as open --- .../behavior/movie/moviedatainterface.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index cbc96176a..5370710b5 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -220,16 +220,14 @@ def _check_duplicates(image_series_kwargs_list): # capture data in kwargs: image_series_kwargs.update(data=data) - # capture time info in kwargs: - with VideoCaptureContext(str(file), stub=stub_test) as vc: - timestamps = starting_times[j] + vc.get_movie_timestamps() - if len(starting_times) != len(file_paths): - starting_times.append(timestamps[-1]) - if check_regular_timestamps(ts=timestamps): - fps = vc.get_movie_fps() - image_series_kwargs.update(starting_time=starting_times[j], rate=fps) - else: - image_series_kwargs.update(timestamps=timestamps) + timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() + if len(starting_times) != len(file_paths): + starting_times.append(timestamps[-1]) + if check_regular_timestamps(ts=timestamps): + fps = video_capture_ob.get_movie_fps() + image_series_kwargs.update(starting_time=starting_times[j], rate=fps) + else: + image_series_kwargs.update(timestamps=timestamps) if module_name is None: nwbfile.add_acquisition(ImageSeries(**image_series_kwargs)) From 7f797173e4ec3915ab8054b01000cc66d1935e62 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 19 Jan 2022 20:13:43 +0530 Subject: [PATCH 07/70] asserting module name and description only if under processing --- tests/test_internals/test_movie_interface.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 68b2b96c7..a4d597cbb 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -83,13 +83,15 @@ def assert_nwbfile_conversion( converter.run_conversion( metadata=metadata, nwbfile_path=nwbfile_path, overwrite=True, conversion_options=dict(Movie=conversion_opts) ) - module_name = module_name if module_name is not None else "acquisition" with NWBHDF5IO(path=nwbfile_path, mode="r") as io: nwbfile = io.read() - mod = nwbfile.modules[module_name] - assert module_name in mod - if module_description: - assert module_description == mod.description + if module_name is None: + mod = nwbfile.acquisition + else: + assert module_name in nwbfile.processing + mod = nwbfile.processing[module_name] + if module_description: + assert module_description == mod.description for no, name in enumerate(custom_names): if external_mode: for file_name in converter.data_interfaces["Movie"].source_data["file_paths"]: @@ -126,7 +128,7 @@ def test_conversion_custom(movie_converter, nwbfile_path): ) -def test_conversion_options(movie_converter, nwbfile_path): +def test_conversion_options(movie_converter, nwbfile_path, create_movies): if HAVE_OPENCV: starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_options_testing_matrix = [ From 64cf4639f0173ba82837eb26a6bfe1c22cf43073 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 21 Jan 2022 10:40:12 +0530 Subject: [PATCH 08/70] restructure tests: rename, remove non test method --- tests/test_internals/test_movie_interface.py | 220 +++++++++---------- 1 file changed, 100 insertions(+), 120 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 5ef1d2a45..001d18c4c 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -1,4 +1,4 @@ -from itertools import product +import shutil import numpy as np import pytest @@ -8,98 +8,55 @@ try: import cv2 - - HAVE_OPENCV = True except ImportError: - HAVE_OPENCV = False + pytestmark = pytest.mark.skip(f"cv2 not installed, skipping test module {__name__} ") @pytest.fixture(scope="module") def create_movies(tmp_path_factory): - if HAVE_OPENCV: - base_path = tmp_path_factory.mktemp("movie_tests") - movie_file1 = base_path / "test1.avi" - movie_file2 = base_path / "test2.avi" - (nf, nx, ny) = (50, 640, 480) - writer1 = cv2.VideoWriter( - filename=str(movie_file1), - apiPreference=None, - fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), - fps=25, - frameSize=(ny, nx), - params=None, - ) - writer2 = cv2.VideoWriter( - filename=str(movie_file2), - apiPreference=None, - fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), - fps=25, - frameSize=(ny, nx), - params=None, - ) - for k in range(nf): - writer1.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) - writer2.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) - writer1.release() - writer2.release() - return [str(movie_file1), str(movie_file2)] + base_path = tmp_path_factory.mktemp("movie_tests") + movie_file1 = base_path / "test1.avi" + movie_file2 = base_path / "test2.avi" + (nf, nx, ny) = (50, 640, 480) + writer1 = cv2.VideoWriter( + filename=str(movie_file1), + apiPreference=None, + fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), + fps=25, + frameSize=(ny, nx), + params=None, + ) + writer2 = cv2.VideoWriter( + filename=str(movie_file2), + apiPreference=None, + fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), + fps=25, + frameSize=(ny, nx), + params=None, + ) + for k in range(nf): + writer1.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) + writer2.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) + writer1.release() + writer2.release() + yield [str(movie_file1), str(movie_file2)] + shutil.rmtree(base_path) -@pytest.fixture(scope="module") +@pytest.fixture def movie_converter(create_movies): - if HAVE_OPENCV: - - class MovieTestNWBConverter(NWBConverter): - data_interface_classes = dict(Movie=MovieInterface) - - source_data = dict(Movie=dict(file_paths=create_movies)) - converter = MovieTestNWBConverter(source_data) - return converter - - -def assert_nwbfile_conversion( - converter, - nwbfile_path, - starting_times: list = None, - module_name: str = None, - module_description: str = None, - metadata: dict = None, - stub_test: bool = False, - external_mode: bool = False, - chunk_data: bool = False, -): - metadata = converter.get_metadata() if metadata is None else metadata - custom_names = [metadata["Behavior"]["Movies"][i]["name"] for i in range(len(metadata["Behavior"]["Movies"]))] - conversion_opts = dict( - starting_times=starting_times, - stub_test=stub_test, - external_mode=external_mode, - chunk_data=chunk_data, - ) - if module_name: - conversion_opts.update(module_name=module_name) - if module_description: - conversion_opts.update(module_description=module_description) - converter.run_conversion( - metadata=metadata, nwbfile_path=nwbfile_path, overwrite=True, conversion_options=dict(Movie=conversion_opts) - ) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - if module_name is None: - mod = nwbfile.acquisition - else: - assert module_name in nwbfile.processing - mod = nwbfile.processing[module_name] - if module_description: - assert module_description == mod.description - for no, name in enumerate(custom_names): - if external_mode: - for file_name in converter.data_interfaces["Movie"].source_data["file_paths"]: - assert file_name in mod[name].external_file - else: - assert name in mod - if starting_times: - assert starting_times[no] == mod[name].starting_time + class MovieTestNWBConverter(NWBConverter): + data_interface_classes = dict(Movie=MovieInterface) + + source_data = dict(Movie=dict(file_paths=create_movies)) + converter = MovieTestNWBConverter(source_data) + yield converter + del converter + + +@pytest.fixture +def get_starting_times(create_movies): + return [np.float(np.random.randint(200)) for i in range(len(create_movies))] @pytest.fixture(scope="module") @@ -108,38 +65,61 @@ def nwbfile_path(tmp_path_factory): return nwbfile_path -def test_conversion_default(movie_converter, create_movies, nwbfile_path): - if HAVE_OPENCV: - starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - assert_nwbfile_conversion(converter=movie_converter, nwbfile_path=nwbfile_path, starting_times=starting_times) - - -def test_conversion_custom(movie_converter, nwbfile_path): - if HAVE_OPENCV: - starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - module_name = "TestModule" - module_description = "This is a test module." - assert_nwbfile_conversion( - converter=movie_converter, - nwbfile_path=nwbfile_path, - module_description=module_description, - module_name=module_name, - starting_times=starting_times, - ) - - -def test_conversion_options(movie_converter, nwbfile_path, create_movies): - if HAVE_OPENCV: - starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - conversion_options_testing_matrix = [ - dict(external_mode=False, stub_test=True, chunk_data=i) for i in [True, False] - ] - for conv_ops in conversion_options_testing_matrix: - assert_nwbfile_conversion( - converter=movie_converter, nwbfile_path=nwbfile_path, starting_times=starting_times, **conv_ops - ) - - -def test_conversion_external_mode(movie_converter, nwbfile_path): - if HAVE_OPENCV: - assert_nwbfile_conversion(converter=movie_converter, nwbfile_path=nwbfile_path, external_mode=True) +def test_movie_starting_times(movie_converter, nwbfile_path): + starting_times = get_starting_times() + conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=False)) + movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) + with NWBHDF5IO(path=nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = movie_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + assert movie_interface_name in mod + assert starting_times[no] == mod[movie_interface_name].starting_time + + +def test_movie_custom_module(movie_converter, nwbfile_path): + starting_times = get_starting_times() + module_name = "TestModule" + module_description = "This is a test module." + conversion_opts = dict(Movie=dict(starting_times=starting_times, + external_mode=False, + module_name=module_name, + module_description=module_description)) + movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) + with NWBHDF5IO(path=nwbfile_path, mode="r") as io: + nwbfile = io.read() + assert module_name in nwbfile.processing + assert module_description == nwbfile.processing[module_name].description + + +def test_movie_chunking(movie_converter, nwbfile_path): + starting_times = get_starting_times() + conversion_options_testing_matrix = [ + dict(external_mode=False, stub_test=True, + starting_times=starting_times, chunk_data=i) for i in [True, False] + ] + for conv_ops in conversion_options_testing_matrix: + movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, + conversion_options=conv_ops) + with NWBHDF5IO(path=nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = movie_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + assert mod[movie_interface_name].data.chunks is not None # TODO + + +def test_movie_external_mode(movie_converter, nwbfile_path): + starting_times = get_starting_times() + conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=True)) + movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) + with NWBHDF5IO(path=nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = movie_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + assert mod[movie_interface_name].external_file[0] == movie_converter[no] From 814113194fda71143365c8eb6f085f1f91af9b61 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Fri, 21 Jan 2022 05:11:20 +0000 Subject: [PATCH 09/70] Automated changes --- tests/test_internals/test_movie_interface.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 001d18c4c..ce58b2808 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -83,10 +83,14 @@ def test_movie_custom_module(movie_converter, nwbfile_path): starting_times = get_starting_times() module_name = "TestModule" module_description = "This is a test module." - conversion_opts = dict(Movie=dict(starting_times=starting_times, - external_mode=False, - module_name=module_name, - module_description=module_description)) + conversion_opts = dict( + Movie=dict( + starting_times=starting_times, + external_mode=False, + module_name=module_name, + module_description=module_description, + ) + ) movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) with NWBHDF5IO(path=nwbfile_path, mode="r") as io: nwbfile = io.read() @@ -97,19 +101,17 @@ def test_movie_custom_module(movie_converter, nwbfile_path): def test_movie_chunking(movie_converter, nwbfile_path): starting_times = get_starting_times() conversion_options_testing_matrix = [ - dict(external_mode=False, stub_test=True, - starting_times=starting_times, chunk_data=i) for i in [True, False] + dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=i) for i in [True, False] ] for conv_ops in conversion_options_testing_matrix: - movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, - conversion_options=conv_ops) + movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conv_ops) with NWBHDF5IO(path=nwbfile_path, mode="r") as io: nwbfile = io.read() mod = nwbfile.acquisition metadata = movie_converter.get_metadata() for no in range(len(metadata["Behavior"]["Movies"])): movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] - assert mod[movie_interface_name].data.chunks is not None # TODO + assert mod[movie_interface_name].data.chunks is not None # TODO def test_movie_external_mode(movie_converter, nwbfile_path): From 4c7b9fef1e3f60767e1537c28ed166865ced05e6 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 21 Jan 2022 12:27:39 +0530 Subject: [PATCH 10/70] fix starting times fixture error --- tests/test_internals/test_movie_interface.py | 21 ++++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index ce58b2808..b8f25e377 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -54,19 +54,14 @@ class MovieTestNWBConverter(NWBConverter): del converter -@pytest.fixture -def get_starting_times(create_movies): - return [np.float(np.random.randint(200)) for i in range(len(create_movies))] - - @pytest.fixture(scope="module") def nwbfile_path(tmp_path_factory): nwbfile_path = str(tmp_path_factory.mktemp("movie_tests") / "test.nwb") return nwbfile_path -def test_movie_starting_times(movie_converter, nwbfile_path): - starting_times = get_starting_times() +def test_movie_starting_times(movie_converter, nwbfile_path, create_movies): + starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=False)) movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) with NWBHDF5IO(path=nwbfile_path, mode="r") as io: @@ -79,8 +74,8 @@ def test_movie_starting_times(movie_converter, nwbfile_path): assert starting_times[no] == mod[movie_interface_name].starting_time -def test_movie_custom_module(movie_converter, nwbfile_path): - starting_times = get_starting_times() +def test_movie_custom_module(movie_converter, nwbfile_path, create_movies): + starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] module_name = "TestModule" module_description = "This is a test module." conversion_opts = dict( @@ -98,8 +93,8 @@ def test_movie_custom_module(movie_converter, nwbfile_path): assert module_description == nwbfile.processing[module_name].description -def test_movie_chunking(movie_converter, nwbfile_path): - starting_times = get_starting_times() +def test_movie_chunking(movie_converter, nwbfile_path, create_movies): + starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_options_testing_matrix = [ dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=i) for i in [True, False] ] @@ -114,8 +109,8 @@ def test_movie_chunking(movie_converter, nwbfile_path): assert mod[movie_interface_name].data.chunks is not None # TODO -def test_movie_external_mode(movie_converter, nwbfile_path): - starting_times = get_starting_times() +def test_movie_external_mode(movie_converter, nwbfile_path, create_movies): + starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=True)) movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) with NWBHDF5IO(path=nwbfile_path, mode="r") as io: From bdd674a149ab6035402a96bceaa4ba7b2aa4d38d Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 21 Jan 2022 13:32:07 +0530 Subject: [PATCH 11/70] fixture to create a temp directory, but fixes --- tests/test_internals/test_movie_interface.py | 23 ++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index b8f25e377..a20f581ca 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -13,8 +13,15 @@ @pytest.fixture(scope="module") -def create_movies(tmp_path_factory): - base_path = tmp_path_factory.mktemp("movie_tests") +def base_path(tmp_path_factory): + test_folder = tmp_path_factory.mktemp("movie_tests") + yield test_folder + print(f"removing {test_folder}") + shutil.rmtree(test_folder) + + +@pytest.fixture(scope="module") +def create_movies(base_path): movie_file1 = base_path / "test1.avi" movie_file2 = base_path / "test2.avi" (nf, nx, ny) = (50, 640, 480) @@ -39,8 +46,7 @@ def create_movies(tmp_path_factory): writer2.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) writer1.release() writer2.release() - yield [str(movie_file1), str(movie_file2)] - shutil.rmtree(base_path) + return [str(movie_file1), str(movie_file2)] @pytest.fixture @@ -55,9 +61,8 @@ class MovieTestNWBConverter(NWBConverter): @pytest.fixture(scope="module") -def nwbfile_path(tmp_path_factory): - nwbfile_path = str(tmp_path_factory.mktemp("movie_tests") / "test.nwb") - return nwbfile_path +def nwbfile_path(base_path): + return str(base_path / "test.nwb") def test_movie_starting_times(movie_converter, nwbfile_path, create_movies): @@ -96,7 +101,7 @@ def test_movie_custom_module(movie_converter, nwbfile_path, create_movies): def test_movie_chunking(movie_converter, nwbfile_path, create_movies): starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_options_testing_matrix = [ - dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=i) for i in [True, False] + dict(Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=i)) for i in [True, False] ] for conv_ops in conversion_options_testing_matrix: movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conv_ops) @@ -106,7 +111,7 @@ def test_movie_chunking(movie_converter, nwbfile_path, create_movies): metadata = movie_converter.get_metadata() for no in range(len(metadata["Behavior"]["Movies"])): movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] - assert mod[movie_interface_name].data.chunks is not None # TODO + assert mod[movie_interface_name].data.chunks is not None # TODO retrive storage_layout of hdf5 dataset def test_movie_external_mode(movie_converter, nwbfile_path, create_movies): From a9693a29ee976c0efd03fcf9abc27cef4f15684d Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Fri, 21 Jan 2022 08:03:02 +0000 Subject: [PATCH 12/70] Automated changes --- tests/test_internals/test_movie_interface.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index a20f581ca..3fc5e16ac 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -101,7 +101,8 @@ def test_movie_custom_module(movie_converter, nwbfile_path, create_movies): def test_movie_chunking(movie_converter, nwbfile_path, create_movies): starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] conversion_options_testing_matrix = [ - dict(Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=i)) for i in [True, False] + dict(Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=i)) + for i in [True, False] ] for conv_ops in conversion_options_testing_matrix: movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conv_ops) From 1330fe5a557b34de9752a9cb39ad6e3a8bf312b0 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 21 Jan 2022 17:44:02 +0530 Subject: [PATCH 13/70] using video context manager for chunk_data=False --- .../behavior/movie/moviedatainterface.py | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index d6c864aa3..5850007d5 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -178,7 +178,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(format="external", external_file=file_list) with VideoCaptureContext(str(file_list[0]), stub=stub_test) as vc: fps = vc.get_movie_fps() - image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times + image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: file = file_list[0] uncompressed_estimate = Path(file).stat().st_size * 70 @@ -189,9 +189,10 @@ def _check_duplicates(image_series_kwargs_list): f"array ({round(available_memory/1e9, 2)} GB available)! Forcing chunk_data to True." ) chunk_data = True - video_capture_ob = VideoCaptureContext(str(file), stub=stub_test) - total_frames = video_capture_ob.get_movie_frame_count() if chunk_data: + video_capture_ob = VideoCaptureContext(str(file), stub=stub_test) + total_frames = video_capture_ob.get_movie_frame_count() + video_capture_ob.current_frame = 0 frame_shape = video_capture_ob.get_frame_shape() maxshape = (total_frames, *frame_shape) best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) @@ -202,29 +203,31 @@ def _check_duplicates(image_series_kwargs_list): dtype=video_capture_ob.get_movie_frame_dtype(), ) data = H5DataIO(iterable, compression=compression, chunks=best_gzip_chunk) + timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() + fps = video_capture_ob.get_movie_fps() else: - iterable = [] - tqdm_pos, tqdm_mininterval = (0, 10) - with tqdm( - desc=f"Reading movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, - ) as pbar: - for frame in video_capture_ob: - iterable.append(frame) - pbar.update(1) - iterable = np.array(iterable) - - data = H5DataIO(iterable, compression=compression) + with VideoCaptureContext(str(file), stub=stub_test) as video_capture_ob: + iterable = [] + tqdm_pos, tqdm_mininterval = (0, 10) + with tqdm( + desc=f"Reading movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, + ) as pbar: + for frame in video_capture_ob: + iterable.append(frame) + pbar.update(1) + iterable = np.array(iterable) + timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() + fps = video_capture_ob.get_movie_fps() + data = H5DataIO(iterable, compression=compression) # capture data in kwargs: image_series_kwargs.update(data=data) - timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() if len(starting_times) != len(file_paths): starting_times.append(timestamps[-1]) if check_regular_timestamps(ts=timestamps): - fps = video_capture_ob.get_movie_fps() image_series_kwargs.update(starting_time=starting_times[j], rate=fps) else: image_series_kwargs.update(timestamps=timestamps) From 8bc7f327c112cbbb07389ce75958b49a54fff35f Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 21 Jan 2022 17:44:24 +0530 Subject: [PATCH 14/70] using parameterization for chunk data --- tests/test_internals/test_movie_interface.py | 31 +++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index a20f581ca..d20bee624 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -20,10 +20,12 @@ def base_path(tmp_path_factory): shutil.rmtree(test_folder) -@pytest.fixture(scope="module") +@pytest.fixture def create_movies(base_path): movie_file1 = base_path / "test1.avi" movie_file2 = base_path / "test2.avi" + movie_file1.unlink(missing_ok=True) + movie_file2.unlink(missing_ok=True) (nf, nx, ny) = (50, 640, 480) writer1 = cv2.VideoWriter( filename=str(movie_file1), @@ -98,20 +100,21 @@ def test_movie_custom_module(movie_converter, nwbfile_path, create_movies): assert module_description == nwbfile.processing[module_name].description -def test_movie_chunking(movie_converter, nwbfile_path, create_movies): +@pytest.mark.parametrize("chunk_data", [True, False]) +def test_movie_chunking(chunk_data, movie_converter, nwbfile_path, create_movies): starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - conversion_options_testing_matrix = [ - dict(Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=i)) for i in [True, False] - ] - for conv_ops in conversion_options_testing_matrix: - movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conv_ops) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - mod = nwbfile.acquisition - metadata = movie_converter.get_metadata() - for no in range(len(metadata["Behavior"]["Movies"])): - movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + conv_ops = dict(Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=chunk_data)) + movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conv_ops) + with NWBHDF5IO(path=nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = movie_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + if chunk_data: assert mod[movie_interface_name].data.chunks is not None # TODO retrive storage_layout of hdf5 dataset + else: + assert mod[movie_interface_name].data.chunks is not None def test_movie_external_mode(movie_converter, nwbfile_path, create_movies): @@ -124,4 +127,4 @@ def test_movie_external_mode(movie_converter, nwbfile_path, create_movies): metadata = movie_converter.get_metadata() for no in range(len(metadata["Behavior"]["Movies"])): movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] - assert mod[movie_interface_name].external_file[0] == movie_converter[no] + assert mod[movie_interface_name].external_file[0] == create_movies[no] From 3bfd44b18a7575bd0fead591aea84d93bca592fd Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Fri, 21 Jan 2022 12:15:48 +0000 Subject: [PATCH 15/70] Automated changes --- tests/test_internals/test_movie_interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index d20bee624..c119a5661 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -103,7 +103,9 @@ def test_movie_custom_module(movie_converter, nwbfile_path, create_movies): @pytest.mark.parametrize("chunk_data", [True, False]) def test_movie_chunking(chunk_data, movie_converter, nwbfile_path, create_movies): starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - conv_ops = dict(Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=chunk_data)) + conv_ops = dict( + Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=chunk_data) + ) movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conv_ops) with NWBHDF5IO(path=nwbfile_path, mode="r") as io: nwbfile = io.read() From c70f807c294b8d5c0d95c302ad1e8e3d72af5ee9 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 21 Jan 2022 18:27:36 +0530 Subject: [PATCH 16/70] fixing Path.unlink missing_ok error --- tests/test_internals/test_movie_interface.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index c119a5661..690acb830 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -24,8 +24,10 @@ def base_path(tmp_path_factory): def create_movies(base_path): movie_file1 = base_path / "test1.avi" movie_file2 = base_path / "test2.avi" - movie_file1.unlink(missing_ok=True) - movie_file2.unlink(missing_ok=True) + if movie_file1.exists(): + movie_file1.unlink() + if movie_file2.exists(): + movie_file2.unlink() (nf, nx, ny) = (50, 640, 480) writer1 = cv2.VideoWriter( filename=str(movie_file1), From 5aa72483bf756b11b2a67ee275c9e310aa66c8f3 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 25 Jan 2022 19:13:55 +0530 Subject: [PATCH 17/70] adding back datachunkiterator for chunk_data=False --- .../behavior/movie/moviedatainterface.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 5850007d5..5df4728b2 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -207,6 +207,10 @@ def _check_duplicates(image_series_kwargs_list): fps = video_capture_ob.get_movie_fps() else: with VideoCaptureContext(str(file), stub=stub_test) as video_capture_ob: + total_frames = video_capture_ob.get_movie_frame_count() + frame_shape = video_capture_ob.get_frame_shape() + maxshape = (total_frames, *frame_shape) + best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) iterable = [] tqdm_pos, tqdm_mininterval = (0, 10) with tqdm( @@ -218,10 +222,22 @@ def _check_duplicates(image_series_kwargs_list): for frame in video_capture_ob: iterable.append(frame) pbar.update(1) - iterable = np.array(iterable) + data=H5DataIO( + DataChunkIterator( + tqdm( + iterable=np.array(iterable), + desc=f"Writing movie data for {Path(file).name}", + position=tqdm_pos, + mininterval=tqdm_mininterval, + ), + iter_axis=0, # nwb standard is time as zero axis + maxshape=tuple(maxshape), + ), + compression="gzip", + chunks=best_gzip_chunk, + ) timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() fps = video_capture_ob.get_movie_fps() - data = H5DataIO(iterable, compression=compression) # capture data in kwargs: image_series_kwargs.update(data=data) From 55e0eaa029f7c1f36112bc4026ee8789565b9fab Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Tue, 25 Jan 2022 13:44:43 +0000 Subject: [PATCH 18/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 5df4728b2..0c2192953 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -222,7 +222,7 @@ def _check_duplicates(image_series_kwargs_list): for frame in video_capture_ob: iterable.append(frame) pbar.update(1) - data=H5DataIO( + data = H5DataIO( DataChunkIterator( tqdm( iterable=np.array(iterable), From af60a40e08f105428faa47d84c965bd06547a332 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 25 Jan 2022 23:36:38 +0530 Subject: [PATCH 19/70] for iterating over videocapturecontext, open and close the video file after each frame --- .../behavior/movie/movie_utils.py | 4 +- .../behavior/movie/moviedatainterface.py | 114 +++++++++--------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index 54bf45b57..f170831aa 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -112,17 +112,19 @@ def __iter__(self): def __next__(self): if not self.isOpened(): - raise StopIteration + super().__init__(*self._args, **self._kwargs) try: if self.current_frame < self.frame_count: success, frame = self.read() self.current_frame += 1 + self.release() if success: return frame else: return np.nan * np.ones(self.get_frame_shape()) else: self.current_frame = 0 + self.release() raise StopIteration except Exception: raise StopIteration diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 5df4728b2..0f4a17d36 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -1,15 +1,15 @@ """Authors: Cody Baker and Ben Dichter.""" from pathlib import Path -import numpy as np from typing import Optional -from tqdm import tqdm from warnings import warn +import numpy as np import psutil -from pynwb import NWBFile -from pynwb.image import ImageSeries from hdmf.backends.hdf5.h5_utils import H5DataIO from hdmf.data_utils import DataChunkIterator +from pynwb import NWBFile +from pynwb.image import ImageSeries +from tqdm import tqdm from ....basedatainterface import BaseDataInterface from ....utils.conversion_tools import check_regular_timestamps, get_module @@ -72,16 +72,16 @@ def get_metadata(self): return metadata def run_conversion( - self, - nwbfile: NWBFile, - metadata: dict, - stub_test: bool = False, - external_mode: bool = True, - starting_times: Optional[list] = None, - chunk_data: bool = True, - module_name: Optional[str] = None, - module_description: Optional[str] = None, - compression: str = "gzip", + self, + nwbfile: NWBFile, + metadata: dict, + stub_test: bool = False, + external_mode: bool = True, + starting_times: Optional[list] = None, + chunk_data: bool = True, + module_name: Optional[str] = None, + module_description: Optional[str] = None, + compression: str = "gzip", ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -133,15 +133,11 @@ def run_conversion( file_paths = self.source_data["file_paths"] - if stub_test: - count_max = 10 - else: - count_max = np.inf if starting_times is not None: assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) - and len(starting_times) == len(file_paths) + isinstance(starting_times, list) + and all([isinstance(x, float) for x in starting_times]) + and len(starting_times) == len(file_paths) ), "Argument 'starting_times' must be a list of floats in one-to-one correspondence with 'file_paths'!" else: starting_times = [0.0] @@ -181,7 +177,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: file = file_list[0] - uncompressed_estimate = Path(file).stat().st_size * 70 + uncompressed_estimate = Path(file).stat().st_size*70 available_memory = psutil.virtual_memory().available if not chunk_data and not stub_test and uncompressed_estimate >= available_memory: warn( @@ -189,55 +185,53 @@ def _check_duplicates(image_series_kwargs_list): f"array ({round(available_memory/1e9, 2)} GB available)! Forcing chunk_data to True." ) chunk_data = True - if chunk_data: - video_capture_ob = VideoCaptureContext(str(file), stub=stub_test) + + with VideoCaptureContext(str(file), stub=stub_test) as video_capture_ob: total_frames = video_capture_ob.get_movie_frame_count() - video_capture_ob.current_frame = 0 frame_shape = video_capture_ob.get_frame_shape() - maxshape = (total_frames, *frame_shape) - best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) - iterable = DataChunkIterator.from_iterable( - data=video_capture_ob, + timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() + fps = video_capture_ob.get_movie_fps() + maxshape = (total_frames, *frame_shape) + best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) + tqdm_pos, tqdm_mininterval = (0, 10) + if chunk_data: + iterable = DataChunkIterator( + data=tqdm( + iterable=video_capture_ob, + desc=f"Copying movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, + ), iter_axis=0, # nwb standard is time as zero axis maxshape=tuple(maxshape), - dtype=video_capture_ob.get_movie_frame_dtype(), ) data = H5DataIO(iterable, compression=compression, chunks=best_gzip_chunk) - timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() - fps = video_capture_ob.get_movie_fps() else: - with VideoCaptureContext(str(file), stub=stub_test) as video_capture_ob: - total_frames = video_capture_ob.get_movie_frame_count() - frame_shape = video_capture_ob.get_frame_shape() - maxshape = (total_frames, *frame_shape) - best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) - iterable = [] - tqdm_pos, tqdm_mininterval = (0, 10) - with tqdm( + iterable = [] + with tqdm( desc=f"Reading movie data for {Path(file).name}", position=tqdm_pos, total=total_frames, mininterval=tqdm_mininterval, - ) as pbar: - for frame in video_capture_ob: - iterable.append(frame) - pbar.update(1) - data=H5DataIO( - DataChunkIterator( - tqdm( - iterable=np.array(iterable), - desc=f"Writing movie data for {Path(file).name}", - position=tqdm_pos, - mininterval=tqdm_mininterval, - ), - iter_axis=0, # nwb standard is time as zero axis - maxshape=tuple(maxshape), - ), - compression="gzip", - chunks=best_gzip_chunk, - ) - timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() - fps = video_capture_ob.get_movie_fps() + ) as pbar: + for frame in video_capture_ob: + iterable.append(frame) + pbar.update(1) + data = H5DataIO( + DataChunkIterator( + tqdm( + iterable=np.array(iterable), + desc=f"Writing movie data for {Path(file).name}", + position=tqdm_pos, + mininterval=tqdm_mininterval, + ), + iter_axis=0, # nwb standard is time as zero axis + maxshape=tuple(maxshape), + ), + compression="gzip", + chunks=best_gzip_chunk, + ) # capture data in kwargs: image_series_kwargs.update(data=data) From 7a863d0026b221d8017d9d507584286aa99e7eb8 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Tue, 25 Jan 2022 18:08:40 +0000 Subject: [PATCH 20/70] Automated changes --- .../behavior/movie/moviedatainterface.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 0f4a17d36..68e561007 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -72,16 +72,16 @@ def get_metadata(self): return metadata def run_conversion( - self, - nwbfile: NWBFile, - metadata: dict, - stub_test: bool = False, - external_mode: bool = True, - starting_times: Optional[list] = None, - chunk_data: bool = True, - module_name: Optional[str] = None, - module_description: Optional[str] = None, - compression: str = "gzip", + self, + nwbfile: NWBFile, + metadata: dict, + stub_test: bool = False, + external_mode: bool = True, + starting_times: Optional[list] = None, + chunk_data: bool = True, + module_name: Optional[str] = None, + module_description: Optional[str] = None, + compression: str = "gzip", ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -135,9 +135,9 @@ def run_conversion( if starting_times is not None: assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) - and len(starting_times) == len(file_paths) + isinstance(starting_times, list) + and all([isinstance(x, float) for x in starting_times]) + and len(starting_times) == len(file_paths) ), "Argument 'starting_times' must be a list of floats in one-to-one correspondence with 'file_paths'!" else: starting_times = [0.0] @@ -177,7 +177,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: file = file_list[0] - uncompressed_estimate = Path(file).stat().st_size*70 + uncompressed_estimate = Path(file).stat().st_size * 70 available_memory = psutil.virtual_memory().available if not chunk_data and not stub_test and uncompressed_estimate >= available_memory: warn( @@ -210,10 +210,10 @@ def _check_duplicates(image_series_kwargs_list): else: iterable = [] with tqdm( - desc=f"Reading movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, + desc=f"Reading movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, ) as pbar: for frame in video_capture_ob: iterable.append(frame) From f89ceaf9fe1c446f9f69a7ff1990289329d6139a Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 26 Jan 2022 16:58:38 +0530 Subject: [PATCH 21/70] using unittests --- tests/test_internals/test_movie_interface.py | 234 +++++++++---------- 1 file changed, 111 insertions(+), 123 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 690acb830..aa5da3258 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -1,134 +1,122 @@ -import shutil - +import unittest +import tempfile import numpy as np -import pytest from pynwb import NWBHDF5IO - +import os from nwb_conversion_tools import NWBConverter, MovieInterface try: import cv2 + skip_test = False except ImportError: - pytestmark = pytest.mark.skip(f"cv2 not installed, skipping test module {__name__} ") - - -@pytest.fixture(scope="module") -def base_path(tmp_path_factory): - test_folder = tmp_path_factory.mktemp("movie_tests") - yield test_folder - print(f"removing {test_folder}") - shutil.rmtree(test_folder) - - -@pytest.fixture -def create_movies(base_path): - movie_file1 = base_path / "test1.avi" - movie_file2 = base_path / "test2.avi" - if movie_file1.exists(): - movie_file1.unlink() - if movie_file2.exists(): - movie_file2.unlink() - (nf, nx, ny) = (50, 640, 480) - writer1 = cv2.VideoWriter( - filename=str(movie_file1), - apiPreference=None, - fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), - fps=25, - frameSize=(ny, nx), - params=None, - ) - writer2 = cv2.VideoWriter( - filename=str(movie_file2), - apiPreference=None, - fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), - fps=25, - frameSize=(ny, nx), - params=None, - ) - for k in range(nf): - writer1.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) - writer2.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) - writer1.release() - writer2.release() - return [str(movie_file1), str(movie_file2)] - - -@pytest.fixture -def movie_converter(create_movies): - class MovieTestNWBConverter(NWBConverter): - data_interface_classes = dict(Movie=MovieInterface) - - source_data = dict(Movie=dict(file_paths=create_movies)) - converter = MovieTestNWBConverter(source_data) - yield converter - del converter - - -@pytest.fixture(scope="module") -def nwbfile_path(base_path): - return str(base_path / "test.nwb") - - -def test_movie_starting_times(movie_converter, nwbfile_path, create_movies): - starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=False)) - movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - mod = nwbfile.acquisition - metadata = movie_converter.get_metadata() - for no in range(len(metadata["Behavior"]["Movies"])): - movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] - assert movie_interface_name in mod - assert starting_times[no] == mod[movie_interface_name].starting_time - - -def test_movie_custom_module(movie_converter, nwbfile_path, create_movies): - starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - module_name = "TestModule" - module_description = "This is a test module." - conversion_opts = dict( - Movie=dict( - starting_times=starting_times, - external_mode=False, - module_name=module_name, - module_description=module_description, + skip_test = True + + +@unittest.skipIf(skip_test, "cv2 not installed") +class TestMovieInterface(unittest.TestCase): + + def setUp(self) -> None: + self.test_dir = tempfile.mkdtemp() + self.movie_files = self.create_movies() + self.nwb_converter = self.create_movie_converter() + self.nwbfile_path = os.path.join(self.test_dir, "movie_test.nwb") + + def create_movies(self): + movie_file1 = os.path.join(self.test_dir, "test1.avi") + movie_file2 = os.path.join(self.test_dir, "test2.avi") + (nf, nx, ny) = (10, 640, 480) + writer1 = cv2.VideoWriter( + filename=movie_file1, + apiPreference=None, + fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), + fps=25, + frameSize=(ny, nx), + params=None, + ) + writer2 = cv2.VideoWriter( + filename=movie_file2, + apiPreference=None, + fourcc=cv2.VideoWriter_fourcc("M", "J", "P", "G"), + fps=25, + frameSize=(ny, nx), + params=None, ) - ) - movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - assert module_name in nwbfile.processing - assert module_description == nwbfile.processing[module_name].description - -@pytest.mark.parametrize("chunk_data", [True, False]) -def test_movie_chunking(chunk_data, movie_converter, nwbfile_path, create_movies): - starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - conv_ops = dict( - Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=chunk_data) - ) - movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conv_ops) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - mod = nwbfile.acquisition - metadata = movie_converter.get_metadata() - for no in range(len(metadata["Behavior"]["Movies"])): - movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] - if chunk_data: + for k in range(nf): + writer1.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) + writer2.write(np.random.randint(0, 255, (nx, ny, 3)).astype("uint8")) + writer1.release() + writer2.release() + return [movie_file1, movie_file2] + + def create_movie_converter(self): + class MovieTestNWBConverter(NWBConverter): + data_interface_classes = dict(Movie=MovieInterface) + + source_data = dict(Movie=dict(file_paths=self.movie_files)) + return MovieTestNWBConverter(source_data) + + def test_movie_starting_times(self): + starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] + conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=False)) + self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts) + with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = self.nwb_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + assert movie_interface_name in mod + assert starting_times[no] == mod[movie_interface_name].starting_time + + def test_movie_custom_module(self): + starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] + module_name = "TestModule" + module_description = "This is a test module." + conversion_opts = dict( + Movie=dict( + starting_times=starting_times, + external_mode=False, + module_name=module_name, + module_description=module_description, + ) + ) + self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts) + with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: + nwbfile = io.read() + assert module_name in nwbfile.processing + assert module_description == nwbfile.processing[module_name].description + + def test_movie_chunking(self): + starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] + conv_ops = dict( + Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=False) + ) + self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conv_ops) + + with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = self.nwb_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] assert mod[movie_interface_name].data.chunks is not None # TODO retrive storage_layout of hdf5 dataset - else: - assert mod[movie_interface_name].data.chunks is not None - -def test_movie_external_mode(movie_converter, nwbfile_path, create_movies): - starting_times = [np.float(np.random.randint(200)) for i in range(len(create_movies))] - conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=True)) - movie_converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, conversion_options=conversion_opts) - with NWBHDF5IO(path=nwbfile_path, mode="r") as io: - nwbfile = io.read() - mod = nwbfile.acquisition - metadata = movie_converter.get_metadata() - for no in range(len(metadata["Behavior"]["Movies"])): - movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] - assert mod[movie_interface_name].external_file[0] == create_movies[no] + def test_movie_external_mode(self): + starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] + conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=True)) + self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, overwrite=True, + conversion_options=conversion_opts) + with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = self.nwb_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + assert mod[movie_interface_name].external_file[0] == self.movie_files[no] From 1050c6f9c24b7824ec5c4ff9d3579b3ba707a0fe Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 26 Jan 2022 11:29:28 +0000 Subject: [PATCH 22/70] Automated changes --- tests/test_internals/test_movie_interface.py | 23 ++++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index aa5da3258..ac35968c0 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -7,6 +7,7 @@ try: import cv2 + skip_test = False except ImportError: skip_test = True @@ -14,7 +15,6 @@ @unittest.skipIf(skip_test, "cv2 not installed") class TestMovieInterface(unittest.TestCase): - def setUp(self) -> None: self.test_dir = tempfile.mkdtemp() self.movie_files = self.create_movies() @@ -59,9 +59,9 @@ class MovieTestNWBConverter(NWBConverter): def test_movie_starting_times(self): starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=False)) - self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, - overwrite=True, - conversion_options=conversion_opts) + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts + ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() mod = nwbfile.acquisition @@ -83,9 +83,9 @@ def test_movie_custom_module(self): module_description=module_description, ) ) - self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, - overwrite=True, - conversion_options=conversion_opts) + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts + ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() assert module_name in nwbfile.processing @@ -96,9 +96,7 @@ def test_movie_chunking(self): conv_ops = dict( Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=False) ) - self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, - overwrite=True, - conversion_options=conv_ops) + self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conv_ops) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() @@ -111,8 +109,9 @@ def test_movie_chunking(self): def test_movie_external_mode(self): starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=True)) - self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, overwrite=True, - conversion_options=conversion_opts) + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts + ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() mod = nwbfile.acquisition From cb64e800505229203961bcfa5074ad899cd3b91e Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 26 Jan 2022 17:02:45 +0530 Subject: [PATCH 23/70] adding teardown --- tests/test_internals/test_movie_interface.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index ac35968c0..74d899a88 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -1,3 +1,4 @@ +import shutil import unittest import tempfile import numpy as np @@ -119,3 +120,7 @@ def test_movie_external_mode(self): for no in range(len(metadata["Behavior"]["Movies"])): movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] assert mod[movie_interface_name].external_file[0] == self.movie_files[no] + + def tearDown(self) -> None: + shutil.rmtree(self.test_dir) + del self.nwb_converter From c9792b4c795bc20cc100870c12f72a722ef46dd7 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 26 Jan 2022 17:14:27 +0530 Subject: [PATCH 24/70] iterable frame setting bug fix --- .../datainterfaces/behavior/movie/movie_utils.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index f170831aa..0577d10a8 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -114,16 +114,17 @@ def __next__(self): if not self.isOpened(): super().__init__(*self._args, **self._kwargs) try: - if self.current_frame < self.frame_count: + if self._current_frame < self.frame_count: + self.current_frame = self._current_frame success, frame = self.read() - self.current_frame += 1 + self._current_frame += 1 self.release() if success: return frame else: return np.nan * np.ones(self.get_frame_shape()) else: - self.current_frame = 0 + self._current_frame = 0 self.release() raise StopIteration except Exception: From 4b902c77aee559aed541a9703753fa1536f0850e Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 2 Feb 2022 18:36:06 +0530 Subject: [PATCH 25/70] using VideoWriter as an attribute of context --- .../behavior/movie/movie_utils.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index 0577d10a8..c4afbcd40 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -15,11 +15,11 @@ PathType = Union[str, Path] -class VideoCaptureContext(cv2.VideoCapture): +class VideoCaptureContext: def __init__(self, *args, stub=False, **kwargs): + self.vc = cv2.VideoCapture(*args, **kwargs) self._args = args self._kwargs = kwargs - super().__init__(*args, **kwargs) self.stub = stub self._current_frame = 0 self.frame_count = self.get_movie_frame_count() @@ -32,12 +32,12 @@ def get_movie_timestamps(self): Return numpy array of the timestamps for a movie file. """ - if not self.isOpened(): + if not self.vc.isOpened(): raise ValueError("movie file is not open") - ts = [self.get(cv2.CAP_PROP_POS_MSEC)] + ts = [self.vc.get(cv2.CAP_PROP_POS_MSEC)] for i in tqdm(range(1, self.get_movie_frame_count()), desc="retrieving video timestamps"): self.current_frame = i - ts.append(self.get(cv2.CAP_PROP_POS_MSEC)) + ts.append(self.vc.get(cv2.CAP_PROP_POS_MSEC)) self.current_frame = 0 return np.array(ts) @@ -47,8 +47,8 @@ def get_movie_fps(self): """ if int(cv2.__version__.split(".")[0]) < 3: - return self.get(cv2.cv.CV_CAP_PROP_FPS) - return self.get(cv2.CAP_PROP_FPS) + return self.vc.get(cv2.cv.CV_CAP_PROP_FPS) + return self.vc.get(cv2.CAP_PROP_FPS) def get_frame_shape(self) -> Tuple: """ @@ -65,9 +65,9 @@ def get_movie_frame_count(self): # if stub the assume a max frame count of 10 return 10 if int(cv2.__version__.split(".")[0]) < 3: - count = self.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT) + count = self.vc.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT) else: - count = self.get(cv2.CAP_PROP_FRAME_COUNT) + count = self.vc.get(cv2.CAP_PROP_FRAME_COUNT) return int(count) @property @@ -80,7 +80,7 @@ def current_frame(self, frame_no): set_arg = cv2.cv.CV_CAP_PROP_POS_FRAMES else: set_arg = cv2.CAP_PROP_POS_FRAMES - set_value = self.set(set_arg, frame_no) + set_value = self.vc.set(set_arg, frame_no) if set_value: self._current_frame = frame_no else: @@ -90,11 +90,11 @@ def get_movie_frame(self, frame_no: int): """ Return the specific frame from a movie. """ - if not self.isOpened(): + if not self.vc.isOpened(): raise ValueError("movie file is not open") assert frame_no < self.get_movie_frame_count(), "frame number is greater than length of movie" self.current_frame = frame_no - success, frame = self.read() + success, frame = self.vc.read() self.current_frame = 0 if success: return frame @@ -111,32 +111,32 @@ def __iter__(self): return self def __next__(self): - if not self.isOpened(): - super().__init__(*self._args, **self._kwargs) + if not self.vc.isOpened(): + self.vc = cv2.VideoCapture(*self._args, **self._kwargs) try: if self._current_frame < self.frame_count: self.current_frame = self._current_frame - success, frame = self.read() + success, frame = self.vc.read() self._current_frame += 1 - self.release() + self.vc.release() if success: return frame else: return np.nan * np.ones(self.get_frame_shape()) else: self._current_frame = 0 - self.release() + self.vc.release() raise StopIteration except Exception: raise StopIteration def __enter__(self): - if not self.isOpened(): - super().__init__(*self._args, **self._kwargs) + if not self.vc.isOpened(): + self.vc = cv2.VideoCapture(*self._args, **self._kwargs) return self def __exit__(self, *args): - self.release() + self.vc.release() def __del__(self): - self.release() + self.vc.release() \ No newline at end of file From fdb2929391afefcebbdbd08ce5d91a5e5c56d5a0 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 2 Feb 2022 13:07:14 +0000 Subject: [PATCH 26/70] Automated changes --- .../datainterfaces/behavior/movie/movie_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index c4afbcd40..15665cc08 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -139,4 +139,4 @@ def __exit__(self, *args): self.vc.release() def __del__(self): - self.vc.release() \ No newline at end of file + self.vc.release() From ad96bc6db99552c4958756a7c176ad88fe402590 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 11 Feb 2022 16:27:15 +0530 Subject: [PATCH 27/70] contextmanager not open, bug fix --- .../behavior/movie/moviedatainterface.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 642c4ba82..207c9c588 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -38,7 +38,6 @@ def __init__(self, file_paths: list): Many movie storage formats segment a sequence of movies over the course of the experiment. Pass the file paths for this movies as a list in sorted, consecutive order. """ - assert HAVE_OPENCV, INSTALL_MESSAGE super().__init__(file_paths=file_paths) def get_metadata_schema(self): @@ -173,7 +172,7 @@ def _check_duplicates(image_series_kwargs_list): file_list = image_series_kwargs.pop("data") if external_mode: image_series_kwargs.update(format="external", external_file=file_list) - with VideoCaptureContext(str(file_list[0]), stub=stub_test) as vc: + with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: @@ -187,7 +186,7 @@ def _check_duplicates(image_series_kwargs_list): ) chunk_data = True - with VideoCaptureContext(str(file), stub=stub_test) as video_capture_ob: + with VideoCaptureContext(str(file)) as video_capture_ob: total_frames = video_capture_ob.get_movie_frame_count() frame_shape = video_capture_ob.get_frame_shape() timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() @@ -196,6 +195,7 @@ def _check_duplicates(image_series_kwargs_list): best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) tqdm_pos, tqdm_mininterval = (0, 10) if chunk_data: + video_capture_ob.vc = cv2.VideoCapture(str(file)) iterable = DataChunkIterator( data=tqdm( iterable=video_capture_ob, @@ -210,15 +210,16 @@ def _check_duplicates(image_series_kwargs_list): data = H5DataIO(iterable, compression=compression, chunks=best_gzip_chunk) else: iterable = [] - with tqdm( - desc=f"Reading movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, - ) as pbar: - for frame in video_capture_ob: - iterable.append(frame) - pbar.update(1) + with VideoCaptureContext(str(file)) as video_capture_ob: + with tqdm( + desc=f"Reading movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, + ) as pbar: + for frame in video_capture_ob: + iterable.append(frame) + pbar.update(1) data = H5DataIO( DataChunkIterator( tqdm( From 97e159f5bc2dcde54d0d2747e65352de0caed234 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 11 Feb 2022 16:27:35 +0530 Subject: [PATCH 28/70] update session_start_time --- tests/test_internals/test_movie_interface.py | 28 +++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 74d899a88..a2b33ee0e 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -5,6 +5,7 @@ from pynwb import NWBHDF5IO import os from nwb_conversion_tools import NWBConverter, MovieInterface +from datetime import datetime try: import cv2 @@ -57,11 +58,19 @@ class MovieTestNWBConverter(NWBConverter): source_data = dict(Movie=dict(file_paths=self.movie_files)) return MovieTestNWBConverter(source_data) + def get_metadata(self): + metadata = self.nwb_converter.get_metadata() + metadata["NWBFile"].update(session_start_time=datetime.now().astimezone().strftime("%Y-%m-%dT%H:%M:%S")) + return metadata + def test_movie_starting_times(self): starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=False)) self.nwb_converter.run_conversion( - nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata=self.get_metadata() ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() @@ -85,7 +94,10 @@ def test_movie_custom_module(self): ) ) self.nwb_converter.run_conversion( - nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata = self.get_metadata() ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() @@ -97,7 +109,12 @@ def test_movie_chunking(self): conv_ops = dict( Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=False) ) - self.nwb_converter.run_conversion(nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conv_ops) + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conv_ops, + metadata=self.get_metadata() + ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() @@ -111,7 +128,10 @@ def test_movie_external_mode(self): starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=True)) self.nwb_converter.run_conversion( - nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata=self.get_metadata() ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() From 08ddda42f1e6f849f4a5a612755de9b2d7059e1b Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Fri, 11 Feb 2022 10:58:16 +0000 Subject: [PATCH 29/70] Automated changes --- tests/test_internals/test_movie_interface.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index a2b33ee0e..97b4e566d 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -70,7 +70,7 @@ def test_movie_starting_times(self): nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts, - metadata=self.get_metadata() + metadata=self.get_metadata(), ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() @@ -97,7 +97,7 @@ def test_movie_custom_module(self): nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts, - metadata = self.get_metadata() + metadata=self.get_metadata(), ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() @@ -110,10 +110,7 @@ def test_movie_chunking(self): Movie=dict(external_mode=False, stub_test=True, starting_times=starting_times, chunk_data=False) ) self.nwb_converter.run_conversion( - nwbfile_path=self.nwbfile_path, - overwrite=True, - conversion_options=conv_ops, - metadata=self.get_metadata() + nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conv_ops, metadata=self.get_metadata() ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: @@ -131,7 +128,7 @@ def test_movie_external_mode(self): nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts, - metadata=self.get_metadata() + metadata=self.get_metadata(), ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() From b8db4c6972616040949fed274ead847723cfce45 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Fri, 11 Feb 2022 11:03:26 +0000 Subject: [PATCH 30/70] Automated changes --- .../datainterfaces/behavior/movie/movie_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index cc858c4d2..0d01338c7 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -34,7 +34,7 @@ def get_movie_timestamps(self): ts2 = [] for no in tqdm(range(self.get_movie_frame_count()), desc="retrieving timestamps"): success, frame = self.vc.read() - ts2.append(self.vc.get(cv2.CAP_PROP_POS_MSEC)/1000) + ts2.append(self.vc.get(cv2.CAP_PROP_POS_MSEC) / 1000) if not success: break return np.array(ts2) From 7fb405cedd6853bb918f7b9412256c20ca5f5abd Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 11 Feb 2022 16:37:47 +0530 Subject: [PATCH 31/70] remove cv2 import --- .../behavior/movie/moviedatainterface.py | 48 ++++++++----------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 207c9c588..b513d6eda 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -12,17 +12,9 @@ from tqdm import tqdm from ....basedatainterface import BaseDataInterface -from ....utils.nwbfile_tools import get_module from ....utils.conversion_tools import check_regular_timestamps from ....utils.json_schema import get_schema_from_hdmf_class, get_base_schema - -try: - import cv2 - - HAVE_OPENCV = True -except ImportError: - HAVE_OPENCV = False -INSTALL_MESSAGE = "Please install opencv to use this interface! (pip install opencv-python)" +from ....utils.nwbfile_tools import get_module class MovieInterface(BaseDataInterface): @@ -72,16 +64,16 @@ def get_metadata(self): return metadata def run_conversion( - self, - nwbfile: NWBFile, - metadata: dict, - stub_test: bool = False, - external_mode: bool = True, - starting_times: Optional[list] = None, - chunk_data: bool = True, - module_name: Optional[str] = None, - module_description: Optional[str] = None, - compression: str = "gzip", + self, + nwbfile: NWBFile, + metadata: dict, + stub_test: bool = False, + external_mode: bool = True, + starting_times: Optional[list] = None, + chunk_data: bool = True, + module_name: Optional[str] = None, + module_description: Optional[str] = None, + compression: str = "gzip", ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -135,9 +127,9 @@ def run_conversion( if starting_times is not None: assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) - and len(starting_times) == len(file_paths) + isinstance(starting_times, list) + and all([isinstance(x, float) for x in starting_times]) + and len(starting_times) == len(file_paths) ), "Argument 'starting_times' must be a list of floats in one-to-one correspondence with 'file_paths'!" else: starting_times = [0.0] @@ -177,7 +169,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: file = file_list[0] - uncompressed_estimate = Path(file).stat().st_size * 70 + uncompressed_estimate = Path(file).stat().st_size*70 available_memory = psutil.virtual_memory().available if not chunk_data and not stub_test and uncompressed_estimate >= available_memory: warn( @@ -195,7 +187,7 @@ def _check_duplicates(image_series_kwargs_list): best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) tqdm_pos, tqdm_mininterval = (0, 10) if chunk_data: - video_capture_ob.vc = cv2.VideoCapture(str(file)) + _ = video_capture_ob.__enter__() iterable = DataChunkIterator( data=tqdm( iterable=video_capture_ob, @@ -212,10 +204,10 @@ def _check_duplicates(image_series_kwargs_list): iterable = [] with VideoCaptureContext(str(file)) as video_capture_ob: with tqdm( - desc=f"Reading movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, + desc=f"Reading movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, ) as pbar: for frame in video_capture_ob: iterable.append(frame) From 7c65f12476fcae8c60e55d67faed34074fcd5f3f Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Fri, 11 Feb 2022 11:08:39 +0000 Subject: [PATCH 32/70] Automated changes --- .../behavior/movie/moviedatainterface.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index b513d6eda..f9f369540 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -64,16 +64,16 @@ def get_metadata(self): return metadata def run_conversion( - self, - nwbfile: NWBFile, - metadata: dict, - stub_test: bool = False, - external_mode: bool = True, - starting_times: Optional[list] = None, - chunk_data: bool = True, - module_name: Optional[str] = None, - module_description: Optional[str] = None, - compression: str = "gzip", + self, + nwbfile: NWBFile, + metadata: dict, + stub_test: bool = False, + external_mode: bool = True, + starting_times: Optional[list] = None, + chunk_data: bool = True, + module_name: Optional[str] = None, + module_description: Optional[str] = None, + compression: str = "gzip", ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -127,9 +127,9 @@ def run_conversion( if starting_times is not None: assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) - and len(starting_times) == len(file_paths) + isinstance(starting_times, list) + and all([isinstance(x, float) for x in starting_times]) + and len(starting_times) == len(file_paths) ), "Argument 'starting_times' must be a list of floats in one-to-one correspondence with 'file_paths'!" else: starting_times = [0.0] @@ -169,7 +169,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: file = file_list[0] - uncompressed_estimate = Path(file).stat().st_size*70 + uncompressed_estimate = Path(file).stat().st_size * 70 available_memory = psutil.virtual_memory().available if not chunk_data and not stub_test and uncompressed_estimate >= available_memory: warn( @@ -204,10 +204,10 @@ def _check_duplicates(image_series_kwargs_list): iterable = [] with VideoCaptureContext(str(file)) as video_capture_ob: with tqdm( - desc=f"Reading movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, + desc=f"Reading movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, ) as pbar: for frame in video_capture_ob: iterable.append(frame) From 4c58e4a87cc429dd34b1571495f6bb393d69818d Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 18 Feb 2022 17:25:18 +0530 Subject: [PATCH 33/70] manual update from main branch --- tests/test_internals/test_interfaces.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/test_internals/test_interfaces.py b/tests/test_internals/test_interfaces.py index 8ab12a5ea..a96e19569 100644 --- a/tests/test_internals/test_interfaces.py +++ b/tests/test_internals/test_interfaces.py @@ -1,13 +1,14 @@ -from pathlib import Path from platform import python_version from sys import platform +from packaging import version from tempfile import mkdtemp +from pathlib import Path +from datetime import datetime import pytest import spikeextractors as se -from hdmf.testing import TestCase -from packaging import version from spikeextractors.testing import check_recordings_equal, check_sortings_equal +from hdmf.testing import TestCase from nwb_conversion_tools import ( NWBConverter, @@ -18,13 +19,6 @@ CEDRecordingInterface, ) -try: - import cv2 - - HAVE_OPENCV = True -except ImportError: - HAVE_OPENCV = False - class TestAssertions(TestCase): def test_import_assertions(self): @@ -58,6 +52,7 @@ class TutorialNWBConverter(NWBConverter): converter = TutorialNWBConverter(source_data=source_data) metadata = converter.get_metadata() metadata["NWBFile"]["session_description"] = "NWB Conversion Tools tutorial." + metadata["NWBFile"]["session_start_time"] = datetime.now().astimezone().strftime("%Y-%m-%dT%H:%M:%S") metadata["NWBFile"]["experimenter"] = ["My name"] metadata["Subject"] = dict(subject_id="Name of imaginary testing subject (required for DANDI upload)") conversion_options = dict(RecordingTutorial=dict(stub_test=stub_test), SortingTutorial=dict()) @@ -83,7 +78,9 @@ class TutorialNWBConverter(NWBConverter): SortingTutorial=dict(), ) converter = TutorialNWBConverter(source_data=source_data) - converter.run_conversion(nwbfile_path=output_file, overwrite=True) + metadata = converter.get_metadata() + metadata["NWBFile"]["session_start_time"] = datetime.now().astimezone().strftime("%Y-%m-%dT%H:%M:%S") + converter.run_conversion(nwbfile_path=output_file, overwrite=True, metadata=metadata) def test_pkl_interface(): @@ -105,7 +102,9 @@ class SpikeInterfaceTestNWBConverter(NWBConverter): Sorting=dict(file_path=str(test_dir / "test_pkl" / "test_sorting.pkl")), ) converter = SpikeInterfaceTestNWBConverter(source_data=source_data) - converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True) + metadata = converter.get_metadata() + metadata["NWBFile"]["session_start_time"] = datetime.now().astimezone().strftime("%Y-%m-%dT%H:%M:%S") + converter.run_conversion(nwbfile_path=nwbfile_path, overwrite=True, metadata=metadata) nwb_recording = se.NwbRecordingExtractor(file_path=nwbfile_path) nwb_sorting = se.NwbSortingExtractor(file_path=nwbfile_path) From 984230cc612976d5d8f2df2a22b1f6e4fddc2170 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Fri, 18 Feb 2022 17:28:51 +0530 Subject: [PATCH 34/70] remove old functions used to interact with movie files --- .../behavior/movie/movie_utils.py | 48 +------------------ 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index d2b085ead..3419f0514 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -140,50 +140,4 @@ def __exit__(self, *args): self.vc.release() def __del__(self): - self.vc.release() - - -def get_movie_timestamps(movie_file: PathType): - """Return numpy array of the timestamps for a movie file. - - Parameters - ---------- - movie_file : PathType - """ - cap = cv2.VideoCapture(str(movie_file)) - timestamps = [cap.get(cv2.CAP_PROP_POS_MSEC)] - success, frame = cap.read() - while success: - timestamps.append(cap.get(cv2.CAP_PROP_POS_MSEC)) - success, frame = cap.read() - cap.release() - return np.array(timestamps) - - -def get_movie_fps(movie_file: PathType): - """Return the internal frames per second (fps) for a movie file. - - Parameters - ---------- - movie_file : PathType - """ - cap = cv2.VideoCapture(str(movie_file)) - if int((cv2.__version__).split(".")[0]) < 3: - fps = cap.get(cv2.cv.CV_CAP_PROP_FPS) - else: - fps = cap.get(cv2.CAP_PROP_FPS) - cap.release() - return fps - - -def get_frame_shape(movie_file: PathType): - """Return the shape of frames from a movie file. - - Parameters - ---------- - movie_file : PathType - """ - cap = cv2.VideoCapture(str(movie_file)) - success, frame = cap.read() - cap.release() - return frame.shape + self.vc.release() \ No newline at end of file From f6fafbff3eb2a5dc32c0276098a8265dd05894a2 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Fri, 18 Feb 2022 12:00:27 +0000 Subject: [PATCH 35/70] Automated changes --- .../datainterfaces/behavior/movie/movie_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index 3419f0514..2e2238596 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -140,4 +140,4 @@ def __exit__(self, *args): self.vc.release() def __del__(self): - self.vc.release() \ No newline at end of file + self.vc.release() From 5b4d92f12218ef5638c31e634489b27ee18255a2 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Sun, 20 Feb 2022 11:08:53 +0530 Subject: [PATCH 36/70] black format --- .../datainterfaces/behavior/movie/moviedatainterface.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index f9f369540..2ad3cc0f4 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -42,13 +42,7 @@ def get_metadata_schema(self): metadata_schema["properties"]["Behavior"] = get_base_schema(tag="Behavior") metadata_schema["properties"]["Behavior"].update( required=["Movies"], - properties=dict( - Movies=dict( - type="array", - minItems=1, - items=image_series_metadata_schema, - ) - ), + properties=dict(Movies=dict(type="array", minItems=1, items=image_series_metadata_schema,)), ) return metadata_schema From 1c0a2fd284de5695e790c6629daa33abf0439c8f Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Sun, 20 Feb 2022 05:39:46 +0000 Subject: [PATCH 37/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 2ad3cc0f4..f9f369540 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -42,7 +42,13 @@ def get_metadata_schema(self): metadata_schema["properties"]["Behavior"] = get_base_schema(tag="Behavior") metadata_schema["properties"]["Behavior"].update( required=["Movies"], - properties=dict(Movies=dict(type="array", minItems=1, items=image_series_metadata_schema,)), + properties=dict( + Movies=dict( + type="array", + minItems=1, + items=image_series_metadata_schema, + ) + ), ) return metadata_schema From cb93993f0798261285c441b970366998ca69619e Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 10:14:05 +0530 Subject: [PATCH 38/70] pycharm reformat code --- .../behavior/movie/movie_utils.py | 4 +- .../behavior/movie/moviedatainterface.py | 38 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index 2e2238596..e1ec611ed 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -37,7 +37,7 @@ def get_movie_timestamps(self): if not success: break timestamps.append(self.vc.get(cv2.CAP_PROP_POS_MSEC)) - return np.array(timestamps) / 1000 + return np.array(timestamps)/1000 def get_movie_fps(self): """Return the internal frames per second (fps) for a movie file.""" @@ -61,7 +61,7 @@ def frame_count(self): def frame_count(self, val: int): assert val > 0, "You must set a positive frame_count (received {val})." assert ( - val <= self._movie_frame_count() + val <= self._movie_frame_count() ), "Cannot set manual frame_count beyond length of video (received {val})." self._frame_count = val diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 2ad3cc0f4..fc34cf07a 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -42,7 +42,7 @@ def get_metadata_schema(self): metadata_schema["properties"]["Behavior"] = get_base_schema(tag="Behavior") metadata_schema["properties"]["Behavior"].update( required=["Movies"], - properties=dict(Movies=dict(type="array", minItems=1, items=image_series_metadata_schema,)), + properties=dict(Movies=dict(type="array", minItems=1, items=image_series_metadata_schema, )), ) return metadata_schema @@ -58,16 +58,16 @@ def get_metadata(self): return metadata def run_conversion( - self, - nwbfile: NWBFile, - metadata: dict, - stub_test: bool = False, - external_mode: bool = True, - starting_times: Optional[list] = None, - chunk_data: bool = True, - module_name: Optional[str] = None, - module_description: Optional[str] = None, - compression: str = "gzip", + self, + nwbfile: NWBFile, + metadata: dict, + stub_test: bool = False, + external_mode: bool = True, + starting_times: Optional[list] = None, + chunk_data: bool = True, + module_name: Optional[str] = None, + module_description: Optional[str] = None, + compression: str = "gzip", ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -121,9 +121,9 @@ def run_conversion( if starting_times is not None: assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) - and len(starting_times) == len(file_paths) + isinstance(starting_times, list) + and all([isinstance(x, float) for x in starting_times]) + and len(starting_times) == len(file_paths) ), "Argument 'starting_times' must be a list of floats in one-to-one correspondence with 'file_paths'!" else: starting_times = [0.0] @@ -163,7 +163,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: file = file_list[0] - uncompressed_estimate = Path(file).stat().st_size * 70 + uncompressed_estimate = Path(file).stat().st_size*70 available_memory = psutil.virtual_memory().available if not chunk_data and not stub_test and uncompressed_estimate >= available_memory: warn( @@ -198,10 +198,10 @@ def _check_duplicates(image_series_kwargs_list): iterable = [] with VideoCaptureContext(str(file)) as video_capture_ob: with tqdm( - desc=f"Reading movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, + desc=f"Reading movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, ) as pbar: for frame in video_capture_ob: iterable.append(frame) From 1a0ac5c19f79d2f026eb711c001b5cde55856ce1 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 21 Feb 2022 04:46:00 +0000 Subject: [PATCH 39/70] Automated changes --- .../behavior/movie/movie_utils.py | 4 +-- .../behavior/movie/moviedatainterface.py | 36 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py index e1ec611ed..2e2238596 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/movie_utils.py @@ -37,7 +37,7 @@ def get_movie_timestamps(self): if not success: break timestamps.append(self.vc.get(cv2.CAP_PROP_POS_MSEC)) - return np.array(timestamps)/1000 + return np.array(timestamps) / 1000 def get_movie_fps(self): """Return the internal frames per second (fps) for a movie file.""" @@ -61,7 +61,7 @@ def frame_count(self): def frame_count(self, val: int): assert val > 0, "You must set a positive frame_count (received {val})." assert ( - val <= self._movie_frame_count() + val <= self._movie_frame_count() ), "Cannot set manual frame_count beyond length of video (received {val})." self._frame_count = val diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index b513d6eda..f9f369540 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -64,16 +64,16 @@ def get_metadata(self): return metadata def run_conversion( - self, - nwbfile: NWBFile, - metadata: dict, - stub_test: bool = False, - external_mode: bool = True, - starting_times: Optional[list] = None, - chunk_data: bool = True, - module_name: Optional[str] = None, - module_description: Optional[str] = None, - compression: str = "gzip", + self, + nwbfile: NWBFile, + metadata: dict, + stub_test: bool = False, + external_mode: bool = True, + starting_times: Optional[list] = None, + chunk_data: bool = True, + module_name: Optional[str] = None, + module_description: Optional[str] = None, + compression: str = "gzip", ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -127,9 +127,9 @@ def run_conversion( if starting_times is not None: assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) - and len(starting_times) == len(file_paths) + isinstance(starting_times, list) + and all([isinstance(x, float) for x in starting_times]) + and len(starting_times) == len(file_paths) ), "Argument 'starting_times' must be a list of floats in one-to-one correspondence with 'file_paths'!" else: starting_times = [0.0] @@ -169,7 +169,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times else: file = file_list[0] - uncompressed_estimate = Path(file).stat().st_size*70 + uncompressed_estimate = Path(file).stat().st_size * 70 available_memory = psutil.virtual_memory().available if not chunk_data and not stub_test and uncompressed_estimate >= available_memory: warn( @@ -204,10 +204,10 @@ def _check_duplicates(image_series_kwargs_list): iterable = [] with VideoCaptureContext(str(file)) as video_capture_ob: with tqdm( - desc=f"Reading movie data for {Path(file).name}", - position=tqdm_pos, - total=total_frames, - mininterval=tqdm_mininterval, + desc=f"Reading movie data for {Path(file).name}", + position=tqdm_pos, + total=total_frames, + mininterval=tqdm_mininterval, ) as pbar: for frame in video_capture_ob: iterable.append(frame) From 3a37b534c5bfc2366f996a3cfeeaf86aa6660f01 Mon Sep 17 00:00:00 2001 From: Saksham Sharda <11567561+Saksham20@users.noreply.github.com> Date: Mon, 21 Feb 2022 21:42:39 +0530 Subject: [PATCH 40/70] Apply suggestions from code review Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- .../datainterfaces/behavior/movie/moviedatainterface.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index f9f369540..8a0aea709 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -97,7 +97,7 @@ def run_conversion( (https://pynwb.readthedocs.io/en/stable/pynwb.image.html#pynwb.image.ImageSeries). The list for the 'Movies' key should correspond one to one to the movie files in the file_paths list. If multiple movies need to be in the same ImageSeries, then supply the same value for "name" key. - Multiple movies in same ImageSeries is only supported if 'external_mode'=True. + Storing multiple movies in the same ImageSeries is only supported if 'external_mode'=True. stub_test : bool, optional If True, truncates the write operation for fast testing. The default is False. external_mode : bool, optional @@ -142,17 +142,16 @@ def run_conversion( f"({len(image_series_kwargs_list)}) vs. file_paths ({len(self.source_data['file_paths'])})!" ) - # check for duplicates in image_series_kwargs list keys: def _check_duplicates(image_series_kwargs_list): image_series_kwargs_list_keys = [i["name"] for i in image_series_kwargs_list] if len(set(image_series_kwargs_list_keys)) < len(image_series_kwargs_list_keys): - assert external_mode, "for multiple video files under the same ImageSeries name, use exernal_mode=True" + assert external_mode, "For multiple video files under the same ImageSeries name, use exernal_mode=True." keys_set = [] image_series_kwargs_list_unique = [] - for no, image_series_kwargs in enumerate(image_series_kwargs_list): + for n, image_series_kwargs in enumerate(image_series_kwargs_list): if image_series_kwargs["name"] not in keys_set: keys_set.append(image_series_kwargs["name"]) - image_series_kwargs_list_unique.append(dict(image_series_kwargs, data=[file_paths[no]])) + image_series_kwargs_list_unique.append(dict(image_series_kwargs, data=[file_paths[n]])) else: idx = keys_set.index(image_series_kwargs["name"]) image_series_kwargs_list_unique[idx]["data"].append(file_paths[no]) From bb68d9c4c651252350560d2b0104e09ad1a8adc1 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 21:47:01 +0530 Subject: [PATCH 41/70] adding compression options --- .../behavior/movie/moviedatainterface.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 8a0aea709..3727dbb51 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -73,7 +73,8 @@ def run_conversion( chunk_data: bool = True, module_name: Optional[str] = None, module_description: Optional[str] = None, - compression: str = "gzip", + compression: Optional[str] = "gzip", + compression_options: Optional[dict] = None, ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -120,6 +121,12 @@ def run_conversion( module_description: str, optional If the processing module specified by module_name does not exist, it will be created with this description. The default description is the same as used by the conversion_tools.get_module function. + compression: str, optional + compression strategy to use for HFDataIO. + https://hdmf.readthedocs.io/en/latest/hdmf.backends.hdf5.h5_utils.html#hdmf.backends.hdf5.h5_utils.H5DataIO + compression_options: int, optional + parameter for compression filter. See HFDataIO doc. + https://hdmf.readthedocs.io/en/latest/hdmf.backends.hdf5.h5_utils.html#hdmf.backends.hdf5.h5_utils.H5DataIO """ from .movie_utils import VideoCaptureContext @@ -198,7 +205,10 @@ def _check_duplicates(image_series_kwargs_list): iter_axis=0, # nwb standard is time as zero axis maxshape=tuple(maxshape), ) - data = H5DataIO(iterable, compression=compression, chunks=best_gzip_chunk) + data = H5DataIO(iterable, + compression=compression, + compression_options=compression_options, + chunks=best_gzip_chunk) else: iterable = [] with VideoCaptureContext(str(file)) as video_capture_ob: @@ -223,6 +233,7 @@ def _check_duplicates(image_series_kwargs_list): maxshape=tuple(maxshape), ), compression="gzip", + compression_options=compression_options, chunks=best_gzip_chunk, ) From 8b8ca554b247cd2ff537a2361e47dde9001c1188 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 21:47:17 +0530 Subject: [PATCH 42/70] move VideoCapture import to module --- .../datainterfaces/behavior/movie/moviedatainterface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 3727dbb51..fd4d6bd7b 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -15,6 +15,7 @@ from ....utils.conversion_tools import check_regular_timestamps from ....utils.json_schema import get_schema_from_hdmf_class, get_base_schema from ....utils.nwbfile_tools import get_module +from .movie_utils import VideoCaptureContext class MovieInterface(BaseDataInterface): @@ -128,8 +129,6 @@ def run_conversion( parameter for compression filter. See HFDataIO doc. https://hdmf.readthedocs.io/en/latest/hdmf.backends.hdf5.h5_utils.html#hdmf.backends.hdf5.h5_utils.H5DataIO """ - from .movie_utils import VideoCaptureContext - file_paths = self.source_data["file_paths"] if starting_times is not None: From 5a1dbc9bbd79f1405489041f334974a65ba299c9 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 21:47:56 +0530 Subject: [PATCH 43/70] enumerate bug --- .../datainterfaces/behavior/movie/moviedatainterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index fd4d6bd7b..e8da34881 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -160,7 +160,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs_list_unique.append(dict(image_series_kwargs, data=[file_paths[n]])) else: idx = keys_set.index(image_series_kwargs["name"]) - image_series_kwargs_list_unique[idx]["data"].append(file_paths[no]) + image_series_kwargs_list_unique[idx]["data"].append(file_paths[n]) return image_series_kwargs_list_unique image_series_kwargs_list_updated = _check_duplicates(image_series_kwargs_list) From a9723d805c51447ea64197702160d79fc13e3a1d Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 21 Feb 2022 16:18:32 +0000 Subject: [PATCH 44/70] Automated changes --- .../behavior/movie/moviedatainterface.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index e8da34881..6ad63fa9e 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -204,10 +204,12 @@ def _check_duplicates(image_series_kwargs_list): iter_axis=0, # nwb standard is time as zero axis maxshape=tuple(maxshape), ) - data = H5DataIO(iterable, - compression=compression, - compression_options=compression_options, - chunks=best_gzip_chunk) + data = H5DataIO( + iterable, + compression=compression, + compression_options=compression_options, + chunks=best_gzip_chunk, + ) else: iterable = [] with VideoCaptureContext(str(file)) as video_capture_ob: From d01ee8637d6047637b3caa7b7891402330efe934 Mon Sep 17 00:00:00 2001 From: Saksham Sharda <11567561+Saksham20@users.noreply.github.com> Date: Mon, 21 Feb 2022 22:06:49 +0530 Subject: [PATCH 45/70] Apply suggestions from code review Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- .../behavior/movie/moviedatainterface.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 6ad63fa9e..9baebe0e1 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -75,7 +75,7 @@ def run_conversion( module_name: Optional[str] = None, module_description: Optional[str] = None, compression: Optional[str] = "gzip", - compression_options: Optional[dict] = None, + compression_options: Optional[int] = None, ): """ Convert the movie data files to ImageSeries and write them in the NWBFile. @@ -123,11 +123,11 @@ def run_conversion( If the processing module specified by module_name does not exist, it will be created with this description. The default description is the same as used by the conversion_tools.get_module function. compression: str, optional - compression strategy to use for HFDataIO. - https://hdmf.readthedocs.io/en/latest/hdmf.backends.hdf5.h5_utils.html#hdmf.backends.hdf5.h5_utils.H5DataIO + Compression strategy to use for HFDataIO. For full list of currently supported filters, see + https://docs.h5py.org/en/latest/high/dataset.html#lossless-compression-filters compression_options: int, optional - parameter for compression filter. See HFDataIO doc. - https://hdmf.readthedocs.io/en/latest/hdmf.backends.hdf5.h5_utils.html#hdmf.backends.hdf5.h5_utils.H5DataIO + Parameter(s) for compression filter. Currently only supports the compression level (integer from 0 to 9) of + compression="gzip". """ file_paths = self.source_data["file_paths"] From a790aee4ec37e038afeb6b17b1c3bde34461e253 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 22:31:09 +0530 Subject: [PATCH 46/70] h5dataio arg name fix --- .../datainterfaces/behavior/movie/moviedatainterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index e8da34881..bb077f73b 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -206,7 +206,7 @@ def _check_duplicates(image_series_kwargs_list): ) data = H5DataIO(iterable, compression=compression, - compression_options=compression_options, + compression_opts=compression_options, chunks=best_gzip_chunk) else: iterable = [] @@ -232,7 +232,7 @@ def _check_duplicates(image_series_kwargs_list): maxshape=tuple(maxshape), ), compression="gzip", - compression_options=compression_options, + compression_opts=compression_options, chunks=best_gzip_chunk, ) From cc4d38c740df2aee1e54592d838d4f133cfa2bcb Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 21 Feb 2022 17:02:49 +0000 Subject: [PATCH 47/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index d3c923654..fc4adbdaa 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -210,10 +210,9 @@ def _check_duplicates(image_series_kwargs_list): compression_options=compression_options, chunks=best_gzip_chunk, ) - data = H5DataIO(iterable, - compression=compression, - compression_opts=compression_options, - chunks=best_gzip_chunk) + data = H5DataIO( + iterable, compression=compression, compression_opts=compression_options, chunks=best_gzip_chunk + ) else: iterable = [] with VideoCaptureContext(str(file)) as video_capture_ob: From 921b050cad40da93b297c3dd690e1b8763f576f9 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 23:28:40 +0530 Subject: [PATCH 48/70] custom starting times for external files mode --- .../behavior/movie/moviedatainterface.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index d3c923654..49a19ccab 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -135,10 +135,7 @@ def run_conversion( assert ( isinstance(starting_times, list) and all([isinstance(x, float) for x in starting_times]) - and len(starting_times) == len(file_paths) - ), "Argument 'starting_times' must be a list of floats in one-to-one correspondence with 'file_paths'!" - else: - starting_times = [0.0] + ), "Argument 'starting_times' must be a list of floats." image_series_kwargs_list = metadata.get("Behavior", dict()).get( "Movies", self.get_metadata()["Behavior"]["Movies"] @@ -164,6 +161,16 @@ def _check_duplicates(image_series_kwargs_list): return image_series_kwargs_list_unique image_series_kwargs_list_updated = _check_duplicates(image_series_kwargs_list) + if starting_times is not None: + assert ( + isinstance(starting_times, list) + and all([isinstance(x, float) for x in starting_times]) + ), "Argument 'starting_times' must be a list of floats." + assert len(starting_times) == len(image_series_kwargs_list_updated),\ + "starting times list length must be equal number of unique ImageSeries " \ + "containers to write to nwb" + else: + starting_times = [0.0] * len(image_series_kwargs_list_updated) for j, image_series_kwargs in enumerate(image_series_kwargs_list_updated): file_list = image_series_kwargs.pop("data") @@ -171,7 +178,7 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(format="external", external_file=file_list) with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() - image_series_kwargs.update(starting_time=0.0, rate=fps) # TODO manage custom starting_times + image_series_kwargs.update(starting_time=starting_times[j], rate=fps) else: file = file_list[0] uncompressed_estimate = Path(file).stat().st_size * 70 From 6c48c3d1c1eaf3c1e47613d61edaf32f83970462 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 21 Feb 2022 17:59:24 +0000 Subject: [PATCH 49/70] Automated changes --- .../behavior/movie/moviedatainterface.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 924925067..c65c04f75 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -132,9 +132,8 @@ def run_conversion( file_paths = self.source_data["file_paths"] if starting_times is not None: - assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) + assert isinstance(starting_times, list) and all( + [isinstance(x, float) for x in starting_times] ), "Argument 'starting_times' must be a list of floats." image_series_kwargs_list = metadata.get("Behavior", dict()).get( @@ -162,13 +161,12 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs_list_updated = _check_duplicates(image_series_kwargs_list) if starting_times is not None: - assert ( - isinstance(starting_times, list) - and all([isinstance(x, float) for x in starting_times]) + assert isinstance(starting_times, list) and all( + [isinstance(x, float) for x in starting_times] ), "Argument 'starting_times' must be a list of floats." - assert len(starting_times) == len(image_series_kwargs_list_updated),\ - "starting times list length must be equal number of unique ImageSeries " \ - "containers to write to nwb" + assert len(starting_times) == len(image_series_kwargs_list_updated), ( + "starting times list length must be equal number of unique ImageSeries " "containers to write to nwb" + ) else: starting_times = [0.0] * len(image_series_kwargs_list_updated) From bfbd5408f2ada3de9bd0dd7dda3058004b643e90 Mon Sep 17 00:00:00 2001 From: Saksham Sharda <11567561+Saksham20@users.noreply.github.com> Date: Mon, 21 Feb 2022 23:35:46 +0530 Subject: [PATCH 50/70] iterable as np array Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- .../datainterfaces/behavior/movie/moviedatainterface.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index c65c04f75..dec4a898d 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -219,7 +219,7 @@ def _check_duplicates(image_series_kwargs_list): iterable, compression=compression, compression_opts=compression_options, chunks=best_gzip_chunk ) else: - iterable = [] + iterable = np.zeros(shape=maxshape, dtype="uint8") with VideoCaptureContext(str(file)) as video_capture_ob: with tqdm( desc=f"Reading movie data for {Path(file).name}", @@ -227,13 +227,13 @@ def _check_duplicates(image_series_kwargs_list): total=total_frames, mininterval=tqdm_mininterval, ) as pbar: - for frame in video_capture_ob: - iterable.append(frame) + for n, frame in enumerate(video_capture_ob): + iterable[n, :, :, :] = frame pbar.update(1) data = H5DataIO( DataChunkIterator( tqdm( - iterable=np.array(iterable), + iterable=iterable, desc=f"Writing movie data for {Path(file).name}", position=tqdm_pos, mininterval=tqdm_mininterval, From dfe5061917c0ac626f35b1261c1a2bcfa8d346d1 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 23:36:27 +0530 Subject: [PATCH 51/70] code reduction --- .../behavior/movie/moviedatainterface.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index dec4a898d..495fda390 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -173,10 +173,10 @@ def _check_duplicates(image_series_kwargs_list): for j, image_series_kwargs in enumerate(image_series_kwargs_list_updated): file_list = image_series_kwargs.pop("data") if external_mode: - image_series_kwargs.update(format="external", external_file=file_list) with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() - image_series_kwargs.update(starting_time=starting_times[j], rate=fps) + image_series_kwargs.update(starting_time=starting_times[j], rate=fps, + format="external", external_file=file_list) else: file = file_list[0] uncompressed_estimate = Path(file).stat().st_size * 70 @@ -207,7 +207,7 @@ def _check_duplicates(image_series_kwargs_list): mininterval=tqdm_mininterval, ), iter_axis=0, # nwb standard is time as zero axis - maxshape=tuple(maxshape), + maxshape=maxshape, ) data = H5DataIO( iterable, @@ -215,9 +215,6 @@ def _check_duplicates(image_series_kwargs_list): compression_options=compression_options, chunks=best_gzip_chunk, ) - data = H5DataIO( - iterable, compression=compression, compression_opts=compression_options, chunks=best_gzip_chunk - ) else: iterable = np.zeros(shape=maxshape, dtype="uint8") with VideoCaptureContext(str(file)) as video_capture_ob: @@ -239,7 +236,7 @@ def _check_duplicates(image_series_kwargs_list): mininterval=tqdm_mininterval, ), iter_axis=0, # nwb standard is time as zero axis - maxshape=tuple(maxshape), + maxshape=maxshape, ), compression="gzip", compression_opts=compression_options, From bf8206618ce582b0da9c7bbdc1b84a185e3ccf40 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Mon, 21 Feb 2022 18:06:32 +0000 Subject: [PATCH 52/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 495fda390..9d47f38c1 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -175,8 +175,9 @@ def _check_duplicates(image_series_kwargs_list): if external_mode: with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() - image_series_kwargs.update(starting_time=starting_times[j], rate=fps, - format="external", external_file=file_list) + image_series_kwargs.update( + starting_time=starting_times[j], rate=fps, format="external", external_file=file_list + ) else: file = file_list[0] uncompressed_estimate = Path(file).stat().st_size * 70 From afbde8305f560b1e40a4c3aad1848e2e24b2d77a Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Mon, 21 Feb 2022 23:43:00 +0530 Subject: [PATCH 53/70] starting_times assertion for external mode false --- .../datainterfaces/behavior/movie/moviedatainterface.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 495fda390..124bb4135 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -243,10 +243,7 @@ def _check_duplicates(image_series_kwargs_list): chunks=best_gzip_chunk, ) - # capture data in kwargs: image_series_kwargs.update(data=data) - if len(starting_times) != len(file_paths): - starting_times.append(timestamps[-1]) if check_regular_timestamps(ts=timestamps): image_series_kwargs.update(starting_time=starting_times[j], rate=fps) else: From 62b39d288848d34462fe08dab9db7eb540f506cc Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 22 Feb 2022 07:52:02 +0530 Subject: [PATCH 54/70] files list separate for imageserieskwargs unique --- .../behavior/movie/moviedatainterface.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 61f754a1e..58a0ee721 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -150,16 +150,18 @@ def _check_duplicates(image_series_kwargs_list): assert external_mode, "For multiple video files under the same ImageSeries name, use exernal_mode=True." keys_set = [] image_series_kwargs_list_unique = [] + file_paths_list = [] for n, image_series_kwargs in enumerate(image_series_kwargs_list): if image_series_kwargs["name"] not in keys_set: keys_set.append(image_series_kwargs["name"]) - image_series_kwargs_list_unique.append(dict(image_series_kwargs, data=[file_paths[n]])) + file_paths_list.append([file_paths[n]]) + image_series_kwargs_list_unique.append(dict(image_series_kwargs)) else: idx = keys_set.index(image_series_kwargs["name"]) - image_series_kwargs_list_unique[idx]["data"].append(file_paths[n]) - return image_series_kwargs_list_unique + file_paths_list[idx].append(file_paths[n]) + return image_series_kwargs_list_unique, file_paths_list - image_series_kwargs_list_updated = _check_duplicates(image_series_kwargs_list) + image_series_kwargs_list_updated, file_paths_list = _check_duplicates(image_series_kwargs_list) if starting_times is not None: assert isinstance(starting_times, list) and all( [isinstance(x, float) for x in starting_times] @@ -170,8 +172,8 @@ def _check_duplicates(image_series_kwargs_list): else: starting_times = [0.0] * len(image_series_kwargs_list_updated) - for j, image_series_kwargs in enumerate(image_series_kwargs_list_updated): - file_list = image_series_kwargs.pop("data") + for j, image_series_kwargs, file_list in enumerate(zip(image_series_kwargs_list_updated, + file_paths_list)): if external_mode: with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() From 56c112527c6c2a91fdb4ed2b380a73b404eed399 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Tue, 22 Feb 2022 02:22:52 +0000 Subject: [PATCH 55/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 58a0ee721..03f24e12b 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -172,8 +172,7 @@ def _check_duplicates(image_series_kwargs_list): else: starting_times = [0.0] * len(image_series_kwargs_list_updated) - for j, image_series_kwargs, file_list in enumerate(zip(image_series_kwargs_list_updated, - file_paths_list)): + for j, image_series_kwargs, file_list in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): if external_mode: with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() From 417992673943d767b0b724f24975461f75ba52e4 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 22 Feb 2022 08:38:08 +0530 Subject: [PATCH 56/70] code review suggestions --- .../datainterfaces/behavior/movie/moviedatainterface.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 03f24e12b..15a0feffa 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -172,7 +172,8 @@ def _check_duplicates(image_series_kwargs_list): else: starting_times = [0.0] * len(image_series_kwargs_list_updated) - for j, image_series_kwargs, file_list in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): + for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, + file_paths_list)): if external_mode: with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() @@ -199,7 +200,7 @@ def _check_duplicates(image_series_kwargs_list): best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) tqdm_pos, tqdm_mininterval = (0, 10) if chunk_data: - _ = video_capture_ob.__enter__() + video_capture_ob = VideoCaptureContext(str(file)) iterable = DataChunkIterator( data=tqdm( iterable=video_capture_ob, From f5d1fff4863a0a780fe21e82c93c8515ee86214f Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Tue, 22 Feb 2022 03:08:56 +0000 Subject: [PATCH 57/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 15a0feffa..9b7c34675 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -172,8 +172,7 @@ def _check_duplicates(image_series_kwargs_list): else: starting_times = [0.0] * len(image_series_kwargs_list_updated) - for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, - file_paths_list)): + for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): if external_mode: with VideoCaptureContext(str(file_list[0])) as vc: fps = vc.get_movie_fps() From d9ade5e38aec815fafd9731a526de5a447476628 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 22 Feb 2022 09:03:34 +0530 Subject: [PATCH 58/70] H5DataIO args bug --- .../datainterfaces/behavior/movie/moviedatainterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 9b7c34675..228e9bc13 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -214,7 +214,7 @@ def _check_duplicates(image_series_kwargs_list): data = H5DataIO( iterable, compression=compression, - compression_options=compression_options, + compression_opts=compression_options, chunks=best_gzip_chunk, ) else: From 33139a784b699cb55de0a469aa2f2dfd7e52c37e Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 22 Feb 2022 23:17:19 +0530 Subject: [PATCH 59/70] starting times as nan --- .../datainterfaces/behavior/movie/moviedatainterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 228e9bc13..9ace3820b 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -170,7 +170,7 @@ def _check_duplicates(image_series_kwargs_list): "starting times list length must be equal number of unique ImageSeries " "containers to write to nwb" ) else: - starting_times = [0.0] * len(image_series_kwargs_list_updated) + starting_times = [np.nan] * len(image_series_kwargs_list_updated) for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): if external_mode: From bca08f18ff7df1bc896969d91072501d52258bf6 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 22 Feb 2022 23:17:43 +0530 Subject: [PATCH 60/70] trimming frames for stub test --- .../datainterfaces/behavior/movie/moviedatainterface.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 9ace3820b..3213a11bb 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -191,6 +191,8 @@ def _check_duplicates(image_series_kwargs_list): chunk_data = True with VideoCaptureContext(str(file)) as video_capture_ob: + if stub_test: + video_capture_ob.frame_count = 10 total_frames = video_capture_ob.get_movie_frame_count() frame_shape = video_capture_ob.get_frame_shape() timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() @@ -200,6 +202,8 @@ def _check_duplicates(image_series_kwargs_list): tqdm_pos, tqdm_mininterval = (0, 10) if chunk_data: video_capture_ob = VideoCaptureContext(str(file)) + if stub_test: + video_capture_ob.frame_count = 10 iterable = DataChunkIterator( data=tqdm( iterable=video_capture_ob, @@ -220,6 +224,8 @@ def _check_duplicates(image_series_kwargs_list): else: iterable = np.zeros(shape=maxshape, dtype="uint8") with VideoCaptureContext(str(file)) as video_capture_ob: + if stub_test: + video_capture_ob.frame_count = 10 with tqdm( desc=f"Reading movie data for {Path(file).name}", position=tqdm_pos, From ebb2adea2c6031f6ec3ba82eb714d426246efe03 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Tue, 22 Feb 2022 23:21:39 +0530 Subject: [PATCH 61/70] starting times as None --- .../datainterfaces/behavior/movie/moviedatainterface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 3213a11bb..5022045d1 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -170,7 +170,7 @@ def _check_duplicates(image_series_kwargs_list): "starting times list length must be equal number of unique ImageSeries " "containers to write to nwb" ) else: - starting_times = [np.nan] * len(image_series_kwargs_list_updated) + starting_times = [None] * len(image_series_kwargs_list_updated) for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): if external_mode: From 212fb41916d849f1a1594a3c7dcce0133b2641ea Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 23 Feb 2022 07:58:01 +0530 Subject: [PATCH 62/70] fix starting _times error --- .../datainterfaces/behavior/movie/moviedatainterface.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 5022045d1..9179d5101 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -170,6 +170,7 @@ def _check_duplicates(image_series_kwargs_list): "starting times list length must be equal number of unique ImageSeries " "containers to write to nwb" ) else: + warn("starting_times not provided, setting to 'None'") starting_times = [None] * len(image_series_kwargs_list_updated) for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): @@ -195,7 +196,8 @@ def _check_duplicates(image_series_kwargs_list): video_capture_ob.frame_count = 10 total_frames = video_capture_ob.get_movie_frame_count() frame_shape = video_capture_ob.get_frame_shape() - timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() + start_time = starting_times[j] if starting_times[j] is not None else 0.0 + timestamps = start_time + video_capture_ob.get_movie_timestamps() fps = video_capture_ob.get_movie_fps() maxshape = (total_frames, *frame_shape) best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) @@ -256,6 +258,11 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=starting_times[j], rate=fps) else: image_series_kwargs.update(timestamps=timestamps) + if starting_times[j] is None: + current_description = image_series_kwargs.get("description", "") + image_series_kwargs.update( + description=current_description + + "Start time not provided. Timestamps may not be accurate.") if module_name is None: nwbfile.add_acquisition(ImageSeries(**image_series_kwargs)) From b76d71fcd21741c20737e8784c19a89dce9cf433 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 23 Feb 2022 07:58:18 +0530 Subject: [PATCH 63/70] tests for starting_time and multiple files --- tests/test_internals/test_movie_interface.py | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 97b4e566d..3d83fafac 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -81,6 +81,23 @@ def test_movie_starting_times(self): assert movie_interface_name in mod assert starting_times[no] == mod[movie_interface_name].starting_time + def test_movie_starting_times_none(self): + conversion_opts = dict(Movie=dict(external_mode=False)) + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata=self.get_metadata(), + ) + with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = self.nwb_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + assert movie_interface_name in mod + assert mod[movie_interface_name].starting_time == 0.0 + def test_movie_custom_module(self): starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] module_name = "TestModule" @@ -138,6 +155,36 @@ def test_movie_external_mode(self): movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] assert mod[movie_interface_name].external_file[0] == self.movie_files[no] + def test_movie_duplicate_kwargs_external(self): + conversion_opts = dict(Movie=dict(external_mode=True)) + metadata = self.get_metadata() + movie_interface_name = metadata["Behavior"]["Movies"][0]["name"] + metadata["Behavior"]["Movies"][1]["name"] = movie_interface_name + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata=metadata, + ) + with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + assert len(mod) == 1 + assert movie_interface_name in mod + + def test_movie_duplicate_kwargs(self): + conversion_opts = dict(Movie=dict(external_mode=False)) + metadata = self.get_metadata() + movie_interface_name = metadata["Behavior"]["Movies"][0]["name"] + metadata["Behavior"]["Movies"][1]["name"] = movie_interface_name + with self.assertRaises(AssertionError): + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata=metadata, + ) + def tearDown(self) -> None: shutil.rmtree(self.test_dir) del self.nwb_converter From 6abfb5d2b963641e1ac67b2f2dc343e989ad4425 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 23 Feb 2022 02:29:06 +0000 Subject: [PATCH 64/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index 9179d5101..c7b335cb5 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -261,8 +261,8 @@ def _check_duplicates(image_series_kwargs_list): if starting_times[j] is None: current_description = image_series_kwargs.get("description", "") image_series_kwargs.update( - description=current_description + - "Start time not provided. Timestamps may not be accurate.") + description=current_description + "Start time not provided. Timestamps may not be accurate." + ) if module_name is None: nwbfile.add_acquisition(ImageSeries(**image_series_kwargs)) From 0125ee9a5c2bbbd912a9e739e676b2a6a5632f7b Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 23 Feb 2022 10:34:37 +0530 Subject: [PATCH 65/70] assert starting times --- .../behavior/movie/moviedatainterface.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index c7b335cb5..e486597d5 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -163,15 +163,16 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs_list_updated, file_paths_list = _check_duplicates(image_series_kwargs_list) if starting_times is not None: - assert isinstance(starting_times, list) and all( - [isinstance(x, float) for x in starting_times] - ), "Argument 'starting_times' must be a list of floats." assert len(starting_times) == len(image_series_kwargs_list_updated), ( "starting times list length must be equal number of unique ImageSeries " "containers to write to nwb" ) else: - warn("starting_times not provided, setting to 'None'") - starting_times = [None] * len(image_series_kwargs_list_updated) + if len(image_series_kwargs_list_updated) == 1: + warn("starting_times not provided, setting to 0.0") + starting_times = [0.0] + else: + raise ValueError("provide starting times as a list of len " + f"{len(image_series_kwargs_list_updated)}") for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): if external_mode: @@ -196,8 +197,7 @@ def _check_duplicates(image_series_kwargs_list): video_capture_ob.frame_count = 10 total_frames = video_capture_ob.get_movie_frame_count() frame_shape = video_capture_ob.get_frame_shape() - start_time = starting_times[j] if starting_times[j] is not None else 0.0 - timestamps = start_time + video_capture_ob.get_movie_timestamps() + timestamps = starting_times[j] + video_capture_ob.get_movie_timestamps() fps = video_capture_ob.get_movie_fps() maxshape = (total_frames, *frame_shape) best_gzip_chunk = (1, frame_shape[0], frame_shape[1], 3) @@ -258,11 +258,6 @@ def _check_duplicates(image_series_kwargs_list): image_series_kwargs.update(starting_time=starting_times[j], rate=fps) else: image_series_kwargs.update(timestamps=timestamps) - if starting_times[j] is None: - current_description = image_series_kwargs.get("description", "") - image_series_kwargs.update( - description=current_description + "Start time not provided. Timestamps may not be accurate." - ) if module_name is None: nwbfile.add_acquisition(ImageSeries(**image_series_kwargs)) From 3bd142375550122df46179833826ece5903e9a41 Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 23 Feb 2022 05:05:16 +0000 Subject: [PATCH 66/70] Automated changes --- .../datainterfaces/behavior/movie/moviedatainterface.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py index e486597d5..4b37b2e4e 100644 --- a/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py +++ b/nwb_conversion_tools/datainterfaces/behavior/movie/moviedatainterface.py @@ -171,8 +171,7 @@ def _check_duplicates(image_series_kwargs_list): warn("starting_times not provided, setting to 0.0") starting_times = [0.0] else: - raise ValueError("provide starting times as a list of len " - f"{len(image_series_kwargs_list_updated)}") + raise ValueError("provide starting times as a list of len " f"{len(image_series_kwargs_list_updated)}") for j, (image_series_kwargs, file_list) in enumerate(zip(image_series_kwargs_list_updated, file_paths_list)): if external_mode: From ebcf479c959efe8750727a418abc1584b4b81f40 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 23 Feb 2022 10:39:59 +0530 Subject: [PATCH 67/70] assert starting times: tests --- tests/test_internals/test_movie_interface.py | 22 ++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 3d83fafac..03d3f80d6 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -83,20 +83,30 @@ def test_movie_starting_times(self): def test_movie_starting_times_none(self): conversion_opts = dict(Movie=dict(external_mode=False)) + with self.assertRaises(ValueError): + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata=self.get_metadata(), + ) + + def test_movie_starting_times_none_duplicate(self): + conversion_opts = dict(Movie=dict(external_mode=True)) + metadata = self.get_metadata() + movie_interface_name = metadata["Behavior"]["Movies"][0]["name"] + metadata["Behavior"]["Movies"][1]["name"] = movie_interface_name self.nwb_converter.run_conversion( nwbfile_path=self.nwbfile_path, overwrite=True, conversion_options=conversion_opts, - metadata=self.get_metadata(), + metadata=metadata, ) with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: nwbfile = io.read() mod = nwbfile.acquisition - metadata = self.nwb_converter.get_metadata() - for no in range(len(metadata["Behavior"]["Movies"])): - movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] - assert movie_interface_name in mod - assert mod[movie_interface_name].starting_time == 0.0 + assert movie_interface_name in mod + assert mod[movie_interface_name].starting_time == 0.0 def test_movie_custom_module(self): starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] From 5e2d5a25afe2930c8e8c985c83d4912c612ec624 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 23 Feb 2022 15:44:53 +0530 Subject: [PATCH 68/70] duplicate kwargs test update --- tests/test_internals/test_movie_interface.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 03d3f80d6..473b2a0b6 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -181,6 +181,7 @@ def test_movie_duplicate_kwargs_external(self): mod = nwbfile.acquisition assert len(mod) == 1 assert movie_interface_name in mod + assert len(mod[movie_interface_name].external_file) == 2 def test_movie_duplicate_kwargs(self): conversion_opts = dict(Movie=dict(external_mode=False)) From bd67a7bd5f27582562aea2d43c60e025b3cadef0 Mon Sep 17 00:00:00 2001 From: Saksham Sharda Date: Wed, 23 Feb 2022 23:04:48 +0530 Subject: [PATCH 69/70] tests for stub_Test --- tests/test_internals/test_movie_interface.py | 21 +++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 473b2a0b6..3aa1ff3b6 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -26,7 +26,7 @@ def setUp(self) -> None: def create_movies(self): movie_file1 = os.path.join(self.test_dir, "test1.avi") movie_file2 = os.path.join(self.test_dir, "test2.avi") - (nf, nx, ny) = (10, 640, 480) + (nf, nx, ny) = (30, 640, 480) writer1 = cv2.VideoWriter( filename=movie_file1, apiPreference=None, @@ -196,6 +196,25 @@ def test_movie_duplicate_kwargs(self): metadata=metadata, ) + def test_movie_stub(self): + starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] + conversion_opts = dict(Movie=dict(starting_times=starting_times, + external_mode=False, + stub_test=True)) + self.nwb_converter.run_conversion( + nwbfile_path=self.nwbfile_path, + overwrite=True, + conversion_options=conversion_opts, + metadata=self.get_metadata(), + ) + with NWBHDF5IO(path=self.nwbfile_path, mode="r") as io: + nwbfile = io.read() + mod = nwbfile.acquisition + metadata = self.nwb_converter.get_metadata() + for no in range(len(metadata["Behavior"]["Movies"])): + movie_interface_name = metadata["Behavior"]["Movies"][no]["name"] + assert mod[movie_interface_name].data.shape[0] == 10 + def tearDown(self) -> None: shutil.rmtree(self.test_dir) del self.nwb_converter From c0df3031f34a6d79b7087fd8d9dc93c43012db0f Mon Sep 17 00:00:00 2001 From: Cody Baker Date: Wed, 23 Feb 2022 17:39:34 +0000 Subject: [PATCH 70/70] Automated changes --- tests/test_internals/test_movie_interface.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_internals/test_movie_interface.py b/tests/test_internals/test_movie_interface.py index 3aa1ff3b6..824121cea 100644 --- a/tests/test_internals/test_movie_interface.py +++ b/tests/test_internals/test_movie_interface.py @@ -198,9 +198,7 @@ def test_movie_duplicate_kwargs(self): def test_movie_stub(self): starting_times = [np.float(np.random.randint(200)) for i in range(len(self.movie_files))] - conversion_opts = dict(Movie=dict(starting_times=starting_times, - external_mode=False, - stub_test=True)) + conversion_opts = dict(Movie=dict(starting_times=starting_times, external_mode=False, stub_test=True)) self.nwb_converter.run_conversion( nwbfile_path=self.nwbfile_path, overwrite=True,