Home | History | Annotate | Download | only in rs
      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      */
     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      */
     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      */
    149     public static double calcDifferenceMetric(Bitmap a, Bitmap b) {
    150         BitmapCompareResult result = compareBitmap(a, b);
    151         return result.mDiff;
    152     }
    153 
    154 }
    155