class PyramidAdaptor(object): def __init__( self, detector, descriptor=None, num_features=2000, num_levels=4, scale_factor=1.2, sigma0=1.0, # N.B.: SIFT use 1.6 for this value first_level=0, pyramid_type=PyramidType.RESIZE, use_block_adaptor=False, do_parallel=kPyramidAdaptorUseParallelComputations, do_sat_features_per_level=False): self.detector = detector self.descriptor = descriptor self.num_features = num_features self.is_detector_equal_to_descriptor = ( self.detector == self.descriptor) self.num_levels = num_levels self.scale_factor = scale_factor self.inv_scale_factor = 1. / scale_factor self.sigma0 = sigma0 self.first_level = first_level self.pyramid_type = pyramid_type self.use_block_adaptor = use_block_adaptor self.do_parallel = do_parallel # do parallel computations self.do_sat_features_per_level = do_sat_features_per_level # saturate number of features for each level self.pyramid = Pyramid(num_levels=num_levels, scale_factor=scale_factor, sigma0=sigma0, first_level=first_level, pyramid_type=pyramid_type) self.initSigmaLevels() self.block_adaptor = None if self.use_block_adaptor: self.block_adaptor = BlockAdaptor(self.detector, self.descriptor, row_divs=kAdaptorNumRowDivs, col_divs=kAdaptorNumColDivs, do_parallel=False) def initSigmaLevels(self): num_levels = max(kNumLevelsInitSigma, self.num_levels) self.scale_factors = np.zeros(num_levels) self.inv_scale_factors = np.zeros(num_levels) self.scale_factors[0] = 1.0 # compute desired number of features per level (by using the scale factor) self.num_features_per_level = np.zeros(num_levels, dtype=np.int) num_desired_features_per_level = self.num_features * ( 1 - self.inv_scale_factor) / ( 1 - math.pow(self.inv_scale_factor, self.num_levels)) sum_num_features = 0 for level in range(self.num_levels - 1): self.num_features_per_level[level] = int( round(num_desired_features_per_level)) sum_num_features += self.num_features_per_level[level] num_desired_features_per_level *= self.inv_scale_factor self.num_features_per_level[self.num_levels - 1] = max( self.num_features - sum_num_features, 0) #print('num_features_per_level:',self.num_features_per_level) if self.first_level == -1: self.scale_factors[0] = 1.0 / self.scale_factor self.inv_scale_factors[0] = 1.0 / self.scale_factors[0] for i in range(1, num_levels): self.scale_factors[i] = self.scale_factors[i - 1] * self.scale_factor self.inv_scale_factors[i] = 1.0 / self.scale_factors[i] #print('self.inv_scale_factors: ', self.inv_scale_factors) # detect on 'unfiltered' pyramid images ('unfiltered' meanining depends on the selected pyramid type) def detect(self, frame, mask=None): if self.num_levels == 1: return self.detector.detect(frame, mask) else: #TODO: manage mask if kVerbose: print('PyramidAdaptor #levels:', self.num_levels, '(from', self.first_level, '), scale_factor:', self.scale_factor, ', sigma0:', self.sigma0, ', type:', self.pyramid_type.name) self.pyramid.compute(frame) kps_all = [] # list are thread-safe def detect_level(scale, pyr_cur, i): kps = [] if self.block_adaptor is None: kps = self.detector.detect(pyr_cur) else: kps = self.block_adaptor.detect(pyr_cur) if kVerbose and False: print("PyramidAdaptor - level", i, ", shape: ", pyr_cur.shape) for kp in kps: #print('kp.pt before: ', kp.pt) kp.pt = (kp.pt[0] * scale, kp.pt[1] * scale) kp.size = kp.size * scale kp.octave = i #print('kp: ', kp.pt, kp.octave) if self.do_sat_features_per_level: kps, _ = sat_num_features( kps, None, self.num_features_per_level[i]) # experimental kps_all.extend(kps) if not self.do_parallel: #print('sequential computations') # process the blocks sequentially for i in range(0, self.num_levels): scale = self.scale_factors[i] pyr_cur = self.pyramid.imgs[i] detect_level(scale, pyr_cur, i) else: #print('parallel computations') futures = [] with ThreadPoolExecutor(max_workers=4) as executor: for i in range(0, self.num_levels): scale = self.scale_factors[i] pyr_cur = self.pyramid.imgs[i] futures.append( executor.submit(detect_level, scale, pyr_cur, i)) wait(futures) # wait all the task are completed return np.array(kps_all) # detect on 'unfiltered' pyramid images ('unfiltered' meanining depends on the selected pyramid type) # compute descriptors on 'filtered' pyramid images ('filtered' meanining depends on the selected pyramid type) def detectAndCompute(self, frame, mask=None): if self.num_levels == 1: return self.detector.detectAndCompute(frame, mask) else: if kVerbose: print('PyramidAdaptor [dc] #levels:', self.num_levels, '(from', self.first_level, '), scale_factor:', self.scale_factor, ', sigma0:', self.sigma0, ', type:', self.pyramid_type.name) self.pyramid.compute(frame) kps_all = [] des_all = [] kps_des_map = {} # i -> (kps,des) def detect_and_compute_level(scale, pyr_cur, pyr_cur_filtered, N, i): kps = [] if self.block_adaptor is None: #kps, des = self.detector.detectAndCompute(pyr_cur) if self.is_detector_equal_to_descriptor: kps, des = self.detector.detectAndCompute(pyr_cur) else: kps = self.detector.detect(pyr_cur) #print('description of filtered') kps, des = self.descriptor.compute( pyr_cur_filtered, kps) else: kps, des = self.block_adaptor.detectAndCompute(pyr_cur) if kVerbose and False: print("PyramidAdaptor - level", i, ", shape: ", pyr_cur.shape) for kp in kps: #print('before: kp.pt:', kp.pt,', size:',kp.size,', octave:',kp.octave,', angle:',kp.angle) kp.pt = (kp.pt[0] * scale, kp.pt[1] * scale) kp.size = kp.size * scale kp.octave = i #print('after: kp.pt:', kp.pt,', size:',kp.size,', octave:',kp.octave,', angle:',kp.angle) if self.do_sat_features_per_level: kps, des = sat_num_features(kps, des, N) # experimental kps_des_map[i] = (kps, des) if not self.do_parallel: #print('sequential computations') # process the blocks sequentially for i in range(0, self.num_levels): scale = self.scale_factors[i] pyr_cur = self.pyramid.imgs[i] pyr_cur_filtered = self.pyramid.imgs_filtered[i] detect_and_compute_level(scale, pyr_cur, pyr_cur_filtered, self.num_features_per_level[i], i) else: #print('parallel computations') futures = [] with ThreadPoolExecutor(max_workers=4) as executor: for i in range(0, self.num_levels): scale = self.scale_factors[i] pyr_cur = self.pyramid.imgs[i] pyr_cur_filtered = self.pyramid.imgs_filtered[i] futures.append( executor.submit(detect_and_compute_level, scale, pyr_cur, pyr_cur_filtered, self.num_features_per_level[i], i)) wait(futures) # wait all the task are completed # now merge the computed results for i, (kps, des) in kps_des_map.items(): kps_all.extend(kps) if des is not None and len(des) > 0: if len(des_all) > 0: des_all = np.vstack([des_all, des]) else: des_all = des return np.array(kps_all), np.array(des_all)
from utils_img import combine_images_horizontally img = cv2.imread('../data/kitti06-12.png', cv2.IMREAD_COLOR) pyramid_params = dict( num_levels=8, scale_factor=1.2, sigma0=1., first_level=-1, # 0: start from image; -1: start from image*scale_factor pyramid_type=PyramidType.RESIZE) pyramid = Pyramid(**pyramid_params) print('pyramid_type: ', pyramid.pyramid_type.name) print('first_level: ', pyramid.first_level) time_start = time.time() pyramid.compute(img) duration = time.time() - time_start print('duration: ', duration) for i in range(0, pyramid.num_levels): name = 'level ' + str(i) + ': img - img_filtered' img_pyr = combine_images_horizontally(pyramid.imgs[i], pyramid.imgs_filtered[i]) cv2.imshow(name, img_pyr) print(name, ' size: ', pyramid.imgs[i].shape) k = cv2.waitKey(0) cv2.destroyAllWindows()