blob: 284d1bd7b8a237c36049c6e4a57f691598ba49e5 [file] [log] [blame]
/*
* 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 "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 ImagePyramid *pyr, CornerList *corners) {
const uint8_t *buf = pyr->layers[0].buffer;
int width = pyr->layers[0].width;
int height = pyr->layers[0].height;
int stride = pyr->layers[0].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
if (num_corners != 0) {
memcpy(corners->corners, frame_corners_xy,
sizeof(*frame_corners_xy) * num_corners);
}
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;
corners->corners[2 * copied_corners + 1] = frame_corners_xy[i].y;
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 ImagePyramid *pyr, CornerList *corners) {
assert(corners);
#if CONFIG_MULTITHREAD
pthread_mutex_lock(&corners->mutex);
#endif // CONFIG_MULTITHREAD
if (!corners->valid) {
corners->valid = compute_corner_list(pyr, 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);
}
}