Go 語言高效能影像處理神器 h2non/bimg

Aklman發表於2021-09-12

推薦背景

日常業務開發中常會遇到各種影像處理需求,如,圖片大小調整、翻轉、旋轉、提取大小、加水印、圖片模糊化,格式轉換,修剪等等;影像處理根據業務需求一部分影像處理需求在前端完成,如,使用者裁剪編輯,打馬賽克,美化,色彩調整;使用者傳完圖片後的邏輯在後端處理,此時就會用到影像處理相關的方法;Go 語言標準庫提供的 image 能夠處理最基本的影像處理任務,但個性化的影像處理任務還是得自己實現。

當標準庫無法滿足日常開發的需求時候,推薦使用 h2non/bimg ,它能夠快速高效完成各種高階影像處理任務。

h2non/bimg 是什麼

h2non/bimg 是基於 C 語言的 libvips 庫封裝的高階影像處理的 Go 包;它的效能極高,所佔記憶體也很小,通常比 ImageMagickGraphicsMagickGo 標準庫快 4 倍,在某些情況下,處理 JPEG 影像的速度甚至比它們快 8 倍

bimg uses internally libvips, a powerful library written in C for image processing which requires a low memory footprint and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native image package, and in some cases it's even 8x faster processing JPEG images.

h2non/bimg 提供以下出片處理 API:

  • 調整大小
  • 放大
  • 裁剪(包括智慧裁剪支援,libvips 8.5+)
  • 旋轉(根據 EXIF 方向自動旋轉)
  • 翻轉(具有基於 EXIF 後設資料的自動翻轉)
  • 翻轉
  • 縮圖
  • 獲取大小
  • 水印(使用文字或影像)
  • 高斯模糊效果
  • 自定義輸出顏色空間(RGB,灰度...)
  • 格式轉換以及壓縮處理
  • EXIF 後設資料(大小,Alpha 通道,配置檔案,方向...)修改
  • 修剪(libvips 8.6+)

h2non/bimg 能夠將影像輸出為 JPEG、PNG 和 WEBP 格式,包括在它們之間進行格式轉換。

怎麼使用

h2non/bimg 因為基於 C 語言的 libvips 庫,因此使用要滿足以下幾個條件:

  • libvips 8.3+ (8.8+ recommended)
  • C compatible compiler such as gcc 4.6+ or clang 3.0+
  • Go 1.3+

提示:

  • GIF、PDF 和 SVG 支援需要 libvips v8.3+。
  • AVIF 支援需要 libvips v8.9+。 使用 AVIF 編碼器/解碼器編譯的 libheif 也需要存在。

安裝

  1. 建立一個測試專案
# 建立專案目錄
$ mkdir bimg
# 切換到專案根目錄
$ cd bimg
# 初始化專案
$ go mod init bimg

  1. 獲取 h2non/bimg 包
$ go get -u github.com/h2non/bimg

通常會報錯,提示需要依賴 libvips ;因此,先安裝 libvips,再獲取 h2non/bimg 包。

mac 安裝執行 $ brew install vips 即可自動載入安裝配置;其他系統版本請參考 libvips document

再次嘗試獲取 h2non/bimg 包,通常會提示 invalid flag in pkg-config --cflags: -Xpreprocessor ;此時 CGO_FLAGS 授權,即執行一下命令:

$ export CGO_CFLAGS_ALLOW=-Xpreprocessor

再次嘗試獲取 h2non/bimg 包時,會成功獲取包。

開始使用

在專案根目錄建立 main.go 檔案,並準備兩張測試圖片;分別是原始圖片 example.png :

以及用作水印的圖片和 logo.png

開始編寫示例:

package main

import (
    "fmt"
    "os"

    "github.com/h2non/bimg"
)

func main() {
    imgPath := "example.png"
    // 列印影像的後設資料
    meta_data := metaData(imgPath)
    fmt.Printf("影像型別:%v\n影像尺寸:%v x %v\n", meta_data.Type, meta_data.Size.Width, meta_data.Size.Height)

    // 調整影像大小
    resize(imgPath, 1200, 800)

    // 旋轉影像,傳旋轉角度
    rotate(imgPath, 180)

    // 自動(隨機)影像,根據 EXIF 方向後設資料(autoRotateOnly)自動旋轉
    autoRotate(imgPath)

    // 格式轉換
    convert(imgPath, bimg.JPEG)

    // 新增文字水印
    watermarkText(imgPath)

    // 新增圖片水印
    logoPath := "logo.png"
    watermarkImage(imgPath, logoPath)

    // 高斯模糊,模糊程度引數
    gaussianBlur(imgPath, 20, 5)

    // 按長寬式縮略
    smartCrop(imgPath, 400, 300)

    // 縮圖,縮圖畫素 200 * 200
    thumbnail(imgPath, 200)

    // 獲取影像型別
    imgType := getType(imgPath)
    fmt.Printf("影像型別:%v\n", imgType)
}

列印影像的後設資料

func metaData(path string) bimg.ImageMetadata {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    metaData, err := bimg.Metadata(buffer)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    return metaData
}

調整影像大小

func resize(path string, width, height int) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    newImage, err := bimg.NewImage(buffer).Resize(width, height)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    _, err = bimg.NewImage(newImage).Size()

    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("001-resize.jpg", newImage)
}

指定角度旋轉

func rotate(path string, angle bimg.Angle) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    // 傳旋轉角度
    newImage, err := bimg.NewImage(buffer).Rotate(angle)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("002-rotate.png", newImage)
}

自動旋轉

func autoRotate(path string) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    newImage, err := bimg.NewImage(buffer).AutoRotate()
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("003-auto-rotate.jpg", newImage)
}

格式轉換

func convert(path string, imageType bimg.ImageType) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    newImage, err := bimg.NewImage(buffer).Convert(imageType)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("004-convert."+bimg.ImageTypes[imageType], newImage)

}

文字水印

func watermarkText(path string) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
    // 水印的相關資料
    watermark := bimg.Watermark{
        Text:       "GoCN 社群",
        DPI:        150,
        Margin:     300,
        Opacity:    0.25, // 不透明度
        Width:      500,
        Font:       "sans bold 14",
        Background: bimg.Color{255, 255, 255},
    }

    newImage, err := bimg.NewImage(buffer).Watermark(watermark)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("005-watermark.jpg", newImage)
}

圖片水印

func watermarkImage(path, logo string) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
    logoBuffer, err := bimg.Read(logo)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }
    // 水印的相關資料
    watermark := bimg.WatermarkImage{
        Left:    100,
        Top:     200,
        Buf:     logoBuffer,
        Opacity: 1, // 不透明度 0~1 之間的浮點數
    }

    newImage, err := bimg.NewImage(buffer).WatermarkImage(watermark)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("006-watermark-image.jpg", newImage)
}

高斯模糊

func gaussianBlur(path string, sigma, minAmpl float64) {

    options := bimg.Options{
        GaussianBlur: bimg.GaussianBlur{sigma, minAmpl},
    }

    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    newImage, err := bimg.NewImage(buffer).Process(options)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("007-gaussianblur.jpg", newImage)
}

指定長寬縮圖像

func smartCrop(path string, width, height int) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    newImage, err := bimg.NewImage(buffer).SmartCrop(width, height)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("008-smartCrop.jpg", newImage)
}

縮圖像

func thumbnail(path string, pixels int) {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    newImage, err := bimg.NewImage(buffer).Thumbnail(pixels)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    bimg.Write("009-thumbnail.jpg", newImage)
}

獲取型別

func getType(path string) string {
    buffer, err := bimg.Read(path)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
    }

    typeName := bimg.NewImage(buffer).Type()

    return typeName
}

執行專案

執行以下命令:

$ go run main.go

執行成功,專案目錄下生成影像處理結果,效果如下:

想了解更多

總結

h2non/bimg 目前 Github Go 影像處理類包中 Star 數量最多,高效能的影像處理包; h2non/bimg 足夠應付日常影像處理類業務需求,使用很方便,也可以在改包基礎上功能擴充套件開發滿足更多個性化的業務開發需求。

參考資料

  1. https://github.com/h2non/bimg
  2. https://pkg.go.dev/github.com/h2non/bimg
更多原創文章乾貨分享,請關注公眾號
  • Go 語言高效能影像處理神器 h2non/bimg
  • 加微信實戰群請加微信(註明:實戰群):gocnio

相關文章