Example #1
0
    def __init__(self, filename, thresh=10):
        self.__fullname = filename
        self.__filename = filename[filename.rfind('\\') + 1:]
        self.__thresh = thresh
        self.__image = cv.LoadImage(filename, cv.CV_LOAD_IMAGE_UNCHANGED)
        self.__grayscale = imgtools.normalize_image(cv.LoadImage(filename,
                                                    cv.CV_LOAD_IMAGE_GRAYSCALE))
        #ext_grayscale = imgtools.image_symmetric_extension(self.__grayscale, max(Surf.SCALES), max(Surf.SCALES))
        self.__integralImage = IntegralImage(self.__grayscale)
#         max_scale = max(Surf.SCALES)
#         self.__integralImage.extend_symmetrically(max_scale, max_scale)
        self.__responses = []
        self.__features = []
        self.__traces = []
        self.__orientation = []
        self.__descriptors = []
Example #2
0
class Surf(object):
    
    OCTAVE_MAP = {9: 1, 15: 1, 21: 1, 27: 2, 39: 2, 51: 3, 75: 3, 99: 4, 147: 4, 195: 5, 291: 5, 387: 5}    
    SCALES = sorted(OCTAVE_MAP.keys())
    FILTER_MAP = np.array([[0, 1, 2, 3], 
                           [1, 3, 4, 5], 
                           [3, 5, 6, 7], 
                           [5, 7, 8, 9], 
                           [7, 9, 10, 11]])
    OCTAVES = sorted(OCTAVE_MAP.values())
    N = 1
    REFINEMENT_ITERATION_COUNT = 5
    HESSIAN_RELATIVE_WEIGHT = 0.912
    STARTING_ANGLE = 30
    LAST_ANGLE = 330
    
    def __init__(self, filename, thresh=10):
        self.__fullname = filename
        self.__filename = filename[filename.rfind('\\') + 1:]
        self.__thresh = thresh
        self.__image = cv.LoadImage(filename, cv.CV_LOAD_IMAGE_UNCHANGED)
        self.__grayscale = imgtools.normalize_image(cv.LoadImage(filename,
                                                    cv.CV_LOAD_IMAGE_GRAYSCALE))
        #ext_grayscale = imgtools.image_symmetric_extension(self.__grayscale, max(Surf.SCALES), max(Surf.SCALES))
        self.__integralImage = IntegralImage(self.__grayscale)
#         max_scale = max(Surf.SCALES)
#         self.__integralImage.extend_symmetrically(max_scale, max_scale)
        self.__responses = []
        self.__features = []
        self.__traces = []
        self.__orientation = []
        self.__descriptors = []
    
    def draw_orientation(self):
        image = cv.CloneImage(self.__image)
        if self.__features is not None:
            for i in range(0, len(self.__features)):
                x, y, s, _ = self.__features[i]
                size = SIZES[s]
                r = size*4
                theta = self.__orientation[i]
                x, y = size*x, size*y
                X, Y = cv.Round(x + r*np.cos(theta)), cv.Round(y + r*np.sin(theta))
                cv.Circle(image, (x, y), r, cv.RGB(255, 255, 0), thickness=1) 
                cv.Line(image, (x, y), (X, Y), cv.RGB(255, 255, 0))
            return image
        else:
            raise NoFeaturesError('No features to draw')

    def save_image(self, filename):
        cv.SaveImage(filename, self.__image)
    
    def get_features(self):
        return self.__features[:]
    
    def get_orientation(self):
        return self.__orientation[:]
    
    def get_descriptors(self):
        return self.__descriptors[:]

    def get_image(self):
        return self.__image
    
    def extract_features(self):
        print 'Analyzing image', self.__fullname
        print '  Computing hessians...'
        self.__build_resp_layers()
        self.__detect_features()
        print '  Finding features...'
        self.__find_features()
        print '   ', len(self.__features), 'interest points found'
        self.__scale_space_refinement()    
        print '   ', len(self.__features), 'interest points after refinement'
        print '  Finding features\' orientation...'
        self.__find_orientation()
        print '  Building descriptors...'
        self.__build_descriptors()
        print 'Extraction success!' 
        self.__save_to_file(self.__fullname + '_points.txt')
        self.__grayscale = None
        self.__integralImage = None
        self.__responses = []        
        self.__traces = []
            
    def __build_resp_layers(self):    
        for s in Surf.SCALES:
            print '   Computing hessian in scale ' + str(s) + '...'
            octave = Surf.OCTAVE_MAP[s]
            scale = 2**(octave - 1)
            w, h = self.__grayscale.width / scale, self.__grayscale.height / scale
            hessian, trace = self.__build_response(w, h, s, scale)
            self.__responses.append(hessian)
            self.__traces.append(trace)
            cv.SaveImage('D:\\test\\hess\\hessian_' + str(s) + '_' + self.__filename + '.jpg', hessian)
            
    def __build_response(self, w, h, s, scale):
        L = s / 3
        scale_factor_dxx_dyy = 6*L*(2*L - 1)
        scale_factor_dxy = 4*L*L
        hessian = cv.CreateImage((w, h), cv.IPL_DEPTH_64F, 1)
        trace = cv.CreateImage((w, h), cv.IPL_DEPTH_8U, 1)
        for (x, y) in [(x, y) for x in range(0, w) for y in range(0, h)]:
            X = x * scale
            Y = y * scale
            dxx = self.__integralImage.dXX(X, Y, L) / scale_factor_dxx_dyy
            dyy = self.__integralImage.dYY(X, Y, L) / scale_factor_dxx_dyy
            dxy = self.__integralImage.dXY(X, Y, L) / scale_factor_dxy
            hessian[y, x] = dxx*dyy - (Surf.HESSIAN_RELATIVE_WEIGHT*dxy)**2
            trace[y, x] = 1 if (dxx + dyy) > 0 else 0
        return (hessian, trace)
        
    #Checks whether (x, y, s) is far enough from the borders        
    def __is_not_outlier(self, x, y, s):
        if s in range(1, 11):
            sigma = SIGMA[s]
            dist = cv.Round(10*cv.Sqrt(2)*sigma + 10)
            w, h = self.__responses[s].width, self.__responses[s].height
            return (x > dist and x < (w-dist) and 
                    y > dist and y < (h-dist))
        else:
            return False    
    
    def __detect_features(self):
        resp_layers = self.__responses
        for i in range(0, len(Surf.FILTER_MAP)):
            for j in range(1, 3):
                b = resp_layers[Surf.FILTER_MAP[i, j-1]]
                indM = Surf.FILTER_MAP[i, j]
                print '   Finding features of scale ' + str(Surf.SCALES[indM]) + '...'
                m = resp_layers[indM]
                t = resp_layers[Surf.FILTER_MAP[i, j+1]]
                for (x, y) in [(x, y) for x in range(1, m.width - 1) for y in range(1, m.height - 1)]:
                    if np.abs(m[y, x]) <= self.__thresh:
                        continue
                    is_max = True
                    for (dx, dy) in [(dx, dy) for dx in range(-1, 2) for dy in range(-1, 2)]:
                        if m[y, x] < max([b[y+dy, x+dx], m[y+dy, x+dx], t[y+dy, x+dx]]):
                            is_max = False
                            break
                    if is_max and self.__is_not_outlier(x, y, indM):
                        self.__features.append((x, y, indM, self.__traces[indM][y, x]))
        img = cv.CloneImage(self.__grayscale)
        from stitching.ui import cvdrawing
        cvdrawing.draw_features(img, self.__features, (1, 0, 0))
        cv.SaveImage('D:\\10.jpg', img)
        
    
    def __is_maximum(self, x, y, b, m, t):
        if np.abs(m[y, x]) <= self.__thresh:
            return False
        for (dx, dy) in [(dx, dy) for dx in range(-1, 2) for dy in range(-1, 2)]:
            if m[y, x] < max([b[y+dy, x+dx], m[y+dy, x+dx], t[y+dy, x+dx]]):
                return False
        return True
    
    #Getting features
    def __find_features(self):
        resp_layers = self.__responses
        for i in range(0, len(Surf.FILTER_MAP)):
            for j in range(1, 3):
                b = resp_layers[Surf.FILTER_MAP[i, j-1]]
                indM = Surf.FILTER_MAP[i, j]
                print '   Finding features of scale ' + str(Surf.SCALES[indM]) + '...'
                m = resp_layers[indM]
                t = resp_layers[Surf.FILTER_MAP[i, j+1]]
                octave_b = Surf.OCTAVES[Surf.FILTER_MAP[i, j-1]]
                scale_b = 2**(octave_b - 1)
                octave_m = Surf.OCTAVES[Surf.FILTER_MAP[i, j-1]]
                scale_m = 2**(octave_m - 1)
                octave_t = Surf.OCTAVES[Surf.FILTER_MAP[i, j+1]]
                scale_t = 2**(octave_t - 1)
                sf_b = scale_b / float(scale_m)
                sf_t = scale_t / float(scale_m)                 
                candidates = mt.nms_2d(m, m.width, m.height, Surf.N)
                for (k, n) in candidates:
                    if np.abs(m[k, n]) <= self.__thresh:
                        continue
                    failed = False
                    for (x, y) in itertools.product(range(k-1, k+2), range(n-1, n+2)):
                        if failed:
                            break
                        failed = b[int(x/sf_b), int(y/sf_b)] >= m[k, n] or t[int(x/sf_t), int(y/sf_t)] >= m[k, n]
                    if not failed and self.__is_not_outlier(n, k, indM):
                        self.__features.append((n, k, indM, self.__traces[indM][k, n]))
        
    def __scale_space_refinement(self):
        responses = self.__responses
        fake_maxima = []
        for i in range(0, len(self.__features)):
            (x, y, s, l_sign) = self.__features[i]
            k = Surf.REFINEMENT_ITERATION_COUNT
            failed = False
            while k >= 0 and self.__is_not_outlier(x, y, s) and not failed:
                k = k - 1 
                dx = mt.du_dx(responses, x, y, s)
                dy = mt.du_dy(responses, x, y, s)
                ds = mt.du_ds(responses, x, y, s)
                dxx = mt.du2_dx2(responses, x, y, s)
                dyy = mt.du2_dy2(responses, x, y, s)
                dxy = mt.du2_dxdy(responses, x, y, s)
                dss = mt.du2_ds2(responses, x, y, s)
                dxds = mt.du2_dxds(responses, x, y, s)
                dyds = mt.du2_dyds(responses, x, y, s)
                detH = (2*dxds*dxy*dyds - (dxds**2)*dyy - (dxy**2)*dss + 
                        dxx*dyy*dss - dxx*(dyds**2))
                if detH <> 0:
                    delta_x = -(dx*(dyy*dss - dyds**2) + 
                                dy*(dxds*dyds - dss*dxy) + 
                                ds*(dxy*dyds - dxds*dyy)) / detH
                    delta_y = -(dx*(dxy*dss - dxds*dyds) + 
                                dy*(dxds**2 - dxx*dss) + 
                                ds*(dxx*dyds - dxds*dxy)) / detH
                    delta_s = -(dx*(dxy*dyds - dxds*dyy) + 
                                dy*(dxds*dxy - dxx*dyds) + 
                                ds*(dxx*dyy - dxy**2)) / detH
                    if (np.abs(delta_x) < 1 and np.abs(delta_y) < 1 and 
                        np.abs(delta_s) < 0.4*(2**Surf.OCTAVES[s])):
                        break
                    else:
                        newX = x + delta_x + 0.5
                        newY = y + delta_y + 0.5
                        newS = int(s + delta_s/(0.4*(2**Surf.OCTAVES[s])) + 0.5)
                        if not (newS in range(0, 11)):
                            failed = True
                            break
                        oldSize, newSize = SIZES[s], SIZES[newS]
                        scale = newSize / float(oldSize)
                        if newSize > oldSize:
                            newX, newY = newX / scale, newY / scale
                        elif newSize < oldSize:
                            newX, newY = newX * scale, newY * scale 
                        (x, y, s) = (int(newX), int(newY), newS)
                else:
                    break
            if (failed or k == -1 or not self.__is_not_outlier(x, y, s) or 
                np.abs(x - self.__features[i][0]) > Surf.REFINEMENT_ITERATION_COUNT or 
                np.abs(y - self.__features[i][1]) > Surf.REFINEMENT_ITERATION_COUNT):
                fake_maxima.append(self.__features[i])
            else:
                self.__features[i] = (x, y, s, l_sign)
        self.__features = mt.list_subtraction(self.__features, fake_maxima)
        
    def __find_orientation(self):
        for (x, y, s, _) in self.__features:
            sigma = SIGMA[s]
            l = int(sigma)
            haar_respX = {}
            haar_respY = {}
            angle = {}
            indexes = set(itertools.product(range(-6, 7), range(-6, 7)))
            for (i, j) in [elem for elem in indexes if (elem[0]**2 + elem[1]**2) <= 36]:
                X, Y = cv.Round(x + i * sigma), cv.Round(y + j * sigma)
                gauss = mt.gaussian(X-x, Y-y, 2*sigma)
                haar_respX[(i, j)] = self.__integralImage.haarX(X, Y, l) * gauss
                haar_respY[(i, j)] = self.__integralImage.haarY(X, Y, l) * gauss
                angle[(i, j)] = cv.FastArctan(haar_respY[(i, j)], haar_respX[(i, j)])
            max_length = 0
            max_resp_x = max_resp_y = 0
            for theta in range(Surf.STARTING_ANGLE, Surf.LAST_ANGLE):
                resp_x = resp_y = 0
                for p in [index for index in angle.keys() if (theta - 30 <= angle[index] and 
                                                              angle[index] <= theta + 30)]:
                    resp_x += haar_respX[p]
                    resp_y += haar_respY[p]
                resp_len = resp_x**2 + resp_y**2
                if resp_len > max_length:
                    max_resp_x, max_resp_y = resp_x, resp_y
                    max_length = resp_len
            self.__orientation.append(cv.FastArctan(max_resp_y, max_resp_x) * mt.TO_RADS_RATIO)        
    
    def __build_descriptors(self):
        for k in range(0, len(self.__features)):
            (x, y, s, _) = self.__features[k]
            sigma = SIGMA[s]
            l = int(0.5*sigma)
            theta = self.__orientation[k]
            cosT, sinT = np.cos(theta), np.sin(theta)
            descriptor = np.zeros((64))
            k = 0
            for (i, j) in itertools.product(range(0, 4), range(0, 4)):
                sum_resp_x = sum_resp_y = sum_abs_resp_x = sum_abs_resp_y = 0
                for (m, n) in itertools.product(range(0, 5), range(0, 5)):
                    X = cv.Round(x + sigma*(cosT*(5*(i-2) + m) - sinT*(5*(j-2) + n)))
                    Y = cv.Round(y + sigma*(sinT*(5*(i-2) + m) + cosT*(5*(j-2) + n)))
                    gauss = mt.gaussian(X-x, Y-y, 3.3*sigma)
                    resp_x = self.__integralImage.haarX(X, Y, l)
                    resp_y = self.__integralImage.haarY(X, Y, l)
                    respX = gauss*(-sinT*resp_x + cosT*resp_y)
                    respY = gauss*(cosT*resp_x + sinT*resp_y)
                    sum_resp_x += respX
                    sum_resp_y += respY
                    sum_abs_resp_x += np.abs(respX)
                    sum_abs_resp_y += np.abs(respY)
                descriptor[k] = sum_resp_x
                descriptor[k+1] = sum_resp_y
                descriptor[k+2] = sum_abs_resp_x
                descriptor[k+3] = sum_abs_resp_y
                k = k + 4
            norm = np.linalg.norm(descriptor)
            self.__descriptors.append(descriptor / norm)   
            
    def __save_to_file(self, filename):
        output = open(filename, 'w')
        for point in self.__features:
            output.write(str(point) + '\n')
        output.flush()
        output.close()