Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 1 | /* |
Yaowu Xu | bde4ac8 | 2016-11-28 15:26:06 -0800 | [diff] [blame] | 2 | * Copyright (c) 2016, Alliance for Open Media. All rights reserved |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 3 | * |
Yaowu Xu | bde4ac8 | 2016-11-28 15:26:06 -0800 | [diff] [blame] | 4 | * 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 Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 10 | */ |
| 11 | |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 12 | #include <stdlib.h> |
| 13 | #include <memory.h> |
| 14 | #include <math.h> |
| 15 | |
Tom Finegan | 44702c8 | 2018-05-22 13:00:39 -0700 | [diff] [blame^] | 16 | #include "config/av1_rtcd.h" |
| 17 | |
Yaowu Xu | f883b42 | 2016-08-30 14:01:10 -0700 | [diff] [blame] | 18 | #include "av1/encoder/corner_match.h" |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 19 | |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 20 | #define SEARCH_SZ 9 |
| 21 | #define SEARCH_SZ_BY2 ((SEARCH_SZ - 1) / 2) |
| 22 | |
Yue Chen | 19e7aa8 | 2016-11-30 14:05:39 -0800 | [diff] [blame] | 23 | #define THRESHOLD_NCC 0.75 |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 24 | |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 25 | /* Compute var(im) * MATCH_SZ_SQ over a MATCH_SZ by MATCH_SZ window of im, |
| 26 | centered at (x, y). |
| 27 | */ |
| 28 | static double compute_variance(unsigned char *im, int stride, int x, int y) { |
Yaowu Xu | fc4585b | 2017-05-05 11:18:22 -0700 | [diff] [blame] | 29 | int sum = 0; |
| 30 | int sumsq = 0; |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 31 | int var; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 32 | 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 Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 39 | var = sumsq * MATCH_SZ_SQ - sum * sum; |
| 40 | return (double)var; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 41 | } |
| 42 | |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 43 | /* 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 Barker | ee67432 | 2017-05-10 15:43:02 +0100 | [diff] [blame] | 47 | double 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 Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 50 | int v1, v2; |
| 51 | int sum1 = 0; |
| 52 | int sum2 = 0; |
| 53 | int sumsq2 = 0; |
| 54 | int cross = 0; |
| 55 | int var2, cov; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 56 | int i, j; |
| 57 | for (i = 0; i < MATCH_SZ; ++i) |
| 58 | for (j = 0; j < MATCH_SZ; ++j) { |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 59 | 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 Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 65 | } |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 66 | var2 = sumsq2 * MATCH_SZ_SQ - sum2 * sum2; |
| 67 | cov = cross * MATCH_SZ_SQ - sum1 * sum2; |
| 68 | return cov / sqrt((double)var2); |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 69 | } |
| 70 | |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 71 | static int is_eligible_point(int pointx, int pointy, int width, int height) { |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 72 | return (pointx >= MATCH_SZ_BY2 && pointy >= MATCH_SZ_BY2 && |
| 73 | pointx + MATCH_SZ_BY2 < width && pointy + MATCH_SZ_BY2 < height); |
| 74 | } |
| 75 | |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 76 | static int is_eligible_distance(int point1x, int point1y, int point2x, |
| 77 | int point2y, int width, int height) { |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 78 | const int thresh = (width < height ? height : width) >> 4; |
| 79 | return ((point1x - point2x) * (point1x - point2x) + |
| 80 | (point1y - point2y) * (point1y - point2y)) <= thresh * thresh; |
| 81 | } |
| 82 | |
| 83 | static void improve_correspondence(unsigned char *frm, unsigned char *ref, |
| 84 | int width, int height, int frm_stride, |
| 85 | int ref_stride, |
Sarah Parker | f9a961c | 2016-09-06 11:25:04 -0700 | [diff] [blame] | 86 | Correspondence *correspondences, |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 87 | int num_correspondences) { |
| 88 | int i; |
| 89 | for (i = 0; i < num_correspondences; ++i) { |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 90 | 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 Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 95 | if (!is_eligible_point(correspondences[i].rx + x, |
| 96 | correspondences[i].ry + y, width, height)) |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 97 | continue; |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 98 | if (!is_eligible_distance(correspondences[i].x, correspondences[i].y, |
| 99 | correspondences[i].rx + x, |
| 100 | correspondences[i].ry + y, width, height)) |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 101 | continue; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 102 | match_ncc = compute_cross_correlation( |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 103 | frm, frm_stride, correspondences[i].x, correspondences[i].y, ref, |
| 104 | ref_stride, correspondences[i].rx + x, correspondences[i].ry + y); |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 105 | if (match_ncc > best_match_ncc) { |
| 106 | best_match_ncc = match_ncc; |
| 107 | best_y = y; |
| 108 | best_x = x; |
| 109 | } |
| 110 | } |
| 111 | } |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 112 | correspondences[i].rx += best_x; |
| 113 | correspondences[i].ry += best_y; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 114 | } |
| 115 | for (i = 0; i < num_correspondences; ++i) { |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 116 | 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 Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 121 | if (!is_eligible_point(correspondences[i].x + x, |
| 122 | correspondences[i].y + y, width, height)) |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 123 | continue; |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 124 | if (!is_eligible_distance( |
| 125 | correspondences[i].x + x, correspondences[i].y + y, |
| 126 | correspondences[i].rx, correspondences[i].ry, width, height)) |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 127 | continue; |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 128 | 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 Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 131 | 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 | |
| 142 | int 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 Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 146 | int *correspondence_pts) { |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 147 | // TODO(sarahparker) Improve this to include 2-way match |
| 148 | int i, j; |
Sarah Parker | f9a961c | 2016-09-06 11:25:04 -0700 | [diff] [blame] | 149 | Correspondence *correspondences = (Correspondence *)correspondence_pts; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 150 | 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 Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 158 | for (j = 0; j < num_ref_corners; ++j) { |
| 159 | double match_ncc; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 160 | 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 Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 167 | 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 Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 170 | if (match_ncc > best_match_ncc) { |
| 171 | best_match_ncc = match_ncc; |
| 172 | best_match_j = j; |
| 173 | } |
| 174 | } |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 175 | // 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 Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 183 | correspondences[num_correspondences].ry = |
David Barker | 15338d5 | 2017-02-13 15:30:59 +0000 | [diff] [blame] | 184 | ref_corners[2 * best_match_j + 1]; |
Sarah Parker | 4dc0f1b | 2016-08-09 17:40:53 -0700 | [diff] [blame] | 185 | num_correspondences++; |
| 186 | } |
| 187 | } |
| 188 | improve_correspondence(frm, ref, width, height, frm_stride, ref_stride, |
| 189 | correspondences, num_correspondences); |
| 190 | return num_correspondences; |
| 191 | } |