ps圖片黑白調整算法——Android實現及性能優化

引言

接續我的上一篇文章:http://blog.csdn.net/mingyueyixi/article/details/78534580

這次是針對安卓上實現Photoshop的黑白調整算法。

其實,實現的方式和java是一致的。只是bitmap比較坑爹一些,如果在循環體中使用bitmap.getPixel() 與 bitmap.setPixel(),將導致耗時大大增加。

警告,警告:

bitmap.getPixel() 與 bitmap.setPixel() 效率極低,循環體中無論如何都不要使用bitmap對象進行的一系列與像素相關的操作。代替的方案是使用像素數組獲取


未完全的優化和測試結果

package com.lu.adog.util.image;

import android.graphics.Bitmap;

/**
 * @author Yue
 * @date 2017/11/17 17:42
 */

public class PSGray {


    /**
     * Photoshop 黑白算法,默認效果
     *
     * @param image
     * @return 新的黑白化圖片
     */
    public static Bitmap createBlackWhiteImage(Bitmap image) {
        return createBlackWhiteImage(image, null);
    }

    /**
     * Photoshop 黑白算法,默認效果
     *
     * @param image
     * @return 新的黑白化圖片
     * @radios 顏色通道配置,依次爲紅、黃、 綠、 青、 藍、紫六個通道
     */
    public static Bitmap createBlackWhiteImage(Bitmap image, float[] radios) {
        int width = image.getWidth();   //獲取位圖的寬
        int height = image.getHeight();  //獲取位圖的高

        Bitmap result = Bitmap.createBitmap(width, height, image.getConfig());

        int alpha = 0xff;
        int r = 0;
        int g = 0;
        int b = 0;
        int max = 0;
        int min = 0;
        int mid = 0;
        int gray = 0;

        float radioMax = 0;
        float radioMaxMid = 0;

        if (radios == null) {
            // 紅        黃         綠         青         藍        紫
            radios = new float[]{0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.8f};
        }
        int[] resultPixle = new int[width*height];
        for (int i = 0; i < width; i++) {//一列列掃描
            for (int j = 0; j < height; j++) {

                gray = image.getPixel(i, j);//此段代碼從bitmap中獲取某個點的顏色,也將導致耗時大大增加。

                alpha = gray >>> 24;
                r = (gray >> 16) & 0x000000ff;
                g = (gray >> 8) & 0x000000ff;
                b = gray & 0x000000ff;


                if (r >= g && r >= b) {
                    max = r;
                    radioMax = radios[0];
                }
                if (g >= r && g >= b) {
                    max = g;
                    radioMax = radios[2];
                }
                if (b >= r && b >= g) {
                    max = b;
                    radioMax = radios[4];
                }
                if (r <= g && r <= b) { // g+ b = cyan 青色
                    min = r;
                    radioMaxMid = radios[3];
                }

                if (b <= r && b <= g) {//r+g = yellow 黃色
                    min = b;
                    radioMaxMid = radios[1];
                }
                if (g <= r && g <= b) {//r+b = m 洋紅
                    min = g;
                    radioMaxMid = radios[5];
                }

                mid = r + g + b - max - min;

//              公式:gray= (max - mid) * ratio_max + (mid - min) * ratio_max_mid + min

                gray = (int) ((max - mid) * radioMax + (mid - min) * radioMaxMid + min);

                gray = (alpha << 24) | (gray << 16) | (gray << 8) | gray;

//                2000x3500大圖,耗時相差2~5倍左右
//bitmap在循環中設置像素點,這個操作會導致耗時嚴重,耗時7秒。4096x4096圖耗時22秒
//                result.setPixel(i, j, gray);
                resultPixle[j*width+i] = gray;//直接改變數組,最後bitmap再設像素
            }
        }

        result.setPixels(resultPixle,0,width,0,0,width,height);//最後bitmap再設像素
        return result;
    }
}

如果在循環體中,使用 bitmap.setPixel(i, j, gray); 直接改變某一點的像素,那麼n次循環下來,會導致效率大大降低,耗時相差2~5倍左右。本來在安卓中使用java來處理圖片就很慢了。。。

測試的結果:

使用上敘的方法,4096x4096圖在無壓縮的情況下,完全處理完畢,需要耗時22秒。測試用的是360手機。其中,三星手機也進行過測試,但由於內存限制,進行了縮放加載,縮放後圖片有2000*2000以上,之後再處理,耗時同樣嚴重,10秒以上。

所以,圖片處理這種事,java上的處理是不給力的,尤其是在移動端這種東東身上。除非是進行類似模糊這種,可以進行縮小之後處理,最後放大,而不影響最終效果的運算。

全部優化後的結果

經過很久很久的排查,我終於懷疑了這一段代碼:

gray = bitmap.getPixel(i, j);

這一段代碼從bitmap中獲取某個點的顏色,這是繼第一次優化後,仍然導致ps黑白化高達10秒,20秒的罪魁禍首。這個方法不能用,那麼我們只能通過最原始的方式——從像素數組獲取指定點的顏色。

完全優化後的代碼:

package com.lu.adog.util.image;

import android.graphics.Bitmap;

import com.lu.adog.util.Logg;

/**
 * @author Yue
 * @date 2017/11/17 17:42
 */

public class PSGray {


    /**
     * Photoshop 黑白算法,默認效果
     *
     * @param image
     * @return 新的黑白化圖片
     */
    public static Bitmap createBlackWhiteImage(Bitmap image) {
        return createBlackWhiteImage(image, null);
    }

    /**
     * Photoshop 黑白算法,默認效果
     *
     * @param image
     * @return 新的黑白化圖片
     * @radios 顏色通道配置,依次爲紅、黃、 綠、 青、 藍、紫六個通道
     */
    public static Bitmap createBlackWhiteImage(Bitmap image, float[] radios) {

        int width = image.getWidth();   //獲取位圖的寬
        int height = image.getHeight();  //獲取位圖的高

        Bitmap result = Bitmap.createBitmap(width, height, image.getConfig());

        int alpha = 0xff;
        int r = 0;
        int g = 0;
        int b = 0;
        int max = 0;
        int min = 0;
        int mid = 0;
        int gray = 0;

        float radioMax = 0;
        float radioMaxMid = 0;

        if (radios == null) {
            // 紅        黃         綠         青         藍        紫
            radios = new float[]{0.4f, 0.6f, 0.4f, 0.6f, 0.2f, 0.8f};
        }
        int[] resultPixle = new int[width * height];
        image.getPixels(resultPixle, 0, width, 0, 0, width, height);

        for (int i = 0; i < width; i++) {//一行行掃描
            for (int j = 0; j < height; j++) {

                gray = resultPixle[j * width + i];
//                gray = image.getPixel(i,j);//此方法效率極低,不要出現在循環體中,否則將導致極度耗時

                alpha = gray >>> 24;
                r = (gray >> 16) & 0x000000ff;
                g = (gray >> 8) & 0x000000ff;
                b = gray & 0x000000ff;


                if (r >= g && r >= b) {
                    max = r;
                    radioMax = radios[0];
                }
                if (g >= r && g >= b) {
                    max = g;
                    radioMax = radios[2];
                }
                if (b >= r && b >= g) {
                    max = b;
                    radioMax = radios[4];
                }
                if (r <= g && r <= b) { // g+ b = cyan 青色
                    min = r;
                    radioMaxMid = radios[3];
                }

                if (b <= r && b <= g) {//r+g = yellow 黃色
                    min = b;
                    radioMaxMid = radios[1];
                }
                if (g <= r && g <= b) {//r+b = m 洋紅
                    min = g;
                    radioMaxMid = radios[5];
                }

                mid = r + g + b - max - min;

//              公式:gray= (max - mid) * ratio_max + (mid - min) * ratio_max_mid + min

                gray = (int) ((max - mid) * radioMax + (mid - min) * radioMaxMid + min);

                gray = (alpha << 24) | (gray << 16) | (gray << 8) | gray;

//                2000x3500大圖,耗時相差2~5倍左右
//bitmap在循環中設置像素點,這個操作會導致耗時嚴重,耗時7秒。4096x4096圖耗時22秒
//                result.setPixel(i, j, gray);
                resultPixle[j * width + i] = gray;//直接改變數組,最後bitmap再設像素
            }
        }

        result.setPixels(resultPixle, 0, width, 0, 0, width, height);//最後bitmap再設像素

        return result;
    }
}

然後,對3000*4100的大圖進行黑白化,耗時居然已經不到1秒了!

至此,大功告成。

最後再次警告:

bitmap.getPixel() 與 bitmap.setPixel() 效率極低,循環體中無論如何都不要使用bitmap對象進行的一系列與像素相關的操作。代替的方案是使用像素數組獲取

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章