1 /*
2  * Copyright (C) 2015 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 #define LOG_TAG "ITS-StatsImage-JNI"
18 #include <android/log.h>
19 #include <inttypes.h>
20 #include <jni.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 
26 #include <string>
27 #include <unordered_map>
28 #include <vector>
29 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
30 #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
31 
32 // StatsFormat defines the different possible stats image formats that can be
33 // used.
34 enum StatsFormat {
35   RAW10_STATS = 0,
36   RAW10_QUAD_BAYER_STATS,
37   RAW16_STATS,
38   RAW16_QUAD_BAYER_STATS,
39 };
40 
41 // A map from strings to StatsFormat values, which can be used to convert from
42 // a string representation of a stats image format to the corresponding
43 // StatsFormat enum value.
44 static std::unordered_map<std::string, StatsFormat> const statsFormatMap = {
45     {"Raw10Stats", RAW10_STATS},
46     {"Raw10QuadBayerStats", RAW10_QUAD_BAYER_STATS},
47     {"Raw16Stats", RAW16_STATS},
48     {"Raw16QuadBayerStats", RAW16_QUAD_BAYER_STATS}
49 };
50 
51 /**
52  * Gets the pixel value in the image buffer given buffer pos and pixel index.
53  *
54  * Parameters:
55  *   buf: unsigned char*
56  *     The buffer containing the image data.
57  *   statsFormat: StatsFormat
58  *     A enum StatsFormat value that specifies the format of stats images.
59  *     See it's valid values in `enum StatsFormat`.
60  *   pixelIndex: const int
61  *     The index of the pixel to compute the value of.
62  *
63  * Returns:
64  *   pixelValue: int
65  *     The value of the current pixel.
66  */
getPixelValue(unsigned char * buf,StatsFormat statsFormat,const int pixelIndex)67 int getPixelValue(unsigned char* buf,
68                   StatsFormat statsFormat,
69                   const int pixelIndex) {
70     int pixelValue;
71     switch (statsFormat) {
72         case RAW10_STATS:
73         case RAW10_QUAD_BAYER_STATS: {
74             // For RAW10 images, each 4 consecutive pixels are packed into 5 bytes.
75 
76             // The index of the current pixel in the 4 pixels group.
77             int pixelSubIndex = pixelIndex % 4;
78             // The number of bytes before the current 4 pixels group.
79             int byteIndex = (pixelIndex / 4) * 5;
80             // pbuf points to the start of the current 4 pixels group.
81             unsigned char* pbuf = buf + byteIndex;
82             // The lower 2 bits.
83             int low = ((*(pbuf + 4)) >> (pixelSubIndex * 2)) & 0x3;
84             // The higher 8 bits.
85             int high = *(pbuf + pixelSubIndex);
86             pixelValue = (high << 2) | low;
87             break;
88         }
89 
90         case RAW16_STATS:
91         case RAW16_QUAD_BAYER_STATS: {
92             // For RAW16 images, each pixel consists of 16 consecutive bytes.
93 
94             // The number of bytes before the current pixel.
95             int byteIndex = pixelIndex * 2;
96             // pbuf points to the starting byte of the current pixel.
97             unsigned char* pbuf = buf + byteIndex;
98             // The lower 8 bits.
99             int low = *pbuf;
100             // The higher 8 bits.
101             int high = *(pbuf + 1);
102             pixelValue = (high << 8) | low;
103             break;
104         }
105 
106         default: {
107             pixelValue = 0;
108             break;
109         }
110     }
111 
112     return pixelValue;
113 }
114 
115 /**
116  * Computes the mean and variance of each channel for grid cell (gy, gx).
117  *
118  * Parameters:
119  *   buf: unsigned char*
120  *     The image buffer.
121  *   statsFormat: StatsFormat
122  *     A enum StatsFormat value that specifies the format of stats images.
123  *     See it's valid values in `enum StatsFormat`.
124  *   isQuadBayer: bool
125  *     Whether the image is a quad bayer image.
126  *   paw: int
127  *     The pixel array width.
128  *   ngx: int
129  *     The number of grid cells in the x direction.
130  *   aax: int
131  *     The x coordinate of the top-left corner of the grid cell.
132  *   aay: int
133  *     The y coordinate of the top-left corner of the grid cell.
134  *   gw: int
135  *     The width of the grid cell.
136  *   gh: int
137  *     The height of the grid cell.
138  *   gy: int
139  *     The y index of the grid cell.
140  *   gx: int
141  *     The x index of the grid cell.
142  *   numOfChannels: int
143  *     The number of channels in the image.
144  *   means: std::vector<float>&
145  *     The output vector for the mean values.
146  *   vars: std::vector<float>&
147  *     The output vector for the variance values.
148  */
computeSingleCellStats(unsigned char * buf,StatsFormat statsFormat,bool isQuadBayer,const int paw,int ngx,const int aax,const int aay,const int gw,const int gh,int gy,int gx,const int numOfChannels,std::vector<float> & means,std::vector<float> & vars)149 void computeSingleCellStats(unsigned char* buf,
150                             StatsFormat statsFormat,
151                             bool isQuadBayer,
152                             const int paw,
153                             int ngx,
154                             const int aax,
155                             const int aay,
156                             const int gw,
157                             const int gh,
158                             int gy,
159                             int gx,
160                             const int numOfChannels,
161                             std::vector<float>& means,
162                             std::vector<float>& vars) {
163     // Vector of sums of pixel values in each channel.
164     std::vector<double> sum(numOfChannels, 0);
165     // Vector of sums of squared pixel values in each channel.
166     std::vector<double> sumSq(numOfChannels, 0);
167     // Vector of number of pixels in each channel.
168     std::vector<int> count(numOfChannels, 0);
169 
170     // Iterate over pixels in the grid cell.
171     for (int y = aay + gy * gh; y < aay + (gy + 1) * gh; y++) {
172         int pixelIndex = y * paw + gx * gw + aax;
173         // The offset of channels of different y values.
174         int chOffsetY = isQuadBayer ? (y & 0x3) * 4 : (y & 0x1) * 2;
175         for (int x = aax + gx * gw; x < aax + (gx + 1) * gw; x++) {
176             // ch is the channel index with range [0, numOfChannels-1].
177             int chOffsetX = isQuadBayer ? (x & 0x3) : (x & 1);
178             int ch = chOffsetY + chOffsetX;
179             int pixelValue = getPixelValue(buf, statsFormat, pixelIndex);
180 
181             sum[ch] += pixelValue;
182             sumSq[ch] += pixelValue * pixelValue;
183             count[ch]++;
184             pixelIndex++;
185         }
186     }
187 
188     const int baseIndex = (gy * ngx + gx) * numOfChannels;
189     for (int ch = 0; ch < numOfChannels; ch++) {
190         if (count[ch] == 0) {
191             ALOGE("Found zero count at grid cell (gy, gx, ch) = (%d, %d, %d).",
192                   gy, gx, ch);
193             continue;
194         }
195         // Computes mean and variance using double precision to avoid negative
196         // variance.
197         double m = sum[ch] / (double) count[ch];
198         double mSq = sumSq[ch] / (double) count[ch];
199         // In probability theory, Var(X) = E[X^2] - E[X]^2.
200         double variance = mSq - m * m;
201         int index = baseIndex + ch;
202         means[index] = (float) m;
203         vars[index] = (float) variance;
204         if (vars[index] < 0) {
205             ALOGE("Variance < 0 at grid cell (gy, gx, ch) = (%d, %d, %d): "
206                   "m=%lf, mSq=%lf, double variance=%lf, float variance=%f.",
207                   gy, gx, ch, m, mSq, variance, vars[index]);
208         }
209     }
210 }
211 
212 /**
213  * Computes the mean and variance values for each grid cell in the active array
214  * crop region.
215  *
216  *
217  * Parameters:
218  *   env: JNIEnv*
219  *     The JNI environment.
220  *   thiz: jobject
221  *     A reference to the object that called this method.
222  *   img: jbyteArray
223  *     The full pixel array read from the sensor.
224  *   statsFormatJStr: jstring
225  *     A jstring value that specifies the format of stats images.
226  *     See it's valid values in `enum StatsFormat`.
227  *   width: jint
228  *     The width of the raw image (before transformed to pixel array).
229  *   height: jint
230  *     The height of the raw image (before transformed to pixel array).
231  *   aax: jint
232  *     The x coordinate of the top-left corner of the active array crop region.
233  *   aay: jint
234  *     The y coordinate of the top-left corner of the active array crop region.
235  *   aaw: jint
236  *     The width of the active array crop region.
237  *   aah: jint
238  *     The height of the active array crop region.
239  *   gridWidth: jint
240  *     The width of stats grid cell.
241  *   gridHeight: jint
242  *     The height of stats grid cell.
243  *
244  * Returns:
245  *   jfloatArray
246  *     A new array of floats containing the mean and variance stats image.
247  */
com_android_cts_verifier_camera_its_computeStatsImage(JNIEnv * env,jobject thiz,jbyteArray img,jstring statsFormatJStr,jint width,jint height,jint aax,jint aay,jint aaw,jint aah,jint gridWidth,jint gridHeight)248 jfloatArray com_android_cts_verifier_camera_its_computeStatsImage(
249     JNIEnv* env,
250     jobject thiz,
251     jbyteArray img,
252     jstring statsFormatJStr,
253     jint width,
254     jint height,
255     jint aax,
256     jint aay,
257     jint aaw,
258     jint aah,
259     jint gridWidth,
260     jint gridHeight) {
261     int bufSize = (int) (env->GetArrayLength(img));
262     unsigned char* buf =
263         (unsigned char*) env->GetByteArrayElements(img, /*is_copy*/ NULL);
264     const char* statsFormatChars =
265         (const char*) env->GetStringUTFChars(statsFormatJStr, /*is_copy*/ NULL);
266 
267     // Size of the full raw image pixel array.
268     const int paw = width;
269     const int pah = height;
270     // Size of each grid cell.
271     const int gw = gridWidth;
272     const int gh = gridHeight;
273 
274     // Set the width or height of active crop region to the width or height of
275     // the raw image if the size of active crop region is larger than the size
276     // of actual image.
277     aaw = std::min(paw, aaw);
278     aah = std::min(pah, aah);
279 
280     // Set the top-left coordinate of active crop region to (0, 0) if the
281     // width or height of active crop region is equal to the width or height of
282     // the raw image.
283     if (paw == aaw) {
284         aax = 0;
285     }
286     if (pah == aah) {
287         aay = 0;
288     }
289 
290     // Number of grid cells (rounding down to full cells only at right+bottom
291     // edges).
292     const int ngx = aaw / gw;
293     const int ngy = aah / gh;
294 
295     const bool isQuadBayer = strstr(statsFormatChars, "QuadBayer") != NULL;
296     // Quad bayer and standard bayer stats images have 16 and 4 channels
297     // respectively.
298     const int numOfChannels = isQuadBayer ? 16 : 4;
299     ALOGI("Computing stats image... "
300           "bufSize=%d, raw image shape (width, height) = (%d, %d), "
301           "crop region (aax, aay, aaw, aah) = (%d, %d, %d, %d), "
302           "grid shape (gw, gh) = (%d, %d), "
303           "stats image shape (ngx, ngy) = (%d, %d), stats image format: %s, "
304           "numOfChannels=%d.",
305           bufSize, paw, pah, aax, aay, aaw, aah, gw, gh, ngx, ngy,
306           statsFormatChars, numOfChannels);
307 
308     // A stats format of enum type can speed up stats image computations.
309     auto iterator = statsFormatMap.find(std::string(statsFormatChars));
310     jfloatArray ret = NULL;
311     if (iterator != statsFormatMap.end()) {
312         StatsFormat statsFormat = iterator->second;
313         const int statsImageSize = ngy * ngx * numOfChannels;
314         std::vector<float> means(statsImageSize, 0);
315         std::vector<float> vars(statsImageSize, 0);
316 
317         // Computes stats for each grid cell.
318         for (int gy = 0; gy < ngy; gy++) {
319             for (int gx = 0; gx < ngx; gx++) {
320                 computeSingleCellStats(buf, statsFormat, isQuadBayer, paw, ngx,
321                                        aax, aay, gw, gh, gy, gx, numOfChannels,
322                                        means, vars);
323             }
324         }
325 
326         ret = env->NewFloatArray(statsImageSize * 2);
327         env->SetFloatArrayRegion(ret, 0, statsImageSize, (float*) means.data());
328         env->SetFloatArrayRegion(ret, statsImageSize, statsImageSize,
329                                  (float*) vars.data());
330     } else {
331         ALOGE("Unsupported stats image format: %s.", statsFormatChars);
332     }
333 
334     // Release the array memory.
335     env->ReleaseByteArrayElements(img, (jbyte*) buf, 0 /* mode */);
336     env->ReleaseStringUTFChars(statsFormatJStr, statsFormatChars);
337 
338     return ret;
339 }
340 
341 static JNINativeMethod gMethods[] = {
342     {"computeStatsImage", "([BLjava/lang/String;IIIIIIII)[F",
343      (void*) com_android_cts_verifier_camera_its_computeStatsImage},
344 };
345 
register_com_android_cts_verifier_camera_its_StatsImage(JNIEnv * env)346 int register_com_android_cts_verifier_camera_its_StatsImage(JNIEnv* env) {
347     jclass clazz =
348         env->FindClass("com/android/cts/verifier/camera/its/StatsImage");
349 
350     return env->RegisterNatives(clazz, gMethods,
351                                 sizeof(gMethods) / sizeof(JNINativeMethod));
352 }
353