def localizeExtremumViaQuadraticFit(i, j, image_index, octave_index, num_intervals, dog_images_in_octave, sigma, contrast_threshold, image_border_width, eigenvalue_ratio=10, num_attempts_until_convergence=5): """Iteratively refine pixel positions of scale-space extrema via quadratic fit around each extremum's neighbors """ logger.debug('Localizing scale-space extrema...') extremum_is_outside_image = False image_shape = dog_images_in_octave[0].shape for attempt_index in range(num_attempts_until_convergence): # need to convert from uint8 to float32 to compute derivatives and need to rescale pixel values to [0, 1] to apply Lowe's thresholds first_image, second_image, third_image = dog_images_in_octave[image_index - 1:image_index + 2] pixel_cube = stack([first_image[i - 1:i + 2, j - 1:j + 2], second_image[i - 1:i + 2, j - 1:j + 2], third_image[i - 1:i + 2, j - 1:j + 2]]).astype('float32') / 255. gradient = computeGradientAtCenterPixel(pixel_cube) hessian = computeHessianAtCenterPixel(pixel_cube) extremum_update = -lstsq(hessian, gradient, rcond=None)[0] if abs(extremum_update[0]) < 0.5 and abs(extremum_update[1]) < 0.5 and abs(extremum_update[2]) < 0.5: break j += int(round(extremum_update[0])) i += int(round(extremum_update[1])) image_index += int(round(extremum_update[2])) # make sure the new pixel_cube will lie entirely within the image if i < image_border_width or i >= image_shape[0] - image_border_width or j < image_border_width or j >= \ image_shape[1] - image_border_width or image_index < 1 or image_index > num_intervals: extremum_is_outside_image = True break if extremum_is_outside_image: logger.debug('Updated extremum moved outside of image before reaching convergence. Skipping...') return None if attempt_index >= num_attempts_until_convergence - 1: logger.debug('Exceeded maximum number of attempts without reaching convergence for this extremum. Skipping...') return None functionValueAtUpdatedExtremum = pixel_cube[1, 1, 1] + 0.5 * dot(gradient, extremum_update) if abs(functionValueAtUpdatedExtremum) * num_intervals >= contrast_threshold: xy_hessian = hessian[:2, :2] xy_hessian_trace = trace(xy_hessian) xy_hessian_det = det(xy_hessian) if xy_hessian_det > 0 and eigenvalue_ratio * (xy_hessian_trace ** 2) < ( (eigenvalue_ratio + 1) ** 2) * xy_hessian_det: # Contrast check passed -- construct and return OpenCV KeyPoint object keypoint = KeyPoint() keypoint.pt = ( (j + extremum_update[0]) * (2 ** octave_index), (i + extremum_update[1]) * (2 ** octave_index)) keypoint.octave = octave_index + image_index * (2 ** 8) + int(round((extremum_update[2] + 0.5) * 255)) * ( 2 ** 16) keypoint.size = sigma * (2 ** ((image_index + extremum_update[2]) / float32(num_intervals))) * ( 2 ** (octave_index + 1)) # octave_index + 1 because the input image was doubled keypoint.response = abs(functionValueAtUpdatedExtremum) return keypoint, image_index return None
def localizar_Extremo_via_AjusteCuadratico(i, j, indice_imagen, indice_octava, num_intervalos, imagenes_dog_por_octava, sigma, contrast_threshold, ancho_borde_imagen, eigenvalue_ratio=10, num_attempts_until_convergence=5): """Refina iterativamente las posiciones de los píxeles de los extremos del espacio de escala mediante un ajuste cuadrático alrededor de los vecinos de cada extremo Por eso es costoso esta wada """ logger.debug('Localizando los extremos del espacio-escala...') extremo_esta_fuera_Imagen = False image_shape = imagenes_dog_por_octava[0].shape for attempt_index in range(num_attempts_until_convergence): ''' Los extremos relacionados con el espacio de escala deben convertirse de uint8 a float32 para calcular derivadas y deben cambiar la escala de los valores de píxeles a [0, 1] para aplicar los umbrales de Lowe's. ''' primera_imagen, segunda_imagen, tercera_imagen = imagenes_dog_por_octava[indice_imagen-1:indice_imagen+2] pixel_cube = stack([primera_imagen[i-1:i+2, j-1:j+2], segunda_imagen[i-1:i+2, j-1:j+2], tercera_imagen[i-1:i+2, j-1:j+2]]).astype('float32') / 255. gradient = calcular_Gradiente_Pixel_Central(pixel_cube) hessiana = calcular_Hessiana_Pixel_central(pixel_cube) extremo_actualizado = -lstsq(hessiana, gradient, rcond=None)[0] if abs(extremo_actualizado[0]) < 0.5 and abs(extremo_actualizado[1]) < 0.5 and abs(extremo_actualizado[2]) < 0.5: break j += int(round(extremo_actualizado[0])) i += int(round(extremo_actualizado[1])) indice_imagen += int(round(extremo_actualizado[2])) # Nos aseguramos de que el nuevo cubo de píxeles se encuentre completamente dentro de la imagen if i < ancho_borde_imagen or i >= image_shape[0] - ancho_borde_imagen or j < ancho_borde_imagen or j >= image_shape[1] - ancho_borde_imagen or indice_imagen < 1 or indice_imagen > num_intervalos: extremo_esta_fuera_Imagen = True break if extremo_esta_fuera_Imagen: logger.debug('El extremo actualizado se movió fuera de la imagen antes de alcanzar la convergencia -> Salto...') return None if attempt_index >= num_attempts_until_convergence - 1: logger.debug('Se superó el número máximo de intentos sin alcanzar la convergencia para este extremo -> Salto...') return None ValorFuncion_Extrenmo_actualizado = pixel_cube[1, 1, 1] + 0.5 * dot(gradient, extremo_actualizado) if abs(ValorFuncion_Extrenmo_actualizado) * num_intervalos >= contrast_threshold: xy_hessian = hessiana[:2, :2] xy_hessian_traza = trace(xy_hessian) xy_hessian_determinante = det(xy_hessian) if xy_hessian_determinante > 0 and eigenvalue_ratio * (xy_hessian_traza ** 2) < ((eigenvalue_ratio + 1) ** 2) * xy_hessian_determinante: # Comprobación de contraste superada: construir y devolver el objeto Punto Clave keypoint = KeyPoint() keypoint.pt = ((j + extremo_actualizado[0]) * (2 ** indice_octava), (i + extremo_actualizado[1]) * (2 ** indice_octava)) keypoint.octave = indice_octava + indice_imagen * (2 ** 8) + int(round((extremo_actualizado[2] + 0.5) * 255)) * (2 ** 16) #indice_octava + 1 porque la imagen de entrada se duplicó Pesha PS PELOTUDO keypoint.size = sigma * (2 ** ((indice_imagen + extremo_actualizado[2]) / float32(num_intervalos))) * (2 ** (indice_octava + 1)) keypoint.response = abs(ValorFuncion_Extrenmo_actualizado) return keypoint, indice_imagen return None
def localizeExtremumViaQuadraticFit(i, j, image_index, octave_index, num_intervals, dog_images_in_octave, sigma, contrast_threshold, image_border_width, eigenvalue_ratio=10, num_attempts_until_convergence=5): """ 在scale-space中,迭代进行每个像素点的位置更新 """ logger.info('Localizing scale-space extrema...') extremum_is_outside_image = False image_shape = dog_images_in_octave[0].shape for attempt_index in range(num_attempts_until_convergence): first_image, second_image, third_image = dog_images_in_octave[image_index-1:image_index+2] pixel_cube = stack([first_image[i-1:i+2, j-1:j+2], second_image[i-1:i+2, j-1:j+2], third_image[i-1:i+2, j-1:j+2]]).astype('float32') / 255. gradient = computeGradientAtCenterPixel(pixel_cube) hessian = computeHessianAtCenterPixel(pixel_cube) extremum_update = -lstsq(hessian, gradient, rcond=None)[0] if abs(extremum_update[0]) < 0.5 and abs(extremum_update[1]) < 0.5 and abs(extremum_update[2]) < 0.5: break j += int(round(extremum_update[0])) i += int(round(extremum_update[1])) image_index += int(round(extremum_update[2])) if i < image_border_width or i >= image_shape[0] - image_border_width or j < image_border_width or j >= image_shape[1] - image_border_width or image_index < 1 or image_index > num_intervals: extremum_is_outside_image = True break functionValueAtUpdatedExtremum = pixel_cube[1, 1, 1] + 0.5 * dot(gradient, extremum_update) if abs(functionValueAtUpdatedExtremum) * num_intervals >= contrast_threshold: xy_hessian = hessian[:2, :2] xy_hessian_trace = trace(xy_hessian) xy_hessian_det = det(xy_hessian) if xy_hessian_det > 0 and eigenvalue_ratio * (xy_hessian_trace ** 2) < ((eigenvalue_ratio + 1) ** 2) * xy_hessian_det: # 设计Keypoints keypoint = KeyPoint() keypoint.pt = ((j + extremum_update[0]) * (2 ** octave_index), (i + extremum_update[1]) * (2 ** octave_index)) keypoint.octave = octave_index + image_index * (2 ** 8) + int(round((extremum_update[2] + 0.5) * 255)) * (2 ** 16) keypoint.size = sigma * (2 ** ((image_index + extremum_update[2]) / float32(num_intervals))) * (2 ** (octave_index + 1)) # octave_index + 1 because the input image was doubled keypoint.response = abs(functionValueAtUpdatedExtremum) return keypoint, image_index return None
def localize_extremum_via_quadratic_fit(i, j, image_index, octave_index, num_intervals, dog_images_in_octave, sigma, contrast_threshold, image_border_width, eigenvalue_ratio=10, num_attempts_until_convergence=5): """ 利用泰勒展开对像素进行亚像素(子像素)精确定位。 """ extremum_is_outside_image = False image_shape = dog_images_in_octave[0].shape for attempt_index in range(num_attempts_until_convergence): # need to convert from uint8 to float32 to compute derivatives and need # to rescale pixel values to [0, 1] to apply Lowe's thresholds first_image, second_image, third_image = dog_images_in_octave[ image_index - 1:image_index + 2] pixel_cube = stack([ first_image[i - 1:i + 2, j - 1:j + 2], second_image[i - 1:i + 2, j - 1:j + 2], third_image[i - 1:i + 2, j - 1:j + 2] ]).astype('float32') / 255. gradient = compute_gradient_at_center_pixel(pixel_cube) hessian = compute_hessian_at_center_pixel(pixel_cube) extremum_update = -lstsq(hessian, gradient, rcond=None)[0] if abs(extremum_update[0]) < 0.5 and abs( extremum_update[1]) < 0.5 and abs(extremum_update[2]) < 0.5: break j += int(round(extremum_update[0])) i += int(round(extremum_update[1])) image_index += int(round(extremum_update[2])) # make sure the new pixel_cube will lie entirely within the image if i < image_border_width or i >= image_shape[0] - image_border_width or j < image_border_width or j >= \ image_shape[1] - image_border_width or image_index < 1 or image_index > num_intervals: extremum_is_outside_image = True break if extremum_is_outside_image: return None if attempt_index >= num_attempts_until_convergence - 1: return None functionValueAtUpdatedExtremum = pixel_cube[1, 1, 1] + 0.5 * dot( gradient, extremum_update) if abs(functionValueAtUpdatedExtremum) * \ num_intervals >= contrast_threshold: xy_hessian = hessian[:2, :2] xy_hessian_trace = trace(xy_hessian) xy_hessian_det = det(xy_hessian) if xy_hessian_det > 0 and eigenvalue_ratio * (xy_hessian_trace**2) < ( (eigenvalue_ratio + 1)**2) * xy_hessian_det: # Contrast check passed -- construct and return OpenCV KeyPoint # object keypoint = KeyPoint() keypoint.pt = ((j + extremum_update[0]) * (2**octave_index), (i + extremum_update[1]) * (2**octave_index)) keypoint.octave = octave_index + image_index * \ (2 ** 8) + int(round((extremum_update[2] + 0.5) * 255)) * (2 ** 16) keypoint.size = sigma * (2**( (image_index + extremum_update[2]) / float32(num_intervals) )) * (2**(octave_index + 1) ) # octave_index + 1 because the input image was doubled keypoint.response = abs(functionValueAtUpdatedExtremum) return keypoint, image_index return None
def DetecteKeyPoint(i, j, ImgIndex, OctaveIndex, NumOfIntervals, DoGImgInOctave, Sigma0, ContrastThreshold, ImgBorderWidth, EigenvalueRatio=10, NumOfAttempts=5): """ 探测极值点是否是关键点 @param i: 极值点坐标i @param j: 极值点坐标j @param ImgIndex: 图片的层数s @param OctaveIndex: 图片所在组数O @param NumOfIntervals: SIFT里的大S @param DoGImgInOctave: 差分高斯金字塔的一组图片,探测时可能移动层数 @param Sigma0: SIFT里的Sigma0 @param ContrastThreshold: SIFT里用于消除噪音点 @param ImgBorderWidth: 抛弃的图片边缘的长度,便于计算,网上说一般为5 @param EigenvalueRatio: 消除边缘响应的阈值里的r @param NumOfAttempts: 关键点的探测次数 @return: 如果探测到关键点就返回KeyPoint,探测不到就返回None """ ImgShape = (len(DoGImgInOctave[0]), len(DoGImgInOctave[0][0])) for AttemptIndex in range(NumOfAttempts): FirstImg, SecondImg, ThirdImg = DoGImgInOctave[ImgIndex - 1:ImgIndex + 2] DoGArray = np.array([ FirstImg[i - 1:i + 2, j - 1:j + 2], SecondImg[i - 1:i + 2, j - 1:j + 2], ThirdImg[i - 1:i + 2, j - 1:j + 2] ]).astype(np.float) / 255 # Lowe要求归一化,为了跟阈值比较,消除噪音点 Gradient = ComputeGradientAtCenterPixel(DoGArray) Hessian = ComputeHessianAtCenterPixel(DoGArray) CoordinatesUpdateValueOfKeyPoint = -1 * np.dot( ComputeInv3(Hessian), np.array([Gradient]).reshape((3, 1))) CoordinatesUpdateValueOfKeyPoint.reshape(-1) # 偏移量 if abs(CoordinatesUpdateValueOfKeyPoint[0]) < 0.5 and abs( CoordinatesUpdateValueOfKeyPoint[1]) < 0.5 and abs( CoordinatesUpdateValueOfKeyPoint[2]) < 0.5: break j += int(np.round(CoordinatesUpdateValueOfKeyPoint[0])) i += int(np.round(CoordinatesUpdateValueOfKeyPoint[1])) ImgIndex += int(np.round(CoordinatesUpdateValueOfKeyPoint[2])) if i < ImgBorderWidth or i >= ImgShape[ 0] - ImgBorderWidth or j < ImgBorderWidth or j >= ImgShape[ 1] - ImgBorderWidth or ImgIndex < 1 or ImgIndex > NumOfIntervals: return None if AttemptIndex >= NumOfAttempts - 1: return None NewKeyPointValue = DoGArray[1, 1, 1] + 0.5 * np.dot( Gradient, CoordinatesUpdateValueOfKeyPoint) if abs(NewKeyPointValue) * NumOfIntervals >= ContrastThreshold: xy_Hessian = Hessian[:2, :2] Tr = xy_Hessian[0, 0] + xy_Hessian[1, 1] Det = xy_Hessian[0, 0] * xy_Hessian[1, 1] - (xy_Hessian[0, 1]**2) if Det > 0 and EigenvalueRatio * (Tr**2) < ( (EigenvalueRatio + 1)**2) * Det: Keypoint = KeyPoint() Keypoint.pt = ((j + CoordinatesUpdateValueOfKeyPoint[0]) * (2**OctaveIndex), (i + CoordinatesUpdateValueOfKeyPoint[1]) * (2**OctaveIndex)) # Keypoint.pt 要以初始图片大小为准,这里的初始图片是扩大二倍后的图片 Keypoint.octave = OctaveIndex + ImgIndex * (2**8) + np.int( np.round( (CoordinatesUpdateValueOfKeyPoint[2]) * 255)) * (2**16) # Keypoint.octave 0-7位保存Octave,8-15位保存ImgIndex, 16-23位保存sigma,防止在后面用Sigma返过来求s时,s直接取整会出错 Keypoint.size = Sigma0 * (2**( (ImgIndex + CoordinatesUpdateValueOfKeyPoint[2]) / np.float32(NumOfIntervals))) * (2**(OctaveIndex + 1)) # Keypoint.size 这里的初始图片是扩大二倍后的图片,各个Sigma要多乘一个2, 所以OctaveIndex + 1 Keypoint.response = abs(NewKeyPointValue) # 为什么不直接从初始图片开始算?SIFT关键点主要在-1组,具体在报告中介绍 return Keypoint, int(ImgIndex + CoordinatesUpdateValueOfKeyPoint[2]) return None
def localize(self, x, y, top, center, bottom, index_img, index_octv, octv): outside_image = False img_shape = center.shape num_intervals = len(octv) + 1 for attempt in range(self.params["convergence_attempts"] + 1): cube = stack([ bottom[x - 1:x + 2, y - 1:y + 2], center[x - 1:x + 2, y - 1:y + 2], top[x - 1:x + 2, y - 1:y + 2] ]).astype('float32') / 255 update = -lstsq(LocateKeypoints.getHessian(cube), LocateKeypoints.getGrad(cube), rcond=None)[0] if all(update < 0.5): break x += int(round(update[0])) y += int(round(update[1])) index_octv += int(round(update[2])) image_border_width = self.params["image_border_width"] if (x < image_border_width or x >= img_shape[0] - image_border_width or y < image_border_width or y >= img_shape[1] - image_border_width or index_img < 1 or index_img >= num_intervals - 1): outside_image = True break top, center, bottom = octv[index_img + 1], octv[index_img], octv[index_img - 1] if outside_image: logger.debug( f'Updated extremum {x},{y} moved outside of image before reaching convergence. Skipping...' ) return None if attempt == self.params["convergence_attempts"]: logger.debug(f'The extremum {x},{y} did not converge. Skipping...') return None functionValueAtUpdatedExtremum = cube[1, 1, 1] + 0.5 * dot( LocateKeypoints.getGrad(cube), update) eigenvalue_ratio = self.params["eigenvalue_ratio"] if abs(functionValueAtUpdatedExtremum ) * num_intervals >= self.params["contrast_threshold"]: xy_hessian = LocateKeypoints.getHessian(cube)[:2, :2] xy_hessian_trace = trace(xy_hessian) xy_hessian_det = det(xy_hessian) if xy_hessian_det > 0 and eigenvalue_ratio * ( xy_hessian_trace**2) < ( (eigenvalue_ratio + 1)**2) * xy_hessian_det: # Contrast check passed -- construct and return OpenCV KeyPoint object keypoint = KeyPoint() keypoint.pt = ((y + update[0]) * (2**index_octv), (x + update[1]) * (2**index_octv)) keypoint.octave = index_octv + index_img * (2**8) + int( round((update[2] + 0.5) * 255)) * (2**16) keypoint.size = self.params["sigma"] * (2**( (index_img + update[2]) / float32(num_intervals))) * ( 2**(index_octv + 1) ) # octave_index + 1 because the input image was doubled keypoint.response = abs(functionValueAtUpdatedExtremum) return keypoint, index_img, index_octv return None