Implement global motion parameter computation

This computes global motion parameters between 2 frames by
matching corresponding points using FAST feature and then
fitting a model using RANSAC.

Change-Id: Ib6664df44090e8cfa4db9f2f9e0556931ccfe5c8
This commit is contained in:
Sarah Parker 2016-08-09 17:40:53 -07:00
Родитель 162f5f792b
Коммит 4dc0f1b186
20 изменённых файлов: 7656 добавлений и 91 удалений

30
third_party/fastfeat/LICENSE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

34
third_party/fastfeat/README поставляемый Normal file
Просмотреть файл

@ -0,0 +1,34 @@
This code was taken from http://www.edwardrosten.com/work/fast.html. Fast 10, 11, and 12
have been deleted.
FAST feature detectors in C Version 2.0
---------------------------------------
The files are valid C and C++ code, and have no special requirements for
compiling, and they do not depend on any libraries. Just compile them along with
the rest of your project.
To use the functions, #include "fast.h"
The corner detectors have the following prototype (where X is 9, 10, 11 or 12):
xy* fastX_detect_nonmax(const unsigned char * data, int xsize, int ysize, int stride, int threshold, int* numcorners)
Where xy is the following simple struct typedef:
typedef struct
{
int x, y;
} xy;
The image is passed in as a block of data and dimensions, and the list of
corners is returned as an array of xy structs, and an integer (numcorners)
with the number of corners returned. The data can be deallocated with free().
Nonmaximal suppression is performed on the corners. Note that the stride
is the number of bytes between rows. If your image has no padding, then this
is the same as xsize.
The detection, scoring and nonmaximal suppression are available as individual
functions. To see how to use the individual functions, see fast.c

22
third_party/fastfeat/fast.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,22 @@
// clang-format off
#include <stdlib.h>
#include "fast.h"
xy* fast9_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners)
{
xy* corners;
int num_corners;
int* scores;
xy* nonmax;
corners = fast9_detect(im, xsize, ysize, stride, b, &num_corners);
scores = fast9_score(im, stride, corners, num_corners, b);
nonmax = nonmax_suppression(corners, scores, num_corners, ret_num_corners);
free(corners);
free(scores);
return nonmax;
}
// clang-format on

20
third_party/fastfeat/fast.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
// clang-format off
#ifndef FAST_H
#define FAST_H
typedef struct { int x, y; } xy;
typedef unsigned char byte;
int fast9_corner_score(const byte* p, const int pixel[], int bstart);
xy* fast9_detect(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
int* fast9_score(const byte* i, int stride, xy* corners, int num_corners, int b);
xy* fast9_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* nonmax_suppression(const xy* corners, const int* scores, int num_corners, int* ret_num_nonmax);
#endif
// clang-format on

5911
third_party/fastfeat/fast_9.c поставляемый Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

119
third_party/fastfeat/nonmax.c поставляемый Normal file
Просмотреть файл

@ -0,0 +1,119 @@
// clang-format off
#include <stdlib.h>
#include "fast.h"
#define Compare(X, Y) ((X)>=(Y))
xy* nonmax_suppression(const xy* corners, const int* scores, int num_corners, int* ret_num_nonmax)
{
int num_nonmax=0;
int last_row;
int* row_start;
int i, j;
xy* ret_nonmax;
const int sz = (int)num_corners;
/*Point above points (roughly) to the pixel above the one of interest, if there
is a feature there.*/
int point_above = 0;
int point_below = 0;
if(num_corners < 1)
{
*ret_num_nonmax = 0;
return 0;
}
ret_nonmax = (xy*)malloc(num_corners * sizeof(xy));
/* Find where each row begins
(the corners are output in raster scan order). A beginning of -1 signifies
that there are no corners on that row. */
last_row = corners[num_corners-1].y;
row_start = (int*)malloc((last_row+1)*sizeof(int));
for(i=0; i < last_row+1; i++)
row_start[i] = -1;
{
int prev_row = -1;
for(i=0; i< num_corners; i++)
if(corners[i].y != prev_row)
{
row_start[corners[i].y] = i;
prev_row = corners[i].y;
}
}
for(i=0; i < sz; i++)
{
int score = scores[i];
xy pos = corners[i];
/*Check left */
if(i > 0)
if(corners[i-1].x == pos.x-1 && corners[i-1].y == pos.y && Compare(scores[i-1], score))
continue;
/*Check right*/
if(i < (sz - 1))
if(corners[i+1].x == pos.x+1 && corners[i+1].y == pos.y && Compare(scores[i+1], score))
continue;
/*Check above (if there is a valid row above)*/
if(pos.y != 0 && row_start[pos.y - 1] != -1)
{
/*Make sure that current point_above is one
row above.*/
if(corners[point_above].y < pos.y - 1)
point_above = row_start[pos.y-1];
/*Make point_above point to the first of the pixels above the current point,
if it exists.*/
for(; corners[point_above].y < pos.y && corners[point_above].x < pos.x - 1; point_above++)
{}
for(j=point_above; corners[j].y < pos.y && corners[j].x <= pos.x + 1; j++)
{
int x = corners[j].x;
if( (x == pos.x - 1 || x ==pos.x || x == pos.x+1) && Compare(scores[j], score))
goto cont;
}
}
/*Check below (if there is anything below)*/
if(pos.y != last_row && row_start[pos.y + 1] != -1 && point_below < sz) /*Nothing below*/
{
if(corners[point_below].y < pos.y + 1)
point_below = row_start[pos.y+1];
/* Make point below point to one of the pixels belowthe current point, if it
exists.*/
for(; point_below < sz && corners[point_below].y == pos.y+1 && corners[point_below].x < pos.x - 1; point_below++)
{}
for(j=point_below; j < sz && corners[j].y == pos.y+1 && corners[j].x <= pos.x + 1; j++)
{
int x = corners[j].x;
if( (x == pos.x - 1 || x ==pos.x || x == pos.x+1) && Compare(scores[j],score))
goto cont;
}
}
ret_nonmax[num_nonmax++] = corners[i];
cont:
;
}
free(row_start);
*ret_num_nonmax = num_nonmax;
return ret_nonmax;
}
// clang-format on

Просмотреть файл

@ -52,7 +52,7 @@ typedef struct mv32 {
// for each parameter. In other words, after a parameter is integerized
// it is clamped between -(1 << ABS_XXX_BITS) and (1 << ABS_XXX_BITS).
//
// XXX_PREC_DIFF, XXX_ENCODE_FACTOR and XXX_DECODE_FACTOR
// XXX_PREC_DIFF and XXX_DECODE_FACTOR
// are computed once here to prevent repetitive
// computation on the decoder side. These are
// to allow the global motion parameters to be encoded in a lower
@ -64,12 +64,10 @@ typedef struct mv32 {
#define GM_TRANS_PREC_BITS 5
#define GM_TRANS_PREC_DIFF (WARPEDMODEL_PREC_BITS - GM_TRANS_PREC_BITS)
#define GM_TRANS_DECODE_FACTOR (1 << GM_TRANS_PREC_DIFF)
#define GM_TRANS_ENCODE_FACTOR (1 / (GM_TRANS_DECODE_FACTOR))
#define GM_ALPHA_PREC_BITS 5
#define GM_ALPHA_PREC_DIFF (WARPEDMODEL_PREC_BITS - GM_ALPHA_PREC_BITS)
#define GM_ALPHA_DECODE_FACTOR (1 << GM_ALPHA_PREC_DIFF)
#define GM_ALPHA_ENCODE_FACTOR (1 / (GM_ALPHA_DECODE_FACTOR))
#define GM_ABS_ALPHA_BITS 8
#define GM_ABS_TRANS_BITS 8

Просмотреть файл

@ -16,32 +16,7 @@
#include "vp10/common/warped_motion.h"
typedef void (*projectPointsType)(int *mat, int *points, int *proj, const int n,
const int stride_points,
const int stride_proj,
const int subsampling_x,
const int subsampling_y);
static void projectPointsHomography(int *mat, int *points, int *proj,
const int n, const int stride_points,
const int stride_proj,
const int subsampling_x,
const int subsampling_y);
static void projectPointsAffine(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x,
const int subsampling_y);
static void projectPointsRotZoom(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x,
const int subsampling_y);
static void projectPointsTranslation(int *mat, int *points, int *proj,
const int n, const int stride_points,
const int stride_proj,
const int subsampling_x,
const int subsampling_y);
static projectPointsType get_projectPointsType(TransformationType type) {
static ProjectPointsType get_project_points_type(TransformationType type) {
switch (type) {
case HOMOGRAPHY: return projectPointsHomography;
case AFFINE: return projectPointsAffine;
@ -51,11 +26,10 @@ static projectPointsType get_projectPointsType(TransformationType type) {
}
}
static void projectPointsTranslation(int *mat, int *points, int *proj,
const int n, const int stride_points,
const int stride_proj,
const int subsampling_x,
const int subsampling_y) {
void projectPointsTranslation(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x,
const int subsampling_y) {
int i;
for (i = 0; i < n; ++i) {
const int x = *(points++), y = *(points++);
@ -105,10 +79,9 @@ void projectPointsRotZoom(int *mat, int *points, int *proj, const int n,
}
}
static void projectPointsAffine(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x,
const int subsampling_y) {
void projectPointsAffine(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x, const int subsampling_y) {
int i;
for (i = 0; i < n; ++i) {
const int x = *(points++), y = *(points++);
@ -133,11 +106,9 @@ static void projectPointsAffine(int *mat, int *points, int *proj, const int n,
}
}
static void projectPointsHomography(int *mat, int *points, int *proj,
const int n, const int stride_points,
const int stride_proj,
const int subsampling_x,
const int subsampling_y) {
void projectPointsHomography(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x, const int subsampling_y) {
int i;
int64_t x, y, Z;
int64_t xp, yp;
@ -382,28 +353,6 @@ static uint8_t warp_interpolate(uint8_t *ref, int x, int y, int width,
}
}
static void warp_plane(WarpedMotionParams *wm, uint8_t *ref, int width,
int height, int stride, uint8_t *pred, int p_col,
int p_row, int p_width, int p_height, int p_stride,
int subsampling_x, int subsampling_y, int x_scale,
int y_scale) {
int i, j;
projectPointsType projectPoints = get_projectPointsType(wm->wmtype);
if (projectPoints == NULL) return;
for (i = p_row; i < p_row + p_height; ++i) {
for (j = p_col; j < p_col + p_width; ++j) {
int in[2], out[2];
in[0] = j;
in[1] = i;
projectPoints(wm->wmmat, in, out, 1, 2, 2, subsampling_x, subsampling_y);
out[0] = ROUND_POWER_OF_TWO_SIGNED(out[0] * x_scale, 4);
out[1] = ROUND_POWER_OF_TWO_SIGNED(out[1] * y_scale, 4);
pred[(j - p_col) + (i - p_row) * p_stride] =
warp_interpolate(ref, out[0], out[1], width, height, stride);
}
}
}
#if CONFIG_VP9_HIGHBITDEPTH
static INLINE void highbd_get_subcolumn(int taps, uint16_t *ref, int32_t *col,
int stride, int x, int y_start) {
@ -517,6 +466,38 @@ static uint16_t highbd_warp_interpolate(uint16_t *ref, int x, int y, int width,
}
}
static double highbd_warp_erroradv(WarpedMotionParams *wm, uint8_t *ref8,
int width, int height, int stride,
uint8_t *dst8, int p_col, int p_row,
int p_width, int p_height, int p_stride,
int subsampling_x, int subsampling_y,
int x_scale, int y_scale, int bd) {
int i, j;
ProjectPointsType projectpoints = get_project_points_type(wm->wmtype);
uint16_t *dst = CONVERT_TO_SHORTPTR(dst8);
uint16_t *ref = CONVERT_TO_SHORTPTR(ref8);
int gm_err = 0, no_gm_err = 0;
int gm_sumerr = 0, no_gm_sumerr = 0;
for (i = p_row; i < p_row + p_height; ++i) {
for (j = p_col; j < p_col + p_width; ++j) {
int in[2], out[2];
in[0] = j;
in[1] = i;
projectpoints(wm->wmmat, in, out, 1, 2, 2, subsampling_x, subsampling_y);
out[0] = ROUND_POWER_OF_TWO_SIGNED(out[0] * x_scale, 4);
out[1] = ROUND_POWER_OF_TWO_SIGNED(out[1] * y_scale, 4);
gm_err = dst[(j - p_col) + (i - p_row) * p_stride] -
highbd_warp_interpolate(ref, out[0], out[1], width, height,
stride, bd);
no_gm_err = dst[(j - p_col) + (i - p_row) * p_stride] -
ref[(j - p_col) + (i - p_row) * stride];
gm_sumerr += gm_err * gm_err;
no_gm_sumerr += no_gm_err * no_gm_err;
}
}
return (double)gm_sumerr / no_gm_sumerr;
}
static void highbd_warp_plane(WarpedMotionParams *wm, uint8_t *ref8, int width,
int height, int stride, uint8_t *pred8, int p_col,
int p_row, int p_width, int p_height,
@ -524,16 +505,16 @@ static void highbd_warp_plane(WarpedMotionParams *wm, uint8_t *ref8, int width,
int subsampling_y, int x_scale, int y_scale,
int bd) {
int i, j;
projectPointsType projectPoints = get_projectPointsType(wm->wmtype);
ProjectPointsType projectpoints = get_project_points_type(wm->wmtype);
uint16_t *pred = CONVERT_TO_SHORTPTR(pred8);
uint16_t *ref = CONVERT_TO_SHORTPTR(ref8);
if (projectPoints == NULL) return;
if (projectpoints == NULL) return;
for (i = p_row; i < p_row + p_height; ++i) {
for (j = p_col; j < p_col + p_width; ++j) {
int in[2], out[2];
in[0] = j;
in[1] = i;
projectPoints(wm->wmmat, in, out, 1, 2, 2, subsampling_x, subsampling_y);
projectpoints(wm->wmmat, in, out, 1, 2, 2, subsampling_x, subsampling_y);
out[0] = ROUND_POWER_OF_TWO_SIGNED(out[0] * x_scale, 4);
out[1] = ROUND_POWER_OF_TWO_SIGNED(out[1] * y_scale, 4);
pred[(j - p_col) + (i - p_row) * p_stride] = highbd_warp_interpolate(
@ -543,6 +524,76 @@ static void highbd_warp_plane(WarpedMotionParams *wm, uint8_t *ref8, int width,
}
#endif // CONFIG_VP9_HIGHBITDEPTH
static double warp_erroradv(WarpedMotionParams *wm, uint8_t *ref, int width,
int height, int stride, uint8_t *dst, int p_col,
int p_row, int p_width, int p_height, int p_stride,
int subsampling_x, int subsampling_y, int x_scale,
int y_scale) {
int gm_err = 0, no_gm_err = 0;
int gm_sumerr = 0, no_gm_sumerr = 0;
int i, j;
ProjectPointsType projectpoints = get_project_points_type(wm->wmtype);
for (i = p_row; i < p_row + p_height; ++i) {
for (j = p_col; j < p_col + p_width; ++j) {
int in[2], out[2];
in[0] = j;
in[1] = i;
projectpoints(wm->wmmat, in, out, 1, 2, 2, subsampling_x, subsampling_y);
out[0] = ROUND_POWER_OF_TWO_SIGNED(out[0] * x_scale, 4);
out[1] = ROUND_POWER_OF_TWO_SIGNED(out[1] * y_scale, 4);
gm_err = dst[(j - p_col) + (i - p_row) * p_stride] -
warp_interpolate(ref, out[0], out[1], width, height, stride);
no_gm_err = dst[(j - p_col) + (i - p_row) * p_stride] -
ref[(j - p_col) + (i - p_row) * stride];
gm_sumerr += gm_err * gm_err;
no_gm_sumerr += no_gm_err * no_gm_err;
}
}
return (double)gm_sumerr / no_gm_sumerr;
}
static void warp_plane(WarpedMotionParams *wm, uint8_t *ref, int width,
int height, int stride, uint8_t *pred, int p_col,
int p_row, int p_width, int p_height, int p_stride,
int subsampling_x, int subsampling_y, int x_scale,
int y_scale) {
int i, j;
ProjectPointsType projectpoints = get_project_points_type(wm->wmtype);
if (projectpoints == NULL) return;
for (i = p_row; i < p_row + p_height; ++i) {
for (j = p_col; j < p_col + p_width; ++j) {
int in[2], out[2];
in[0] = j;
in[1] = i;
projectpoints(wm->wmmat, in, out, 1, 2, 2, subsampling_x, subsampling_y);
out[0] = ROUND_POWER_OF_TWO_SIGNED(out[0] * x_scale, 4);
out[1] = ROUND_POWER_OF_TWO_SIGNED(out[1] * y_scale, 4);
pred[(j - p_col) + (i - p_row) * p_stride] =
warp_interpolate(ref, out[0], out[1], width, height, stride);
}
}
}
double vp10_warp_erroradv(WarpedMotionParams *wm,
#if CONFIG_VP9_HIGHBITDEPTH
int use_hbd, int bd,
#endif // CONFIG_VP9_HIGHBITDEPTH
uint8_t *ref, int width, int height, int stride,
uint8_t *dst, int p_col, int p_row, int p_width,
int p_height, int p_stride, int subsampling_x,
int subsampling_y, int x_scale, int y_scale) {
#if CONFIG_VP9_HIGHBITDEPTH
if (use_hbd)
return highbd_warp_erroradv(
wm, ref, width, height, stride, dst, p_col, p_row, p_width, p_height,
p_stride, subsampling_x, subsampling_y, x_scale, y_scale, bd);
else
#endif // CONFIG_VP9_HIGHBITDEPTH
return warp_erroradv(wm, ref, width, height, stride, dst, p_col, p_row,
p_width, p_height, p_stride, subsampling_x,
subsampling_y, x_scale, y_scale);
}
void vp10_warp_plane(WarpedMotionParams *wm,
#if CONFIG_VP9_HIGHBITDEPTH
int use_hbd, int bd,

Просмотреть файл

@ -37,6 +37,24 @@
#define WARPEDDIFF_PREC_BITS (WARPEDMODEL_PREC_BITS - WARPEDPIXEL_PREC_BITS)
typedef void (*ProjectPointsType)(int *mat, int *points, int *proj, const int n,
const int stride_points,
const int stride_proj,
const int subsampling_x,
const int subsampling_y);
void projectPointsHomography(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x, const int subsampling_y);
void projectPointsAffine(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x, const int subsampling_y);
void projectPointsRotZoom(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x, const int subsampling_y);
void projectPointsTranslation(int *mat, int *points, int *proj, const int n,
const int stride_points, const int stride_proj,
const int subsampling_x, const int subsampling_y);
typedef enum {
UNKNOWN_TRANSFORM = -1,
HOMOGRAPHY, // homography, 8-parameter
@ -54,6 +72,15 @@ typedef struct {
int wmmat[8]; // For homography wmmat[9] is assumed to be 1
} WarpedMotionParams;
double vp10_warp_erroradv(WarpedMotionParams *wm,
#if CONFIG_VP9_HIGHBITDEPTH
int use_hbd, int bd,
#endif // CONFIG_VP9_HIGHBITDEPTH
uint8_t *ref, int width, int height, int stride,
uint8_t *dst, int p_col, int p_row, int p_width,
int p_height, int p_stride, int subsampling_x,
int subsampling_y, int x_scale, int y_scale);
void vp10_warp_plane(WarpedMotionParams *wm,
#if CONFIG_VP9_HIGHBITDEPTH
int use_hbd, int bd,

Просмотреть файл

@ -3203,28 +3203,28 @@ static void write_global_motion_params(Global_Motion_Params *params,
case GLOBAL_ZERO: break;
case GLOBAL_AFFINE:
vp10_write_primitive_symmetric(
w, params->motion_params.wmmat[4] * GM_ALPHA_ENCODE_FACTOR,
w, params->motion_params.wmmat[4] >> GM_ALPHA_PREC_DIFF,
GM_ABS_ALPHA_BITS);
vp10_write_primitive_symmetric(
w, (params->motion_params.wmmat[5] * GM_ALPHA_ENCODE_FACTOR) -
w, (params->motion_params.wmmat[5] >> GM_ALPHA_PREC_DIFF) -
(1 << GM_ALPHA_PREC_BITS),
GM_ABS_ALPHA_BITS);
// fallthrough intended
case GLOBAL_ROTZOOM:
vp10_write_primitive_symmetric(
w, (params->motion_params.wmmat[2] * GM_ALPHA_ENCODE_FACTOR) -
w, (params->motion_params.wmmat[2] >> GM_ALPHA_PREC_DIFF) -
(1 << GM_ALPHA_PREC_BITS),
GM_ABS_ALPHA_BITS);
vp10_write_primitive_symmetric(
w, params->motion_params.wmmat[3] * GM_ALPHA_ENCODE_FACTOR,
w, params->motion_params.wmmat[3] >> GM_ALPHA_PREC_DIFF,
GM_ABS_ALPHA_BITS);
// fallthrough intended
case GLOBAL_TRANSLATION:
vp10_write_primitive_symmetric(
w, params->motion_params.wmmat[0] * GM_TRANS_ENCODE_FACTOR,
w, params->motion_params.wmmat[0] >> GM_TRANS_PREC_DIFF,
GM_ABS_TRANS_BITS);
vp10_write_primitive_symmetric(
w, params->motion_params.wmmat[1] * GM_TRANS_ENCODE_FACTOR,
w, params->motion_params.wmmat[1] >> GM_TRANS_PREC_DIFF,
GM_ABS_TRANS_BITS);
break;
default: assert(0);

Просмотреть файл

@ -0,0 +1,35 @@
/*
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <assert.h>
#include "vp10/encoder/corner_detect.h"
#include "third_party/fastfeat/fast.h"
// Fast_9 wrapper
#define FAST_BARRIER 40
int FastCornerDetect(unsigned char *buf, int width, int height, int stride,
int *points, int max_points) {
int num_points;
xy *frm_corners_xy = fast9_detect_nonmax(buf, width, height, stride,
FAST_BARRIER, &num_points);
num_points = (num_points <= max_points ? num_points : max_points);
if (num_points > 0 && frm_corners_xy) {
memcpy(points, frm_corners_xy, sizeof(xy) * num_points);
free(frm_corners_xy);
return num_points;
} else {
return 0;
}
}

Просмотреть файл

@ -0,0 +1,21 @@
/*
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef VP10_ENCODER_CORNER_DETECT_H_
#define VP10_ENCODER_CORNER_DETECT_H_
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int FastCornerDetect(unsigned char *buf, int width, int height, int stride,
int *points, int max_points);
#endif // VP10_ENCODER_CORNER_DETECT_H

210
vp10/encoder/corner_match.c Normal file
Просмотреть файл

@ -0,0 +1,210 @@
/*
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <math.h>
#include "vp10/encoder/corner_match.h"
#define MATCH_SZ 15
#define MATCH_SZ_BY2 ((MATCH_SZ - 1) / 2)
#define MATCH_SZ_SQ (MATCH_SZ * MATCH_SZ)
#define SEARCH_SZ 9
#define SEARCH_SZ_BY2 ((SEARCH_SZ - 1) / 2)
#define THRESHOLD_NCC 0.80
static double compute_variance(unsigned char *im, int stride, int x, int y,
double *mean) {
double sum = 0.0;
double sumsq = 0.0;
double var;
int i, j;
for (i = 0; i < MATCH_SZ; ++i)
for (j = 0; j < MATCH_SZ; ++j) {
sum += im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)];
sumsq += im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)] *
im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)];
}
var = (sumsq * MATCH_SZ_SQ - sum * sum) / (MATCH_SZ_SQ * MATCH_SZ_SQ);
if (mean) *mean = sum / MATCH_SZ_SQ;
return var;
}
static double compute_cross_correlation(unsigned char *im1, int stride1, int x1,
int y1, unsigned char *im2, int stride2,
int x2, int y2) {
double sum1 = 0;
double sum2 = 0;
double cross = 0;
double corr;
int i, j;
for (i = 0; i < MATCH_SZ; ++i)
for (j = 0; j < MATCH_SZ; ++j) {
sum1 += im1[(i + y1 - MATCH_SZ_BY2) * stride1 + (j + x1 - MATCH_SZ_BY2)];
sum2 += im2[(i + y2 - MATCH_SZ_BY2) * stride2 + (j + x2 - MATCH_SZ_BY2)];
cross +=
im1[(i + y1 - MATCH_SZ_BY2) * stride1 + (j + x1 - MATCH_SZ_BY2)] *
im2[(i + y2 - MATCH_SZ_BY2) * stride2 + (j + x2 - MATCH_SZ_BY2)];
}
corr = (cross * MATCH_SZ_SQ - sum1 * sum2) / (MATCH_SZ_SQ * MATCH_SZ_SQ);
return corr;
}
static int is_eligible_point(double pointx, double pointy, int width,
int height) {
return (pointx >= MATCH_SZ_BY2 && pointy >= MATCH_SZ_BY2 &&
pointx + MATCH_SZ_BY2 < width && pointy + MATCH_SZ_BY2 < height);
}
static int is_eligible_distance(double point1x, double point1y, double point2x,
double point2y, int width, int height) {
const int thresh = (width < height ? height : width) >> 4;
return ((point1x - point2x) * (point1x - point2x) +
(point1y - point2y) * (point1y - point2y)) <= thresh * thresh;
}
static void improve_correspondence(unsigned char *frm, unsigned char *ref,
int width, int height, int frm_stride,
int ref_stride,
correspondence *correspondences,
int num_correspondences) {
int i;
for (i = 0; i < num_correspondences; ++i) {
double template_norm =
compute_variance(frm, frm_stride, (int)correspondences[i].x,
(int)correspondences[i].y, NULL);
int x, y, best_x = 0, best_y = 0;
double best_match_ncc = 0.0;
for (y = -SEARCH_SZ_BY2; y <= SEARCH_SZ_BY2; ++y) {
for (x = -SEARCH_SZ_BY2; x <= SEARCH_SZ_BY2; ++x) {
double match_ncc;
double subimage_norm;
if (!is_eligible_point((int)correspondences[i].rx + x,
(int)correspondences[i].ry + y, width, height))
continue;
if (!is_eligible_distance(
(int)correspondences[i].x, (int)correspondences[i].y,
(int)correspondences[i].rx + x, (int)correspondences[i].ry + y,
width, height))
continue;
subimage_norm =
compute_variance(ref, ref_stride, (int)correspondences[i].rx + x,
(int)correspondences[i].ry + y, NULL);
match_ncc = compute_cross_correlation(
frm, frm_stride, (int)correspondences[i].x,
(int)correspondences[i].y, ref, ref_stride,
(int)correspondences[i].rx + x,
(int)correspondences[i].ry + y) /
sqrt(template_norm * subimage_norm);
if (match_ncc > best_match_ncc) {
best_match_ncc = match_ncc;
best_y = y;
best_x = x;
}
}
}
correspondences[i].rx += (double)best_x;
correspondences[i].ry += (double)best_y;
}
for (i = 0; i < num_correspondences; ++i) {
double template_norm =
compute_variance(ref, ref_stride, (int)correspondences[i].rx,
(int)correspondences[i].ry, NULL);
int x, y, best_x = 0, best_y = 0;
double best_match_ncc = 0.0;
for (y = -SEARCH_SZ_BY2; y <= SEARCH_SZ_BY2; ++y)
for (x = -SEARCH_SZ_BY2; x <= SEARCH_SZ_BY2; ++x) {
double match_ncc;
double subimage_norm;
if (!is_eligible_point((int)correspondences[i].x + x,
(int)correspondences[i].y + y, width, height))
continue;
if (!is_eligible_distance((int)correspondences[i].x + x,
(int)correspondences[i].y + y,
(int)correspondences[i].rx,
(int)correspondences[i].ry, width, height))
continue;
subimage_norm =
compute_variance(frm, frm_stride, (int)correspondences[i].x + x,
(int)correspondences[i].y + y, NULL);
match_ncc =
compute_cross_correlation(
frm, frm_stride, (int)correspondences[i].x + x,
(int)correspondences[i].y + y, ref, ref_stride,
(int)correspondences[i].rx, (int)correspondences[i].ry) /
sqrt(template_norm * subimage_norm);
if (match_ncc > best_match_ncc) {
best_match_ncc = match_ncc;
best_y = y;
best_x = x;
}
}
correspondences[i].x += best_x;
correspondences[i].y += best_y;
}
}
int determine_correspondence(unsigned char *frm, int *frm_corners,
int num_frm_corners, unsigned char *ref,
int *ref_corners, int num_ref_corners, int width,
int height, int frm_stride, int ref_stride,
double *correspondence_pts) {
// TODO(sarahparker) Improve this to include 2-way match
int i, j;
correspondence *correspondences = (correspondence *)correspondence_pts;
int num_correspondences = 0;
for (i = 0; i < num_frm_corners; ++i) {
double best_match_ncc = 0.0;
double template_norm;
int best_match_j = -1;
if (!is_eligible_point(frm_corners[2 * i], frm_corners[2 * i + 1], width,
height))
continue;
template_norm = compute_variance(frm, frm_stride, frm_corners[2 * i],
frm_corners[2 * i + 1], NULL);
for (j = 0; j < num_ref_corners; ++j) {
double match_ncc;
double subimage_norm;
if (!is_eligible_point(ref_corners[2 * j], ref_corners[2 * j + 1], width,
height))
continue;
if (!is_eligible_distance(frm_corners[2 * i], frm_corners[2 * i + 1],
ref_corners[2 * j], ref_corners[2 * j + 1],
width, height))
continue;
subimage_norm = compute_variance(ref, ref_stride, ref_corners[2 * j],
ref_corners[2 * j + 1], NULL);
match_ncc = compute_cross_correlation(frm, frm_stride, frm_corners[2 * i],
frm_corners[2 * i + 1], ref,
ref_stride, ref_corners[2 * j],
ref_corners[2 * j + 1]) /
sqrt(template_norm * subimage_norm);
if (match_ncc > best_match_ncc) {
best_match_ncc = match_ncc;
best_match_j = j;
}
}
if (best_match_ncc > THRESHOLD_NCC) {
correspondences[num_correspondences].x = (double)frm_corners[2 * i];
correspondences[num_correspondences].y = (double)frm_corners[2 * i + 1];
correspondences[num_correspondences].rx =
(double)ref_corners[2 * best_match_j];
correspondences[num_correspondences].ry =
(double)ref_corners[2 * best_match_j + 1];
num_correspondences++;
}
}
improve_correspondence(frm, ref, width, height, frm_stride, ref_stride,
correspondences, num_correspondences);
return num_correspondences;
}

Просмотреть файл

@ -0,0 +1,29 @@
/*
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef VP10_ENCODER_CORNER_MATCH_H_
#define VP10_ENCODER_CORNER_MATCH_H_
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
typedef struct {
double x, y;
double rx, ry;
} correspondence;
int determine_correspondence(unsigned char *frm, int *frm_corners,
int num_frm_corners, unsigned char *ref,
int *ref_corners, int num_ref_corners, int width,
int height, int frm_stride, int ref_stride,
double *correspondence_pts);
#endif // VP10_ENCODER_CORNER_MATCH_H

Просмотреть файл

@ -4407,6 +4407,7 @@ static int input_fpmb_stats(FIRSTPASS_MB_STATS *firstpass_mb_stats,
#if CONFIG_GLOBAL_MOTION
#define MIN_TRANS_THRESH 8
#define GLOBAL_MOTION_ADVANTAGE_THRESH 0.60
#define GLOBAL_MOTION_MODEL ROTZOOM
static void convert_to_params(double *H, TransformationType type,
Global_Motion_Params *model) {
@ -4472,20 +4473,36 @@ static void encode_frame_internal(VP10_COMP *cpi) {
rdc->ex_search_count = 0; // Exhaustive mesh search hits.
#if CONFIG_GLOBAL_MOTION
// TODO(sarahparker) this is a placeholder for gm computation
vpx_clear_system_state();
vp10_zero(cpi->global_motion_used);
if (cpi->common.frame_type == INTER_FRAME && cpi->Source) {
YV12_BUFFER_CONFIG *ref_buf;
int frame;
double H[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 1 };
double H[9] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
for (frame = LAST_FRAME; frame <= ALTREF_FRAME; ++frame) {
ref_buf = get_ref_frame_buffer(cpi, frame);
if (ref_buf) {
if (compute_global_motion_feature_based(cpi, GLOBAL_MOTION_MODEL,
cpi->Source, ref_buf, 0.5, H))
if (compute_global_motion_feature_based(GLOBAL_MOTION_MODEL,
cpi->Source, ref_buf, H)) {
convert_model_to_params(H, GLOBAL_MOTION_MODEL,
&cm->global_motion[frame]);
if (get_gmtype(&cm->global_motion[frame]) > GLOBAL_ZERO) {
// compute the advantage of using gm parameters over 0 motion
double erroradvantage = vp10_warp_erroradv(
&cm->global_motion[frame].motion_params,
#if CONFIG_VP9_HIGHBITDEPTH
xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH, xd->bd,
#endif // CONFIG_VP9_HIGHBITDEPTH
ref_buf->y_buffer, ref_buf->y_width, ref_buf->y_height,
ref_buf->y_stride, cpi->Source->y_buffer, 0, 0,
cpi->Source->y_width, cpi->Source->y_height,
cpi->Source->y_stride, 0, 0, 16, 16);
if (erroradvantage > GLOBAL_MOTION_ADVANTAGE_THRESH)
// Not enough advantage in using a global model. Make 0.
memset(&cm->global_motion[frame], 0,
sizeof(cm->global_motion[frame]));
}
}
}
}
}

Просмотреть файл

@ -18,17 +18,71 @@
#include "vp10/encoder/segmentation.h"
#include "vp10/encoder/global_motion.h"
#include "vp10/encoder/corner_detect.h"
#include "vp10/encoder/corner_match.h"
#include "vp10/encoder/ransac.h"
int compute_global_motion_feature_based(struct VP10_COMP *cpi,
TransformationType type,
#define MAX_CORNERS 4096
#define MIN_INLIER_PROB 0.1
INLINE RansacType get_ransac_type(TransformationType type) {
switch (type) {
case HOMOGRAPHY: return ransacHomography;
case AFFINE: return ransacAffine;
case ROTZOOM: return ransacRotZoom;
case TRANSLATION: return ransacTranslation;
default: assert(0); return NULL;
}
}
// computes global motion parameters by fitting a model using RANSAC
static int compute_global_motion_params(TransformationType type,
double *correspondences,
int num_correspondences, double *params,
int *inlier_map) {
int result;
int num_inliers = 0;
RansacType ransac = get_ransac_type(type);
if (ransac == NULL) return 0;
result = ransac(correspondences, num_correspondences, &num_inliers,
inlier_map, params);
if (!result && num_inliers < MIN_INLIER_PROB * num_correspondences) {
result = 1;
num_inliers = 0;
}
return num_inliers;
}
int compute_global_motion_feature_based(TransformationType type,
YV12_BUFFER_CONFIG *frm,
YV12_BUFFER_CONFIG *ref,
double inlier_prob, double *H) {
(void)cpi;
(void)type;
(void)frm;
(void)ref;
(void)inlier_prob;
(void)H;
return 0;
double *params) {
int num_frm_corners, num_ref_corners;
int num_correspondences;
double *correspondences;
int num_inliers;
int frm_corners[2 * MAX_CORNERS], ref_corners[2 * MAX_CORNERS];
int *inlier_map = NULL;
// compute interest points in images using FAST features
num_frm_corners = FastCornerDetect(frm->y_buffer, frm->y_width, frm->y_height,
frm->y_stride, frm_corners, MAX_CORNERS);
num_ref_corners = FastCornerDetect(ref->y_buffer, ref->y_width, ref->y_height,
ref->y_stride, ref_corners, MAX_CORNERS);
// find correspondences between the two images
correspondences =
(double *)malloc(num_frm_corners * 4 * sizeof(*correspondences));
num_correspondences = determine_correspondence(
frm->y_buffer, (int *)frm_corners, num_frm_corners, ref->y_buffer,
(int *)ref_corners, num_ref_corners, frm->y_width, frm->y_height,
frm->y_stride, ref->y_stride, correspondences);
inlier_map = (int *)malloc(num_correspondences * sizeof(*inlier_map));
num_inliers = compute_global_motion_params(
type, correspondences, num_correspondences, params, inlier_map);
free(correspondences);
free(inlier_map);
return (num_inliers > 0);
}

Просмотреть файл

@ -17,11 +17,11 @@
extern "C" {
#endif
int compute_global_motion_feature_based(struct VP10_COMP *cpi,
TransformationType type,
// compute global motion parameters between two frames
int compute_global_motion_feature_based(TransformationType type,
YV12_BUFFER_CONFIG *frm,
YV12_BUFFER_CONFIG *ref,
double inlier_prob, double *H);
double *params);
#ifdef __cplusplus
} // extern "C"
#endif

940
vp10/encoder/ransac.c Normal file
Просмотреть файл

@ -0,0 +1,940 @@
/*
* (c) 2010 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <memory.h>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "vp10/encoder/ransac.h"
#define MAX_PARAMDIM 9
#define MAX_MINPTS 4
#define MAX_DEGENERATE_ITER 10
#define MINPTS_MULTIPLIER 5
// svdcmp
// Adopted from Numerical Recipes in C
static const double TINY_NEAR_ZERO = 1.0E-12;
static inline double SIGN(double a, double b) {
return ((b) >= 0 ? fabs(a) : -fabs(a));
}
static inline double PYTHAG(double a, double b) {
double absa, absb, ct;
absa = fabs(a);
absb = fabs(b);
if (absa > absb) {
ct = absb / absa;
return absa * sqrt(1.0 + ct * ct);
} else {
ct = absa / absb;
return (absb == 0) ? 0 : absb * sqrt(1.0 + ct * ct);
}
}
int IMIN(int a, int b) { return (((a) < (b)) ? (a) : (b)); }
int IMAX(int a, int b) { return (((a) < (b)) ? (b) : (a)); }
void MultiplyMat(double *m1, double *m2, double *res, const int M1,
const int N1, const int N2) {
int timesInner = N1;
int timesRows = M1;
int timesCols = N2;
double sum;
int row, col, inner;
for (row = 0; row < timesRows; ++row) {
for (col = 0; col < timesCols; ++col) {
sum = 0;
for (inner = 0; inner < timesInner; ++inner)
sum += m1[row * N1 + inner] * m2[inner * N2 + col];
*(res++) = sum;
}
}
}
static int svdcmp_(double **u, int m, int n, double w[], double **v) {
const int max_its = 30;
int flag, i, its, j, jj, k, l, nm;
double anorm, c, f, g, h, s, scale, x, y, z;
double *rv1 = (double *)malloc(sizeof(*rv1) * (n + 1));
g = scale = anorm = 0.0;
for (i = 0; i < n; i++) {
l = i + 1;
rv1[i] = scale * g;
g = s = scale = 0.0;
if (i < m) {
for (k = i; k < m; k++) scale += fabs(u[k][i]);
if (scale) {
for (k = i; k < m; k++) {
u[k][i] /= scale;
s += u[k][i] * u[k][i];
}
f = u[i][i];
g = -SIGN(sqrt(s), f);
h = f * g - s;
u[i][i] = f - g;
for (j = l; j < n; j++) {
for (s = 0.0, k = i; k < m; k++) s += u[k][i] * u[k][j];
f = s / h;
for (k = i; k < m; k++) u[k][j] += f * u[k][i];
}
for (k = i; k < m; k++) u[k][i] *= scale;
}
}
w[i] = scale * g;
g = s = scale = 0.0;
if (i < m && i != n - 1) {
for (k = l; k < n; k++) scale += fabs(u[i][k]);
if (scale) {
for (k = l; k < n; k++) {
u[i][k] /= scale;
s += u[i][k] * u[i][k];
}
f = u[i][l];
g = -SIGN(sqrt(s), f);
h = f * g - s;
u[i][l] = f - g;
for (k = l; k < n; k++) rv1[k] = u[i][k] / h;
for (j = l; j < m; j++) {
for (s = 0.0, k = l; k < n; k++) s += u[j][k] * u[i][k];
for (k = l; k < n; k++) u[j][k] += s * rv1[k];
}
for (k = l; k < n; k++) u[i][k] *= scale;
}
}
anorm = fmax(anorm, (fabs(w[i]) + fabs(rv1[i])));
}
for (i = n - 1; i >= 0; i--) {
if (i < n - 1) {
if (g) {
for (j = l; j < n; j++) v[j][i] = (u[i][j] / u[i][l]) / g;
for (j = l; j < n; j++) {
for (s = 0.0, k = l; k < n; k++) s += u[i][k] * v[k][j];
for (k = l; k < n; k++) v[k][j] += s * v[k][i];
}
}
for (j = l; j < n; j++) v[i][j] = v[j][i] = 0.0;
}
v[i][i] = 1.0;
g = rv1[i];
l = i;
}
for (i = IMIN(m, n) - 1; i >= 0; i--) {
l = i + 1;
g = w[i];
for (j = l; j < n; j++) u[i][j] = 0.0;
if (g) {
g = 1.0 / g;
for (j = l; j < n; j++) {
for (s = 0.0, k = l; k < m; k++) s += u[k][i] * u[k][j];
f = (s / u[i][i]) * g;
for (k = i; k < m; k++) u[k][j] += f * u[k][i];
}
for (j = i; j < m; j++) u[j][i] *= g;
} else {
for (j = i; j < m; j++) u[j][i] = 0.0;
}
++u[i][i];
}
for (k = n - 1; k >= 0; k--) {
for (its = 0; its < max_its; its++) {
flag = 1;
for (l = k; l >= 0; l--) {
nm = l - 1;
if ((double)(fabs(rv1[l]) + anorm) == anorm || nm < 0) {
flag = 0;
break;
}
if ((double)(fabs(w[nm]) + anorm) == anorm) break;
}
if (flag) {
c = 0.0;
s = 1.0;
for (i = l; i <= k; i++) {
f = s * rv1[i];
rv1[i] = c * rv1[i];
if ((double)(fabs(f) + anorm) == anorm) break;
g = w[i];
h = PYTHAG(f, g);
w[i] = h;
h = 1.0 / h;
c = g * h;
s = -f * h;
for (j = 0; j < m; j++) {
y = u[j][nm];
z = u[j][i];
u[j][nm] = y * c + z * s;
u[j][i] = z * c - y * s;
}
}
}
z = w[k];
if (l == k) {
if (z < 0.0) {
w[k] = -z;
for (j = 0; j < n; j++) v[j][k] = -v[j][k];
}
break;
}
if (its == max_its - 1) {
return 1;
}
assert(k > 0);
x = w[l];
nm = k - 1;
y = w[nm];
g = rv1[nm];
h = rv1[k];
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
g = PYTHAG(f, 1.0);
f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x;
c = s = 1.0;
for (j = l; j <= nm; j++) {
i = j + 1;
g = rv1[i];
y = w[i];
h = s * g;
g = c * g;
z = PYTHAG(f, h);
rv1[j] = z;
c = f / z;
s = h / z;
f = x * c + g * s;
g = g * c - x * s;
h = y * s;
y *= c;
for (jj = 0; jj < n; jj++) {
x = v[jj][j];
z = v[jj][i];
v[jj][j] = x * c + z * s;
v[jj][i] = z * c - x * s;
}
z = PYTHAG(f, h);
w[j] = z;
if (z) {
z = 1.0 / z;
c = f * z;
s = h * z;
}
f = c * g + s * y;
x = c * y - s * g;
for (jj = 0; jj < m; jj++) {
y = u[jj][j];
z = u[jj][i];
u[jj][j] = y * c + z * s;
u[jj][i] = z * c - y * s;
}
}
rv1[l] = 0.0;
rv1[k] = f;
w[k] = x;
}
}
free(rv1);
return 0;
}
static int SVD(double *U, double *W, double *V, double *matx, int M, int N) {
// Assumes allocation for U is MxN
double **nrU, **nrV;
int problem, i;
nrU = (double **)malloc((M) * sizeof(*nrU));
nrV = (double **)malloc((N) * sizeof(*nrV));
problem = !(nrU && nrV);
if (!problem) {
problem = 0;
for (i = 0; i < M; i++) {
nrU[i] = &U[i * N];
}
for (i = 0; i < N; i++) {
nrV[i] = &V[i * N];
}
}
if (problem) {
return 1;
}
/* copy from given matx into nrU */
for (i = 0; i < M; i++) {
memcpy(&(nrU[i][0]), matx + N * i, N * sizeof(*matx));
}
/* HERE IT IS: do SVD */
if (svdcmp_(nrU, M, N, W, nrV)) {
return 1;
}
/* free Numerical Recipes arrays */
free(nrU);
free(nrV);
return 0;
}
int PseudoInverse(double *inv, double *matx, const int M, const int N) {
double *U, *W, *V, ans;
int i, j, k;
U = (double *)malloc(M * N * sizeof(*matx));
W = (double *)malloc(N * sizeof(*matx));
V = (double *)malloc(N * N * sizeof(*matx));
if (!(U && W && V)) {
return 1;
}
if (SVD(U, W, V, matx, M, N)) {
return 1;
}
for (i = 0; i < N; i++) {
if (fabs(W[i]) < TINY_NEAR_ZERO) {
return 1;
}
}
for (i = 0; i < N; i++) {
for (j = 0; j < M; j++) {
ans = 0;
for (k = 0; k < N; k++) {
ans += V[k + N * i] * U[k + N * j] / W[k];
}
inv[j + M * i] = ans;
}
}
free(U);
free(W);
free(V);
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// ransac
typedef int (*isDegenerateType)(double *p);
typedef void (*normalizeType)(double *p, int np, double *T);
typedef void (*denormalizeType)(double *H, double *T1, double *T2);
typedef int (*findTransformationType)(int points, double *points1,
double *points2, double *H);
static int get_rand_indices(int npoints, int minpts, int *indices) {
int i, j;
unsigned int seed = (unsigned int)npoints;
int ptr = rand_r(&seed) % npoints;
if (minpts > npoints) return 0;
indices[0] = ptr;
ptr = (ptr == npoints - 1 ? 0 : ptr + 1);
i = 1;
while (i < minpts) {
int index = rand_r(&seed) % npoints;
while (index) {
ptr = (ptr == npoints - 1 ? 0 : ptr + 1);
for (j = 0; j < i; ++j) {
if (indices[j] == ptr) break;
}
if (j == i) index--;
}
indices[i++] = ptr;
}
return 1;
}
int ransac_(double *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_mask, double *bestH, const int minpts,
const int paramdim, isDegenerateType isDegenerate,
normalizeType normalize, denormalizeType denormalize,
findTransformationType findTransformation,
ProjectPointsType projectpoints, TransformationType type) {
static const double INLIER_THRESHOLD_NORMALIZED = 0.1;
static const double INLIER_THRESHOLD_UNNORMALIZED = 1.0;
static const double PROBABILITY_REQUIRED = 0.9;
static const double EPS = 1e-12;
static const int MIN_TRIALS = 20;
const double inlier_threshold =
(normalize && denormalize ? INLIER_THRESHOLD_NORMALIZED
: INLIER_THRESHOLD_UNNORMALIZED);
int N = 10000, trial_count = 0;
int i;
int ret_val = 0;
int max_inliers = 0;
double best_variance = 0.0;
double H[MAX_PARAMDIM];
WarpedMotionParams wm;
double points1[2 * MAX_MINPTS];
double points2[2 * MAX_MINPTS];
int indices[MAX_MINPTS];
double *best_inlier_set1;
double *best_inlier_set2;
double *inlier_set1;
double *inlier_set2;
double *corners1;
int *corners1_int;
double *corners2;
int *image1_coord;
int *inlier_mask;
double *cnp1, *cnp2;
double T1[9], T2[9];
// srand((unsigned)time(NULL)) ;
// better to make this deterministic for a given sequence for ease of testing
srand(npoints);
*number_of_inliers = 0;
if (npoints < minpts * MINPTS_MULTIPLIER) {
printf("Cannot find motion with %d matches\n", npoints);
return 1;
}
memset(&wm, 0, sizeof(wm));
best_inlier_set1 = (double *)malloc(sizeof(*best_inlier_set1) * npoints * 2);
best_inlier_set2 = (double *)malloc(sizeof(*best_inlier_set2) * npoints * 2);
inlier_set1 = (double *)malloc(sizeof(*inlier_set1) * npoints * 2);
inlier_set2 = (double *)malloc(sizeof(*inlier_set2) * npoints * 2);
corners1 = (double *)malloc(sizeof(*corners1) * npoints * 2);
corners1_int = (int *)malloc(sizeof(*corners1_int) * npoints * 2);
corners2 = (double *)malloc(sizeof(*corners2) * npoints * 2);
image1_coord = (int *)malloc(sizeof(*image1_coord) * npoints * 2);
inlier_mask = (int *)malloc(sizeof(*inlier_mask) * npoints);
for (cnp1 = corners1, cnp2 = corners2, i = 0; i < npoints; ++i) {
*(cnp1++) = *(matched_points++);
*(cnp1++) = *(matched_points++);
*(cnp2++) = *(matched_points++);
*(cnp2++) = *(matched_points++);
}
matched_points -= 4 * npoints;
if (normalize && denormalize) {
normalize(corners1, npoints, T1);
normalize(corners2, npoints, T2);
}
while (N > trial_count) {
int num_inliers = 0;
double sum_distance = 0.0;
double sum_distance_squared = 0.0;
int degenerate = 1;
int num_degenerate_iter = 0;
while (degenerate) {
num_degenerate_iter++;
if (!get_rand_indices(npoints, minpts, indices)) {
ret_val = 1;
goto finish_ransac;
}
i = 0;
while (i < minpts) {
int index = indices[i];
// add to list
points1[i * 2] = corners1[index * 2];
points1[i * 2 + 1] = corners1[index * 2 + 1];
points2[i * 2] = corners2[index * 2];
points2[i * 2 + 1] = corners2[index * 2 + 1];
i++;
}
degenerate = isDegenerate(points1);
if (num_degenerate_iter > MAX_DEGENERATE_ITER) {
ret_val = 1;
goto finish_ransac;
}
}
if (findTransformation(minpts, points1, points2, H)) {
trial_count++;
continue;
}
for (i = 0; i < npoints; ++i) {
corners1_int[2 * i] = (int)corners1[i * 2];
corners1_int[2 * i + 1] = (int)corners1[i * 2 + 1];
}
vp10_integerize_model(H, type, &wm);
projectpoints(wm.wmmat, corners1_int, image1_coord, npoints, 2, 2, 0, 0);
for (i = 0; i < npoints; ++i) {
double dx =
(image1_coord[i * 2] >> WARPEDPIXEL_PREC_BITS) - corners2[i * 2];
double dy = (image1_coord[i * 2 + 1] >> WARPEDPIXEL_PREC_BITS) -
corners2[i * 2 + 1];
double distance = sqrt(dx * dx + dy * dy);
inlier_mask[i] = distance < inlier_threshold;
if (inlier_mask[i]) {
inlier_set1[num_inliers * 2] = corners1[i * 2];
inlier_set1[num_inliers * 2 + 1] = corners1[i * 2 + 1];
inlier_set2[num_inliers * 2] = corners2[i * 2];
inlier_set2[num_inliers * 2 + 1] = corners2[i * 2 + 1];
num_inliers++;
sum_distance += distance;
sum_distance_squared += distance * distance;
}
}
if (num_inliers >= max_inliers) {
double mean_distance = sum_distance / ((double)num_inliers);
double variance = sum_distance_squared / ((double)num_inliers - 1.0) -
mean_distance * mean_distance * ((double)num_inliers) /
((double)num_inliers - 1.0);
if ((num_inliers > max_inliers) ||
(num_inliers == max_inliers && variance < best_variance)) {
best_variance = variance;
max_inliers = num_inliers;
memcpy(bestH, H, paramdim * sizeof(*bestH));
memcpy(best_inlier_set1, inlier_set1,
num_inliers * 2 * sizeof(*best_inlier_set1));
memcpy(best_inlier_set2, inlier_set2,
num_inliers * 2 * sizeof(*best_inlier_set2));
memcpy(best_inlier_mask, inlier_mask,
npoints * sizeof(*best_inlier_mask));
if (num_inliers > 0) {
double fracinliers = (double)num_inliers / (double)npoints;
double pNoOutliers = 1 - pow(fracinliers, minpts);
int temp;
pNoOutliers = fmax(EPS, pNoOutliers);
pNoOutliers = fmin(1 - EPS, pNoOutliers);
temp = (int)(log(1.0 - PROBABILITY_REQUIRED) / log(pNoOutliers));
if (temp > 0 && temp < N) {
N = IMAX(temp, MIN_TRIALS);
}
}
}
}
trial_count++;
}
findTransformation(max_inliers, best_inlier_set1, best_inlier_set2, bestH);
if (normalize && denormalize) {
denormalize(bestH, T1, T2);
}
*number_of_inliers = max_inliers;
finish_ransac:
free(best_inlier_set1);
free(best_inlier_set2);
free(inlier_set1);
free(inlier_set2);
free(corners1);
free(corners2);
free(image1_coord);
free(inlier_mask);
return ret_val;
}
///////////////////////////////////////////////////////////////////////////////
static void normalizeHomography(double *pts, int n, double *T) {
// Assume the points are 2d coordinates with scale = 1
double *p = pts;
double mean[2] = { 0, 0 };
double msqe = 0;
double scale;
int i;
for (i = 0; i < n; ++i, p += 2) {
mean[0] += p[0];
mean[1] += p[1];
}
mean[0] /= n;
mean[1] /= n;
for (p = pts, i = 0; i < n; ++i, p += 2) {
p[0] -= mean[0];
p[1] -= mean[1];
msqe += sqrt(p[0] * p[0] + p[1] * p[1]);
}
msqe /= n;
scale = sqrt(2) / msqe;
T[0] = scale;
T[1] = 0;
T[2] = -scale * mean[0];
T[3] = 0;
T[4] = scale;
T[5] = -scale * mean[1];
T[6] = 0;
T[7] = 0;
T[8] = 1;
for (p = pts, i = 0; i < n; ++i, p += 2) {
p[0] *= scale;
p[1] *= scale;
}
}
static void invnormalize_mat(double *T, double *iT) {
double is = 1.0 / T[0];
double m0 = -T[2] * is;
double m1 = -T[5] * is;
iT[0] = is;
iT[1] = 0;
iT[2] = m0;
iT[3] = 0;
iT[4] = is;
iT[5] = m1;
iT[6] = 0;
iT[7] = 0;
iT[8] = 1;
}
static void denormalizeHomography(double *H, double *T1, double *T2) {
double iT2[9];
double H2[9];
invnormalize_mat(T2, iT2);
MultiplyMat(H, T1, H2, 3, 3, 3);
MultiplyMat(iT2, H2, H, 3, 3, 3);
}
static void denormalizeAffine(double *H, double *T1, double *T2) {
double Ha[MAX_PARAMDIM];
Ha[0] = H[0];
Ha[1] = H[1];
Ha[2] = H[4];
Ha[3] = H[2];
Ha[4] = H[3];
Ha[5] = H[5];
Ha[6] = Ha[7] = 0;
Ha[8] = 1;
denormalizeHomography(Ha, T1, T2);
H[0] = Ha[2];
H[1] = Ha[5];
H[2] = Ha[0];
H[3] = Ha[1];
H[4] = Ha[3];
H[5] = Ha[4];
}
static void denormalizeRotZoom(double *H, double *T1, double *T2) {
double Ha[MAX_PARAMDIM];
Ha[0] = H[0];
Ha[1] = H[1];
Ha[2] = H[2];
Ha[3] = -H[1];
Ha[4] = H[0];
Ha[5] = H[3];
Ha[6] = Ha[7] = 0;
Ha[8] = 1;
denormalizeHomography(Ha, T1, T2);
H[0] = Ha[2];
H[1] = Ha[5];
H[2] = Ha[0];
H[3] = Ha[1];
}
static void denormalizeTranslation(double *H, double *T1, double *T2) {
double Ha[MAX_PARAMDIM];
Ha[0] = 1;
Ha[1] = 0;
Ha[2] = H[0];
Ha[3] = 0;
Ha[4] = 1;
Ha[5] = H[1];
Ha[6] = Ha[7] = 0;
Ha[8] = 1;
denormalizeHomography(Ha, T1, T2);
H[0] = Ha[2];
H[1] = Ha[5];
}
static int is_collinear3(double *p1, double *p2, double *p3) {
static const double collinear_eps = 1e-3;
const double v =
(p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0]);
return fabs(v) < collinear_eps;
}
static int isDegenerateTranslation(double *p) {
return (p[0] - p[2]) * (p[0] - p[2]) + (p[1] - p[3]) * (p[1] - p[3]) <= 2;
}
static int isDegenerateAffine(double *p) {
return is_collinear3(p, p + 2, p + 4);
}
static int isDegenerateHomography(double *p) {
return is_collinear3(p, p + 2, p + 4) || is_collinear3(p, p + 2, p + 6) ||
is_collinear3(p, p + 4, p + 6) || is_collinear3(p + 2, p + 4, p + 6);
}
int findTranslation(const int np, double *pts1, double *pts2, double *mat) {
int i;
double sx, sy, dx, dy;
double sumx, sumy;
double T1[9], T2[9];
normalizeHomography(pts1, np, T1);
normalizeHomography(pts2, np, T2);
sumx = 0;
sumy = 0;
for (i = 0; i < np; ++i) {
dx = *(pts2++);
dy = *(pts2++);
sx = *(pts1++);
sy = *(pts1++);
sumx += dx - sx;
sumy += dy - sy;
}
mat[0] = sumx / np;
mat[1] = sumy / np;
denormalizeTranslation(mat, T1, T2);
return 0;
}
int findRotZoom(const int np, double *pts1, double *pts2, double *mat) {
const int np2 = np * 2;
double *a = (double *)malloc(sizeof(*a) * np2 * 9);
double *b = a + np2 * 4;
double *temp = b + np2;
int i;
double sx, sy, dx, dy;
double T1[9], T2[9];
normalizeHomography(pts1, np, T1);
normalizeHomography(pts2, np, T2);
for (i = 0; i < np; ++i) {
dx = *(pts2++);
dy = *(pts2++);
sx = *(pts1++);
sy = *(pts1++);
a[i * 2 * 4 + 0] = sx;
a[i * 2 * 4 + 1] = sy;
a[i * 2 * 4 + 2] = 1;
a[i * 2 * 4 + 3] = 0;
a[(i * 2 + 1) * 4 + 0] = sy;
a[(i * 2 + 1) * 4 + 1] = -sx;
a[(i * 2 + 1) * 4 + 2] = 0;
a[(i * 2 + 1) * 4 + 3] = 1;
b[2 * i] = dx;
b[2 * i + 1] = dy;
}
if (PseudoInverse(temp, a, np2, 4)) {
free(a);
return 1;
}
MultiplyMat(temp, b, mat, 4, np2, 1);
denormalizeRotZoom(mat, T1, T2);
free(a);
return 0;
}
int findAffine(const int np, double *pts1, double *pts2, double *mat) {
const int np2 = np * 2;
double *a = (double *)malloc(sizeof(*a) * np2 * 13);
double *b = a + np2 * 6;
double *temp = b + np2;
int i;
double sx, sy, dx, dy;
double T1[9], T2[9];
normalizeHomography(pts1, np, T1);
normalizeHomography(pts2, np, T2);
for (i = 0; i < np; ++i) {
dx = *(pts2++);
dy = *(pts2++);
sx = *(pts1++);
sy = *(pts1++);
a[i * 2 * 6 + 0] = sx;
a[i * 2 * 6 + 1] = sy;
a[i * 2 * 6 + 2] = 0;
a[i * 2 * 6 + 3] = 0;
a[i * 2 * 6 + 4] = 1;
a[i * 2 * 6 + 5] = 0;
a[(i * 2 + 1) * 6 + 0] = 0;
a[(i * 2 + 1) * 6 + 1] = 0;
a[(i * 2 + 1) * 6 + 2] = sx;
a[(i * 2 + 1) * 6 + 3] = sy;
a[(i * 2 + 1) * 6 + 4] = 0;
a[(i * 2 + 1) * 6 + 5] = 1;
b[2 * i] = dx;
b[2 * i + 1] = dy;
}
if (PseudoInverse(temp, a, np2, 6)) {
free(a);
return 1;
}
MultiplyMat(temp, b, mat, 6, np2, 1);
denormalizeAffine(mat, T1, T2);
free(a);
return 0;
}
int findHomography(const int np, double *pts1, double *pts2, double *mat) {
// Implemented from Peter Kovesi's normalized implementation
const int np3 = np * 3;
double *a = (double *)malloc(sizeof(*a) * np3 * 18);
double *U = a + np3 * 9;
double S[9], V[9 * 9];
int i, mini;
double sx, sy, dx, dy;
double T1[9], T2[9];
normalizeHomography(pts1, np, T1);
normalizeHomography(pts2, np, T2);
for (i = 0; i < np; ++i) {
dx = *(pts2++);
dy = *(pts2++);
sx = *(pts1++);
sy = *(pts1++);
a[i * 3 * 9 + 0] = a[i * 3 * 9 + 1] = a[i * 3 * 9 + 2] = 0;
a[i * 3 * 9 + 3] = -sx;
a[i * 3 * 9 + 4] = -sy;
a[i * 3 * 9 + 5] = -1;
a[i * 3 * 9 + 6] = dy * sx;
a[i * 3 * 9 + 7] = dy * sy;
a[i * 3 * 9 + 8] = dy;
a[(i * 3 + 1) * 9 + 0] = sx;
a[(i * 3 + 1) * 9 + 1] = sy;
a[(i * 3 + 1) * 9 + 2] = 1;
a[(i * 3 + 1) * 9 + 3] = a[(i * 3 + 1) * 9 + 4] = a[(i * 3 + 1) * 9 + 5] =
0;
a[(i * 3 + 1) * 9 + 6] = -dx * sx;
a[(i * 3 + 1) * 9 + 7] = -dx * sy;
a[(i * 3 + 1) * 9 + 8] = -dx;
a[(i * 3 + 2) * 9 + 0] = -dy * sx;
a[(i * 3 + 2) * 9 + 1] = -dy * sy;
a[(i * 3 + 2) * 9 + 2] = -dy;
a[(i * 3 + 2) * 9 + 3] = dx * sx;
a[(i * 3 + 2) * 9 + 4] = dx * sy;
a[(i * 3 + 2) * 9 + 5] = dx;
a[(i * 3 + 2) * 9 + 6] = a[(i * 3 + 2) * 9 + 7] = a[(i * 3 + 2) * 9 + 8] =
0;
}
if (SVD(U, S, V, a, np3, 9)) {
free(a);
return 1;
} else {
double minS = 1e12;
mini = -1;
for (i = 0; i < 9; ++i) {
if (S[i] < minS) {
minS = S[i];
mini = i;
}
}
}
for (i = 0; i < 9; i++) mat[i] = V[i * 9 + mini];
denormalizeHomography(mat, T1, T2);
free(a);
if (mat[8] == 0.0) {
return 1;
}
return 0;
}
int findHomographyScale1(const int np, double *pts1, double *pts2,
double *mat) {
// This implementation assumes h33 = 1, but does not seem to give good results
const int np2 = np * 2;
double *a = (double *)malloc(sizeof(*a) * np2 * 17);
double *b = a + np2 * 8;
double *temp = b + np2;
int i, j;
double sx, sy, dx, dy;
double T1[9], T2[9];
normalizeHomography(pts1, np, T1);
normalizeHomography(pts2, np, T2);
for (i = 0, j = np; i < np; ++i, ++j) {
dx = *(pts2++);
dy = *(pts2++);
sx = *(pts1++);
sy = *(pts1++);
a[i * 8 + 0] = a[j * 8 + 3] = sx;
a[i * 8 + 1] = a[j * 8 + 4] = sy;
a[i * 8 + 2] = a[j * 8 + 5] = 1;
a[i * 8 + 3] = a[i * 8 + 4] = a[i * 8 + 5] = a[j * 8 + 0] = a[j * 8 + 1] =
a[j * 8 + 2] = 0;
a[i * 8 + 6] = -dx * sx;
a[i * 8 + 7] = -dx * sy;
a[j * 8 + 6] = -dy * sx;
a[j * 8 + 7] = -dy * sy;
b[i] = dx;
b[j] = dy;
}
if (PseudoInverse(temp, a, np2, 8)) {
free(a);
return 1;
}
MultiplyMat(temp, b, &*mat, 8, np2, 1);
mat[8] = 1;
denormalizeHomography(mat, T1, T2);
free(a);
return 0;
}
int ransacTranslation(double *matched_points, int npoints,
int *number_of_inliers, int *best_inlier_mask,
double *bestH) {
return ransac_(matched_points, npoints, number_of_inliers, best_inlier_mask,
bestH, 3, 2, isDegenerateTranslation,
NULL, // normalizeHomography,
NULL, // denormalizeRotZoom,
findTranslation, projectPointsTranslation, TRANSLATION);
}
int ransacRotZoom(double *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_mask, double *bestH) {
return ransac_(matched_points, npoints, number_of_inliers, best_inlier_mask,
bestH, 3, 4, isDegenerateAffine,
NULL, // normalizeHomography,
NULL, // denormalizeRotZoom,
findRotZoom, projectPointsRotZoom, ROTZOOM);
}
int ransacAffine(double *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_mask, double *bestH) {
return ransac_(matched_points, npoints, number_of_inliers, best_inlier_mask,
bestH, 3, 6, isDegenerateAffine,
NULL, // normalizeHomography,
NULL, // denormalizeAffine,
findAffine, projectPointsAffine, AFFINE);
}
int ransacHomography(double *matched_points, int npoints,
int *number_of_inliers, int *best_inlier_mask,
double *bestH) {
int result = ransac_(matched_points, npoints, number_of_inliers,
best_inlier_mask, bestH, 4, 8, isDegenerateHomography,
NULL, // normalizeHomography,
NULL, // denormalizeHomography,
findHomography, projectPointsHomography, HOMOGRAPHY);
if (!result) {
// normalize so that H33 = 1
int i;
double m = 1.0 / bestH[8];
for (i = 0; i < 8; ++i) bestH[i] *= m;
bestH[8] = 1.0;
}
return result;
}

37
vp10/encoder/ransac.h Normal file
Просмотреть файл

@ -0,0 +1,37 @@
/*
* Copyright (c) 2010 The WebM project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef VP10_ENCODER_RANSAC_H_
#define VP10_ENCODER_RANSAC_H_
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory.h>
#include "vp10/common/warped_motion.h"
typedef int (*RansacType)(double *matched_points, int npoints,
int *number_of_inliers, int *best_inlier_mask,
double *bestH);
/* Each of these functions fits a motion model from a set of
corresponding points in 2 frames using RANSAC.*/
int ransacHomography(double *matched_points, int npoints,
int *number_of_inliers, int *best_inlier_indices,
double *bestH);
int ransacAffine(double *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_indices, double *bestH);
int ransacRotZoom(double *matched_points, int npoints, int *number_of_inliers,
int *best_inlier_indices, double *bestH);
int ransacTranslation(double *matched_points, int npoints,
int *number_of_inliers, int *best_inlier_indices,
double *bestH);
#endif // VP10_ENCODER_RANSAC_H

Просмотреть файл

@ -36,8 +36,18 @@ VP10_CX_SRCS-yes += encoder/ethread.h
VP10_CX_SRCS-yes += encoder/ethread.c
VP10_CX_SRCS-yes += encoder/extend.c
VP10_CX_SRCS-yes += encoder/firstpass.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += ../third_party/fastfeat/fast.h
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += ../third_party/fastfeat/nonmax.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += ../third_party/fastfeat/fast_9.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += ../third_party/fastfeat/fast.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/corner_match.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/corner_match.h
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/corner_detect.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/corner_detect.h
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/global_motion.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/global_motion.h
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/ransac.c
VP10_CX_SRCS-$(CONFIG_GLOBAL_MOTION) += encoder/ransac.h
VP10_CX_SRCS-yes += encoder/block.h
VP10_CX_SRCS-yes += encoder/bitstream.h
VP10_CX_SRCS-yes += encoder/encodemb.h