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)
Example #2
0
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()