From 0738ea3b0c8d31c5a517374a0d5a8ece6b5c67a9 Mon Sep 17 00:00:00 2001 From: Luigi Feriani Date: Thu, 12 Mar 2020 23:35:10 +0000 Subject: [PATCH 1/3] compressVideo.py: fixed bug when saving background of non-loopbio video. Implemented len() for all reader except readImages and ReadVideoFFMPEG --- .../analysis/compress/Readers/readDatFiles.py | 3 + .../analysis/compress/Readers/readImages.py | 13 ++-- .../analysis/compress/Readers/readLoopBio.py | 14 ++-- .../compress/Readers/readVideoCapture.py | 10 ++- .../compress/Readers/readVideoHDF5.py | 5 +- tierpsy/analysis/compress/compressVideo.py | 76 +++++++++---------- .../analysis/compress/selectVideoReader.py | 6 +- 7 files changed, 70 insertions(+), 57 deletions(-) diff --git a/tierpsy/analysis/compress/Readers/readDatFiles.py b/tierpsy/analysis/compress/Readers/readDatFiles.py index 8125f662..67f4fa90 100644 --- a/tierpsy/analysis/compress/Readers/readDatFiles.py +++ b/tierpsy/analysis/compress/Readers/readDatFiles.py @@ -73,5 +73,8 @@ def read(self): else: return (0, [], [], []) + def __len__(self): + return self.num_frames + def release(self): pass diff --git a/tierpsy/analysis/compress/Readers/readImages.py b/tierpsy/analysis/compress/Readers/readImages.py index 7ee0403a..eec09996 100644 --- a/tierpsy/analysis/compress/Readers/readImages.py +++ b/tierpsy/analysis/compress/Readers/readImages.py @@ -14,7 +14,7 @@ RETURN_AS_IT_IS = -1 RETURN_UINT8_GRAY = 0 RETURN_RGB = 1 - + class readImages: """ Reads a single tif image, to be expanded for reading videos/stacks""" def __init__(self, dir_name, f_ext, imread_flag=RETURN_UINT8_GRAY): @@ -22,10 +22,10 @@ def __init__(self, dir_name, f_ext, imread_flag=RETURN_UINT8_GRAY): self.dir_name = dir_name if not os.path.exists(self.dir_name): raise FileNotFoundError('Error: Directory (%s) does not exist.' % self.dir_name) - + self.files = glob.glob(os.path.join(self.dir_name, '*' +f_ext)) - - #I do not want to change this behaviour so i reserve it. + + #I do not want to change this behaviour so i reserve it. IS_ANDRE_BROW_FLUO = (f_ext == 'tif') & all('_X' in os.path.basename(x) for x in self.files) if IS_ANDRE_BROW_FLUO: get_number = lambda file: int(os.path.split(file)[1].split('_X')[1].split('.tif')[0]) @@ -40,7 +40,7 @@ def __init__(self, dir_name, f_ext, imread_flag=RETURN_UINT8_GRAY): self.width = image.shape[1] self.dtype = image.dtype self.num_frames = len(self.files) - + # initialize pointer for frames self.curr_frame = -1 @@ -53,5 +53,8 @@ def read(self): else: return(0, []) + def __len__(self): + return self.num_frames + def release(self): pass diff --git a/tierpsy/analysis/compress/Readers/readLoopBio.py b/tierpsy/analysis/compress/Readers/readLoopBio.py index 2ecfa8ce..ccf26f75 100644 --- a/tierpsy/analysis/compress/Readers/readLoopBio.py +++ b/tierpsy/analysis/compress/Readers/readLoopBio.py @@ -11,15 +11,15 @@ def __init__(self, video_file): import imgstore self.vid = imgstore.new_for_filename(video_file) - + self.first_frame = self.vid.frame_min self.frame_max = self.vid.frame_max - + img, (frame_number, frame_timestamp) = self.vid.get_next_image() self.height = img.shape[0] self.width = img.shape[1] self.dtype = img.dtype - + self.vid.close() self.vid = imgstore.new_for_filename(video_file) self.frames_read = [] @@ -31,7 +31,7 @@ def read(self): return 1, img else: return 0, None - + def read_frame(self, frame_number): frame_to_read = self.first_frame + frame_number if frame_to_read < self.frame_max: @@ -40,7 +40,9 @@ def read_frame(self, frame_number): return 1, img else: return 0, None - + + def __len__(self): + return self.frame_max - self.first_frame + 1 + def release(self): return self.vid.close() - \ No newline at end of file diff --git a/tierpsy/analysis/compress/Readers/readVideoCapture.py b/tierpsy/analysis/compress/Readers/readVideoCapture.py index ad5ea3fd..7152c545 100644 --- a/tierpsy/analysis/compress/Readers/readVideoCapture.py +++ b/tierpsy/analysis/compress/Readers/readVideoCapture.py @@ -16,7 +16,7 @@ def __init__(self, video_file): # get video frame, stop program when no frame is retrive (end of file) ret, image = vid.read() vid.release() - + if ret: self.height = image.shape[0] self.width = image.shape[1] @@ -27,10 +27,12 @@ def __init__(self, video_file): raise OSError( 'Cannot get an image from %s.\n It is likely that either the file name is wrong, the file is corrupt or OpenCV was not installed with ffmpeg support.' % video_file) - + def read(self): return self.vid.read() - + + def __len__(self): + return int(self.vid.get(cv2.CAP_PROP_FRAME_COUNT)) + def release(self): return self.vid.release() - \ No newline at end of file diff --git a/tierpsy/analysis/compress/Readers/readVideoHDF5.py b/tierpsy/analysis/compress/Readers/readVideoHDF5.py index 4d884ca1..e1eb5fe5 100755 --- a/tierpsy/analysis/compress/Readers/readVideoHDF5.py +++ b/tierpsy/analysis/compress/Readers/readVideoHDF5.py @@ -26,7 +26,7 @@ def __init__(self, fileName, full_img_period=np.inf): self.width = self.dataset.shape[2] self.height = self.dataset.shape[1] self.dtype = self.dataset.dtype - + self.tot_pix = self.height * self.width # initialize pointer for frames @@ -49,6 +49,9 @@ def read(self): else: return (0, []) + def __len__(self): + return self.tot_frames + def release(self): # close the buffer self.fid.close() diff --git a/tierpsy/analysis/compress/compressVideo.py b/tierpsy/analysis/compress/compressVideo.py index 10179056..e32e1aff 100755 --- a/tierpsy/analysis/compress/compressVideo.py +++ b/tierpsy/analysis/compress/compressVideo.py @@ -122,16 +122,16 @@ def normalizeImage(img): # normalise image intensities if the data type is other # than uint8 image = image.astype(np.double) - + imax = img.max() imin = img.min() factor = 255/(imax-imin) - + imgN = ne.evaluate('(img-imin)*factor') imgN = imgN.astype(np.uint8) return imgN, (imin, imax) - + def reduceBuffer(Ibuff, is_light_background): if is_light_background: return np.min(Ibuff, axis=0) @@ -175,36 +175,36 @@ def createImgGroup(fid, name, tot_frames, im_height, im_width, is_expandable=Tru return img_dataset -def initMasksGroups(fid, expected_frames, im_height, im_width, +def initMasksGroups(fid, expected_frames, im_height, im_width, attr_params, save_full_interval, is_expandable=True): # open node to store the compressed (masked) data mask_dataset = createImgGroup(fid, "/mask", expected_frames, im_height, im_width, is_expandable) - + tot_save_full = (expected_frames // save_full_interval) + 1 full_dataset = createImgGroup(fid, "/full_data", tot_save_full, im_height, im_width, is_expandable) full_dataset._v_attrs['save_interval'] = save_full_interval - + assert all(x in ['expected_fps', 'is_light_background', 'microns_per_pixel'] for x in attr_params) set_unit_conversions(mask_dataset, **attr_params) set_unit_conversions(full_dataset, **attr_params) if is_expandable: - mean_intensity = fid.create_earray('/', + mean_intensity = fid.create_earray('/', 'mean_intensity', atom=tables.Float32Atom(), shape=(0,), expectedrows=expected_frames, filters=TABLE_FILTERS) else: - mean_intensity = fid.create_carray('/', + mean_intensity = fid.create_carray('/', 'mean_intensity', atom=tables.Float32Atom(), shape=(expected_frames,), filters=TABLE_FILTERS) - + return mask_dataset, full_dataset, mean_intensity @@ -230,13 +230,13 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, ''' #get the default values if there is any bad parameter - output = compress_defaults(masked_image_file, - expected_fps, - buffer_size = buffer_size, + output = compress_defaults(masked_image_file, + expected_fps, + buffer_size = buffer_size, save_full_interval = save_full_interval) - buffer_size = output['buffer_size'] - save_full_interval = output['save_full_interval'] + buffer_size = output['buffer_size'] + save_full_interval = output['save_full_interval'] if len(bgnd_param) > 0: is_bgnd_subtraction = True @@ -253,14 +253,14 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, # processes identifier. base_name = masked_image_file.rpartition('.')[0].rpartition(os.sep)[-1] - + # select the video reader class according to the file type. vid = selectVideoReader(video_file) - + # delete any previous if it existed with tables.File(masked_image_file, "w") as mask_fid: pass - + #Extract metadata if is_extract_timestamp: # extract and store video metadata using ffprobe @@ -270,7 +270,7 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, else: expected_frames = 1 - + # Initialize background subtraction if required if is_bgnd_subtraction: @@ -282,10 +282,10 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, frame_number = 0 full_frame_number = 0 image_prev = np.zeros([]) - + # Initialise FOV splitting if needed if is_fov_tosplit: - # masked video does not exist yet so have to initialise from data + # masked video does not exist yet so have to initialise from data # use either background or first frame if is_bgnd_subtraction: img_fov = bgnd_subtractor.bgnd.astype(np.uint8) @@ -293,17 +293,17 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, ret, img_fov = vid.read() # close and reopen the video, to restart from the beginning vid.release() - vid = selectVideoReader(video_file); - # TODO: change class creator so it only needs the video name? by using + vid = selectVideoReader(video_file); + # TODO: change class creator so it only needs the video name? by using # Tierpsy's functions such as selectVideoReader it can then read the first image by itself - + camera_serial = parse_camera_serial(masked_image_file) - + fovsplitter = FOVMultiWellsSplitter(img_fov, camera_serial=camera_serial, px2um=microns_per_pixel, **fovsplitter_param) - + # initialize timers @@ -325,26 +325,26 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, microns_per_pixel = microns_per_pixel, is_light_background = int(mask_param['is_light_background']) ) - mask_dataset, full_dataset, mean_intensity = initMasksGroups(mask_fid, + mask_dataset, full_dataset, mean_intensity = initMasksGroups(mask_fid, expected_frames, vid.height, vid.width, attr_params, save_full_interval) - + if is_bgnd_subtraction: bg_dataset = createImgGroup(mask_fid, "/bgnd", 1, vid.height, vid.width, is_expandable=False) # because we only save the one background: - bg_dataset._v_attrs['save_interval'] = vid.frame_max-vid.first_frame + 1 + bg_dataset._v_attrs['save_interval'] = len(vid) bg_dataset[0,:,:] = img_fov - + if vid.dtype != np.uint8: # this will worm as flags to be sure that the normalization took place. - normalization_range = mask_fid.create_earray('/', + normalization_range = mask_fid.create_earray('/', 'normalization_range', atom=tables.Float32Atom(), shape=(0, 2), expectedrows=expected_frames, filters=TABLE_FILTERS ) - + while frame_number < max_frame: ret, image = vid.read() @@ -396,23 +396,23 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, Ibuff = Ibuff[:ind_buff + 1] # mask buffer and save data into the hdf5 file - if (ind_buff == buffer_size - 1 or ret == 0) and Ibuff.size > 0: + if (ind_buff == buffer_size - 1 or ret == 0) and Ibuff.size > 0: if is_bgnd_subtraction: Ibuff_b = bgnd_subtractor.apply(Ibuff, frame_number) else: Ibuff_b = Ibuff - + #calculate the max/min in the of the buffer img_reduce = reduceBuffer(Ibuff_b, mask_param['is_light_background']) mask = getROIMask(img_reduce, **mask_param) - + Ibuff *= mask # now apply the well_mask if is MWP if is_fov_tosplit: fovsplitter.apply_wells_mask(Ibuff) # Ibuff will be modified after this - + # add buffer to the hdf5 file frame_first_buff = frame_number - Ibuff.shape[0] mask_dataset.append(Ibuff) @@ -421,20 +421,20 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, # calculate the progress and put it in a string progress_str = progressTime.get_str(frame_number) print_flush(base_name + ' ' + progress_str) - + # finish process if ret == 0: break # close the video vid.release() - + # save fovsplitting data if is_fov_tosplit: fovsplitter.write_fov_wells_to_file(masked_image_file) read_and_save_timestamp(masked_image_file) print_flush(base_name + ' Compressed video done.') - + diff --git a/tierpsy/analysis/compress/selectVideoReader.py b/tierpsy/analysis/compress/selectVideoReader.py index 33423aef..713987b7 100644 --- a/tierpsy/analysis/compress/selectVideoReader.py +++ b/tierpsy/analysis/compress/selectVideoReader.py @@ -16,7 +16,7 @@ def selectVideoReader(video_file): isMJPGvideo = video_file.endswith('.mjpg') isDATfiles = video_file.endswith('spool.dat') isLoopBio = video_file.endswith('.yaml') - + isImages = any(video_file.endswith(x) for x in IMG_EXT) @@ -30,7 +30,7 @@ def selectVideoReader(video_file): vid = ReadVideoFFMPEG(video_file) elif isDATfiles: video_dir = os.path.split(video_file)[0] - vid = readDatFile(video_dir) + vid = readDatFiles(video_dir) elif isLoopBio: # use opencv VideoCapture vid = readLoopBio(video_file) @@ -42,7 +42,7 @@ def selectVideoReader(video_file): vid = readImages(video_dir, f_ext) else: vid = readVideoCapture(video_file) - + #raise an error if it is not a valid video (cannot read a frame) if vid.width == 0 or vid.height == 0: raise RuntimeError From 82398949e6993281560acea27406574e6eb5a14f Mon Sep 17 00:00:00 2001 From: Luigi Feriani Date: Fri, 13 Mar 2020 00:07:37 +0000 Subject: [PATCH 2/3] compressVideo.py: bugfix --- tierpsy/analysis/compress/compressVideo.py | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tierpsy/analysis/compress/compressVideo.py b/tierpsy/analysis/compress/compressVideo.py index e32e1aff..4c98e6c7 100755 --- a/tierpsy/analysis/compress/compressVideo.py +++ b/tierpsy/analysis/compress/compressVideo.py @@ -284,19 +284,19 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, image_prev = np.zeros([]) # Initialise FOV splitting if needed - if is_fov_tosplit: - # masked video does not exist yet so have to initialise from data - # use either background or first frame - if is_bgnd_subtraction: - img_fov = bgnd_subtractor.bgnd.astype(np.uint8) - else: - ret, img_fov = vid.read() - # close and reopen the video, to restart from the beginning - vid.release() - vid = selectVideoReader(video_file); - # TODO: change class creator so it only needs the video name? by using - # Tierpsy's functions such as selectVideoReader it can then read the first image by itself + # masked video does not exist yet so have to initialise from data + # use either background or first frame + if is_bgnd_subtraction: + img_fov = bgnd_subtractor.bgnd.astype(np.uint8) + else: + ret, img_fov = vid.read() + # close and reopen the video, to restart from the beginning + vid.release() + vid = selectVideoReader(video_file); + # TODO: change class creator so it only needs the video name? by using + # Tierpsy's functions such as selectVideoReader it can then read the first image by itself + if is_FOV_tosplit: camera_serial = parse_camera_serial(masked_image_file) fovsplitter = FOVMultiWellsSplitter(img_fov, From 136afc5b4e84f827dbec70aaca7ed25f6ed71c17 Mon Sep 17 00:00:00 2001 From: Luigi Feriani Date: Fri, 13 Mar 2020 00:11:13 +0000 Subject: [PATCH 3/3] compressVideo.py: bugfix --- tierpsy/analysis/compress/compressVideo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tierpsy/analysis/compress/compressVideo.py b/tierpsy/analysis/compress/compressVideo.py index 4c98e6c7..2656d6cc 100755 --- a/tierpsy/analysis/compress/compressVideo.py +++ b/tierpsy/analysis/compress/compressVideo.py @@ -296,7 +296,7 @@ def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, # TODO: change class creator so it only needs the video name? by using # Tierpsy's functions such as selectVideoReader it can then read the first image by itself - if is_FOV_tosplit: + if is_fov_tosplit: camera_serial = parse_camera_serial(masked_image_file) fovsplitter = FOVMultiWellsSplitter(img_fov,