Android平台下的图片/视频转Ascii码图片/视频 (二)

    忙里偷闲又来写一篇文章,最近在更新一些好玩的图片算法,当然也没落下优化ascii码的图像效果,这次我将更换一种计算ascii码的方式,这样能更好的添加一些效果,并且更加清楚的讲解一下原理。

    在上一篇文章里,有很多人留言给我说,为什么那个图片缩放比为7,改成6或者5为什么生成的图片就不正常了,这一点在我刚开始看类似的参考文献时也比较费解,不过在我多次的尝试时,发现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字号大小来绘制每一个字符,对每一个汉字绘制出来以后需要用到多少个像素点(用像素点的多少来代表汉字的复杂程度),遍历计算完,发现最大宽高为8像素,然后我对base String进行排序,汉字复杂程度降序排列,如果过于亮的地方就用笔画少的字替换没毛病吧?

  • 紧接着对原图像进行缩放,使他的宽为屏幕宽,这样生成的图片不会过大,也不会过小导致效果不好看。
    (这里就不贴缩放代码了,想必各位android老铁缩放图片比我熟练)
  • 紧接着就到了重头戏了,刚才我第一步算出来了每一个待替换字符绘制出来的最大宽高,然后就以这个宽高一个块一个块像国际象棋格子一样的,替换掉同样大小的原图像素块,这里bitmap只能重新创建一个,因为是decode的bitmap,原来的像素数组不可修改。然后两层for循环遍历每一个分割的小块,计算出原图像这个小块左上角的像素点的灰度值,计算出这个灰度值在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码图片/视频 (二)

参考文献:
无参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,294评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,780评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,001评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,593评论 1 289
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,687评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,679评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,667评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,426评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,872评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,180评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,346评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,019评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,658评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,268评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,495评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,275评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,207评论 2 352