| /* |
| * 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 "av1/common/av1_common_int.h" |
| #include "av1/encoder/encoder.h" |
| #include "aom_dsp/mathutils.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 |