圖片轉字符畫
文字轉成圖片
代碼實現功能描述
- 看到很多人喜歡在代碼開始或者結束位置打印比較好看的圖案,比如佛祖保佑無BUG;正好在學習go,于是就想實現一下這個功能。將圖片內容或者文字內容轉成字符串形式輸出,方便我們可以將喜歡的圖案轉成字符串放到我們寫的代碼中。詳細代碼下載
圖片轉字符畫
文字轉字符畫
實現思路簡述
- 遍歷獲取圖片像素的RGB信息,根據RGB的值去選擇對應像素點的替換字符。
- 把文字轉成字符畫,就是先把文字內容寫到圖片中,然后回到遍歷圖片像素信息方式實現字符替換像素
代碼整體實現
1、通過文件路徑獲取一個圖片
// GetImage 根據路徑獲取一個圖片
func GetImage(imagePath string) *image.Image {
// 讀取image文件
imageFile, _ := ioutil.ReadFile(imagePath)
// bytes.buffer 是一個緩沖byte類型的緩沖器存放著都是byte
imageBytes := bytes.NewBuffer(imageFile)
// 解碼
im, _, err := image.Decode(imageBytes)
if err != nil {
log.Printf("解碼圖像失敗%#v", err)
return nil
}
return &im
}
2、根據圖片的寬度,等比例縮放圖片
如果有的圖片太寬的話,打印的時候終端屏幕顯示寬度太窄,導致顯示換行問題
// equalScaleImageFromWidth 根據寬度,等比例縮放圖片, 返回新的像素值(等比例處理后的)
// maxWidth 圖片最大寬度 如果超過此值就按照寬度 targetWidth 等比例縮放圖片
func equalScaleImageFromWidth(img image.Image, maxWidth int, targetWidth int) *image.Image {
// 原圖像界限范圍
bounds := img.Bounds()
dx := bounds.Dx()
dy := bounds.Dy()
log.Printf("原圖寬=%d,高=%d", dx, dy)
m := img
if dx > maxWidth {
// 按照寬度縮放
m = resize.Resize(uint(targetWidth), 0, img, resize.Lanczos3)
}
return &m
}
3、將圖片保存到文件中
// SaveNewImage 保存一個新的圖片文件
func SaveNewImage(targetPath string, newImg *image.Image) {
f, err := os.Create(targetPath)
if err != nil {
log.Printf("創建文件失敗%#v", err)
}
defer f.Close()
// 獲取文件后綴判斷編碼方法
suffix := path.Ext(targetPath) // strings.Replace(path.Ext(targetPath), ".", "", -1)
if suffix == ".jpg" || suffix == ".jpge" {
err = jpeg.Encode(f, *newImg, nil)
} else if suffix == "png" {
err = png.Encode(f, *newImg)
} else if suffix == "gif" {
err = gif.Encode(f, *newImg, nil)
} else {
log.Printf("非可處理圖片格式")
}
if err != nil {
log.Printf("保存一個新的圖片失敗%#v", err)
}
log.Printf("保存一個新的圖片成功!")
}
4、實現圖片轉字符串
這里我簡單根據像素RGB相加的值判斷顏色選擇字符(越大越接近白色,選擇空格替換)
// ImageToChars 圖像轉成字符展示
// RGBA(R,G,B,A) 像素值
// R:紅色值 G:綠色值 B:藍色值 A:Alpha透明度
func ImageToChars(img *image.Image, inputChars string) string {
sourceImage := *img
bounds := sourceImage.Bounds()
dx := bounds.Dx()
dy := bounds.Dy()
imgChars := "** "
if inputChars != "" {
imgChars = inputChars
}
resultString := ""
intSliceRGB := []int{}
maxIntRGB := 0
minIntRGB := 255 * 3
for i := 0; i < dy; i++ {
for j := 0; j < dx; j++ {
colorRgb := sourceImage.At(j, i)
// 獲取 uint32 像素值 >> 8 轉換為 255 值
r, g, b, _ := colorRgb.RGBA()
// sumRGB 越大越趨近于白色,越小越趨近于黑色
sumRGB := int(uint8(r>>8)) + int(uint8(g>>8)) + int(uint8(b>>8))
// 找到最大值和最小值,方便將像素值劃分不同區間段
if maxIntRGB < sumRGB {
maxIntRGB = sumRGB
}
if minIntRGB > sumRGB {
minIntRGB = sumRGB
}
intSliceRGB = append(intSliceRGB, sumRGB)
}
}
for index, val := range intSliceRGB {
// partLen為區間跨度 我們按照傳入字符串元素個數平均分配像素值,將像素值分成幾個區間
// +1 防止下標溢出
// 例如 像素值 0~500 用兩個字符替換 0~250 251~500 兩個區間分別與其對應即可
partLen := (maxIntRGB-minIntRGB)/(len(imgChars)) + 1
// 根據像素值取不同的字符替換像素
str := string(imgChars[(val-minIntRGB)/partLen])
resultString += str
// 判斷換行
if (index+1)%dx == 0 {
resultString += "\n"
}
}
return resultString
}
5、把字符串寫入文件
我們也可以把程序打包二進制文件,在我們需要的地方調用,然后打印出我們喜歡的內容即可
// WriteStringToFile 通過 io.WriteString 寫入文件
func WriteStringToFile(fileName string, writeInfo string) {
f, err := os.Create(fileName)
if err != nil {
log.Printf("創建文件失敗:%s,日志%#v", fileName, err)
return
}
log.Printf("成功創建文件:%s", fileName)
defer f.Close()
// 將文件寫進去
if _, err = io.WriteString(f, writeInfo); err != nil {
log.Printf("WriteStringToFile 寫入文件失敗:%+v", err)
return
}
log.Printf("WriteStringToFile 寫入文件成功")
}
6、根據文字內容創建圖片
這里需要我們下載ttf的字體樣式,然后根據字體樣式把文字渲染到圖片上,用到了
github.com/golang/freetype
// CrateImageFromString 根據字符串創建圖片
func CrateImageFromString(fileName string, imgString string, width int, height int, fontSize float64) *image.Image {
// 讀字體數據
fontBytes, err := ioutil.ReadFile("ht.ttf")
if err != nil {
log.Printf("CrateImageFromString 讀字體信息失敗:%#v", err)
return nil
}
font, err := freetype.ParseFont(fontBytes)
if err != nil {
log.Printf("CrateImageFromString 解析字體失敗:%#v", err)
return nil
}
// 設置圖片范圍和底色
img := image.NewRGBA(image.Rect(0, 0, width, height))
draw.Draw(img, img.Bounds(), image.White, image.ZP, draw.Src)
c := freetype.NewContext()
c.SetDPI(100) // 設置像素分辨率 每英寸點數 越大像素越好
c.SetFont(font)
c.SetFontSize(fontSize)
c.SetClip(img.Bounds())
c.SetDst(img)
c.SetSrc(image.Black)
// 設置字體顯示位置
// >>6 把Int26_6轉回int
pt := freetype.Pt(10, int(c.PointToFixed(fontSize)>>6))
for _, s := range strings.Split(imgString, "\n") {
_, err = c.DrawString(s, pt)
if err != nil {
log.Printf("CrateImageFromString 字符串畫圖失敗:%#v", err)
return nil
}
pt.Y += c.PointToFixed(fontSize)
}
// 創建圖片文件
imgFile, err := os.Create(fileName)
if err != nil {
log.Printf("創建文件失敗:%s,日志%#v", fileName, err)
return nil
}
defer imgFile.Close()
// 保存圖像到文件
err = png.Encode(imgFile, img)
if err != nil {
log.Fatal(err)
}
return GetImage(fileName)
}