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