xref: /aosp_15_r20/external/libaom/aom_dsp/flow_estimation/corner_detect.c (revision 77c1e3ccc04c968bd2bc212e87364f250e820521)
1 /*
2  * Copyright (c) 2016, 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 #include <stdlib.h>
13 #include <stdio.h>
14 #include <memory.h>
15 #include <math.h>
16 #include <assert.h>
17 
18 #include "third_party/fastfeat/fast.h"
19 
20 #include "aom_dsp/aom_dsp_common.h"
21 #include "aom_dsp/flow_estimation/corner_detect.h"
22 #include "aom_mem/aom_mem.h"
23 #include "aom_util/aom_pthread.h"
24 #include "av1/common/common.h"
25 
26 #define FAST_BARRIER 18
27 
av1_get_corner_list_size(void)28 size_t av1_get_corner_list_size(void) { return sizeof(CornerList); }
29 
av1_alloc_corner_list(void)30 CornerList *av1_alloc_corner_list(void) {
31   CornerList *corners = (CornerList *)aom_calloc(1, sizeof(*corners));
32   if (!corners) {
33     return NULL;
34   }
35 
36   corners->valid = false;
37 #if CONFIG_MULTITHREAD
38   pthread_mutex_init(&corners->mutex, NULL);
39 #endif  // CONFIG_MULTITHREAD
40   return corners;
41 }
42 
compute_corner_list(const YV12_BUFFER_CONFIG * frame,int bit_depth,int downsample_level,CornerList * corners)43 static bool compute_corner_list(const YV12_BUFFER_CONFIG *frame, int bit_depth,
44                                 int downsample_level, CornerList *corners) {
45   ImagePyramid *pyr = frame->y_pyramid;
46   const int layers =
47       aom_compute_pyramid(frame, bit_depth, downsample_level + 1, pyr);
48 
49   if (layers < 0) {
50     return false;
51   }
52 
53   // Clamp downsampling ratio base on max number of layers allowed
54   // for this frame size
55   downsample_level = layers - 1;
56 
57   const uint8_t *buf = pyr->layers[downsample_level].buffer;
58   int width = pyr->layers[downsample_level].width;
59   int height = pyr->layers[downsample_level].height;
60   int stride = pyr->layers[downsample_level].stride;
61 
62   int *scores = NULL;
63   int num_corners;
64   xy *const frame_corners_xy = aom_fast9_detect_nonmax(
65       buf, width, height, stride, FAST_BARRIER, &scores, &num_corners);
66   if (num_corners < 0) return false;
67 
68   if (num_corners <= MAX_CORNERS) {
69     // Use all detected corners
70     for (int i = 0; i < num_corners; i++) {
71       corners->corners[2 * i + 0] =
72           frame_corners_xy[i].x * (1 << downsample_level);
73       corners->corners[2 * i + 1] =
74           frame_corners_xy[i].y * (1 << downsample_level);
75     }
76     corners->num_corners = num_corners;
77   } else {
78     // There are more than MAX_CORNERS corners avilable, so pick out a subset
79     // of the sharpest corners, as these will be the most useful for flow
80     // estimation
81     int histogram[256];
82     av1_zero(histogram);
83     for (int i = 0; i < num_corners; i++) {
84       assert(FAST_BARRIER <= scores[i] && scores[i] <= 255);
85       histogram[scores[i]] += 1;
86     }
87 
88     int threshold = -1;
89     int found_corners = 0;
90     for (int bucket = 255; bucket >= 0; bucket--) {
91       if (found_corners + histogram[bucket] > MAX_CORNERS) {
92         // Set threshold here
93         threshold = bucket;
94         break;
95       }
96       found_corners += histogram[bucket];
97     }
98     assert(threshold != -1 && "Failed to select a valid threshold");
99 
100     int copied_corners = 0;
101     for (int i = 0; i < num_corners; i++) {
102       if (scores[i] > threshold) {
103         assert(copied_corners < MAX_CORNERS);
104         corners->corners[2 * copied_corners + 0] =
105             frame_corners_xy[i].x * (1 << downsample_level);
106         corners->corners[2 * copied_corners + 1] =
107             frame_corners_xy[i].y * (1 << downsample_level);
108         copied_corners += 1;
109       }
110     }
111     assert(copied_corners == found_corners);
112     corners->num_corners = copied_corners;
113   }
114 
115   free(scores);
116   free(frame_corners_xy);
117   return true;
118 }
119 
av1_compute_corner_list(const YV12_BUFFER_CONFIG * frame,int bit_depth,int downsample_level,CornerList * corners)120 bool av1_compute_corner_list(const YV12_BUFFER_CONFIG *frame, int bit_depth,
121                              int downsample_level, CornerList *corners) {
122   assert(corners);
123 
124 #if CONFIG_MULTITHREAD
125   pthread_mutex_lock(&corners->mutex);
126 #endif  // CONFIG_MULTITHREAD
127 
128   if (!corners->valid) {
129     corners->valid =
130         compute_corner_list(frame, bit_depth, downsample_level, corners);
131   }
132   bool valid = corners->valid;
133 
134 #if CONFIG_MULTITHREAD
135   pthread_mutex_unlock(&corners->mutex);
136 #endif  // CONFIG_MULTITHREAD
137   return valid;
138 }
139 
140 #ifndef NDEBUG
141 // Check if a corner list has already been computed.
142 // This is mostly a debug helper - as it is necessary to hold corners->mutex
143 // while reading the valid flag, we cannot just write:
144 //   assert(corners->valid);
145 // This function allows the check to be correctly written as:
146 //   assert(aom_is_corner_list_valid(corners));
aom_is_corner_list_valid(CornerList * corners)147 bool aom_is_corner_list_valid(CornerList *corners) {
148   assert(corners);
149 
150   // Per the comments in the CornerList struct, we must take this mutex
151   // before reading or writing the "valid" flag, and hold it while computing
152   // the pyramid, to ensure proper behaviour if multiple threads call this
153   // function simultaneously
154 #if CONFIG_MULTITHREAD
155   pthread_mutex_lock(&corners->mutex);
156 #endif  // CONFIG_MULTITHREAD
157 
158   bool valid = corners->valid;
159 
160 #if CONFIG_MULTITHREAD
161   pthread_mutex_unlock(&corners->mutex);
162 #endif  // CONFIG_MULTITHREAD
163 
164   return valid;
165 }
166 #endif
167 
av1_invalidate_corner_list(CornerList * corners)168 void av1_invalidate_corner_list(CornerList *corners) {
169   if (corners) {
170 #if CONFIG_MULTITHREAD
171     pthread_mutex_lock(&corners->mutex);
172 #endif  // CONFIG_MULTITHREAD
173     corners->valid = false;
174 #if CONFIG_MULTITHREAD
175     pthread_mutex_unlock(&corners->mutex);
176 #endif  // CONFIG_MULTITHREAD
177   }
178 }
179 
av1_free_corner_list(CornerList * corners)180 void av1_free_corner_list(CornerList *corners) {
181   if (corners) {
182 #if CONFIG_MULTITHREAD
183     pthread_mutex_destroy(&corners->mutex);
184 #endif  // CONFIG_MULTITHREAD
185     aom_free(corners);
186   }
187 }
188