|  | /* | 
|  | * Copyright (c) 2016, Alliance for Open Media. All rights reserved. | 
|  | * | 
|  | * This source code is subject to the terms of the BSD 2 Clause License and | 
|  | * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License | 
|  | * was not distributed with this source code in the LICENSE file, you can | 
|  | * obtain it at www.aomedia.org/license/software. 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 www.aomedia.org/license/patent. | 
|  | */ | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <memory.h> | 
|  | #include <math.h> | 
|  | #include <assert.h> | 
|  |  | 
|  | #include "third_party/fastfeat/fast.h" | 
|  |  | 
|  | #include "aom_dsp/aom_dsp_common.h" | 
|  | #include "aom_dsp/flow_estimation/corner_detect.h" | 
|  | #include "aom_mem/aom_mem.h" | 
|  | #include "aom_util/aom_pthread.h" | 
|  | #include "av1/common/common.h" | 
|  |  | 
|  | #define FAST_BARRIER 18 | 
|  |  | 
|  | size_t av1_get_corner_list_size(void) { return sizeof(CornerList); } | 
|  |  | 
|  | CornerList *av1_alloc_corner_list(void) { | 
|  | CornerList *corners = (CornerList *)aom_calloc(1, sizeof(*corners)); | 
|  | if (!corners) { | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | corners->valid = false; | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_init(&corners->mutex, NULL); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  | return corners; | 
|  | } | 
|  |  | 
|  | static bool compute_corner_list(const YV12_BUFFER_CONFIG *frame, int bit_depth, | 
|  | int downsample_level, CornerList *corners) { | 
|  | ImagePyramid *pyr = frame->y_pyramid; | 
|  | const int layers = | 
|  | aom_compute_pyramid(frame, bit_depth, downsample_level + 1, pyr); | 
|  |  | 
|  | if (layers < 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Clamp downsampling ratio base on max number of layers allowed | 
|  | // for this frame size | 
|  | downsample_level = layers - 1; | 
|  |  | 
|  | const uint8_t *buf = pyr->layers[downsample_level].buffer; | 
|  | int width = pyr->layers[downsample_level].width; | 
|  | int height = pyr->layers[downsample_level].height; | 
|  | int stride = pyr->layers[downsample_level].stride; | 
|  |  | 
|  | int *scores = NULL; | 
|  | int num_corners; | 
|  | xy *const frame_corners_xy = aom_fast9_detect_nonmax( | 
|  | buf, width, height, stride, FAST_BARRIER, &scores, &num_corners); | 
|  | if (num_corners < 0) return false; | 
|  |  | 
|  | if (num_corners <= MAX_CORNERS) { | 
|  | // Use all detected corners | 
|  | for (int i = 0; i < num_corners; i++) { | 
|  | corners->corners[2 * i + 0] = | 
|  | frame_corners_xy[i].x * (1 << downsample_level); | 
|  | corners->corners[2 * i + 1] = | 
|  | frame_corners_xy[i].y * (1 << downsample_level); | 
|  | } | 
|  | corners->num_corners = num_corners; | 
|  | } else { | 
|  | // There are more than MAX_CORNERS corners avilable, so pick out a subset | 
|  | // of the sharpest corners, as these will be the most useful for flow | 
|  | // estimation | 
|  | int histogram[256]; | 
|  | av1_zero(histogram); | 
|  | for (int i = 0; i < num_corners; i++) { | 
|  | assert(FAST_BARRIER <= scores[i] && scores[i] <= 255); | 
|  | histogram[scores[i]] += 1; | 
|  | } | 
|  |  | 
|  | int threshold = -1; | 
|  | int found_corners = 0; | 
|  | for (int bucket = 255; bucket >= 0; bucket--) { | 
|  | if (found_corners + histogram[bucket] > MAX_CORNERS) { | 
|  | // Set threshold here | 
|  | threshold = bucket; | 
|  | break; | 
|  | } | 
|  | found_corners += histogram[bucket]; | 
|  | } | 
|  | assert(threshold != -1 && "Failed to select a valid threshold"); | 
|  |  | 
|  | int copied_corners = 0; | 
|  | for (int i = 0; i < num_corners; i++) { | 
|  | if (scores[i] > threshold) { | 
|  | assert(copied_corners < MAX_CORNERS); | 
|  | corners->corners[2 * copied_corners + 0] = | 
|  | frame_corners_xy[i].x * (1 << downsample_level); | 
|  | corners->corners[2 * copied_corners + 1] = | 
|  | frame_corners_xy[i].y * (1 << downsample_level); | 
|  | copied_corners += 1; | 
|  | } | 
|  | } | 
|  | assert(copied_corners == found_corners); | 
|  | corners->num_corners = copied_corners; | 
|  | } | 
|  |  | 
|  | free(scores); | 
|  | free(frame_corners_xy); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool av1_compute_corner_list(const YV12_BUFFER_CONFIG *frame, int bit_depth, | 
|  | int downsample_level, CornerList *corners) { | 
|  | assert(corners); | 
|  |  | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_lock(&corners->mutex); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  |  | 
|  | if (!corners->valid) { | 
|  | corners->valid = | 
|  | compute_corner_list(frame, bit_depth, downsample_level, corners); | 
|  | } | 
|  | bool valid = corners->valid; | 
|  |  | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_unlock(&corners->mutex); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  | return valid; | 
|  | } | 
|  |  | 
|  | #ifndef NDEBUG | 
|  | // Check if a corner list has already been computed. | 
|  | // This is mostly a debug helper - as it is necessary to hold corners->mutex | 
|  | // while reading the valid flag, we cannot just write: | 
|  | //   assert(corners->valid); | 
|  | // This function allows the check to be correctly written as: | 
|  | //   assert(aom_is_corner_list_valid(corners)); | 
|  | bool aom_is_corner_list_valid(CornerList *corners) { | 
|  | assert(corners); | 
|  |  | 
|  | // Per the comments in the CornerList struct, we must take this mutex | 
|  | // before reading or writing the "valid" flag, and hold it while computing | 
|  | // the pyramid, to ensure proper behaviour if multiple threads call this | 
|  | // function simultaneously | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_lock(&corners->mutex); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  |  | 
|  | bool valid = corners->valid; | 
|  |  | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_unlock(&corners->mutex); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  |  | 
|  | return valid; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void av1_invalidate_corner_list(CornerList *corners) { | 
|  | if (corners) { | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_lock(&corners->mutex); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  | corners->valid = false; | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_unlock(&corners->mutex); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  | } | 
|  | } | 
|  |  | 
|  | void av1_free_corner_list(CornerList *corners) { | 
|  | if (corners) { | 
|  | #if CONFIG_MULTITHREAD | 
|  | pthread_mutex_destroy(&corners->mutex); | 
|  | #endif  // CONFIG_MULTITHREAD | 
|  | aom_free(corners); | 
|  | } | 
|  | } |