Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a6f598d7 authored by Ahan Wu's avatar Ahan Wu Committed by android-build-merger
Browse files

Merge "Use Otsu's thresholding get better transition effect." into qt-dev am: 50d066f0

am: 4d4721f5

Change-Id: Ibe46740faf8780437dbd7597b269c6ba4ff995dd
parents 377447b2 4d4721f5
Loading
Loading
Loading
Loading
+114 −24
Original line number Diff line number Diff line
@@ -30,13 +30,15 @@ import android.os.Message;
import android.util.Log;

/**
 * A helper class that computes histogram and percentile 85 from a bitmap.
 * Percentile 85 will be computed each time the user picks a new image wallpaper.
 * A helper class that computes threshold from a bitmap.
 * Threshold will be computed each time the user picks a new image wallpaper.
 */
class ImageProcessHelper {
    private static final String TAG = ImageProcessHelper.class.getSimpleName();
    private static final float DEFAULT_PER85 = 0.8f;
    private static final int MSG_UPDATE_PER85 = 1;
    private static final float DEFAULT_THRESHOLD = 0.8f;
    private static final float DEFAULT_OTSU_THRESHOLD = 0f;
    private static final float MAX_THRESHOLD = 0.89f;
    private static final int MSG_UPDATE_THRESHOLD = 1;

    /**
     * This color matrix will be applied to each pixel to get luminance from rgb by below formula:
@@ -53,8 +55,8 @@ class ImageProcessHelper {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_UPDATE_PER85:
                    mPer85 = (float) msg.obj;
                case MSG_UPDATE_THRESHOLD:
                    mThreshold = (float) msg.obj;
                    return true;
                default:
                    return false;
@@ -62,20 +64,20 @@ class ImageProcessHelper {
        }
    });

    private float mPer85 = DEFAULT_PER85;
    private float mThreshold = DEFAULT_THRESHOLD;

    void startComputingPercentile85(Bitmap bitmap) {
        new Per85ComputeTask(mHandler).execute(bitmap);
    void start(Bitmap bitmap) {
        new ThresholdComputeTask(mHandler).execute(bitmap);
    }

    float getPercentile85() {
        return mPer85;
    float getThreshold() {
        return Math.min(mThreshold, MAX_THRESHOLD);
    }

    private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
    private static class ThresholdComputeTask extends AsyncTask<Bitmap, Void, Float> {
        private Handler mUpdateHandler;

        Per85ComputeTask(Handler handler) {
        ThresholdComputeTask(Handler handler) {
            super(handler);
            mUpdateHandler = handler;
        }
@@ -84,35 +86,55 @@ class ImageProcessHelper {
        protected Float doInBackground(Bitmap... bitmaps) {
            Bitmap bitmap = bitmaps[0];
            if (bitmap != null) {
                int[] histogram = processHistogram(bitmap);
                return computePercentile85(bitmap, histogram);
                return new Threshold().compute(bitmap);
            }
            Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
            return DEFAULT_PER85;
            Log.e(TAG, "ThresholdComputeTask: Can't get bitmap");
            return DEFAULT_THRESHOLD;
        }

        @Override
        protected void onPostExecute(Float result) {
            Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
            Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_THRESHOLD, result);
            mUpdateHandler.sendMessage(msg);
        }
    }

    private static class Threshold {
        public float compute(Bitmap bitmap) {
            Bitmap grayscale = toGrayscale(bitmap);
            int[] histogram = getHistogram(grayscale);
            boolean isSolidColor = isSolidColor(grayscale, histogram);

            // We will see gray wallpaper during the transition if solid color wallpaper is set,
            // please refer to b/130360362#comment16.
            // As a result, we use Percentile85 rather than Otsus if a solid color wallpaper is set.
            ThresholdAlgorithm algorithm = isSolidColor ? new Percentile85() : new Otsus();
            return algorithm.compute(grayscale, histogram);
        }

        private int[] processHistogram(Bitmap bitmap) {
        private Bitmap toGrayscale(Bitmap bitmap) {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();

            Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
            Canvas canvas = new Canvas(target);
            Bitmap grayscale = Bitmap.createBitmap(width, height, bitmap.getConfig());
            Canvas canvas = new Canvas(grayscale);
            ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
            Paint paint = new Paint();
            paint.setColorFilter(new ColorMatrixColorFilter(cm));
            canvas.drawBitmap(bitmap, new Matrix(), paint);

            return grayscale;
        }

        private int[] getHistogram(Bitmap grayscale) {
            int width = grayscale.getWidth();
            int height = grayscale.getHeight();

            // TODO: Fine tune the performance here, tracking on b/123615079.
            int[] histogram = new int[256];
            for (int row = 0; row < height; row++) {
                for (int col = 0; col < width; col++) {
                    int pixel = target.getPixel(col, row);
                    int pixel = grayscale.getPixel(col, row);
                    int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
                    histogram[y]++;
                }
@@ -121,8 +143,29 @@ class ImageProcessHelper {
            return histogram;
        }

        private float computePercentile85(Bitmap bitmap, int[] histogram) {
            float per85 = DEFAULT_PER85;
        private boolean isSolidColor(Bitmap bitmap, int[] histogram) {
            boolean solidColor = false;
            int pixels = bitmap.getWidth() * bitmap.getHeight();

            // In solid color case, only one element of histogram has value,
            // which is pixel counts and the value of other elements should be 0.
            for (int value : histogram) {
                if (value != 0 && value != pixels) {
                    break;
                }
                if (value == pixels) {
                    solidColor = true;
                    break;
                }
            }
            return solidColor;
        }
    }

    private static class Percentile85 implements ThresholdAlgorithm {
        @Override
        public float compute(Bitmap bitmap, int[] histogram) {
            float per85 = DEFAULT_THRESHOLD;
            int pixelCount = bitmap.getWidth() * bitmap.getHeight();
            float[] acc = new float[256];
            for (int i = 0; i < acc.length; i++) {
@@ -141,4 +184,51 @@ class ImageProcessHelper {
            return per85;
        }
    }

    private static class Otsus implements ThresholdAlgorithm {
        @Override
        public float compute(Bitmap bitmap, int[] histogram) {
            float threshold = DEFAULT_OTSU_THRESHOLD;
            float maxVariance = 0;
            float pixelCount = bitmap.getWidth() * bitmap.getHeight();
            float[] w = new float[2];
            float[] m = new float[2];
            float[] u = new float[2];

            for (int i = 0; i < histogram.length; i++) {
                m[1] += i * histogram[i];
            }

            w[1] = pixelCount;
            for (int tonalValue = 0; tonalValue < histogram.length; tonalValue++) {
                float dU;
                float variance;
                float numPixels = histogram[tonalValue];
                float tmp = numPixels * tonalValue;
                w[0] += numPixels;
                w[1] -= numPixels;

                if (w[0] == 0 || w[1] == 0) {
                    continue;
                }

                m[0] += tmp;
                m[1] -= tmp;
                u[0] = m[0] / w[0];
                u[1] = m[1] / w[1];
                dU = u[0] - u[1];
                variance = w[0] * w[1] * dU * dU;

                if (variance > maxVariance) {
                    threshold = (tonalValue + 1f) / histogram.length;
                    maxVariance = variance;
                }
            }
            return threshold;
        }
    }

    private interface ThresholdAlgorithm {
        float compute(Bitmap bitmap, int[] histogram);
    }
}
+4 −4
Original line number Diff line number Diff line
@@ -78,8 +78,8 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
            mBitmap = mWallpaperManager.getBitmap();
            mBitmapWidth = mBitmap.getWidth();
            mBitmapHeight = mBitmap.getHeight();
            // Compute per85 as transition threshold, this is an async work.
            mImageProcessHelper.startComputingPercentile85(mBitmap);
            // Compute threshold of the image, this is an async work.
            mImageProcessHelper.start(mBitmap);
            mWallpaperManager.forgetLoadedWallpaper();
        }
    }
@@ -108,13 +108,13 @@ public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,

    @Override
    public void onDrawFrame(GL10 gl) {
        float per85 = mImageProcessHelper.getPercentile85();
        float threshold = mImageProcessHelper.getThreshold();
        float reveal = mImageRevealHelper.getReveal();

        glClear(GL_COLOR_BUFFER_BIT);

        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), per85);
        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_PER85), threshold);
        glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);

        scaleViewport(reveal);