????忙里偷閑又來寫一篇文章,最近在更新一些好玩的圖片算法,當然也沒落下優(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();
最后放一個效果圖吧:



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