文章持續更新,可以關注公眾號程式猿阿朗或訪問未讀程式碼部落格。
本文 Github.com/niumoo/JavaNotes 已經收錄,歡迎Star。
哈嘍,大家好啊,我是阿朗。
已經 2022 年了,最近北京冬奧會的吉祥物冰墩墩很火,據說一墩難求,各種視訊新聞應接不暇。程式設計師要有程式設計師的方式,今天我來用 Java 畫一個由字元組成的冰墩墩送給大家,這篇文章記錄字元圖案的生成思路以及過程。
下面是一個由字元W@#&8*0.
等字元組成的冰墩墩圖案。
1. 字元圖案思路
我們都知道數字圖片是一個二維影像,它使用一個有限的二維陣列儲存每個畫素點顏色資訊,這些畫素點的顏色資訊通常使用 RGB 模式進行記錄。所以從本質上看,我們常見的圖片就是一個儲存了畫素資訊的二維陣列。
基於以上的圖片原理,我們可以發現,如果想要把一個圖片轉換成字元圖案,只需要把每個畫素點的顏色資訊轉換成某個字元就可以了,所以可以理出把圖片轉換成字元圖案的步驟如下。
- 縮放圖片到指定大小,為了保證輸出的字元數量不會太多。
- 遍歷圖片的畫素點,獲取每個畫素點的顏色資訊。
- 根據畫素點的顏色資訊,轉換成灰度(亮度)資訊。
- 把亮度資訊轉換成相應的字元。
- 輸出字元圖案,也就是列印二維字元陣列。
2. 圖片的縮放
如上所述,我們既然想要把每個畫素點的顏色資訊轉換成某個字元,如果畫素點過多的話,雖然會增加字元圖片的還原度,但是看起來會非常麻煩,因為那麼多字元你的螢幕可能顯示不完。
因此,我們要先對圖片進行縮放,縮放到一定大小後再進行字元化。這裡為了方便,直接使用 Java 自帶的圖片處理方式進行圖片縮放,下面的程式碼示例都是指定寬度進行縮放,高度等比例計算後進行縮放。
Java 中調整圖片大小主要有兩種方式:
- 使用
java.awt.Graphics2D
調整圖片大小。 - 使用
Image.getScaledInstance
調整圖片大小。
2.1. java.awt.Graphics2D
Graphics2D 是 Java 平臺提供的可以渲染二維形狀、文字、影像的基礎類,下面是使用 Graphics2D 進行圖片大小調整的簡單示例。
/**
* 圖片縮放
*
* @param srcImagePath 圖片路徑
* @param targetWidth 目標寬度
* @return
* @throws IOException
*/
public static BufferedImage resizeImage(String srcImagePath, int targetWidth) throws IOException {
Image srcImage = ImageIO.read(new File(srcImagePath));
int targetHeight = getTargetHeight(targetWidth, srcImage);
BufferedImage resizedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2D = resizedImage.createGraphics();
graphics2D.drawImage(srcImage, 0, 0, targetWidth, targetHeight, null);
graphics2D.dispose();
return resizedImage;
}
/**
* 根據指定寬度,計算等比例高度
*
* @param targetWidth 目標寬度
* @param srcImage 圖片資訊
* @return
*/
private static int getTargetHeight(int targetWidth, Image srcImage) {
int targetHeight = srcImage.getHeight(null);
if (targetWidth < srcImage.getWidth(null)) {
targetHeight = Math.round((float)targetHeight / ((float)srcImage.getWidth(null) / (float)targetWidth));
}
return targetHeight;
}
程式碼中的 BufferedImage.TYPE_INT_RGB
表示所使用的顏色模型,所有的顏色模型可以在 Java doc - Image 文件中看到。
調整大小後的圖片可以通過以下方式儲存。
BufferedImage image = resizeImage("/Users/darcy/Downloads/bingdundun.jpeg", 200);
File file = new File("/Users/darcy/Downloads/bingdundun_resize.jpg");
ImageIO.write(image, "jpg", file);
下面把原圖為 416 x 500 的冰墩墩圖片縮放到 200 x 240 的效果。
2.2. Image.getScaledInstance
這是 Java 原生功能調整圖片大小的另一種方式,使用這種方式調整圖片大小簡單方便,生成的圖片質量也不錯,程式碼比較簡潔,但是這種方式的效率並不高。
/**
* 圖片縮放
*
* @param srcImagePath 圖片路徑
* @param targetWidth 目標寬度
* @return
* @throws IOException
*/
public static BufferedImage resizeImage2(String srcImagePath, int targetWidth) throws IOException {
Image srcImage = ImageIO.read(new File(srcImagePath));
int targetHeight = getTargetHeight(targetWidth, srcImage);
Image image = srcImage.getScaledInstance(targetWidth, targetHeight, Image.SCALE_DEFAULT);
BufferedImage bufferedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
bufferedImage.getGraphics().drawImage(image, 0, 0, null);
return bufferedImage;
}
// getTargetHeight 同 java.awt.Graphics2D 中示例程式碼
程式碼中的 Image.SCALE_DEFAULT
表示圖片縮放使用的演算法,在Java doc - Image 文件中可以檢視所有可以使用的演算法。
3. RGB 灰度計算
我們知道圖片是由畫素點組成的,每個畫素點儲存了顏色資訊,通常是 RGB 資訊,所以我們想要把每個畫素點轉換成字元,也就是把畫素點中的 RGB 資訊的灰度表達出來,不同的灰度給出不同的字元進行表示。
比如我們把灰度分為 10 個等級,每個等級從高到低選擇一個字元進行標識。
'W', '@', '#', '8', '&', '*', 'o', ':', '.', ' '
那麼如何進行灰度計算呢?目前常見的計算方法有平均值法、加權均值法、伽馬校正法等。這裡直接使用與伽馬校正線性相似的數學公式進行計算,這也是 MATLAB、 Pillow和 OpenCV 使用的方法。
4. 輸出字元圖片
前期準備已經完成了,我們已經把圖片進行了縮放,同時也知道了如何把圖片中的每個畫素點上的 RGB 資訊轉換成灰度值,那麼我們只需要遍歷縮放後的圖片的 RGB 資訊,進行灰度轉換,然後選擇對應的字元進行列印即可。
public static void main(String[] args) throws Exception {
BufferedImage image = resizeImage("/Users/darcy/Downloads/bingdundun.jpeg", 150);
printImage(image);
}
/**
* 字元圖片列印
*
* @param image
* @throws IOException
*/
public static void printImage(BufferedImage image) throws IOException {
final char[] PIXEL_CHAR_ARRAY = {'W', '@', '#', '8', '&', '*', 'o', ':', '.', ' '};
int width = image.getWidth();
int height = image.getHeight();
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int rgb = image.getRGB(j, i);
Color color = new Color(rgb);
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
// 一個用於計算RGB畫素點灰度的公式
Double grayscale = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
double index = grayscale / (Math.ceil(255 / PIXEL_CHAR_ARRAY.length) + 0.5);
System.out.print(PIXEL_CHAR_ARRAY[(int)(Math.floor(index))]);
}
System.out.println();
}
}
// resizeImage 同第二部分程式碼
這裡我選擇一張冰墩墩的圖片,可以看到輸出後的效果。
5. 其他字元圖片
下面是一些其他圖片轉字元圖的效果展示。
2022 年,虎虎生威字元畫。
進擊的巨人人物 - 三笠字元畫。
一如既往,文章中的程式碼存放在:github.com/niumoo/lab-notes
參考
https://www.kdnuggets.com/201...
https://en.wikipedia.org/wiki...
訂閱
可以微信搜一搜程式猿阿朗或訪問未讀程式碼部落格閱讀。
本文 Github.com/niumoo/JavaNotes 已經收錄,歡迎Star。