def saveTrajData(trajectories_data, masked_image_file, skeletons_file): #save data into the skeletons file with tables.File(skeletons_file, "a") as ske_file_id: trajectories_data_f = ske_file_id.create_table( '/', 'trajectories_data', obj=trajectories_data.to_records( index=False, column_dtypes={t[0]: t[1] for t in TRAJECTORIES_DATA_DTYPES}), filters=TABLE_FILTERS) plate_worms = ske_file_id.get_node('/plate_worms') if 'bgnd_param' in plate_worms._v_attrs: bgnd_param = plate_worms._v_attrs['bgnd_param'] else: bgnd_param = bytes(json.dumps({}), 'utf-8') #default empty trajectories_data_f._v_attrs['bgnd_param'] = bgnd_param #read and the units information information fps, microns_per_pixel, is_light_background = \ copy_unit_conversions(trajectories_data_f, masked_image_file) if not '/timestamp' in ske_file_id: read_and_save_timestamp(masked_image_file, skeletons_file) ske_file_id.flush()
def _ini_plate_worms(traj_fid, masked_image_file): # intialize main table int_dtypes = [('worm_index_blob', np.int), ('worm_index_joined', np.int), ('frame_number', np.int)] dd = [ 'coord_x', 'coord_y', 'box_length', 'box_width', 'angle', 'area', 'bounding_box_xmin', 'bounding_box_xmax', 'bounding_box_ymin', 'bounding_box_ymax', 'threshold' ] float32_dtypes = [(x, np.float32) for x in dd] plate_worms_dtype = np.dtype(int_dtypes + float32_dtypes) plate_worms = traj_fid.create_table('/', "plate_worms", plate_worms_dtype, "Worm feature List", filters=TABLE_FILTERS) #find if it is a mask from fluorescence and save it in the new group plate_worms._v_attrs['is_light_background'] = is_light_background plate_worms._v_attrs['expected_fps'] = expected_fps #make sure it is in a "serializable" format plate_worms._v_attrs['bgnd_param'] = bytes(json.dumps(bgnd_param), 'utf-8') read_and_save_timestamp(masked_image_file, trajectories_file) return plate_worms
def fix_timestamps(masked_file, skeletons_file): timestamp, timestamp_time = read_and_save_timestamp(masked_file, dst_file='') if os.path.exists(skeletons_file): read_and_save_timestamp(masked_file, dst_file=skeletons_file) with pd.HDFStore(skeletons_file, 'r') as fid: trajectories_data = fid['trajectories_data'] ind = trajectories_data['frame_number'].values.astype(np.int32) trajectories_data['timestamp_raw'] = timestamp[ind] trajectories_data['timestamp_time'] = timestamp_time[ind] if np.all(np.isnan(timestamp)) or np.all(np.diff(trajectories_data['timestamp_raw'])>=0): save_modified_table(skeletons_file, trajectories_data, 'trajectories_data') else: return (masked_file, skeletons_file)
def compressVideo(video_file, masked_image_file, mask_param, expected_fps=25, microns_per_pixel=None, bgnd_param={}, buffer_size=-1, save_full_interval=-1, max_frame=1e32, is_extract_timestamp=False, fovsplitter_param={}): ''' Compresses video by selecting pixels that are likely to have worms on it and making the rest of the image zero. By creating a large amount of redundant data, any lossless compression algorithm will dramatically increase its efficiency. The masked images are saved as hdf5 with gzip compression. The mask is calculated over a minimum projection of an image stack. This projection preserves darker regions (or brighter regions, in the case of fluorescent labelling) where the worm has more probability to be located. Additionally it has the advantage of reducing the processing load by only requiring to calculate the mask once per image stack. video_file -- original video file masked_image_file -- buffer_size -- size of the image stack used to calculate the minimal projection and the mask save_full_interval -- have often a full image is saved max_frame -- last frame saved (default a very large number, so it goes until the end of the video) mask_param -- parameters used to calculate the mask ''' #get the default values if there is any bad parameter 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'] if len(bgnd_param) > 0: is_bgnd_subtraction = True assert bgnd_param['buff_size'] > 0 and bgnd_param['frame_gap'] > 0 else: is_bgnd_subtraction = False if len(fovsplitter_param) > 0: is_fov_tosplit = True assert all(key in fovsplitter_param for key in ['total_n_wells', 'whichsideup', 'well_shape']) assert fovsplitter_param['total_n_wells'] > 0 else: is_fov_tosplit = False # 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 #NOTE: i cannot calculate /timestamp until i am sure of the total number of frames print_flush(base_name + ' Extracting video metadata...') expected_frames = store_meta_data(video_file, masked_image_file) else: expected_frames = 1 # Initialize background subtraction if required if is_bgnd_subtraction: print_flush(base_name + ' Initializing background subtraction.') bgnd_subtractor = BackgroundSubtractorVideo(video_file, **bgnd_param) # intialize some variables max_intensity, min_intensity = np.nan, np.nan frame_number = 0 full_frame_number = 0 image_prev = np.zeros([]) # Initialise FOV splitting if needed 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) if is_fov_tosplit: # 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) wells_mask = fovsplitter.wells_mask else: wells_mask = None # initialize timers print_flush(base_name + ' Starting video compression.') if expected_frames == 1: progressTime = TimeCounter('Compressing video.') else: #if we know the number of frames display it in the progress progressTime = TimeCounter('Compressing video.', expected_frames) with tables.File(masked_image_file, "r+") as mask_fid: #initialize masks groups attr_params = dict(expected_fps=expected_fps, microns_per_pixel=microns_per_pixel, is_light_background=int( mask_param['is_light_background'])) 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) 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', atom=tables.Float32Atom(), shape=(0, 2), expectedrows=expected_frames, filters=TABLE_FILTERS) while frame_number < max_frame: ret, image = vid.read() if ret != 0: # increase frame number frame_number += 1 # opencv can give an artificial rgb image. Let's get it back to # gray scale. if image.ndim == 3: image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) if image.dtype != np.uint8: # normalise image intensities if the data type is other # than uint8 image, img_norm_range = normalizeImage(image) normalization_range.append(img_norm_range) #limit the image range to 1 to 255, 0 is a reserved value for the background assert image.dtype == np.uint8 image = np.clip(image, 1, 255) # Add a full frame every save_full_interval if frame_number % save_full_interval == 1: full_dataset.append(image[np.newaxis, :, :]) full_frame_number += 1 # buffer index ind_buff = (frame_number - 1) % buffer_size # initialize the buffer when the index correspond to 0 if ind_buff == 0: Ibuff = np.zeros((buffer_size, vid.height, vid.width), dtype=np.uint8) # add image to the buffer Ibuff[ind_buff, :, :] = image.copy() mean_int = np.mean(image) assert mean_int >= 0 mean_intensity.append(np.array([mean_int])) else: # sometimes the last image is all zeros, control for this case if np.all(Ibuff[ind_buff] == 0): frame_number -= 1 ind_buff -= 1 # close the buffer 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 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, wells_mask=wells_mask, **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) if frame_number % 500 == 0: # 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 # now that the whole video is read, we definitely have a better estimate # for its number of frames. so set the save_interval again if is_bgnd_subtraction: # bg_dataset._v_attrs['save_interval'] = len(vid) # the above line is not accurate when using ffmpeg, # it's just safer to do: bg_dataset._v_attrs['save_interval'] = mask_dataset.shape[0] # close the video vid.release() # save fovsplitting data if is_fov_tosplit: fovsplitter.write_fov_wells_to_file(masked_image_file) if fovsplitter.is_dubious: print(f'Check {masked_image_file} for plate alignment') read_and_save_timestamp(masked_image_file) print_flush(base_name + ' Compressed video done.')