Android開發學習之路-圖片顏色獲取器開發(1)


系列第一篇,從簡單的開始,一步一步完成這個小項目。

顏色獲取就是通過分析圖片中的每個像素的顏色,來分析整個圖片的主調顏色,有了主調顏色,我們可以用於圖片所在卡片的背景或者標題顏色,這樣整體感更加強烈。

有興趣的可以學習下使用谷歌提供的Palette,也是做同樣的工作,博客地址:http://www.cnblogs.com/Fndroid/p/5201236.html

先看效果圖:

分析原理比較簡單,就是獲取圖片的所有像素的顏色,然后統計,把統計的數目排序,然后返回給用戶。

但是這里要先注意幾個問題:

① 獲取顏色的過程會不會導致UI線程卡頓;

② 怎么實現排序(如何優化后面研究);

因為考慮到當圖片像素較多的時候,分析可能不會馬上完成,所以分析過程應該在子線程中完成,避免阻塞UI線程。其次,統計和排序都可以通過Java提供的數據結構來簡單實現,暫且不考慮性能因素,實現功能為先。

 

① 在Android中,圖片一般會用Bitmap來表示,而Bitmap中有一個叫做getPixels的方法:

public void getPixels(@ColorInt int[] pixels, int offset, int stride, int x, int y, int width, int height)

參數分別是:

pixiels:存放識別出顏色的數組

offset:數組的起始下標

stride:每行的數據量,比如一下張200*200的圖片,在stride設置為200的時候,pixels[200]為第二行的第一個像素顏色,當stride設置為400的時候,pixels[200]為額外的信息,不包含顏色,而第二行的第一個像素的顏色應該在pixels[400]

x、y:開始的x和y

width、height:需要獲取顏色的寬度和高度,和x、y構成一個矩形

② 獲取完顏色之后,對顏色進行統計,因為得到了一個數組,所以對數組進行統計,如果有相同的要加一,得出每個顏色出現的次數,算法簡單如下

HashMap<Integer, Integer> colors = new HashMap<>();
for (int pixel : pixels) {
   Integer num = colors.get(pixel);
   if (num == null) {
       colors.put(pixel, 1);
   } else {
       num += 1;
       colors.put(pixel, num);
   }
}

③ 排序,因為需要返回一個有序的數組,這里方便的可以用TreeMap直接排,要注意TreeMap是對key排序的,而且默認是升序

TreeMap<Integer, Integer> sortedColors = new TreeMap<>();
for (Map.Entry<Integer, Integer> entry : colors.entrySet()) {
    sortedColors.put(entry.getValue(), entry.getKey());
}

④ 返回一個有序數組,這里簡單的遍歷一下存到ArrayList中即可

ArrayList<Integer> result = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : sortedColors.entrySet()) {
    result.add(entry.getValue());
}

⑤ 如果直接調用函數求,會可能阻塞UI,所以要用子線程來做這個工作,簡單的話直接new一個Thread

⑥ 在子線程中如果任務執行完畢,通過Handler發送消息,通知UI更新

 

下面給出整個類:

public class ColorCaptureUtil {
    private static final String TAG = "ColorCaptureUtil";
    private Handler mHandler;
    public static final int SUCCESS = 1;

    /**
     * Construct a ColorCaptureUtil for analysing the color of the bitmap
     * @param handler When the analysing done, it would be used to send back the result and call for update
     */
    public ColorCaptureUtil(Handler handler) {
        mHandler = handler;
    }

    /**
     * Capture the bitmap's colors by counting every pixels, use the constructor's Handler to send back
     * message, the ArrayList which contains the colors and sorted by the color quantity would be send
     * back with the message in the Message.obj
     * @param bitmap The Bitmap for analysing
     */
    public void getBitmapColors(Bitmap bitmap){
        getBitmapColors(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight());
    }

    /**
     * Capture the bitmap's colors by counting every pixels, use the constructor's Handler to send back
     * message, the ArrayList which contains the colors and sorted by the color quantity would be send
     * back with the message in the Message.obj
     * @param bitmap The Bitmap for analysing
     * @param fromX  The start X in the bitmap, can not be negative
     * @param fromY  The start Y in the bitmap, can not be negative
     * @param toX    The end X in the bitmap, can not less than fromX
     * @param toY    The end Y in the bitmap, can not less than fromY
     */
    public void getBitmapColors(Bitmap bitmap, int fromX, int fromY, int toX, int
            toY) {
        new Thread(new MyRunnable(bitmap,fromX,fromY,toX,toY,mHandler)).start();
    }

    private class MyRunnable implements Runnable{
        private Bitmap bitmap;
        private int fromX,fromY,toX,toY;
        private Handler mHandler;

        public MyRunnable(Bitmap bitmap, int fromX, int fromY, int toX, int toY, Handler handler) {
            this.bitmap = bitmap;
            this.fromX = fromX;
            this.fromY = fromY;
            this.toX = toX;
            this.toY = toY;
            this.mHandler = handler;
        }

        @Override
        public void run() {
            int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
            HashMap<Integer, Integer> colors = new HashMap<>();
            TreeMap<Integer, Integer> sortedColors = new TreeMap<>();
            ArrayList<Integer> result = new ArrayList<>();
            bitmap.getPixels(pixels, 0, bitmap.getWidth(), fromX, fromY, toX - fromX, toY - fromY);
            for (int pixel : pixels) {
                Integer num = colors.get(pixel);
                if (num == null) {
                    colors.put(pixel, 1);
                } else {
                    num += 1;
                    colors.put(pixel, num);
                }
            }
            for (Map.Entry<Integer, Integer> entry : colors.entrySet()) {
                sortedColors.put(entry.getValue(), entry.getKey());
            }
            for (Map.Entry<Integer, Integer> entry : sortedColors.entrySet()) {
                result.add(entry.getValue());
                Log.d(TAG, "run: color:"+entry.getValue()+",count:"+entry.getKey());
            }
            Message msg = new Message();
            msg.obj = result;
            msg.what = SUCCESS;
            mHandler.sendMessage(msg);
        }
    }
}

可以看到,在構造的時候需要一個Handler,當我們處理完圖片顏色數據的時候,就是通過這個Handler來通知UI線程,然后把統計的數據存到Message.obj中,在主線程中直接取出即可得到顏色,數據是按升序排列的,如果想得到最多的顏色,需要取出size-1下標的值

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == ColorCaptureUtil.SUCCESS) {
                ArrayList<Integer> colors = (ArrayList<Integer>) msg.obj;
                color.setBackgroundColor(colors.get(colors.size() - 1));
            }
        }
    };

 

第一部分的內容比較簡單,正是因為比較簡單,所以接下來還是有一些問題需要解決的

① 解析速度問題,考慮是否要使用其他排序算法?是否需要處理全部的像素點?能否進行局部采樣?

② 當圖片像效果圖中的第四張(如下)一樣的時候,統計出現最多的像素顏色是否合理?

③ 直接使用new Thread是否合理?當有大量圖片需要處理的時候會不會出現問題?

 

盡快更新下篇,歡迎指正交流。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM