1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "phash_fingerprinter.h"
18 
19 #include "array2d.h"
20 #include "fast_dct-inl.h"
21 #include "fast_dct.h"
22 #include "phash_config.h"
23 
24 namespace android {
25 
26 constexpr int kResizedLength = 8;
27 
GenerateFingerprint(const uint8_t * frame)28 int64_t PhashFingerprinter::GenerateFingerprint(const uint8_t* frame) {
29     // 1. verify input
30     if (frame == nullptr) {
31         return 0;
32     }
33 
34     // 2. Compute the DCT
35     Array2D<const uint8_t> dct_in(frame);
36     Array2D<int16_t> dct_out(dct_buf_);
37 
38     FastDCT<const uint8_t, int16_t> dct(kImageLength);
39     dct.ForwardTransform2D(dct_in, &dct_out);
40 
41     // 3. Simplify the DCT by only using the coefficients in the top-left 8x8
42     // window (i.e. those for the lowest frequencies).
43     double total = 0;
44     for (int x = 0; x < kResizedLength; x++) {
45         for (int y = 0; y < kResizedLength; y++) {
46             total += dct_out(x, y);
47         }
48     }
49     // Remove the DC component at (0, 0) from total.
50     total -= dct_out(0, 0);
51 
52     // 4. Compute the average
53     double average = total / ((kResizedLength * kResizedLength) - 1);
54 
55     // 5. Further simplify the DCT by setting each value to 0 or 1 based on
56     // whether the DCT value is larger than the average, then store each value as
57     // a bit in an int64.
58     int64_t fingerprint = 0;
59     for (int x = 0; x < kResizedLength; x++) {
60         for (int y = 0; y < kResizedLength; y++) {
61             fingerprint <<= 1;
62             fingerprint |= (dct_out(x, y) > average ? 1 : 0);
63         }
64     }
65     // Remove the DC component at (0, 0)
66     fingerprint &= ~(1ULL << 63);
67 
68     return fingerprint;
69 }
70 
71 } // namespace android
72