????忙里偷閑又來(lái)寫(xiě)一篇文章,最近在更新一些好玩的圖片算法尾序,當(dāng)然也沒(méi)落下優(yōu)化ascii碼的圖像效果帮掉,這次我將更換一種計(jì)算ascii碼的方式躯概,這樣能更好的添加一些效果,并且更加清楚的講解一下原理即寒。
????在上一篇文章里,有很多人留言給我說(shuō),為什么那個(gè)圖片縮放比為7歹篓,改成6或者5為什么生成的圖片就不正常了瘫证,這一點(diǎn)在我剛開(kāi)始看類似的參考文獻(xiàn)時(shí)也比較費(fèi)解,不過(guò)在我多次的嘗試時(shí)滋捶,發(fā)現(xiàn)7這個(gè)比例是最好的痛悯,至于原因,我講出來(lái)想必你們肯定會(huì)失望了重窟,因?yàn)槲蚁胝f(shuō)的是12號(hào)字符#8XOHLTI)i=+;:,.這幾個(gè)在staticlayout里繪制出來(lái)以后大概占用7像素载萌,想想,一個(gè)像素替換成了一個(gè)ascii碼,如果圖片縮放為屏幕寬的1/7巡扇,替換成字符以后扭仁,又會(huì)變成正好占滿屏幕寬的ascii碼圖片。所以當(dāng)改成縮放比為6厅翔,或者5以下時(shí)乖坠,由于一行像素替換成字符時(shí),staticlayout再生成屏幕寬的圖片時(shí)刀闷,因?yàn)橐粋€(gè)字符占用7像素熊泵,所以在未到達(dá)換行符\n時(shí)會(huì)提前換行,導(dǎo)致生成的圖片不正確甸昏。
????因?yàn)槲乙膊幌矚g猜或者試出來(lái)的方式來(lái)搞效果顽分,因此我又想出了另一種方法生成ascii碼圖片,我選取了筆畫(huà)具有代表性的漢字:"一七刀九上工土開(kāi)天月”,當(dāng)然我方法預(yù)留了可以使用其他字的地方施蜜,然后我對(duì)這些字進(jìn)行以下處理:
- 首先用Canvas繪制出這些字 使用Paint.FontMetrics fontMetrics = paint.getFontMetrics();測(cè)量出字繪制出來(lái)以后黑色的像素塊float distance = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;這個(gè)distance變量為繪制中心距離繪制baseline的距離卒蘸,(這個(gè)地方不懂的具體可以百度android drawtext)
final String base = "一七刀九上工土開(kāi)天月";// 隨機(jī)字符串 當(dāng)然如果你想更改成別的字符串也行,不過(guò)字符串的每個(gè)字最好涵蓋各個(gè)復(fù)雜程度的字符
// final String base = "#8XOHLTI)i=+; :,.";// 隨機(jī)字符串
float maxCharWidth = 0;
Map<Character, Integer> blackMap = new HashMap<>();
// final String base = "#8XOHLTI)i=+;:,.";// 隨機(jī)字符串
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);
}
});
這里就是遍歷每一個(gè)字符翻默,繪制出來(lái)以后缸沃,保存所有字符最大的寬高,以此來(lái)像活字印刷一樣修械,一個(gè)字符作為一個(gè)替換的像素群塊,比如我以10字號(hào)大小來(lái)繪制每一個(gè)字符趾牧,對(duì)每一個(gè)漢字繪制出來(lái)以后需要用到多少個(gè)像素點(diǎn)(用像素點(diǎn)的多少來(lái)代表漢字的復(fù)雜程度),遍歷計(jì)算完肯污,發(fā)現(xiàn)最大寬高為8像素,然后我對(duì)base String進(jìn)行排序武氓,漢字復(fù)雜程度降序排列,如果過(guò)于亮的地方就用筆畫(huà)少的字替換沒(méi)毛病吧仇箱?
- 緊接著對(duì)原圖像進(jìn)行縮放县恕,使他的寬為屏幕寬,這樣生成的圖片不會(huì)過(guò)大剂桥,也不會(huì)過(guò)小導(dǎo)致效果不好看忠烛。
(這里就不貼縮放代碼了,想必各位android老鐵縮放圖片比我熟練) - 緊接著就到了重頭戲了权逗,剛才我第一步算出來(lái)了每一個(gè)待替換字符繪制出來(lái)的最大寬高,然后就以這個(gè)寬高一個(gè)塊一個(gè)塊像國(guó)際象棋格子一樣的美尸,替換掉同樣大小的原圖像素塊冤议,這里bitmap只能重新創(chuàng)建一個(gè),因?yàn)槭莇ecode的bitmap师坎,原來(lái)的像素?cái)?shù)組不可修改恕酸。然后兩層for循環(huán)遍歷每一個(gè)分割的小塊,計(jì)算出原圖像這個(gè)小塊左上角的像素點(diǎn)的灰度值胯陋,計(jì)算出這個(gè)灰度值在0到255內(nèi)的哪個(gè)地方蕊温,因?yàn)槿绻麕鎿Q的字符為9個(gè),那么就要分割成10份遏乔,要給空字符做為替換過(guò)于明亮的地方义矛。(這里我就不求每一個(gè)原圖像小塊,比如10x10盟萨,內(nèi)所有的像素點(diǎn)的灰度值了凉翻,java的效率你懂得,其實(shí)用一個(gè)像素來(lái)代表這個(gè)小像素塊足夠了),比如這個(gè)像素的灰度值為10捻激,在0到255里制轰,屬于10個(gè)部分中的第一部分,是比較暗的部分胞谭,那么我就用10個(gè)可替換漢字中筆畫(huà)最多的漢字來(lái)替換(當(dāng)然在最開(kāi)始提供可替換字符時(shí)垃杖,最好不要提供筆畫(huà)過(guò)于多的漢字,因?yàn)檫@樣會(huì)導(dǎo)致生成的圖片過(guò)于胡一片)韭赘。
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);
}
}
- 還有一個(gè)神來(lái)之筆的步驟:如果你想生成彩色的字符,那么就把原圖像蓋在下面势就,讓目前生成的字符畫(huà)作為模板泉瞻,讓下面的顏色投過(guò)來(lái),話不多說(shuō)苞冯,上代碼:
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();
最后放一個(gè)效果圖吧:
系列文章:
Android平臺(tái)下的圖片/視頻轉(zhuǎn)Ascii碼圖片/視頻 (一)
Android平臺(tái)下的圖片/視頻轉(zhuǎn)Ascii碼圖片/視頻 (二)
參考文獻(xiàn):
無(wú)參考