1 /*
2  * Copyright 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 package android.hardware.camera2.cts.rs;
17 
18 import android.graphics.Bitmap;
19 import android.graphics.Color;
20 import android.renderscript.Allocation;
21 import android.renderscript.Element;
22 import android.renderscript.RenderScript;
23 import android.renderscript.ScriptIntrinsicHistogram;
24 
25 /**
26  * Utility class providing methods for various pixel-wise ARGB bitmap operations.
27  */
28 public class BitmapUtils {
29     private static final String TAG = "BitmapUtils";
30     private static final int COLOR_BIT_DEPTH = 256;
31     public static int NUM_CHANNELS = 4;
32 
33     /**
34      * Return the histograms for each color channel (interleaved).
35      *
36      * @param rs a {@link RenderScript} context to use.
37      * @param bmap a {@link Bitmap} to generate the histograms for.
38      * @return an array containing NUM_CHANNELS * COLOR_BIT_DEPTH histogram bucket values, with
39      * the color channels interleaved.
40      */
calcHistograms(RenderScript rs, Bitmap bmap)41     public static int[] calcHistograms(RenderScript rs, Bitmap bmap) {
42         ScriptIntrinsicHistogram hist = ScriptIntrinsicHistogram.create(rs, Element.U8_4(rs));
43         Allocation sums = Allocation.createSized(rs, Element.I32_4(rs), COLOR_BIT_DEPTH);
44 
45         // Setup input allocation (ARGB 8888 bitmap).
46         Allocation input = Allocation.createFromBitmap(rs, bmap);
47 
48         hist.setOutput(sums);
49         hist.forEach(input);
50         int[] output = new int[COLOR_BIT_DEPTH * NUM_CHANNELS];
51         sums.copyTo(output);
52         return output;
53     }
54 
55     // Some stats output from comparing two Bitmap using calcDifferenceMetric
56     public static class BitmapCompareResult {
57         // difference between two bitmaps using average of per-pixel differences.
58         public double mDiff;
59         // If the LHS Bitmap has same RGB values for all pixels
60         public boolean mLhsFlat;
61         // If the RHS Bitmap has same RGB values for all pixels
62         public boolean mRhsFlat;
63         // The R/G/B average pixel value of LHS Bitmap
64         public double[] mLhsAverage = new double[3];
65         // The R/G/B average pixel value of RHS Bitmap
66         public double[] mRhsAverage = new double[3];
67     }
68 
69     /**
70      * Compare two bitmaps and also return some statistics about the two Bitmap.
71      *
72      * @param a first {@link android.graphics.Bitmap}.
73      * @param b second {@link android.graphics.Bitmap}.
74      * @return the results in a BitmapCompareResult
75      */
compareBitmap(Bitmap a, Bitmap b)76     public static BitmapCompareResult compareBitmap(Bitmap a, Bitmap b) {
77         if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
78             throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
79                     a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
80                     b.getHeight());
81         }
82         // TODO: Optimize this in renderscript to avoid copy.
83         int[] aPixels = new int[a.getHeight() * a.getWidth()];
84         int[] bPixels = new int[aPixels.length];
85         a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
86                 a.getHeight());
87         b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
88                 b.getHeight());
89         double diff = 0;
90         double[] aSum = new double[3];
91         double[] bSum = new double[3];
92         int[] aFirstPix = new int[3];
93         int[] bFirstPix = new int[3];
94         aFirstPix[0] = Color.red(aPixels[0]);
95         aFirstPix[1] = Color.green(aPixels[1]);
96         aFirstPix[2] = Color.blue(aPixels[2]);
97         bFirstPix[0] = Color.red(bPixels[0]);
98         bFirstPix[1] = Color.green(bPixels[1]);
99         bFirstPix[2] = Color.blue(bPixels[2]);
100         boolean isAFlat = true, isBFlat = true;
101 
102         for (int i = 0; i < aPixels.length; i++) {
103             int aPix = aPixels[i];
104             int bPix = bPixels[i];
105             int aR = Color.red(aPix);
106             int aG = Color.green(aPix);
107             int aB = Color.blue(aPix);
108             int bR = Color.red(bPix);
109             int bG = Color.green(bPix);
110             int bB = Color.blue(bPix);
111             aSum[0] += aR;
112             aSum[1] += aG;
113             aSum[2] += aB;
114             bSum[0] += bR;
115             bSum[1] += bG;
116             bSum[2] += bB;
117 
118             if (isAFlat && (aR != aFirstPix[0] || aG != aFirstPix[1] || aB != aFirstPix[2])) {
119                 isAFlat = false;
120             }
121 
122             if (isBFlat && (bR != bFirstPix[0] || bG != bFirstPix[1] || bB != bFirstPix[2])) {
123                 isBFlat = false;
124             }
125 
126             diff += Math.abs(aR - bR); // red
127             diff += Math.abs(aG - bG); // green
128             diff += Math.abs(aB - bB); // blue
129         }
130         diff /= (aPixels.length * 3);
131         BitmapCompareResult result = new BitmapCompareResult();
132         result.mDiff = diff;
133         result.mLhsFlat = isAFlat;
134         result.mRhsFlat = isBFlat;
135         for (int i = 0; i < 3; i++) {
136             result.mLhsAverage[i] = aSum[i] / aPixels.length;
137             result.mRhsAverage[i] = bSum[i] / bPixels.length;
138         }
139         return result;
140     }
141 
142     /**
143      * Find the difference between two bitmaps using average of per-pixel differences.
144      *
145      * @param a first {@link android.graphics.Bitmap}.
146      * @param b second {@link android.graphics.Bitmap}.
147      * @return the difference.
148      */
calcDifferenceMetric(Bitmap a, Bitmap b)149     public static double calcDifferenceMetric(Bitmap a, Bitmap b) {
150         BitmapCompareResult result = compareBitmap(a, b);
151         return result.mDiff;
152     }
153 
154 }
155