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