【golang字符畫】實現代碼中打印好看的圖案

圖片轉字符畫

文字轉成圖片

代碼實現功能描述

  • 看到很多人喜歡在代碼開始或者結束位置打印比較好看的圖案,比如佛祖保佑無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)
}
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。