def test_bbb_convert_frame_add_cols(frame_data_all): """Frame with additional columns is correctly converted to np array.""" frame = frame_data_all expected_keys = ('frameId', 'timedelta', 'timestamp', 'decodedId', 'detectionsUnion') # one col, single value for whole columns arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={'camId': 2}) assert 'camId' in arr.dtype.names assert np.all(arr['camId'] == 2) # two cols, single value arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={'camId': 2, 'second': 3}) assert 'camId' in arr.dtype.names assert 'second' in arr.dtype.names assert np.all(arr['camId'] == 2) assert np.all(arr['second'] == 3) # list for whole column arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={'camId': range(0, 4)}) assert 'camId' in arr.dtype.names assert np.all(arr['camId'] == np.array(range(0, 4))) # existing column with pytest.raises(AssertionError): arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={'frameId': 9})
def run(gt_file, videos, images, visualize_debug, output, fix_utc_2014, nb_bits=12): """ Converts bb_binary ground truth Cap'n Proto files to hdf5 files and extracts the corresponding rois from videos or images. """ def get_filenames(f): if f is None: return [] else: return [line.rstrip('\n') for line in f.readlines()] gen_factory = FrameGeneratorFactory(get_filenames(videos), get_filenames(images)) if os.path.exists(output): os.remove(output) distribution = DistributionCollection([('bits', Bernoulli(), nb_bits)]) dset = DistributionHDF5Dataset(output, distribution) camIdxs = [] periods = [] for fname in gt_file: fc = load_frame_container(fname) camIdx, start_dt, end_dt = parse_video_fname(fname) if fix_utc_2014 and start_dt.year == 2014: start_dt -= timedelta(hours=2) gt_frames = [] gen = gen_factory.get_generator(camIdx, start_dt) for frame, (video_frame, video_filename) in zip(fc.frames, gen): gt = {} np_frame = convert_frame_to_numpy(frame) rois, mask, positions = extract_gt_rois(np_frame, video_frame, start_dt) for name in np_frame.dtype.names: gt[name] = np_frame[name][mask] bits = [int_id_to_binary(id)[::-1] for id in gt["decodedId"]] gt["bits"] = 2 * np.array(bits, dtype=np.float) - 1 gt["tags"] = 2 * (rois / 255.).astype(np.float16) - 1 gt['filename'] = os.path.basename(video_filename) gt['camIdx'] = camIdx gt_frames.append(gt) print('.', end='', flush=True) print() gt_period = GTPeriod(camIdx, start_dt, end_dt, fname, gt_frames) periods.append( [int(gt_period.start.timestamp()), int(gt_period.end.timestamp())]) camIdxs.append(gt_period.camIdx) append_gt_to_hdf5(gt_period, dset) dset.attrs['periods'] = np.array(periods) dset.attrs['camIdxs'] = np.array(camIdxs) visualize_detection_tiles(dset, os.path.splitext(output)[0]) dset.close()
def get_detections_on_timestamp(self, timestamp): dset = ds.DetectionSet() frame = self.frames[timestamp.frame] data = convert_frame_to_numpy(frame) #print 'timestamp: ' + str(frame.timestamp) for detection_data in data: dset.add_detection( ds.Detection( detection_data['idx'], timestamp, np.array([ detection_data['ypos'], detection_data['xpos'] ]), # rotated, otherwise will be portrait orientation detection_data['localizerSaliency'], detection_data['decodedId'] [::-1], # reversed, we want least significant bit last detection_data['xRotation'], detection_data['yRotation'], detection_data['zRotation'])) return dset
def test_bbb_convert_frame_det(frame_data_all): """Frame and detections are correctly converted to np array.""" frame = frame_data_all expected_keys = ['frameId', 'timedelta', 'timestamp'] expected_keys.extend(get_detection_keys(frame.detectionsUnion.which())) arr = convert_frame_to_numpy(frame, keys=expected_keys) bbb_check_frame_data(frame, arr, expected_keys)
def test_bbb_convert_frame_default(frame_data_all): """Convert frame to np arrays without explicitly requesting certain keys.""" frame = frame_data_all frame_keys = set(['frameId', 'dataSourceIdx', 'frameIdx', 'timestamp', 'timedelta']) detection_keys = set(get_detection_keys(frame.detectionsUnion.which())) expected_keys = frame_keys | detection_keys | set(['camId']) # extract frame without explicitly asking for keys arr = convert_frame_to_numpy(frame, add_cols={'camId': 0}) bbb_check_frame_data(frame, arr, expected_keys)
def test_bbb_frame_container_errors(frame_data_all): """Test minimal keys when building `FrameContainer`""" frame = frame_data_all expected_keys = ('timestamp', 'xpos', 'ypos', 'detectionsUnion') arr = convert_frame_to_numpy(frame, keys=expected_keys) bbb_check_frame_data(frame, arr, expected_keys) detections = pd.DataFrame(arr) with pytest.raises(AssertionError) as error_information: build_frame_container_from_df(detections, frame.detectionsUnion.which(), 0) assert 'decodedId' in str(error_information.value)
def test_bbb_convert_only_frame(frame_data_all): """Frame is correctly converted to np array and detections are ignored.""" frame = frame_data_all expected_keys = ('frameId', 'timedelta', 'timestamp', 'detectionsUnion') arr = _convert_frame_to_numpy(frame, keys=expected_keys) bbb_check_frame_data(frame, arr, expected_keys) # now compare behaviour of helper and parent function arr_frame = convert_frame_to_numpy(frame, keys=expected_keys) assert np.all(arr == arr_frame)
def test_bbb_convert_detections(frame_data_all): """Detections are correctly converted to np array and frame is ignored.""" frame = frame_data_all expected_keys = get_detection_keys(frame.detectionsUnion.which()) detections = _get_detections(frame) arr = _convert_detections_to_numpy(detections, keys=expected_keys) bbb_check_frame_data(frame, arr, expected_keys) # now compare behaviour of helper and parent function arr_frame = convert_frame_to_numpy(frame, keys=expected_keys) assert np.all(arr == arr_frame)
def test_bbb_convert_frame_default(frame_data_all): """Convert frame to np arrays without explicitly requesting certain keys.""" frame = frame_data_all frame_keys = set( ['frameId', 'dataSourceIdx', 'frameIdx', 'timestamp', 'timedelta']) detection_keys = set(get_detection_keys(frame.detectionsUnion.which())) expected_keys = frame_keys | detection_keys | set(['camId']) # extract frame without explicitly asking for keys arr = convert_frame_to_numpy(frame, add_cols={'camId': 0}) bbb_check_frame_data(frame, arr, expected_keys)
def test_bbb_convert_frame_add_cols(frame_data_all): """Frame with additional columns is correctly converted to np array.""" frame = frame_data_all expected_keys = ('frameId', 'timedelta', 'timestamp', 'decodedId', 'detectionsUnion') # one col, single value for whole columns arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={'camId': 2}) assert 'camId' in arr.dtype.names assert np.all(arr['camId'] == 2) # two cols, single value arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={ 'camId': 2, 'second': 3 }) assert 'camId' in arr.dtype.names assert 'second' in arr.dtype.names assert np.all(arr['camId'] == 2) assert np.all(arr['second'] == 3) # list for whole column arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={'camId': range(0, 4)}) assert 'camId' in arr.dtype.names assert np.all(arr['camId'] == np.array(range(0, 4))) # existing column with pytest.raises(AssertionError): arr = convert_frame_to_numpy(frame, keys=expected_keys, add_cols={'frameId': 9})
def run(bb_gt_files, video_dir, image_dir, visualize_debug, force, output): """ Converts bb_binary ground truth Cap'n Proto files to hdf5 files and extracts the corresponding rois from videos or images. """ gen_factory = FrameGeneratorFactory(video_dir, image_dir) if force and os.path.exists(output): os.remove(output) dset = HDF5Dataset(output) camIdxs = [] periods = [] for fname in bb_gt_files: fc = load_frame_container(fname) camIdx, start_dt, end_dt = parse_video_fname(fname) basename = os.path.basename(fname) gt_frames = [] print(basename) gen = gen_factory.get_generator(camIdx, start_dt) first = True for frame, (video_frame, video_filename) in zip(fc.frames, gen): gt = {} np_frame = convert_frame_to_numpy(frame) rois, mask, positions = extract_gt_rois(np_frame, video_frame, start_dt) for name in np_frame.dtype.names: gt[name] = np_frame[name][mask] gt["bits"] = np.array( [int_id_to_binary(id)[::-1] for id in gt["decodedId"]]) gt["tags"] = rois gt['filename'] = os.path.basename(video_filename) gt_frames.append(gt) if first and visualize_debug: visualize_detections(gt, positions, video_frame) first = False print('.', end='') gt_period = GTPeriod(camIdx, start_dt, end_dt, fname, gt_frames) periods.append( [int(gt_period.start.timestamp()), int(gt_period.end.timestamp())]) camIdxs.append(gt_period.camIdx) append_gt_to_hdf5(gt_period, dset) dset.attrs['periods'] = np.array(periods) dset.attrs['camIdxs'] = np.array(camIdxs) dset.close()
def get_detections_on_timestamp( self, timestamp ): dset = ds.DetectionSet() frame = self.frames[ timestamp.frame ] data = convert_frame_to_numpy( frame ) for detection_data in data: dset.add_detection( ds.Detection( detection_data[ 'idx' ], timestamp, np.array( [ detection_data[ 'ypos' ], detection_data[ 'xpos' ] ] ), # rotated, otherwise will be portrait orientation detection_data[ 'localizerSaliency' ], detection_data[ 'decodedId' ][::-1] # reversed, we want least significant bit last ) ) return dset
def run(bb_gt_files, video_dir, image_dir, visualize_debug, force, output): """ Converts bb_binary ground truth Cap'n Proto files to hdf5 files and extracts the corresponding rois from videos or images. """ gen_factory = FrameGeneratorFactory(video_dir, image_dir) if force and os.path.exists(output): os.remove(output) dset = HDF5Dataset(output) camIdxs = [] periods = [] for fname in bb_gt_files: fc = load_frame_container(fname) camIdx, start_dt, end_dt = parse_video_fname(fname) basename = os.path.basename(fname) gt_frames = [] print(basename) gen = gen_factory.get_generator(camIdx, start_dt) first = True for frame, (video_frame, video_filename) in zip(fc.frames, gen): gt = {} np_frame = convert_frame_to_numpy(frame) rois, mask, positions = extract_gt_rois(np_frame, video_frame, start_dt) for name in np_frame.dtype.names: gt[name] = np_frame[name][mask] gt["bits"] = np.array([int_id_to_binary(id)[::-1] for id in gt["decodedId"]]) gt["tags"] = rois gt['filename'] = os.path.basename(video_filename) gt_frames.append(gt) if first and visualize_debug: visualize_detections(gt, positions, video_frame) first = False print('.', end='') gt_period = GTPeriod(camIdx, start_dt, end_dt, fname, gt_frames) periods.append([int(gt_period.start.timestamp()), int(gt_period.end.timestamp())]) camIdxs.append(gt_period.camIdx) append_gt_to_hdf5(gt_period, dset) dset.attrs['periods'] = np.array(periods) dset.attrs['camIdxs'] = np.array(camIdxs) dset.close()
def main(): # loading data if not os.path.exists( config.DATA_FOLDER ): print 'Error: folder not found' return dset_store = ds.DetectionSetStore() repo = Repository.load( config.DATA_FOLDER ) start_time = datetime( config.DATE[ 0 ], config.DATE[ 1 ], config.DATE[ 2 ], config.TIME[ 0 ], config.TIME[ 1 ], tzinfo=pytz.utc ) end_time = datetime( config.DATE[ 0 ], config.DATE[ 1 ], config.DATE[ 2 ], config.TIME[ 0 ], config.TIME[ 1 ]+1, tzinfo=pytz.utc ) fnames = repo.iter_fnames( begin=start_time, end=end_time ) for fname in fnames: frame_container = load_frame_container( fname ) cam = frame_container.camId dset_store.source = frame_container.dataSources[ 0 ].filename previous_timestamp = None frame_index = config.FRAME_START for frame in list( frame_container.frames )[ config.FRAME_START : config.FRAME_END + 1 ]: timestamp = ds.TimeStamp( frame_index, cam ) timestamp.connect_with_previous( previous_timestamp ) previous_timestamp = timestamp dset = ds.DetectionSet() dset_store.store[ timestamp ] = dset data = convert_frame_to_numpy( frame ) for detection_data in data: dset.add_detection( ds.Detection( detection_data[ 'idx' ], timestamp, np.array( [ detection_data[ 'ypos' ], detection_data[ 'xpos' ] ] ), # rotated, otherwise will be portrait orientation detection_data[ 'localizerSaliency' ], detection_data[ 'decodedId' ][::-1] # reversed, we want least significant bit last ) ) dset.build_kd_tree() frame_index += 1 # break because we only load the first fname break # loading truth if not os.path.isfile( config.PATHS_FILE ): print 'Error: file not found' return with open( config.PATHS_FILE, 'rb' ) as paths_file: input = pickle.load( paths_file ) if input[ 'source' ] != dset_store.source: print 'Error: data sources do not match' return paths_input = input[ 'paths' ] # match for tag_id in paths_input.keys(): for path_id in paths_input[ tag_id ].keys(): for frame,detection_data in paths_input[ tag_id ][ path_id ].items(): old_detection_id, pos_x, pos_y, readability = detection_data timestamp = dset_store.get_timestamp( frame ) new_detection_id = None distance = None if timestamp is not None and readability < 3: dset = dset_store.get( timestamp ) distances, indices = dset.kd_tree.query( [ pos_x, pos_y ], k=1 ) distance = distances[ 0 ][ 0 ] index = indices[ 0 ][ 0 ] if distance <= MATCH_DISTANCE_LIMIT: new_detection_id = index # use this if you're matching to the same output for test purposes: #if new_detection_id != old_detection_id: # print 'mismatch old: ' + str(old_detection_id) + ', new: ' + str(new_detection_id) paths_input[ tag_id ][ path_id ][ frame ] = ( new_detection_id, pos_x, pos_y, readability ) # saving truth with open( config.PATHS_FILE, 'wb' ) as paths_file: pickle.dump( input, paths_file ) print 'done'
def load_data( self ): if not os.path.exists( config.DATA_FOLDER ): print 'Error: folder not found' return self.block_inputs( True ) self.dset_store = ds.DetectionSetStore() self.path_manager = None self.paths_load_progress.setValue( 0 ) self.paths_load_label.setText( '' ) try: repo = Repository( config.DATA_FOLDER ) start_time = datetime( config.DATE[ 0 ], config.DATE[ 1 ], config.DATE[ 2 ], config.TIME[ 0 ], config.TIME[ 1 ], tzinfo=pytz.utc ) fnames = repo.iter_fnames( begin=start_time ) for fname in fnames: frame_container = load_frame_container( fname ) cam = frame_container.camId #frame_container.fromTimestamp # already available #frame_container.toTimestamp # already available self.dset_store.source = frame_container.dataSources[ 0 ].filename previous_timestamp = None self.data_load_progress.setMaximum( config.FRAME_END + 1 - config.FRAME_START ) self.app.processEvents() frame_index = config.FRAME_START for frame in list( frame_container.frames )[ config.FRAME_START : config.FRAME_END + 1 ]: #timestamp = frame.timestamp # not included yet #frame.id # not included yet timestamp = ds.TimeStamp( frame_index, cam ) timestamp.connect_with_previous( previous_timestamp ) previous_timestamp = timestamp dset = ds.DetectionSet() self.dset_store.store[ timestamp ] = dset data = convert_frame_to_numpy( frame ) for detection_data in data: dset.add_detection( ds.Detection( detection_data[ 'idx' ], timestamp, np.array( [ detection_data[ 'ypos' ], detection_data[ 'xpos' ] ] ), # rotated, otherwise will be portrait orientation detection_data[ 'localizerSaliency' ], detection_data[ 'decodedId' ][::-1] # reversed, we want least significant bit last ) ) frame_index += 1 self.data_load_progress.setValue( frame_index - config.FRAME_START ) self.app.processEvents() self.data_load_label.setText( str( len( self.dset_store.store ) ) + ' frames loaded' ) self.app.processEvents() # break because we only load the first fname break except: pass self.block_inputs( False )
def load_data(self): if not os.path.exists(config.DATA_FOLDER): print 'Error: folder not found' return self.block_inputs(True) self.dset_store = ds.DetectionSetStore() self.path_manager = None self.paths_load_progress.setValue(0) self.paths_load_label.setText('') try: repo = Repository(config.DATA_FOLDER) start_time = datetime(config.DATE[0], config.DATE[1], config.DATE[2], config.TIME[0], config.TIME[1], tzinfo=pytz.utc) fnames = repo.iter_fnames(begin=start_time) for fname in fnames: frame_container = load_frame_container(fname) cam = frame_container.camId #frame_container.fromTimestamp # already available #frame_container.toTimestamp # already available self.dset_store.source = frame_container.dataSources[ 0].filename previous_timestamp = None self.data_load_progress.setMaximum(config.FRAME_END + 1 - config.FRAME_START) self.app.processEvents() frame_index = config.FRAME_START for frame in list(frame_container.frames )[config.FRAME_START:config.FRAME_END + 1]: #timestamp = frame.timestamp # not included yet #frame.id # not included yet timestamp = ds.TimeStamp(frame_index, cam) timestamp.connect_with_previous(previous_timestamp) previous_timestamp = timestamp dset = ds.DetectionSet() self.dset_store.store[timestamp] = dset data = convert_frame_to_numpy(frame) for detection_data in data: dset.add_detection( ds.Detection( detection_data['idx'], timestamp, np.array( [ detection_data['ypos'], detection_data['xpos'] ] ), # rotated, otherwise will be portrait orientation detection_data['localizerSaliency'], detection_data['decodedId'] [:: -1] # reversed, we want least significant bit last )) frame_index += 1 self.data_load_progress.setValue(frame_index - config.FRAME_START) self.app.processEvents() self.data_load_label.setText( str(len(self.dset_store.store)) + ' frames loaded') self.app.processEvents() # break because we only load the first fname break except: pass self.block_inputs(False)
def test_bbb_fc_from_df(frame_data_all): """Data in DataFrame correctly converted to `FrameContainer`.""" frame = frame_data_all union_type = frame.detectionsUnion.which() expected_keys = ['timestamp', ] expected_keys.extend(get_detection_keys(union_type)) arr = convert_frame_to_numpy(frame, expected_keys) bbb_check_frame_data(frame, arr, expected_keys) detections = make_df_from_np(arr, union_type) expected_detections = detections.copy() n_detections = len(getattr(frame.detectionsUnion, union_type)) offset = 0 # test minimal set dfr = detections.copy() fco, offset = build_frame_container_from_df(dfr, union_type, offset) assert offset == 1 assert fco.id == 0 assert fco.camId == 0 assert fco.fromTimestamp == expected_detections.timestamp[0] assert fco.toTimestamp == expected_detections.timestamp[3] assert len(fco.frames) == 1 frame0 = fco.frames[0] assert frame0.id == offset - 1 assert frame0.frameIdx == 0 assert frame0.timestamp == expected_detections.timestamp[0] assert len(getattr(frame0.detectionsUnion, union_type)) == 4 arr = convert_frame_to_numpy(frame0, expected_keys) converted_detections = make_df_from_np(arr, union_type) assert_frame_equal(expected_detections, converted_detections) # test without readability dfr = detections.copy() if union_type == 'detectionsTruth': dfr.drop('readability', axis=1, inplace=True) fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 2 # test offset and id to test for fixed assignments assert fco.id == 1 assert fco.camId == 1 assert len(fco.frames) == 1 frame0 = fco.frames[0] assert frame0.id == offset - 1 # test if offset is considered assert frame0.frameIdx == 0 if union_type == 'detectionsTruth': for i in range(0, n_detections): # test for default readability value assert str(frame0.detectionsUnion.detectionsTruth[i].readability) == 'unknown' # test with datetime instead of unixtimestamp dfr = detections.copy() dfr.timestamp = dfr.timestamp.apply(to_datetime) fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 3 assert fco.fromTimestamp == expected_detections.timestamp[0] assert len(fco.frames) == 1 # test with additional column for frames dfr = detections.copy() dfr['dataSourceIdx'] = 99 fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 4 assert len(fco.frames) == 1 frame0 = fco.frames[0] assert frame0.dataSourceIdx == dfr.dataSourceIdx[0] # test with additional column for detections dfr = detections.copy() dfr['xposHive'] = range(0, n_detections) fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 5 assert len(fco.frames) == 1 converted_detections = getattr(fco.frames[0].detectionsUnion, union_type) for i in range(0, n_detections): assert converted_detections[i].xposHive == dfr.xposHive[i] # test with camId column dfr = detections.copy() dfr['camId'] = [offset] * dfr.shape[0] dfr.loc[0, 'camId'] = offset - 1 fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 6 assert len(fco.frames) == 1 converted_detections = getattr(fco.frames[0].detectionsUnion, union_type) assert len(converted_detections) == n_detections - 1 # one detection removed!
def test_bbb_fc_from_df(frame_data_all): """Data in DataFrame correctly converted to `FrameContainer`.""" frame = frame_data_all union_type = frame.detectionsUnion.which() expected_keys = [ 'timestamp', ] expected_keys.extend(get_detection_keys(union_type)) arr = convert_frame_to_numpy(frame, expected_keys) bbb_check_frame_data(frame, arr, expected_keys) detections = make_df_from_np(arr, union_type) expected_detections = detections.copy() n_detections = len(getattr(frame.detectionsUnion, union_type)) offset = 0 # test minimal set dfr = detections.copy() fco, offset = build_frame_container_from_df(dfr, union_type, offset) assert offset == 1 assert fco.id == 0 assert fco.camId == 0 assert fco.fromTimestamp == expected_detections.timestamp[0] assert fco.toTimestamp == expected_detections.timestamp[3] assert len(fco.frames) == 1 frame0 = fco.frames[0] assert frame0.id == offset - 1 assert frame0.frameIdx == 0 assert frame0.timestamp == expected_detections.timestamp[0] assert len(getattr(frame0.detectionsUnion, union_type)) == 4 arr = convert_frame_to_numpy(frame0, expected_keys) converted_detections = make_df_from_np(arr, union_type) assert_frame_equal(expected_detections, converted_detections) # test without readability dfr = detections.copy() if union_type == 'detectionsTruth': dfr.drop('readability', axis=1, inplace=True) fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 2 # test offset and id to test for fixed assignments assert fco.id == 1 assert fco.camId == 1 assert len(fco.frames) == 1 frame0 = fco.frames[0] assert frame0.id == offset - 1 # test if offset is considered assert frame0.frameIdx == 0 if union_type == 'detectionsTruth': for i in range(0, n_detections): # test for default readability value assert str(frame0.detectionsUnion.detectionsTruth[i].readability ) == 'unknown' # test with datetime instead of unixtimestamp dfr = detections.copy() dfr.timestamp = dfr.timestamp.apply(to_datetime) fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 3 assert fco.fromTimestamp == expected_detections.timestamp[0] assert len(fco.frames) == 1 # test with additional column for frames dfr = detections.copy() dfr['dataSourceIdx'] = 99 fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 4 assert len(fco.frames) == 1 frame0 = fco.frames[0] assert frame0.dataSourceIdx == dfr.dataSourceIdx[0] # test with additional column for detections dfr = detections.copy() dfr['xposHive'] = range(0, n_detections) fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 5 assert len(fco.frames) == 1 converted_detections = getattr(fco.frames[0].detectionsUnion, union_type) for i in range(0, n_detections): assert converted_detections[i].xposHive == dfr.xposHive[i] # test with camId column dfr = detections.copy() dfr['camId'] = [offset] * dfr.shape[0] dfr.loc[0, 'camId'] = offset - 1 fco, offset = build_frame_container_from_df(dfr, union_type, offset, frame_offset=offset) assert offset == 6 assert len(fco.frames) == 1 converted_detections = getattr(fco.frames[0].detectionsUnion, union_type) assert len( converted_detections) == n_detections - 1 # one detection removed!