blob: 29e934debdf5a595468859626e4c096144750ff0 [file] [log] [blame]
Sarah Parker4dc0f1b2016-08-09 17:40:53 -07001/*
Yaowu Xubde4ac82016-11-28 15:26:06 -08002 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
Sarah Parker4dc0f1b2016-08-09 17:40:53 -07003 *
Yaowu Xubde4ac82016-11-28 15:26:06 -08004 * This source code is subject to the terms of the BSD 2 Clause License and
5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6 * was not distributed with this source code in the LICENSE file, you can
7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8 * Media Patent License 1.0 was not distributed with this source code in the
9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070010 */
11
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070012#include <stdlib.h>
13#include <memory.h>
14#include <math.h>
15
Tom Finegan44702c82018-05-22 13:00:39 -070016#include "config/av1_rtcd.h"
17
Yaowu Xuf883b422016-08-30 14:01:10 -070018#include "av1/encoder/corner_match.h"
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070019
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070020#define SEARCH_SZ 9
21#define SEARCH_SZ_BY2 ((SEARCH_SZ - 1) / 2)
22
Yue Chen19e7aa82016-11-30 14:05:39 -080023#define THRESHOLD_NCC 0.75
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070024
David Barker15338d52017-02-13 15:30:59 +000025/* Compute var(im) * MATCH_SZ_SQ over a MATCH_SZ by MATCH_SZ window of im,
26 centered at (x, y).
27*/
28static double compute_variance(unsigned char *im, int stride, int x, int y) {
Yaowu Xufc4585b2017-05-05 11:18:22 -070029 int sum = 0;
30 int sumsq = 0;
David Barker15338d52017-02-13 15:30:59 +000031 int var;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070032 int i, j;
33 for (i = 0; i < MATCH_SZ; ++i)
34 for (j = 0; j < MATCH_SZ; ++j) {
35 sum += im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)];
36 sumsq += im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)] *
37 im[(i + y - MATCH_SZ_BY2) * stride + (j + x - MATCH_SZ_BY2)];
38 }
David Barker15338d52017-02-13 15:30:59 +000039 var = sumsq * MATCH_SZ_SQ - sum * sum;
40 return (double)var;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070041}
42
David Barker15338d52017-02-13 15:30:59 +000043/* Compute corr(im1, im2) * MATCH_SZ * stddev(im1), where the
44 correlation/standard deviation are taken over MATCH_SZ by MATCH_SZ windows
45 of each image, centered at (x1, y1) and (x2, y2) respectively.
46*/
David Barkeree674322017-05-10 15:43:02 +010047double compute_cross_correlation_c(unsigned char *im1, int stride1, int x1,
48 int y1, unsigned char *im2, int stride2,
49 int x2, int y2) {
David Barker15338d52017-02-13 15:30:59 +000050 int v1, v2;
51 int sum1 = 0;
52 int sum2 = 0;
53 int sumsq2 = 0;
54 int cross = 0;
55 int var2, cov;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070056 int i, j;
57 for (i = 0; i < MATCH_SZ; ++i)
58 for (j = 0; j < MATCH_SZ; ++j) {
David Barker15338d52017-02-13 15:30:59 +000059 v1 = im1[(i + y1 - MATCH_SZ_BY2) * stride1 + (j + x1 - MATCH_SZ_BY2)];
60 v2 = im2[(i + y2 - MATCH_SZ_BY2) * stride2 + (j + x2 - MATCH_SZ_BY2)];
61 sum1 += v1;
62 sum2 += v2;
63 sumsq2 += v2 * v2;
64 cross += v1 * v2;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070065 }
David Barker15338d52017-02-13 15:30:59 +000066 var2 = sumsq2 * MATCH_SZ_SQ - sum2 * sum2;
67 cov = cross * MATCH_SZ_SQ - sum1 * sum2;
68 return cov / sqrt((double)var2);
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070069}
70
David Barker15338d52017-02-13 15:30:59 +000071static int is_eligible_point(int pointx, int pointy, int width, int height) {
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070072 return (pointx >= MATCH_SZ_BY2 && pointy >= MATCH_SZ_BY2 &&
73 pointx + MATCH_SZ_BY2 < width && pointy + MATCH_SZ_BY2 < height);
74}
75
David Barker15338d52017-02-13 15:30:59 +000076static int is_eligible_distance(int point1x, int point1y, int point2x,
77 int point2y, int width, int height) {
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070078 const int thresh = (width < height ? height : width) >> 4;
79 return ((point1x - point2x) * (point1x - point2x) +
80 (point1y - point2y) * (point1y - point2y)) <= thresh * thresh;
81}
82
83static void improve_correspondence(unsigned char *frm, unsigned char *ref,
84 int width, int height, int frm_stride,
85 int ref_stride,
Sarah Parkerf9a961c2016-09-06 11:25:04 -070086 Correspondence *correspondences,
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070087 int num_correspondences) {
88 int i;
89 for (i = 0; i < num_correspondences; ++i) {
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070090 int x, y, best_x = 0, best_y = 0;
91 double best_match_ncc = 0.0;
92 for (y = -SEARCH_SZ_BY2; y <= SEARCH_SZ_BY2; ++y) {
93 for (x = -SEARCH_SZ_BY2; x <= SEARCH_SZ_BY2; ++x) {
94 double match_ncc;
David Barker15338d52017-02-13 15:30:59 +000095 if (!is_eligible_point(correspondences[i].rx + x,
96 correspondences[i].ry + y, width, height))
Sarah Parker4dc0f1b2016-08-09 17:40:53 -070097 continue;
David Barker15338d52017-02-13 15:30:59 +000098 if (!is_eligible_distance(correspondences[i].x, correspondences[i].y,
99 correspondences[i].rx + x,
100 correspondences[i].ry + y, width, height))
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700101 continue;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700102 match_ncc = compute_cross_correlation(
David Barker15338d52017-02-13 15:30:59 +0000103 frm, frm_stride, correspondences[i].x, correspondences[i].y, ref,
104 ref_stride, correspondences[i].rx + x, correspondences[i].ry + y);
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700105 if (match_ncc > best_match_ncc) {
106 best_match_ncc = match_ncc;
107 best_y = y;
108 best_x = x;
109 }
110 }
111 }
David Barker15338d52017-02-13 15:30:59 +0000112 correspondences[i].rx += best_x;
113 correspondences[i].ry += best_y;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700114 }
115 for (i = 0; i < num_correspondences; ++i) {
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700116 int x, y, best_x = 0, best_y = 0;
117 double best_match_ncc = 0.0;
118 for (y = -SEARCH_SZ_BY2; y <= SEARCH_SZ_BY2; ++y)
119 for (x = -SEARCH_SZ_BY2; x <= SEARCH_SZ_BY2; ++x) {
120 double match_ncc;
David Barker15338d52017-02-13 15:30:59 +0000121 if (!is_eligible_point(correspondences[i].x + x,
122 correspondences[i].y + y, width, height))
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700123 continue;
David Barker15338d52017-02-13 15:30:59 +0000124 if (!is_eligible_distance(
125 correspondences[i].x + x, correspondences[i].y + y,
126 correspondences[i].rx, correspondences[i].ry, width, height))
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700127 continue;
David Barker15338d52017-02-13 15:30:59 +0000128 match_ncc = compute_cross_correlation(
129 ref, ref_stride, correspondences[i].rx, correspondences[i].ry, frm,
130 frm_stride, correspondences[i].x + x, correspondences[i].y + y);
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700131 if (match_ncc > best_match_ncc) {
132 best_match_ncc = match_ncc;
133 best_y = y;
134 best_x = x;
135 }
136 }
137 correspondences[i].x += best_x;
138 correspondences[i].y += best_y;
139 }
140}
141
142int determine_correspondence(unsigned char *frm, int *frm_corners,
143 int num_frm_corners, unsigned char *ref,
144 int *ref_corners, int num_ref_corners, int width,
145 int height, int frm_stride, int ref_stride,
David Barker15338d52017-02-13 15:30:59 +0000146 int *correspondence_pts) {
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700147 // TODO(sarahparker) Improve this to include 2-way match
148 int i, j;
Sarah Parkerf9a961c2016-09-06 11:25:04 -0700149 Correspondence *correspondences = (Correspondence *)correspondence_pts;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700150 int num_correspondences = 0;
151 for (i = 0; i < num_frm_corners; ++i) {
152 double best_match_ncc = 0.0;
153 double template_norm;
154 int best_match_j = -1;
155 if (!is_eligible_point(frm_corners[2 * i], frm_corners[2 * i + 1], width,
156 height))
157 continue;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700158 for (j = 0; j < num_ref_corners; ++j) {
159 double match_ncc;
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700160 if (!is_eligible_point(ref_corners[2 * j], ref_corners[2 * j + 1], width,
161 height))
162 continue;
163 if (!is_eligible_distance(frm_corners[2 * i], frm_corners[2 * i + 1],
164 ref_corners[2 * j], ref_corners[2 * j + 1],
165 width, height))
166 continue;
David Barker15338d52017-02-13 15:30:59 +0000167 match_ncc = compute_cross_correlation(
168 frm, frm_stride, frm_corners[2 * i], frm_corners[2 * i + 1], ref,
169 ref_stride, ref_corners[2 * j], ref_corners[2 * j + 1]);
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700170 if (match_ncc > best_match_ncc) {
171 best_match_ncc = match_ncc;
172 best_match_j = j;
173 }
174 }
David Barker15338d52017-02-13 15:30:59 +0000175 // Note: We want to test if the best correlation is >= THRESHOLD_NCC,
176 // but need to account for the normalization in compute_cross_correlation.
177 template_norm = compute_variance(frm, frm_stride, frm_corners[2 * i],
178 frm_corners[2 * i + 1]);
179 if (best_match_ncc > THRESHOLD_NCC * sqrt(template_norm)) {
180 correspondences[num_correspondences].x = frm_corners[2 * i];
181 correspondences[num_correspondences].y = frm_corners[2 * i + 1];
182 correspondences[num_correspondences].rx = ref_corners[2 * best_match_j];
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700183 correspondences[num_correspondences].ry =
David Barker15338d52017-02-13 15:30:59 +0000184 ref_corners[2 * best_match_j + 1];
Sarah Parker4dc0f1b2016-08-09 17:40:53 -0700185 num_correspondences++;
186 }
187 }
188 improve_correspondence(frm, ref, width, height, frm_stride, ref_stride,
189 correspondences, num_correspondences);
190 return num_correspondences;
191}