xref: /aosp_15_r20/external/libgav1/src/warp_prediction.cc (revision 095378508e87ed692bf8dfeb34008b65b3735891)
1*09537850SAkhilesh Sanikop // Copyright 2019 The libgav1 Authors
2*09537850SAkhilesh Sanikop //
3*09537850SAkhilesh Sanikop // Licensed under the Apache License, Version 2.0 (the "License");
4*09537850SAkhilesh Sanikop // you may not use this file except in compliance with the License.
5*09537850SAkhilesh Sanikop // You may obtain a copy of the License at
6*09537850SAkhilesh Sanikop //
7*09537850SAkhilesh Sanikop //      http://www.apache.org/licenses/LICENSE-2.0
8*09537850SAkhilesh Sanikop //
9*09537850SAkhilesh Sanikop // Unless required by applicable law or agreed to in writing, software
10*09537850SAkhilesh Sanikop // distributed under the License is distributed on an "AS IS" BASIS,
11*09537850SAkhilesh Sanikop // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*09537850SAkhilesh Sanikop // See the License for the specific language governing permissions and
13*09537850SAkhilesh Sanikop // limitations under the License.
14*09537850SAkhilesh Sanikop 
15*09537850SAkhilesh Sanikop #include "src/warp_prediction.h"
16*09537850SAkhilesh Sanikop 
17*09537850SAkhilesh Sanikop #include <cmath>
18*09537850SAkhilesh Sanikop #include <cstdint>
19*09537850SAkhilesh Sanikop #include <cstdlib>
20*09537850SAkhilesh Sanikop 
21*09537850SAkhilesh Sanikop #include "src/tile.h"
22*09537850SAkhilesh Sanikop #include "src/utils/block_parameters_holder.h"
23*09537850SAkhilesh Sanikop #include "src/utils/common.h"
24*09537850SAkhilesh Sanikop #include "src/utils/constants.h"
25*09537850SAkhilesh Sanikop #include "src/utils/logging.h"
26*09537850SAkhilesh Sanikop 
27*09537850SAkhilesh Sanikop namespace libgav1 {
28*09537850SAkhilesh Sanikop namespace {
29*09537850SAkhilesh Sanikop 
30*09537850SAkhilesh Sanikop constexpr int kWarpModelTranslationClamp = 1 << 23;
31*09537850SAkhilesh Sanikop constexpr int kWarpModelAffineClamp = 1 << 13;
32*09537850SAkhilesh Sanikop constexpr int kLargestMotionVectorDiff = 256;
33*09537850SAkhilesh Sanikop 
34*09537850SAkhilesh Sanikop constexpr uint16_t kDivisorLookup[257] = {
35*09537850SAkhilesh Sanikop     16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768,
36*09537850SAkhilesh Sanikop     15709, 15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142,
37*09537850SAkhilesh Sanikop     15087, 15033, 14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564,
38*09537850SAkhilesh Sanikop     14513, 14463, 14413, 14364, 14315, 14266, 14218, 14170, 14122, 14075, 14028,
39*09537850SAkhilesh Sanikop     13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662, 13618, 13574, 13530,
40*09537850SAkhilesh Sanikop     13487, 13443, 13400, 13358, 13315, 13273, 13231, 13190, 13148, 13107, 13066,
41*09537850SAkhilesh Sanikop     13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633,
42*09537850SAkhilesh Sanikop     12596, 12558, 12520, 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228,
43*09537850SAkhilesh Sanikop     12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848,
44*09537850SAkhilesh Sanikop     11815, 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555, 11523, 11491,
45*09537850SAkhilesh Sanikop     11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185, 11155,
46*09537850SAkhilesh Sanikop     11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838,
47*09537850SAkhilesh Sanikop     10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538,
48*09537850SAkhilesh Sanikop     10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255,
49*09537850SAkhilesh Sanikop     10230, 10205, 10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986,
50*09537850SAkhilesh Sanikop     9963,  9939,  9916,  9892,  9869,  9846,  9823,  9800,  9777,  9754,  9732,
51*09537850SAkhilesh Sanikop     9709,  9687,  9664,  9642,  9620,  9598,  9576,  9554,  9533,  9511,  9489,
52*09537850SAkhilesh Sanikop     9468,  9447,  9425,  9404,  9383,  9362,  9341,  9321,  9300,  9279,  9259,
53*09537850SAkhilesh Sanikop     9239,  9218,  9198,  9178,  9158,  9138,  9118,  9098,  9079,  9059,  9039,
54*09537850SAkhilesh Sanikop     9020,  9001,  8981,  8962,  8943,  8924,  8905,  8886,  8867,  8849,  8830,
55*09537850SAkhilesh Sanikop     8812,  8793,  8775,  8756,  8738,  8720,  8702,  8684,  8666,  8648,  8630,
56*09537850SAkhilesh Sanikop     8613,  8595,  8577,  8560,  8542,  8525,  8508,  8490,  8473,  8456,  8439,
57*09537850SAkhilesh Sanikop     8422,  8405,  8389,  8372,  8355,  8339,  8322,  8306,  8289,  8273,  8257,
58*09537850SAkhilesh Sanikop     8240,  8224,  8208,  8192};
59*09537850SAkhilesh Sanikop 
60*09537850SAkhilesh Sanikop // Number of fractional bits of lookup in divisor lookup table.
61*09537850SAkhilesh Sanikop constexpr int kDivisorLookupBits = 8;
62*09537850SAkhilesh Sanikop // Number of fractional bits of entries in divisor lookup table.
63*09537850SAkhilesh Sanikop constexpr int kDivisorLookupPrecisionBits = 14;
64*09537850SAkhilesh Sanikop 
65*09537850SAkhilesh Sanikop // 7.11.3.7.
66*09537850SAkhilesh Sanikop template <typename T>
GenerateApproximateDivisor(T value,int16_t * division_factor,int16_t * division_shift)67*09537850SAkhilesh Sanikop void GenerateApproximateDivisor(T value, int16_t* division_factor,
68*09537850SAkhilesh Sanikop                                 int16_t* division_shift) {
69*09537850SAkhilesh Sanikop   const int n = FloorLog2(std::abs(value));
70*09537850SAkhilesh Sanikop   const T e = std::abs(value) - (static_cast<T>(1) << n);
71*09537850SAkhilesh Sanikop   const int entry = (n > kDivisorLookupBits)
72*09537850SAkhilesh Sanikop                         ? RightShiftWithRounding(e, n - kDivisorLookupBits)
73*09537850SAkhilesh Sanikop                         : static_cast<int>(e << (kDivisorLookupBits - n));
74*09537850SAkhilesh Sanikop   *division_shift = n + kDivisorLookupPrecisionBits;
75*09537850SAkhilesh Sanikop   *division_factor =
76*09537850SAkhilesh Sanikop       (value < 0) ? -kDivisorLookup[entry] : kDivisorLookup[entry];
77*09537850SAkhilesh Sanikop }
78*09537850SAkhilesh Sanikop 
79*09537850SAkhilesh Sanikop // 7.11.3.8.
LeastSquareProduct(int a,int b)80*09537850SAkhilesh Sanikop int LeastSquareProduct(int a, int b) { return ((a * b) >> 2) + a + b; }
81*09537850SAkhilesh Sanikop 
82*09537850SAkhilesh Sanikop // 7.11.3.8.
DiagonalClamp(int32_t value)83*09537850SAkhilesh Sanikop int DiagonalClamp(int32_t value) {
84*09537850SAkhilesh Sanikop   return Clip3(value,
85*09537850SAkhilesh Sanikop                (1 << kWarpedModelPrecisionBits) - kWarpModelAffineClamp + 1,
86*09537850SAkhilesh Sanikop                (1 << kWarpedModelPrecisionBits) + kWarpModelAffineClamp - 1);
87*09537850SAkhilesh Sanikop }
88*09537850SAkhilesh Sanikop 
89*09537850SAkhilesh Sanikop // 7.11.3.8.
NonDiagonalClamp(int32_t value)90*09537850SAkhilesh Sanikop int NonDiagonalClamp(int32_t value) {
91*09537850SAkhilesh Sanikop   return Clip3(value, -kWarpModelAffineClamp + 1, kWarpModelAffineClamp - 1);
92*09537850SAkhilesh Sanikop }
93*09537850SAkhilesh Sanikop 
GetShearParameter(int value)94*09537850SAkhilesh Sanikop int16_t GetShearParameter(int value) {
95*09537850SAkhilesh Sanikop   return static_cast<int16_t>(
96*09537850SAkhilesh Sanikop       LeftShift(RightShiftWithRoundingSigned(Clip3(value, INT16_MIN, INT16_MAX),
97*09537850SAkhilesh Sanikop                                              kWarpParamRoundingBits),
98*09537850SAkhilesh Sanikop                 kWarpParamRoundingBits));
99*09537850SAkhilesh Sanikop }
100*09537850SAkhilesh Sanikop 
101*09537850SAkhilesh Sanikop }  // namespace
102*09537850SAkhilesh Sanikop 
SetupShear(GlobalMotion * const warp_params)103*09537850SAkhilesh Sanikop bool SetupShear(GlobalMotion* const warp_params) {
104*09537850SAkhilesh Sanikop   int16_t division_shift;
105*09537850SAkhilesh Sanikop   int16_t division_factor;
106*09537850SAkhilesh Sanikop   const auto* const params = warp_params->params;
107*09537850SAkhilesh Sanikop   GenerateApproximateDivisor<int32_t>(params[2], &division_factor,
108*09537850SAkhilesh Sanikop                                       &division_shift);
109*09537850SAkhilesh Sanikop   const int alpha = params[2] - (1 << kWarpedModelPrecisionBits);
110*09537850SAkhilesh Sanikop   const int beta = params[3];
111*09537850SAkhilesh Sanikop   const int64_t v = LeftShift(params[4], kWarpedModelPrecisionBits);
112*09537850SAkhilesh Sanikop   const int gamma =
113*09537850SAkhilesh Sanikop       RightShiftWithRoundingSigned(v * division_factor, division_shift);
114*09537850SAkhilesh Sanikop   const int64_t w = static_cast<int64_t>(params[3]) * params[4];
115*09537850SAkhilesh Sanikop   const int delta =
116*09537850SAkhilesh Sanikop       params[5] -
117*09537850SAkhilesh Sanikop       RightShiftWithRoundingSigned(w * division_factor, division_shift) -
118*09537850SAkhilesh Sanikop       (1 << kWarpedModelPrecisionBits);
119*09537850SAkhilesh Sanikop 
120*09537850SAkhilesh Sanikop   warp_params->alpha = GetShearParameter(alpha);
121*09537850SAkhilesh Sanikop   warp_params->beta = GetShearParameter(beta);
122*09537850SAkhilesh Sanikop   warp_params->gamma = GetShearParameter(gamma);
123*09537850SAkhilesh Sanikop   warp_params->delta = GetShearParameter(delta);
124*09537850SAkhilesh Sanikop   if ((4 * std::abs(warp_params->alpha) + 7 * std::abs(warp_params->beta) >=
125*09537850SAkhilesh Sanikop        (1 << kWarpedModelPrecisionBits)) ||
126*09537850SAkhilesh Sanikop       (4 * std::abs(warp_params->gamma) + 4 * std::abs(warp_params->delta) >=
127*09537850SAkhilesh Sanikop        (1 << kWarpedModelPrecisionBits))) {
128*09537850SAkhilesh Sanikop     return false;  // NOLINT (easier condition to understand).
129*09537850SAkhilesh Sanikop   }
130*09537850SAkhilesh Sanikop 
131*09537850SAkhilesh Sanikop   return true;
132*09537850SAkhilesh Sanikop }
133*09537850SAkhilesh Sanikop 
WarpEstimation(const int num_samples,const int block_width4x4,const int block_height4x4,const int row4x4,const int column4x4,const MotionVector & mv,const int candidates[kMaxLeastSquaresSamples][4],GlobalMotion * const warp_params)134*09537850SAkhilesh Sanikop bool WarpEstimation(const int num_samples, const int block_width4x4,
135*09537850SAkhilesh Sanikop                     const int block_height4x4, const int row4x4,
136*09537850SAkhilesh Sanikop                     const int column4x4, const MotionVector& mv,
137*09537850SAkhilesh Sanikop                     const int candidates[kMaxLeastSquaresSamples][4],
138*09537850SAkhilesh Sanikop                     GlobalMotion* const warp_params) {
139*09537850SAkhilesh Sanikop   // |a| fits into int32_t. To avoid cast to int64_t in the following
140*09537850SAkhilesh Sanikop   // computation, we declare |a| as int64_t.
141*09537850SAkhilesh Sanikop   int64_t a[2][2] = {};
142*09537850SAkhilesh Sanikop   int bx[2] = {};
143*09537850SAkhilesh Sanikop   int by[2] = {};
144*09537850SAkhilesh Sanikop 
145*09537850SAkhilesh Sanikop   // Note: for simplicity, the spec always uses absolute coordinates
146*09537850SAkhilesh Sanikop   // in the warp estimation process. subpixel_mid_x, subpixel_mid_y,
147*09537850SAkhilesh Sanikop   // and candidates are relative to the top left of the frame.
148*09537850SAkhilesh Sanikop   // In contrast, libaom uses a mixture of coordinate systems.
149*09537850SAkhilesh Sanikop   // In av1/common/warped_motion.c:find_affine_int(). The coordinate is relative
150*09537850SAkhilesh Sanikop   // to the top left of the block.
151*09537850SAkhilesh Sanikop   // mid_y/mid_x: the row/column coordinate of the center of the block.
152*09537850SAkhilesh Sanikop   const int mid_y = MultiplyBy4(row4x4) + MultiplyBy2(block_height4x4) - 1;
153*09537850SAkhilesh Sanikop   const int mid_x = MultiplyBy4(column4x4) + MultiplyBy2(block_width4x4) - 1;
154*09537850SAkhilesh Sanikop   const int subpixel_mid_y = MultiplyBy8(mid_y);
155*09537850SAkhilesh Sanikop   const int subpixel_mid_x = MultiplyBy8(mid_x);
156*09537850SAkhilesh Sanikop   const int reference_subpixel_mid_y = subpixel_mid_y + mv.mv[0];
157*09537850SAkhilesh Sanikop   const int reference_subpixel_mid_x = subpixel_mid_x + mv.mv[1];
158*09537850SAkhilesh Sanikop 
159*09537850SAkhilesh Sanikop   for (int i = 0; i < num_samples; ++i) {
160*09537850SAkhilesh Sanikop     // candidates[][0] and candidates[][1] are the row/column coordinates of the
161*09537850SAkhilesh Sanikop     // sample point in this block, to the top left of the frame.
162*09537850SAkhilesh Sanikop     // candidates[][2] and candidates[][3] are the row/column coordinates of the
163*09537850SAkhilesh Sanikop     // sample point in this reference block, to the top left of the frame.
164*09537850SAkhilesh Sanikop     // sy/sx: the row/column coordinates of the sample point, with center of
165*09537850SAkhilesh Sanikop     // the block as origin.
166*09537850SAkhilesh Sanikop     const int sy = candidates[i][0] - subpixel_mid_y;
167*09537850SAkhilesh Sanikop     const int sx = candidates[i][1] - subpixel_mid_x;
168*09537850SAkhilesh Sanikop     // dy/dx: the row/column coordinates of the sample point in the reference
169*09537850SAkhilesh Sanikop     // block, with center of the reference block as origin.
170*09537850SAkhilesh Sanikop     const int dy = candidates[i][2] - reference_subpixel_mid_y;
171*09537850SAkhilesh Sanikop     const int dx = candidates[i][3] - reference_subpixel_mid_x;
172*09537850SAkhilesh Sanikop     if (std::abs(sx - dx) < kLargestMotionVectorDiff &&
173*09537850SAkhilesh Sanikop         std::abs(sy - dy) < kLargestMotionVectorDiff) {
174*09537850SAkhilesh Sanikop       a[0][0] += LeastSquareProduct(sx, sx) + 8;
175*09537850SAkhilesh Sanikop       a[0][1] += LeastSquareProduct(sx, sy) + 4;
176*09537850SAkhilesh Sanikop       a[1][1] += LeastSquareProduct(sy, sy) + 8;
177*09537850SAkhilesh Sanikop       bx[0] += LeastSquareProduct(sx, dx) + 8;
178*09537850SAkhilesh Sanikop       bx[1] += LeastSquareProduct(sy, dx) + 4;
179*09537850SAkhilesh Sanikop       by[0] += LeastSquareProduct(sx, dy) + 4;
180*09537850SAkhilesh Sanikop       by[1] += LeastSquareProduct(sy, dy) + 8;
181*09537850SAkhilesh Sanikop     }
182*09537850SAkhilesh Sanikop   }
183*09537850SAkhilesh Sanikop 
184*09537850SAkhilesh Sanikop   // a[0][1] == a[1][0], because the matrix is symmetric. We don't have to
185*09537850SAkhilesh Sanikop   // compute a[1][0].
186*09537850SAkhilesh Sanikop   const int64_t determinant = a[0][0] * a[1][1] - a[0][1] * a[0][1];
187*09537850SAkhilesh Sanikop   if (determinant == 0) return false;
188*09537850SAkhilesh Sanikop 
189*09537850SAkhilesh Sanikop   int16_t division_shift;
190*09537850SAkhilesh Sanikop   int16_t division_factor;
191*09537850SAkhilesh Sanikop   GenerateApproximateDivisor<int64_t>(determinant, &division_factor,
192*09537850SAkhilesh Sanikop                                       &division_shift);
193*09537850SAkhilesh Sanikop 
194*09537850SAkhilesh Sanikop   division_shift -= kWarpedModelPrecisionBits;
195*09537850SAkhilesh Sanikop 
196*09537850SAkhilesh Sanikop   const int64_t params_2 = a[1][1] * bx[0] - a[0][1] * bx[1];
197*09537850SAkhilesh Sanikop   const int64_t params_3 = -a[0][1] * bx[0] + a[0][0] * bx[1];
198*09537850SAkhilesh Sanikop   const int64_t params_4 = a[1][1] * by[0] - a[0][1] * by[1];
199*09537850SAkhilesh Sanikop   const int64_t params_5 = -a[0][1] * by[0] + a[0][0] * by[1];
200*09537850SAkhilesh Sanikop   auto* const params = warp_params->params;
201*09537850SAkhilesh Sanikop 
202*09537850SAkhilesh Sanikop   if (division_shift <= 0) {
203*09537850SAkhilesh Sanikop     division_factor <<= -division_shift;
204*09537850SAkhilesh Sanikop     params[2] = static_cast<int32_t>(params_2) * division_factor;
205*09537850SAkhilesh Sanikop     params[3] = static_cast<int32_t>(params_3) * division_factor;
206*09537850SAkhilesh Sanikop     params[4] = static_cast<int32_t>(params_4) * division_factor;
207*09537850SAkhilesh Sanikop     params[5] = static_cast<int32_t>(params_5) * division_factor;
208*09537850SAkhilesh Sanikop   } else {
209*09537850SAkhilesh Sanikop     params[2] = RightShiftWithRoundingSigned(params_2 * division_factor,
210*09537850SAkhilesh Sanikop                                              division_shift);
211*09537850SAkhilesh Sanikop     params[3] = RightShiftWithRoundingSigned(params_3 * division_factor,
212*09537850SAkhilesh Sanikop                                              division_shift);
213*09537850SAkhilesh Sanikop     params[4] = RightShiftWithRoundingSigned(params_4 * division_factor,
214*09537850SAkhilesh Sanikop                                              division_shift);
215*09537850SAkhilesh Sanikop     params[5] = RightShiftWithRoundingSigned(params_5 * division_factor,
216*09537850SAkhilesh Sanikop                                              division_shift);
217*09537850SAkhilesh Sanikop   }
218*09537850SAkhilesh Sanikop 
219*09537850SAkhilesh Sanikop   params[2] = DiagonalClamp(params[2]);
220*09537850SAkhilesh Sanikop   params[3] = NonDiagonalClamp(params[3]);
221*09537850SAkhilesh Sanikop   params[4] = NonDiagonalClamp(params[4]);
222*09537850SAkhilesh Sanikop   params[5] = DiagonalClamp(params[5]);
223*09537850SAkhilesh Sanikop 
224*09537850SAkhilesh Sanikop   const int vx = mv.mv[1] * (1 << (kWarpedModelPrecisionBits - 3)) -
225*09537850SAkhilesh Sanikop                  (mid_x * (params[2] - (1 << kWarpedModelPrecisionBits)) +
226*09537850SAkhilesh Sanikop                   mid_y * params[3]);
227*09537850SAkhilesh Sanikop   const int vy = mv.mv[0] * (1 << (kWarpedModelPrecisionBits - 3)) -
228*09537850SAkhilesh Sanikop                  (mid_x * params[4] +
229*09537850SAkhilesh Sanikop                   mid_y * (params[5] - (1 << kWarpedModelPrecisionBits)));
230*09537850SAkhilesh Sanikop   params[0] =
231*09537850SAkhilesh Sanikop       Clip3(vx, -kWarpModelTranslationClamp, kWarpModelTranslationClamp - 1);
232*09537850SAkhilesh Sanikop   params[1] =
233*09537850SAkhilesh Sanikop       Clip3(vy, -kWarpModelTranslationClamp, kWarpModelTranslationClamp - 1);
234*09537850SAkhilesh Sanikop   return true;
235*09537850SAkhilesh Sanikop }
236*09537850SAkhilesh Sanikop 
237*09537850SAkhilesh Sanikop }  // namespace libgav1
238