blob: 64deade0649688cdf5f67e9e451c52eb52507135 [file] [log] [blame]
Todd Nguyen302d0972017-06-16 16:16:29 -07001/*
2 * Copyright (c) 2017, Alliance for Open Media. All rights reserved
3 *
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.
10 */
11
12#define _POSIX_C_SOURCE 200112L // rand_r()
13#include <assert.h>
14#include <float.h>
15#include <limits.h>
16#include <math.h>
17#include <stdlib.h>
18#include <time.h>
19
20#include "av1/encoder/bgsprite.h"
21
22#include "aom_mem/aom_mem.h"
Todd Nguyen8493f912017-07-20 12:45:12 -070023#include "./aom_scale_rtcd.h"
Todd Nguyen302d0972017-06-16 16:16:29 -070024#include "av1/common/mv.h"
25#include "av1/common/warped_motion.h"
26#include "av1/encoder/encoder.h"
27#include "av1/encoder/global_motion.h"
28#include "av1/encoder/mathutils.h"
Todd Nguyen8493f912017-07-20 12:45:12 -070029#include "av1/encoder/temporal_filter.h"
30
31/* Blending Modes:
32 * 0 = Median
33 * 1 = Mean
34 */
35#define BGSPRITE_BLENDING_MODE 1
36
37/* Interpolation for panorama alignment sampling:
38 * 0 = Nearest neighbor
39 * 1 = Bilinear
40 */
41#define BGSPRITE_INTERPOLATION 0
Todd Nguyen302d0972017-06-16 16:16:29 -070042
43#define TRANSFORM_MAT_DIM 3
44
45typedef struct {
46#if CONFIG_HIGHBITDEPTH
47 uint16_t y;
48 uint16_t u;
49 uint16_t v;
50#else
51 uint8_t y;
52 uint8_t u;
53 uint8_t v;
54#endif // CONFIG_HIGHBITDEPTH
55} YuvPixel;
56
57// Maps to convert from matrix form to param vector form.
58static const int params_to_matrix_map[] = { 2, 3, 0, 4, 5, 1, 6, 7 };
59static const int matrix_to_params_map[] = { 2, 5, 0, 1, 3, 4, 6, 7 };
60
61// Convert the parameter array to a 3x3 matrix form.
62static void params_to_matrix(const double *const params, double *target) {
63 for (int i = 0; i < MAX_PARAMDIM - 1; i++) {
64 assert(params_to_matrix_map[i] < MAX_PARAMDIM - 1);
65 target[i] = params[params_to_matrix_map[i]];
66 }
67 target[8] = 1;
68}
69
70// Convert a 3x3 matrix to a parameter array form.
71static void matrix_to_params(const double *const matrix, double *target) {
72 for (int i = 0; i < MAX_PARAMDIM - 1; i++) {
73 assert(matrix_to_params_map[i] < MAX_PARAMDIM - 1);
74 target[i] = matrix[matrix_to_params_map[i]];
75 }
76}
77
78// Do matrix multiplication on params.
79static void multiply_params(double *const m1, double *const m2,
80 double *target) {
81 double m1_matrix[MAX_PARAMDIM];
82 double m2_matrix[MAX_PARAMDIM];
83 double result[MAX_PARAMDIM];
84
85 params_to_matrix(m1, m1_matrix);
86 params_to_matrix(m2, m2_matrix);
87 multiply_mat(m2_matrix, m1_matrix, result, TRANSFORM_MAT_DIM,
88 TRANSFORM_MAT_DIM, TRANSFORM_MAT_DIM);
89 matrix_to_params(result, target);
90}
91
92// Finds x and y limits of a single transformed image.
93// Width and height are the size of the input video.
94static void find_frame_limit(int width, int height,
95 const double *const transform, int *x_min,
96 int *x_max, int *y_min, int *y_max) {
97 double transform_matrix[MAX_PARAMDIM];
98 double xy_matrix[3] = { 0, 0, 1 };
99 double uv_matrix[3] = { 0 };
100// Macro used to update frame limits based on transformed coordinates.
101#define UPDATELIMITS(u, v, x_min, x_max, y_min, y_max) \
102 { \
103 if ((int)ceil(u) > *x_max) { \
104 *x_max = (int)ceil(u); \
105 } \
106 if ((int)floor(u) < *x_min) { \
107 *x_min = (int)floor(u); \
108 } \
109 if ((int)ceil(v) > *y_max) { \
110 *y_max = (int)ceil(v); \
111 } \
112 if ((int)floor(v) < *y_min) { \
113 *y_min = (int)floor(v); \
114 } \
115 }
116
117 params_to_matrix(transform, transform_matrix);
118 xy_matrix[0] = 0;
119 xy_matrix[1] = 0;
120 multiply_mat(transform_matrix, xy_matrix, uv_matrix, TRANSFORM_MAT_DIM,
121 TRANSFORM_MAT_DIM, 1);
122 *x_max = (int)ceil(uv_matrix[0]);
123 *x_min = (int)floor(uv_matrix[0]);
124 *y_max = (int)ceil(uv_matrix[1]);
125 *y_min = (int)floor(uv_matrix[1]);
126
127 xy_matrix[0] = width;
128 xy_matrix[1] = 0;
129 multiply_mat(transform_matrix, xy_matrix, uv_matrix, TRANSFORM_MAT_DIM,
130 TRANSFORM_MAT_DIM, 1);
131 UPDATELIMITS(uv_matrix[0], uv_matrix[1], x_min, x_max, y_min, y_max);
132
133 xy_matrix[0] = width;
134 xy_matrix[1] = height;
135 multiply_mat(transform_matrix, xy_matrix, uv_matrix, TRANSFORM_MAT_DIM,
136 TRANSFORM_MAT_DIM, 1);
137 UPDATELIMITS(uv_matrix[0], uv_matrix[1], x_min, x_max, y_min, y_max);
138
139 xy_matrix[0] = 0;
140 xy_matrix[1] = height;
141 multiply_mat(transform_matrix, xy_matrix, uv_matrix, TRANSFORM_MAT_DIM,
142 TRANSFORM_MAT_DIM, 1);
143 UPDATELIMITS(uv_matrix[0], uv_matrix[1], x_min, x_max, y_min, y_max);
144
145#undef UPDATELIMITS
146}
147
148// Finds x and y limits for arrays. Also finds the overall max and minimums
149static void find_limits(int width, int height, const double **const params,
150 int num_frames, int *x_min, int *x_max, int *y_min,
151 int *y_max, int *pano_x_min, int *pano_x_max,
152 int *pano_y_min, int *pano_y_max) {
153 *pano_x_max = INT_MIN;
154 *pano_x_min = INT_MAX;
155 *pano_y_max = INT_MIN;
156 *pano_y_min = INT_MAX;
157 for (int i = 0; i < num_frames; ++i) {
158 find_frame_limit(width, height, (const double *const)params[i], &x_min[i],
159 &x_max[i], &y_min[i], &y_max[i]);
160 if (x_max[i] > *pano_x_max) {
161 *pano_x_max = x_max[i];
162 }
163 if (x_min[i] < *pano_x_min) {
164 *pano_x_min = x_min[i];
165 }
166 if (y_max[i] > *pano_y_max) {
167 *pano_y_max = y_max[i];
168 }
169 if (y_min[i] < *pano_y_min) {
170 *pano_y_min = y_min[i];
171 }
172 }
173}
174
175// Inverts a 3x3 matrix that is in the parameter form.
176static void invert_params(const double *const params, double *target) {
177 double temp[MAX_PARAMDIM] = { 0 };
178 params_to_matrix(params, temp);
179
180 // Find determinant of matrix (expansion by minors).
181 const double det = temp[0] * ((temp[4] * temp[8]) - (temp[5] * temp[7])) -
182 temp[1] * ((temp[3] * temp[8]) - (temp[5] * temp[6])) +
183 temp[2] * ((temp[3] * temp[7]) - (temp[4] * temp[6]));
184 assert(det != 0);
185
186 // inverse is transpose of cofactor * 1/det.
187 double inverse[MAX_PARAMDIM] = { 0 };
188 inverse[0] = (temp[4] * temp[8] - temp[7] * temp[5]) / det;
189 inverse[1] = (temp[2] * temp[7] - temp[1] * temp[8]) / det;
190 inverse[2] = (temp[1] * temp[5] - temp[2] * temp[4]) / det;
191 inverse[3] = (temp[5] * temp[6] - temp[3] * temp[8]) / det;
192 inverse[4] = (temp[0] * temp[8] - temp[2] * temp[6]) / det;
193 inverse[5] = (temp[3] * temp[2] - temp[0] * temp[5]) / det;
194 inverse[6] = (temp[3] * temp[7] - temp[6] * temp[4]) / det;
195 inverse[7] = (temp[6] * temp[1] - temp[0] * temp[7]) / det;
196 inverse[8] = (temp[0] * temp[4] - temp[3] * temp[1]) / det;
197
198 matrix_to_params(inverse, target);
199}
200
Todd Nguyen8493f912017-07-20 12:45:12 -0700201#if BGSPRITE_BLENDING_MODE == 0
202// swaps two YuvPixels.
Todd Nguyen302d0972017-06-16 16:16:29 -0700203static void swap_yuv(YuvPixel *a, YuvPixel *b) {
204 const YuvPixel temp = *b;
205 *b = *a;
206 *a = temp;
207}
208
209// Partitions array to find pivot index in qselect.
210static int partition(YuvPixel arr[], int left, int right, int pivot_idx) {
211 YuvPixel pivot = arr[pivot_idx];
212
213 // Move pivot to the end.
214 swap_yuv(&arr[pivot_idx], &arr[right]);
215
216 int p_idx = left;
217 for (int i = left; i < right; ++i) {
218 if (arr[i].y <= pivot.y) {
219 swap_yuv(&arr[i], &arr[p_idx]);
220 p_idx++;
221 }
222 }
223
224 swap_yuv(&arr[p_idx], &arr[right]);
225
226 return p_idx;
227}
228
229// Returns the kth element in array, partially sorted in place (quickselect).
230static YuvPixel qselect(YuvPixel arr[], int left, int right, int k) {
231 if (left >= right) {
232 return arr[left];
233 }
Todd Nguyen8493f912017-07-20 12:45:12 -0700234 unsigned int seed = (int)time(NULL);
Todd Nguyen302d0972017-06-16 16:16:29 -0700235 int pivot_idx = left + rand_r(&seed) % (right - left + 1);
236 pivot_idx = partition(arr, left, right, pivot_idx);
237
238 if (k == pivot_idx) {
239 return arr[k];
240 } else if (k < pivot_idx) {
241 return qselect(arr, left, pivot_idx - 1, k);
242 } else {
243 return qselect(arr, pivot_idx + 1, right, k);
244 }
245}
Todd Nguyen8493f912017-07-20 12:45:12 -0700246#endif // BGSPRITE_BLENDING_MODE == 0
Todd Nguyen302d0972017-06-16 16:16:29 -0700247
248// Stitches images together to create ARF and stores it in 'panorama'.
249static void stitch_images(YV12_BUFFER_CONFIG **const frames,
Todd Nguyen2fc23092017-07-11 12:00:36 -0700250 const int num_frames, const int center_idx,
251 const double **const params, const int *const x_min,
252 const int *const x_max, const int *const y_min,
253 const int *const y_max, int pano_x_min,
254 int pano_x_max, int pano_y_min, int pano_y_max,
255 YV12_BUFFER_CONFIG *panorama) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700256 const int width = pano_x_max - pano_x_min + 1;
257 const int height = pano_y_max - pano_y_min + 1;
258
259 // Create temp_pano[y][x][num_frames] stack of pixel values
260 YuvPixel ***temp_pano = aom_malloc(height * sizeof(*temp_pano));
261 for (int i = 0; i < height; ++i) {
262 temp_pano[i] = aom_malloc(width * sizeof(**temp_pano));
263 for (int j = 0; j < width; ++j) {
264 temp_pano[i][j] = aom_malloc(num_frames * sizeof(***temp_pano));
265 }
266 }
267 // Create count[y][x] to count how many values in stack for median filtering
268 int **count = aom_malloc(height * sizeof(*count));
269 for (int i = 0; i < height; ++i) {
270 count[i] = aom_calloc(width, sizeof(**count)); // counts initialized to 0
271 }
272
273 // Re-sample images onto panorama (pre-median filtering).
274 const int x_offset = -pano_x_min;
275 const int y_offset = -pano_y_min;
276 const int frame_width = frames[0]->y_width;
277 const int frame_height = frames[0]->y_height;
278 for (int i = 0; i < num_frames; ++i) {
279 // Find transforms from panorama coordinate system back to single image
280 // coordinate system for sampling.
281 int transformed_width = x_max[i] - x_min[i] + 1;
282 int transformed_height = y_max[i] - y_min[i] + 1;
283
284 double transform_matrix[MAX_PARAMDIM];
285 double transform_params[MAX_PARAMDIM - 1];
286 invert_params(params[i], transform_params);
287 params_to_matrix(transform_params, transform_matrix);
288
289#if CONFIG_HIGHBITDEPTH
290 const uint16_t *y_buffer16 = CONVERT_TO_SHORTPTR(frames[i]->y_buffer);
291 const uint16_t *u_buffer16 = CONVERT_TO_SHORTPTR(frames[i]->u_buffer);
292 const uint16_t *v_buffer16 = CONVERT_TO_SHORTPTR(frames[i]->v_buffer);
Todd Nguyen8493f912017-07-20 12:45:12 -0700293#endif // CONFIG_HIGHBITDEPTH
Todd Nguyen302d0972017-06-16 16:16:29 -0700294
295 for (int y = 0; y < transformed_height; ++y) {
296 for (int x = 0; x < transformed_width; ++x) {
297 // Do transform.
298 double xy_matrix[3] = { x + x_min[i], y + y_min[i], 1 };
299 double uv_matrix[3] = { 0 };
300 multiply_mat(transform_matrix, xy_matrix, uv_matrix, TRANSFORM_MAT_DIM,
301 TRANSFORM_MAT_DIM, 1);
Todd Nguyen8493f912017-07-20 12:45:12 -0700302
303 // Coordinates used for nearest neighbor interpolation.
Todd Nguyen302d0972017-06-16 16:16:29 -0700304 int image_x = (int)round(uv_matrix[0]);
305 int image_y = (int)round(uv_matrix[1]);
306
Todd Nguyen8493f912017-07-20 12:45:12 -0700307 // Temporary values for bilinear interpolation
308 double interpolated_yvalue = 0.0;
309 double interpolated_uvalue = 0.0;
310 double interpolated_vvalue = 0.0;
311 double interpolated_fraction = 0.0;
312 int interpolation_count = 0;
313
314#if BGSPRITE_INTERPOLATION == 1
315 // Coordintes used for bilinear interpolation.
316 double x_base;
317 double y_base;
318 double x_decimal = modf(uv_matrix[0], &x_base);
319 double y_decimal = modf(uv_matrix[1], &y_base);
320
321 if ((x_decimal > 0.2 && x_decimal < 0.8) ||
322 (y_decimal > 0.2 && y_decimal < 0.8)) {
323 for (int u = 0; u < 2; ++u) {
324 for (int v = 0; v < 2; ++v) {
325 int interp_x = (int)x_base + u;
326 int interp_y = (int)y_base + v;
327 if (interp_x >= 0 && interp_x < frame_width && interp_y >= 0 &&
328 interp_y < frame_height) {
329 interpolation_count++;
330
331 interpolated_fraction +=
332 fabs(u - x_decimal) * fabs(v - y_decimal);
333 int ychannel_idx = interp_y * frames[i]->y_stride + interp_x;
334 int uvchannel_idx = (interp_y >> frames[i]->subsampling_y) *
335 frames[i]->uv_stride +
336 (interp_x >> frames[i]->subsampling_x);
337#if CONFIG_HIGHBITDEPTH
338 if (frames[i]->flags & YV12_FLAG_HIGHBITDEPTH) {
339 interpolated_yvalue += (1 - fabs(u - x_decimal)) *
340 (1 - fabs(v - y_decimal)) *
341 y_buffer16[ychannel_idx];
342 interpolated_uvalue += (1 - fabs(u - x_decimal)) *
343 (1 - fabs(v - y_decimal)) *
344 u_buffer16[uvchannel_idx];
345 interpolated_vvalue += (1 - fabs(u - x_decimal)) *
346 (1 - fabs(v - y_decimal)) *
347 v_buffer16[uvchannel_idx];
348 } else {
349#endif // CONFIG_HIGHBITDEPTH
350 interpolated_yvalue += (1 - fabs(u - x_decimal)) *
351 (1 - fabs(v - y_decimal)) *
352 frames[i]->y_buffer[ychannel_idx];
353 interpolated_uvalue += (1 - fabs(u - x_decimal)) *
354 (1 - fabs(v - y_decimal)) *
355 frames[i]->u_buffer[uvchannel_idx];
356 interpolated_vvalue += (1 - fabs(u - x_decimal)) *
357 (1 - fabs(v - y_decimal)) *
358 frames[i]->v_buffer[uvchannel_idx];
359#if CONFIG_HIGHBITDEPTH
360 }
361#endif // CONFIG_HIGHBITDEPTH
362 }
363 }
364 }
365 }
366#endif // BGSPRITE_INTERPOLATION == 1
367
368 if (BGSPRITE_INTERPOLATION && interpolation_count > 2) {
369 if (interpolation_count != 4) {
370 interpolated_yvalue /= interpolated_fraction;
371 interpolated_uvalue /= interpolated_fraction;
372 interpolated_vvalue /= interpolated_fraction;
373 }
374 int pano_x = x + x_min[i] + x_offset;
375 int pano_y = y + y_min[i] + y_offset;
376
377#if CONFIG_HIGHBITDEPTH
378 if (frames[i]->flags & YV12_FLAG_HIGHBITDEPTH) {
379 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].y =
380 (uint16_t)interpolated_yvalue;
381 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].u =
382 (uint16_t)interpolated_uvalue;
383 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].v =
384 (uint16_t)interpolated_vvalue;
385 } else {
386#endif // CONFIG_HIGHBITDEPTH
387 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].y =
388 (uint8_t)interpolated_yvalue;
389 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].u =
390 (uint8_t)interpolated_uvalue;
391 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].v =
392 (uint8_t)interpolated_vvalue;
393#if CONFIG_HIGHBITDEPTH
394 }
395#endif // CONFIG_HIGHBITDEPTH
396 ++count[pano_y][pano_x];
397 } else if (image_x >= 0 && image_x < frame_width && image_y >= 0 &&
398 image_y < frame_height) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700399 // Place in panorama stack.
400 int pano_x = x + x_min[i] + x_offset;
401 int pano_y = y + y_min[i] + y_offset;
402
403 int ychannel_idx = image_y * frames[i]->y_stride + image_x;
404 int uvchannel_idx =
405 (image_y >> frames[i]->subsampling_y) * frames[i]->uv_stride +
406 (image_x >> frames[i]->subsampling_x);
407#if CONFIG_HIGHBITDEPTH
408 if (frames[i]->flags & YV12_FLAG_HIGHBITDEPTH) {
409 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].y =
410 y_buffer16[ychannel_idx];
411 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].u =
412 u_buffer16[uvchannel_idx];
413 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].v =
414 v_buffer16[uvchannel_idx];
415 } else {
416#endif // CONFIG_HIGHBITDEPTH
417 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].y =
418 frames[i]->y_buffer[ychannel_idx];
419 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].u =
420 frames[i]->u_buffer[uvchannel_idx];
421 temp_pano[pano_y][pano_x][count[pano_y][pano_x]].v =
422 frames[i]->v_buffer[uvchannel_idx];
Todd Nguyen302d0972017-06-16 16:16:29 -0700423#if CONFIG_HIGHBITDEPTH
424 }
425#endif // CONFIG_HIGHBITDEPTH
Todd Nguyen8493f912017-07-20 12:45:12 -0700426 ++count[pano_y][pano_x];
Todd Nguyen302d0972017-06-16 16:16:29 -0700427 }
428 }
429 }
430 }
431
Todd Nguyen8493f912017-07-20 12:45:12 -0700432#if BGSPRITE_BLENDING_MODE == 1
433 // Apply mean filtering and store result in temp_pano[y][x][0].
434 for (int y = 0; y < height; ++y) {
435 for (int x = 0; x < width; ++x) {
436 if (count[y][x] == 0) {
437 // Just make the pixel black.
438 // TODO(toddnguyen): Color the pixel with nearest neighbor
439 } else {
440 // Find
441 uint32_t y_sum = 0;
442 uint32_t u_sum = 0;
443 uint32_t v_sum = 0;
444 for (int i = 0; i < count[y][x]; ++i) {
445 y_sum += temp_pano[y][x][i].y;
446 u_sum += temp_pano[y][x][i].u;
447 v_sum += temp_pano[y][x][i].v;
448 }
449
450 const uint32_t unsigned_count = (uint32_t)count[y][x];
451
452#if CONFIG_HIGHBITDEPTH
453 if (panorama->flags & YV12_FLAG_HIGHBITDEPTH) {
454 temp_pano[y][x][0].y = (uint16_t)OD_DIVU(y_sum, unsigned_count);
455 temp_pano[y][x][0].u = (uint16_t)OD_DIVU(u_sum, unsigned_count);
456 temp_pano[y][x][0].v = (uint16_t)OD_DIVU(v_sum, unsigned_count);
457 } else {
458#endif // CONFIG_HIGHBITDEPTH
459 temp_pano[y][x][0].y = (uint8_t)OD_DIVU(y_sum, unsigned_count);
460 temp_pano[y][x][0].u = (uint8_t)OD_DIVU(u_sum, unsigned_count);
461 temp_pano[y][x][0].v = (uint8_t)OD_DIVU(v_sum, unsigned_count);
462#if CONFIG_HIGHBITDEPTH
463 }
464#endif // CONFIG_HIGHBITDEPTH
465 }
466 }
467 }
468#else
Todd Nguyen302d0972017-06-16 16:16:29 -0700469 // Apply median filtering using quickselect.
470 for (int y = 0; y < height; ++y) {
471 for (int x = 0; x < width; ++x) {
472 if (count[y][x] == 0) {
473 // Just make the pixel black.
474 // TODO(toddnguyen): Color the pixel with nearest neighbor
475 } else {
476 // Find
477 const int median_idx = (int)floor(count[y][x] / 2);
478 YuvPixel median =
479 qselect(temp_pano[y][x], 0, count[y][x] - 1, median_idx);
480
481 // Make the median value the 0th index for UV subsampling later
482 temp_pano[y][x][0] = median;
483 assert(median.y == temp_pano[y][x][0].y &&
484 median.u == temp_pano[y][x][0].u &&
485 median.v == temp_pano[y][x][0].v);
486 }
487 }
488 }
Todd Nguyen8493f912017-07-20 12:45:12 -0700489#endif // BGSPRITE_BLENDING_MODE == 1
Todd Nguyen302d0972017-06-16 16:16:29 -0700490
Todd Nguyen2fc23092017-07-11 12:00:36 -0700491 // NOTE(toddnguyen): Right now the ARF in the cpi struct is fixed size at
492 // the same size as the frames. For now, we crop the generated panorama.
Todd Nguyen8493f912017-07-20 12:45:12 -0700493 // assert(panorama->y_width < width && panorama->y_height < height);
Todd Nguyen2fc23092017-07-11 12:00:36 -0700494 const int crop_x_offset = x_min[center_idx] + x_offset;
495 const int crop_y_offset = y_min[center_idx] + y_offset;
Todd Nguyen302d0972017-06-16 16:16:29 -0700496
497#if CONFIG_HIGHBITDEPTH
498 if (panorama->flags & YV12_FLAG_HIGHBITDEPTH) {
499 // Use median Y value.
500 uint16_t *pano_y_buffer16 = CONVERT_TO_SHORTPTR(panorama->y_buffer);
501 for (int y = 0; y < panorama->y_height; ++y) {
502 for (int x = 0; x < panorama->y_width; ++x) {
503 const int ychannel_idx = y * panorama->y_stride + x;
504 if (count[y + crop_y_offset][x + crop_x_offset] > 0) {
505 pano_y_buffer16[ychannel_idx] =
506 temp_pano[y + crop_y_offset][x + crop_x_offset][0].y;
507 } else {
508 pano_y_buffer16[ychannel_idx] = 0;
509 }
510 }
511 }
512
513 // UV subsampling with median UV values
514 uint16_t *pano_u_buffer16 = CONVERT_TO_SHORTPTR(panorama->u_buffer);
515 uint16_t *pano_v_buffer16 = CONVERT_TO_SHORTPTR(panorama->v_buffer);
516
517 for (int y = 0; y < panorama->uv_height; ++y) {
518 for (int x = 0; x < panorama->uv_width; ++x) {
519 uint32_t avg_count = 0;
520 uint32_t u_sum = 0;
521 uint32_t v_sum = 0;
522
523 // Look at surrounding pixels for subsampling
524 for (int s_x = 0; s_x < panorama->subsampling_x + 1; ++s_x) {
525 for (int s_y = 0; s_y < panorama->subsampling_y + 1; ++s_y) {
526 int y_sample = crop_y_offset + (y << panorama->subsampling_y) + s_y;
527 int x_sample = crop_x_offset + (x << panorama->subsampling_x) + s_x;
528 if (y_sample > 0 && y_sample < height && x_sample > 0 &&
529 x_sample < width && count[y_sample][x_sample] > 0) {
530 u_sum += temp_pano[y_sample][x_sample][0].u;
531 v_sum += temp_pano[y_sample][x_sample][0].v;
532 avg_count++;
533 }
534 }
535 }
536
537 const int uvchannel_idx = y * panorama->uv_stride + x;
538 if (avg_count != 0) {
539 pano_u_buffer16[uvchannel_idx] = (uint16_t)OD_DIVU(u_sum, avg_count);
540 pano_v_buffer16[uvchannel_idx] = (uint16_t)OD_DIVU(v_sum, avg_count);
541 } else {
542 pano_u_buffer16[uvchannel_idx] = 0;
543 pano_v_buffer16[uvchannel_idx] = 0;
544 }
545 }
546 }
547 } else {
548#endif // CONFIG_HIGHBITDEPTH
549 // Use median Y value.
550 for (int y = 0; y < panorama->y_height; ++y) {
551 for (int x = 0; x < panorama->y_width; ++x) {
552 const int ychannel_idx = y * panorama->y_stride + x;
553 if (count[y + crop_y_offset][x + crop_x_offset] > 0) {
554 panorama->y_buffer[ychannel_idx] =
555 temp_pano[y + crop_y_offset][x + crop_x_offset][0].y;
556 } else {
557 panorama->y_buffer[ychannel_idx] = 0;
558 }
559 }
560 }
561
562 // UV subsampling with median UV values
563 for (int y = 0; y < panorama->uv_height; ++y) {
564 for (int x = 0; x < panorama->uv_width; ++x) {
565 uint16_t avg_count = 0;
566 uint16_t u_sum = 0;
567 uint16_t v_sum = 0;
568
569 // Look at surrounding pixels for subsampling
570 for (int s_x = 0; s_x < panorama->subsampling_x + 1; ++s_x) {
571 for (int s_y = 0; s_y < panorama->subsampling_y + 1; ++s_y) {
572 int y_sample = crop_y_offset + (y << panorama->subsampling_y) + s_y;
573 int x_sample = crop_x_offset + (x << panorama->subsampling_x) + s_x;
574 if (y_sample > 0 && y_sample < height && x_sample > 0 &&
575 x_sample < width && count[y_sample][x_sample] > 0) {
576 u_sum += temp_pano[y_sample][x_sample][0].u;
577 v_sum += temp_pano[y_sample][x_sample][0].v;
578 avg_count++;
579 }
580 }
581 }
582
583 const int uvchannel_idx = y * panorama->uv_stride + x;
584 if (avg_count != 0) {
585 panorama->u_buffer[uvchannel_idx] =
586 (uint8_t)OD_DIVU(u_sum, avg_count);
587 panorama->v_buffer[uvchannel_idx] =
588 (uint8_t)OD_DIVU(v_sum, avg_count);
589 } else {
590 panorama->u_buffer[uvchannel_idx] = 0;
591 panorama->v_buffer[uvchannel_idx] = 0;
592 }
593 }
594 }
595#if CONFIG_HIGHBITDEPTH
596 }
597#endif // CONFIG_HIGHBITDEPTH
598
599 for (int i = 0; i < height; ++i) {
600 for (int j = 0; j < width; ++j) {
601 aom_free(temp_pano[i][j]);
602 }
603 aom_free(temp_pano[i]);
604 aom_free(count[i]);
605 }
606 aom_free(count);
607 aom_free(temp_pano);
608}
609
610int av1_background_sprite(AV1_COMP *cpi, int distance) {
611 YV12_BUFFER_CONFIG *frames[MAX_LAG_BUFFERS] = { NULL };
Todd Nguyen302d0972017-06-16 16:16:29 -0700612 static const double identity_params[MAX_PARAMDIM - 1] = {
613 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0
614 };
615
Todd Nguyen2fc23092017-07-11 12:00:36 -0700616 const int frames_after_arf =
617 av1_lookahead_depth(cpi->lookahead) - distance - 1;
618 int frames_fwd = (cpi->oxcf.arnr_max_frames - 1) >> 1;
619 int frames_bwd;
620
621 // Define the forward and backwards filter limits for this arnr group.
622 if (frames_fwd > frames_after_arf) frames_fwd = frames_after_arf;
623 if (frames_fwd > distance) frames_fwd = distance;
624 frames_bwd = frames_fwd;
625
626#if CONFIG_EXT_REFS
627 const GF_GROUP *const gf_group = &cpi->twopass.gf_group;
628 if (gf_group->rf_level[gf_group->index] == GF_ARF_LOW) {
629 cpi->alt_ref_buffer = av1_lookahead_peek(cpi->lookahead, distance)->img;
630 cpi->is_arf_filter_off[gf_group->arf_update_idx[gf_group->index]] = 1;
631 frames_fwd = 0;
632 frames_bwd = 0;
633 } else {
634 cpi->is_arf_filter_off[gf_group->arf_update_idx[gf_group->index]] = 0;
635 }
636#endif // CONFIG_EXT_REFS
637
638 const int start_frame = distance + frames_fwd;
639 const int frames_to_stitch = frames_bwd + 1 + frames_fwd;
640
Todd Nguyen302d0972017-06-16 16:16:29 -0700641 // Get frames to be included in background sprite.
Todd Nguyen2fc23092017-07-11 12:00:36 -0700642 for (int frame = 0; frame < frames_to_stitch; ++frame) {
643 const int which_buffer = start_frame - frame;
644 struct lookahead_entry *buf =
645 av1_lookahead_peek(cpi->lookahead, which_buffer);
646 frames[frames_to_stitch - 1 - frame] = &buf->img;
Todd Nguyen302d0972017-06-16 16:16:29 -0700647 }
648
Todd Nguyen8493f912017-07-20 12:45:12 -0700649 YV12_BUFFER_CONFIG temp_bg;
650 memset(&temp_bg, 0, sizeof(temp_bg));
651 aom_alloc_frame_buffer(&temp_bg, frames[0]->y_width, frames[0]->y_height,
652 frames[0]->subsampling_x, frames[0]->subsampling_y,
653#if CONFIG_HIGHBITDEPTH
654 frames[0]->flags & YV12_FLAG_HIGHBITDEPTH,
655#endif
656 frames[0]->border, 0);
657 aom_yv12_copy_frame(frames[0], &temp_bg);
658 temp_bg.bit_depth = frames[0]->bit_depth;
659
Todd Nguyen302d0972017-06-16 16:16:29 -0700660 // Allocate empty arrays for parameters between frames.
Todd Nguyen2fc23092017-07-11 12:00:36 -0700661 double **params = aom_malloc(frames_to_stitch * sizeof(*params));
662 for (int i = 0; i < frames_to_stitch; ++i) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700663 params[i] = aom_malloc(sizeof(identity_params));
664 memcpy(params[i], identity_params, sizeof(identity_params));
665 }
666
667 // Use global motion to find affine transformations between frames.
668 // params[i] will have the transform from frame[i] to frame[i-1].
669 // params[0] will have the identity matrix because it has no previous frame.
670 TransformationType model = AFFINE;
Todd Nguyen8493f912017-07-20 12:45:12 -0700671 int inliers_by_motion[RANSAC_NUM_MOTIONS];
Todd Nguyen2fc23092017-07-11 12:00:36 -0700672 for (int frame = 0; frame < frames_to_stitch - 1; ++frame) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700673 const int global_motion_ret = compute_global_motion_feature_based(
674 model, frames[frame + 1], frames[frame],
675#if CONFIG_HIGHBITDEPTH
676 cpi->common.bit_depth,
677#endif // CONFIG_HIGHBITDEPTH
678 inliers_by_motion, params[frame + 1], RANSAC_NUM_MOTIONS);
679
680 // Quit if global motion had an error.
681 if (global_motion_ret == 0) {
Todd Nguyen2fc23092017-07-11 12:00:36 -0700682 for (int i = 0; i < frames_to_stitch; ++i) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700683 aom_free(params[i]);
684 }
685 aom_free(params);
686 return 1;
687 }
688 }
689
690 // Compound the transformation parameters.
Todd Nguyen2fc23092017-07-11 12:00:36 -0700691 for (int i = 1; i < frames_to_stitch; ++i) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700692 multiply_params(params[i - 1], params[i], params[i]);
693 }
694
695 // Compute frame limits for final stitched images.
696 int pano_x_max = INT_MIN;
697 int pano_x_min = INT_MAX;
698 int pano_y_max = INT_MIN;
699 int pano_y_min = INT_MAX;
Todd Nguyen2fc23092017-07-11 12:00:36 -0700700 int *x_max = aom_malloc(frames_to_stitch * sizeof(*x_max));
701 int *x_min = aom_malloc(frames_to_stitch * sizeof(*x_min));
702 int *y_max = aom_malloc(frames_to_stitch * sizeof(*y_max));
703 int *y_min = aom_malloc(frames_to_stitch * sizeof(*y_min));
Todd Nguyen302d0972017-06-16 16:16:29 -0700704
705 find_limits(cpi->initial_width, cpi->initial_height,
Todd Nguyen2fc23092017-07-11 12:00:36 -0700706 (const double **const)params, frames_to_stitch, x_min, x_max,
707 y_min, y_max, &pano_x_min, &pano_x_max, &pano_y_min, &pano_y_max);
Todd Nguyen302d0972017-06-16 16:16:29 -0700708
Todd Nguyen2fc23092017-07-11 12:00:36 -0700709 // Center panorama on the ARF.
Todd Nguyen8493f912017-07-20 12:45:12 -0700710 const int center_idx = frames_bwd;
Todd Nguyen2fc23092017-07-11 12:00:36 -0700711 assert(center_idx >= 0 && center_idx < frames_to_stitch);
Todd Nguyen302d0972017-06-16 16:16:29 -0700712
713 // Recompute transformations to adjust to center image.
714 // Invert center image's transform.
715 double inverse[MAX_PARAMDIM - 1] = { 0 };
716 invert_params(params[center_idx], inverse);
717
718 // Multiply the inverse to all transformation parameters.
Todd Nguyen2fc23092017-07-11 12:00:36 -0700719 for (int i = 0; i < frames_to_stitch; ++i) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700720 multiply_params(inverse, params[i], params[i]);
721 }
722
723 // Recompute frame limits for new adjusted center.
724 find_limits(cpi->initial_width, cpi->initial_height,
Todd Nguyen2fc23092017-07-11 12:00:36 -0700725 (const double **const)params, frames_to_stitch, x_min, x_max,
726 y_min, y_max, &pano_x_min, &pano_x_max, &pano_y_min, &pano_y_max);
Todd Nguyen302d0972017-06-16 16:16:29 -0700727
728 // Stitch Images.
Todd Nguyen2fc23092017-07-11 12:00:36 -0700729 stitch_images(frames, frames_to_stitch, center_idx,
730 (const double **const)params, x_min, x_max, y_min, y_max,
Todd Nguyen8493f912017-07-20 12:45:12 -0700731 pano_x_min, pano_x_max, pano_y_min, pano_y_max, &temp_bg);
732
733 // Apply temporal filter.
734 av1_temporal_filter(cpi, &temp_bg, distance);
Todd Nguyen302d0972017-06-16 16:16:29 -0700735
736 // Free memory.
Todd Nguyen8493f912017-07-20 12:45:12 -0700737 aom_free_frame_buffer(&temp_bg);
Todd Nguyen2fc23092017-07-11 12:00:36 -0700738 for (int i = 0; i < frames_to_stitch; ++i) {
Todd Nguyen302d0972017-06-16 16:16:29 -0700739 aom_free(params[i]);
740 }
741 aom_free(params);
742 aom_free(x_max);
743 aom_free(x_min);
744 aom_free(y_max);
745 aom_free(y_min);
746
747 return 0;
748}