| /* | |
| * Copyright (c) 2021, Alliance for Open Media. All rights reserved | |
| * | |
| * This source code is subject to the terms of the BSD 3-Clause Clear License | |
| * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear | |
| * License was not distributed with this source code in the LICENSE file, you | |
| * can obtain it at aomedia.org/license/software-license/bsd-3-c-c/. If the | |
| * Alliance for Open Media Patent License 1.0 was not distributed with this | |
| * source code in the PATENTS file, you can obtain it at | |
| * aomedia.org/license/patent-license/. | |
| */ | |
| #include <math.h> | |
| #include <limits.h> | |
| #include "config/aom_config.h" | |
| #include "aom_dsp/mathutils.h" | |
| #include "av1/common/av1_common_int.h" | |
| #include "av1/encoder/encoder.h" | |
| #include "av1/encoder/optical_flow.h" | |
| #include "av1/encoder/reconinter_enc.h" | |
| #include "aom_mem/aom_mem.h" | |
| #if CONFIG_OPTICAL_FLOW_API | |
| typedef struct LOCALMV { | |
| double row; | |
| double col; | |
| } LOCALMV; | |
| // Computes optical flow by applying algorithm at | |
| // multiple pyramid levels of images (lower-resolution, smoothed images) | |
| // This accounts for larger motions. | |
| // Inputs: | |
| // from_frame Frame buffer. | |
| // to_frame: Frame buffer. MVs point from_frame -> to_frame. | |
| // from_frame_idx: Index of from_frame. | |
| // to_frame_idx: Index of to_frame. Return all zero MVs when idx are equal. | |
| // bit_depth: | |
| // opfl_params: contains algorithm-specific parameters. | |
| // mv_filter: MV_FILTER_NONE, MV_FILTER_SMOOTH, or MV_FILTER_MEDIAN. | |
| // method: LUCAS_KANADE, | |
| // mvs: pointer to MVs. Contains initialization, and modified | |
| // based on optical flow. Must have | |
| // dimensions = from_frame->y_crop_width * from_frame->y_crop_height | |
| void optical_flow(const YV12_BUFFER_CONFIG *from_frame, | |
| const YV12_BUFFER_CONFIG *to_frame, const int from_frame_idx, | |
| const int to_frame_idx, const int bit_depth, | |
| const OPFL_PARAMS *opfl_params, | |
| const MV_FILTER_TYPE mv_filter, const OPTFLOW_METHOD method, | |
| MV *mvs) { | |
| // parameters to be used in later implementations | |
| (void)to_frame; | |
| (void)bit_depth; | |
| (void)opfl_params; | |
| (void)mv_filter; | |
| (void)method; | |
| const int frame_height = from_frame->y_crop_height; | |
| const int frame_width = from_frame->y_crop_width; | |
| // TODO(any): deal with the case where frames are not of the same dimensions | |
| assert(frame_height == to_frame->y_crop_height && | |
| frame_width == to_frame->y_crop_width); | |
| if (from_frame_idx == to_frame_idx) { | |
| // immediately return all zero mvs when frame indices are equal | |
| for (int yy = 0; yy < frame_height; yy++) { | |
| for (int xx = 0; xx < frame_width; xx++) { | |
| MV mv = { .row = 0, .col = 0 }; | |
| mvs[yy * frame_width + xx] = mv; | |
| } | |
| } | |
| return; | |
| } | |
| // Initialize double mvs based on input parameter mvs array | |
| LOCALMV *localmvs = aom_malloc(frame_height * frame_width * sizeof(LOCALMV)); | |
| for (int i = 0; i < frame_width * frame_height; i++) { | |
| MV mv = mvs[i]; | |
| LOCALMV localmv = { .row = ((double)mv.row) / 8, | |
| .col = ((double)mv.col) / 8 }; | |
| localmvs[i] = localmv; | |
| } | |
| // Apply optical flow algorithm | |
| // Update original mvs array | |
| for (int j = 0; j < frame_height; j++) { | |
| for (int i = 0; i < frame_width; i++) { | |
| int idx = j * frame_width + i; | |
| int new_x = (int)(localmvs[idx].row + i); | |
| int new_y = (int)(localmvs[idx].col + j); | |
| if ((fabs(localmvs[idx].row) >= 0.125 || | |
| fabs(localmvs[idx].col) >= 0.125)) { | |
| // if mv points outside of frame (lost feature), keep old mv. | |
| if (new_x < frame_width && new_x >= 0 && new_y < frame_height && | |
| new_y >= 0) { | |
| MV mv = { .row = (int16_t)round(8 * localmvs[idx].row), | |
| .col = (int16_t)round(8 * localmvs[idx].col) }; | |
| mvs[idx] = mv; | |
| } | |
| } | |
| } | |
| } | |
| aom_free(localmvs); | |
| } | |
| #endif |