Android實現(xiàn)圖片轉(zhuǎn)字符畫效果

開門見山!先上效果圖:


原圖
轉(zhuǎn)換字符畫

字符稍微密集了一點,不過放大來看大家應(yīng)該能夠看到確確實實是字符畫。
那我們在安卓端是如何實現(xiàn)?
Android開發(fā)中對圖片的操作,顯示一般都是通過Bitmap進行的,我們可以通過圖片路徑獲取Bitmap對象:

static public Bitmap getBitmapByUri(Context context, Uri uri) {
        Bitmap bit = null;
        try {
            bit = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri));
        } catch (Exception ex) {
            Log.i("utils", "" + ex.getMessage());
        }
        return bit;
    }

一個圖片的每一個像素其實都是一個值,這個值代表著這個像素的顏色,我們可以通過位運算來獲取這個像素的ARGB值。
在安卓開發(fā)中要獲取一個圖片的每一個像素值其實很簡單:

//按照參數(shù)范圍獲取像素數(shù)組
bitmap.getPixels(...);
//或者獲取單個位置像素
bitmap.getPixel(x,y);

當(dāng)我們獲取到了像素值,轉(zhuǎn)換成ARGB值后,我們獲取帶了RGB三個值,要如何判斷什么顏色用什么字?要知道調(diào)色輪盤的顏色數(shù)不勝數(shù):


截取自iconfont的調(diào)色板

這么多的顏色我們應(yīng)該用什么樣的標(biāo)準(zhǔn)給這么多顏色歸類?灰度值是個很好的辦法,什么是灰度值?灰度值的范圍只有0到255,計算方式一般是RGB三個值的平均值(也可以通過對RGB值進行加權(quán)計算不同的灰度),在很多圖像處理里面的圖片灰度化步驟用的就是這種方法。


灰度化示例(轉(zhuǎn)自百度百科圖片)

原理跟思路清楚了,我們實現(xiàn)下把Bitmap轉(zhuǎn)化成灰度值數(shù)組的方法:

 static public int[][] getBitmap2GaryArray(Bitmap bitmap) {
        int width = bitmap.getWidth();            //獲取位圖的寬
        int height = bitmap.getHeight();        //獲取位圖的高
        int[][] datas = new int[width][height];    //通過位圖的大小創(chuàng)建像素點數(shù)組
        //也可以使用getPixels方法來獲取像素數(shù)組
        //bitmap.getPixels(datas, 0, width, 0, 0, width, height);
        int alpha = 0xFF << 24;
        bitmap.getPixels();
        for (int i = 0; i < width; i++) {
            for (int j = 0; j < height; j++) {
                int grey = bitmap.getPixel(i, j);
                int red = (grey & 0x00ff0000) >> 16; //取高兩位
                int green = (grey & 0x0000ff00) >> 8; //取中兩位
                int blue = grey & 0x000000ff; //取低兩位

                grey = (int) ((float) red * 0.4 + (float) green * 0.3 + (float) blue * 0.3);
                datas[i][j] = grey;
            }
        }
        return datas;
    }

在獲取像素前我們還需要多做一步,為了防止圖片過大(類似2K圖/4K圖),我們需要在獲取像素前做一次統(tǒng)一標(biāo)準(zhǔn)化的壓縮,我設(shè)置為寬為200,高等比例壓縮。

...
//寬為200時,計算壓縮比例是多少
float xScale = (float) 200 / bitmap.getWidth();
bitmap = BitmapUtils.compressBitmap(bitmap, xScale, xScale);
...

static public Bitmap compressBitmap(Bitmap bitmap, float sx, float sy) {
        Matrix matrix = new Matrix();
        matrix.setScale(sx, sy);
        Log.i("utils_compressBitmap", "" + sx + "," + sy);
        Bitmap bit = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
                bitmap.getHeight(), matrix, true);

        Log.i("utils_compressBitmap", "" + bit.getWidth() + "," + bit.getHeight());
        //記得把不用的bitmap進行回收,以防止OOM
        bitmap.recycle();
        return bit;
    }

當(dāng)我們通過壓縮好的圖片獲取到了它的灰度值數(shù)組,現(xiàn)在我們就可以根據(jù)灰度值轉(zhuǎn)換為對應(yīng)的文字了,我給了灰度值15個等級,根據(jù)顏色的深度給對應(yīng)的中文字:(0是黑色,255是白色)

static String[] arr = {"餮", "淼", "圓", "困", "品", "回", "田", "凸", "口", "王", "天", "干", "工", "十", "一"};

我們制定好字符等級,那么要怎么根據(jù)數(shù)組制作圖片呢?
上面說過圖片的操作在Android中一般都在Bitmap進行的,所以我們要想繪制一張新的圖片,那么就創(chuàng)建一個新的Bitmap對象,繪制的事情交給萬能的畫布就好了,畫布帶有文字繪制接口完美的符合我們需求:

static public Bitmap array2Bitmap(int[][] garyDatas, int width, int height) {
        //繪制一個字對應(yīng)一個像素,所以新繪制的Bitmap的大小應(yīng)該乘上字體大小
        Bitmap whiteBgBitmap = Bitmap.createBitmap(width * 6 + 20, height * 6 + 20, Bitmap.Config.ARGB_8888);
        //在Bitmap上創(chuàng)建畫布
        Canvas canvas = new Canvas(whiteBgBitmap);
        //繪制白色背景
        canvas.drawARGB(255, 255, 255, 255);
        //初始化畫筆
        Paint mPaint = new Paint();
        mPaint.setStrokeWidth(1);
        mPaint.setColor(Color.BLACK);
        mPaint.setTextSize(6);

        int x = 0;
         //遍歷灰度值數(shù)組
        for (int xIndex = 10; x < width; xIndex += 6) {
            int y = 0;
            for (int yIndex = 10; y < height; yIndex += 6) {
                //獲取灰度值對應(yīng)的字符
                int charIndex = garyDatas[x][y] / 18;
                String _char = arr[charIndex];
                //在對應(yīng)的坐標(biāo)繪制字符
                canvas.drawText(_char, xIndex, yIndex, mPaint);
                y++;
            }
            x++;
        }
        return whiteBgBitmap;
    }

繪制完成后輸出Bitmap,下一步是把Bitmap保存為本地圖片,關(guān)鍵代碼如下:

...
File photo = new File(Environment.getExternalStorageDirectory() + "/" + dirName, String.format("CharPic_%d.jpg",System.currentTimeMillis()));

File dir = new File(photo.getParent());
if(!dir.exists()){
      dir.mkdirs();
 }
photo.createNewFile();
saveBitmapToJPG(bmp, photo);
...

static private void saveBitmapToJPG(Bitmap bitmap, File photo) throws IOException {
        OutputStream stream = new FileOutputStream(photo);
        bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream);
        stream.close();
        bitmap.recycle();
    }

下一步我們?yōu)榱嗽谙到y(tǒng)相冊更好的找到我們的圖片,我們可以把圖片發(fā)送一個廣播來通知系統(tǒng)相冊:

Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(photo);
mediaScanIntent.setData(contentUri);
context.sendBroadcast(mediaScanIntent);

以上就是圖片轉(zhuǎn)成字符畫的全部代碼與講解??赡苡械娜藭栠@樣的功能,除了酷炫,有趣,牛逼之外,做出來有什么用?我只能問得好!乍一看好像用處不大,但是基于這個功能我們可以做短視頻轉(zhuǎn)換字符畫視頻。下一篇我將會講一下如何把視頻轉(zhuǎn)換成字符畫視頻,本篇的內(nèi)容到此為止,如有問題,歡迎提出,如有錯誤,歡迎指正,謝謝。

奉上完整的源碼(已完成視頻轉(zhuǎn)換跟圖片轉(zhuǎn)換功能),覺得有趣的請star一下唄。
完整項目源碼地址:https://github.com/452kinton/CharacterDance

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容