Android平臺下的圖片/視頻轉Ascii碼圖片/視頻 (二)

????忙里偷閑又來寫一篇文章,最近在更新一些好玩的圖片算法,當然也沒落下優(yōu)化ascii碼的圖像效果,這次我將更換一種計算ascii碼的方式,這樣能更好的添加一些效果,并且更加清楚的講解一下原理。

????在上一篇文章里,有很多人留言給我說,為什么那個圖片縮放比為7,改成6或者5為什么生成的圖片就不正常了,這一點在我剛開始看類似的參考文獻時也比較費解,不過在我多次的嘗試時,發(fā)現(xiàn)7這個比例是最好的,至于原因,我講出來想必你們肯定會失望了,因為我想說的是12號字符#8XOHLTI)i=+;:,.這幾個在staticlayout里繪制出來以后大概占用7像素,想想,一個像素替換成了一個ascii碼,如果圖片縮放為屏幕寬的1/7,替換成字符以后,又會變成正好占滿屏幕寬的ascii碼圖片。所以當改成縮放比為6,或者5以下時,由于一行像素替換成字符時,staticlayout再生成屏幕寬的圖片時,因為一個字符占用7像素,所以在未到達換行符\n時會提前換行,導致生成的圖片不正確。

????因為我也不喜歡猜或者試出來的方式來搞效果,因此我又想出了另一種方法生成ascii碼圖片,我選取了筆畫具有代表性的漢字:"一七刀九上工土開天月”,當然我方法預留了可以使用其他字的地方,然后我對這些字進行以下處理:

  • 首先用Canvas繪制出這些字 使用Paint.FontMetrics fontMetrics = paint.getFontMetrics();測量出字繪制出來以后黑色的像素塊float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;這個distance變量為繪制中心距離繪制baseline的距離,(這個地方不懂的具體可以百度android drawtext)
final String base = "一七刀九上工土開天月";// 隨機字符串  當然如果你想更改成別的字符串也行,不過字符串的每個字最好涵蓋各個復雜程度的字符
//            final String base = "#8XOHLTI)i=+; :,.";// 隨機字符串
            float maxCharWidth = 0;
            Map<Character, Integer> blackMap = new HashMap<>();
//        final String base = "#8XOHLTI)i=+;:,.";// 隨機字符串
            Paint paint = new Paint();
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.BLACK);
            paint.setStrokeWidth(0.1f);
            paint.setTextAlign(Paint.Align.CENTER);
            paint.setTextSize(10);
            Paint.FontMetrics fontMetrics = paint.getFontMetrics();
            float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
            for (int i = 0; i < base.length(); i++) {
                float width = paint.measureText(base.charAt(i) + "");
                Bitmap bitmap = Bitmap.createBitmap(((int) width), ((int) (fontMetrics.bottom - fontMetrics.top)), Bitmap.Config.ARGB_8888);
                Canvas canvas = new Canvas(bitmap);
                canvas.drawColor(Color.WHITE);
                canvas.drawText(base.charAt(i) + "", 0, 1, bitmap.getWidth() / 2, bitmap.getHeight() / 2 + distance, paint);
                int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
                bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
                int gray = 0;
                for (int pixel : pixels) {
                    if (pixel != -1) {
                        gray++;
                    }
                }
                blackMap.put(base.charAt(i), gray);
                int charWidth = bitmap.getWidth();
                int charHeight = bitmap.getHeight();
                if (maxCharWidth < Math.max(charWidth, charHeight)) {
                    maxCharWidth = Math.max(charWidth, charHeight);
                }
                bitmap.recycle();
            }
//            maxCharWidth *= (5 / 6f);
            Character[] characters = new Character[base.length()];
            for (int i = 0; i < characters.length; i++) {
                characters[i] = base.charAt(i);
            }
            Arrays.sort(characters, new Comparator<Character>() {
                @Override
                public int compare(Character o1, Character o2) {
                    return blackMap.get(o2) - blackMap.get(o1);
                }
            });

這里就是遍歷每一個字符,繪制出來以后,保存所有字符最大的寬高,以此來像活字印刷一樣,一個字符作為一個替換的像素群塊,比如我以10字號大小來繪制每一個字符,對每一個漢字繪制出來以后需要用到多少個像素點(用像素點的多少來代表漢字的復雜程度),遍歷計算完,發(fā)現(xiàn)最大寬高為8像素,然后我對base String進行排序,漢字復雜程度降序排列,如果過于亮的地方就用筆畫少的字替換沒毛病吧?

  • 緊接著對原圖像進行縮放,使他的寬為屏幕寬,這樣生成的圖片不會過大,也不會過小導致效果不好看。
    (這里就不貼縮放代碼了,想必各位android老鐵縮放圖片比我熟練)
  • 緊接著就到了重頭戲了,剛才我第一步算出來了每一個待替換字符繪制出來的最大寬高,然后就以這個寬高一個塊一個塊像國際象棋格子一樣的,替換掉同樣大小的原圖像素塊,這里bitmap只能重新創(chuàng)建一個,因為是decode的bitmap,原來的像素數(shù)組不可修改。然后兩層for循環(huán)遍歷每一個分割的小塊,計算出原圖像這個小塊左上角的像素點的灰度值,計算出這個灰度值在0到255內的哪個地方,因為如果帶替換的字符為9個,那么就要分割成10份,要給空字符做為替換過于明亮的地方。(這里我就不求每一個原圖像小塊,比如10x10,內所有的像素點的灰度值了,java的效率你懂得,其實用一個像素來代表這個小像素塊足夠了),比如這個像素的灰度值為10,在0到255里,屬于10個部分中的第一部分,是比較暗的部分,那么我就用10個可替換漢字中筆畫最多的漢字來替換(當然在最開始提供可替換字符時,最好不要提供筆畫過于多的漢字,因為這樣會導致生成的圖片過于胡一片)。
for (int y = 0; y < image.getHeight(); y += maxCharWidth) {
                for (int x = 0; x < image.getWidth(); x += maxCharWidth) {
//                    Log.i("icv", "繪制x=" + (x + 6f) + " y=" + (y + 6));
                    final int pixel = image.getPixel(x, y);
                    final int r = (pixel & 0xff0000) >> 16, g = (pixel & 0xff00) >> 8, b = pixel & 0xff;
                    final int gray = (int) (0.299f * r + 0.578f * g + 0.114f * b);
                    final int index = Math.round(gray * (characters.length + 1) / 255);
                    paint.setColor(Color.rgb(gray, gray, gray));
                    outCanvas.drawText(index >= characters.length ? " " : String.valueOf(characters[index]), x + maxCharWidth / 2f, y + maxCharWidth / 2f + distance, paint);
                }
            }
  • 還有一個神來之筆的步驟:如果你想生成彩色的字符,那么就把原圖像蓋在下面,讓目前生成的字符畫作為模板,讓下面的顏色投過來,話不多說,上代碼:
          if (isColorful) {
                for (int y = 0; y < outImage.getHeight(); y++) {
                    for (int x = 0; x < outImage.getWidth(); x++) {
                        if (outImage.getPixel(x, y) != Color.WHITE) {
                            outImage.setPixel(x, y, image.getPixel(x, y));
                        }
                    }
                }
            }
            image.recycle();

最后放一個效果圖吧:


大公主黑白.png

大公主彩色.png

抖音

系列文章:
Android平臺下的圖片/視頻轉Ascii碼圖片/視頻 (一)
Android平臺下的圖片/視頻轉Ascii碼圖片/視頻 (二)

參考文獻:
無參考

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

友情鏈接更多精彩內容