在2023年整合 TensorFlow Go (2.14.0) 和 OpenCV Go (4.8.1) 的方法 | by Jason Kuan | Nov, 2023


一般而言,如果你的程式會需要 TensorFlow model來推論時,普遍的作法會使用TensorFlow Serving API來解決,serving在你給他有哪些模型後,它會自己管理資源,我的程式再透過grpc去溝通。但有些時候,在做一些簡單的應用時,你想要直接把讀取模型的部份直接寫在程式內時,通常就必須額外花心力去查找你所使用的語言該如何使用,而以下就是我在使用Golang時整理的方法。

在使用之前,我發現TensorFlow官方已經不再支援Golang,目前專案轉由社群來支持。

The TensorFlow team is not currently maintaning the Documentation for installing the Go bindings for TensorFlow.

而實際我們使用的 Golang TensorFlow 底層實際是C語言,所以需要先在自己的電腦環境中安裝 libtensorflow。以我目前寫這篇文章所用的版本是libtensorflow-cpu-linux-x86_64–2.14.0.tar.gz。

官方libtensorflow安裝文件(不建議閱讀中文版,因為很久沒更新)

照文件安裝後,用gcc編譯執行測試後,應該能看到下面這行

Hello from TensorFlow C library version 2.14.0

TensorFlow for C 安裝後,就可以安裝社群版的 Golang TensorFlow wrapper。

wamuir的社群板Golang TensorFlow文件

go get github.com/wamuir/graft/tensorflow@v0.6.0

這邊的0.6.0對應的是 TensorFlow C 2.14.0,請自行注意版本相依性!

這樣就完成了TensorFlow的準備。

GoCV 其實也是一個wrapper,透過Golang呼叫系統的OpenCV,因此同樣地,我們一樣也要在系統內先安裝OpenCV,以目前寫文章的這個時間點,我們安裝的是v4.8.1。

GoCV安裝步驟

安裝完畢後,印出下面訊息代表安裝完成。

gocv version: 0.35.0
opencv lib version: 4.8.1

在Python時務操作上,我們通常會有下面幾個步驟:

  1. 讀取Model、讀取Image。
  2. Image Resize到適當的比例、確保RGB再像素中的0~255正規化到[-1.0, 1.0]當輸入。
  3. 推論結果。
import cv2
import numpy as np
import tensorflow as tf

def main():
# 1. read image and pb model
img = cv2.imread("images/test.jpg")
model = tf.keras.models.load_model("models/face_detection/")

# 2. preprocess image
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.resize(img, (300, 300))
img = img - 127.5
img = img * 0.007843
img = np.expand_dims(img, axis=0)

# 3. inference
output = model.predict(img)

print(f"output: {output}")

if __name__ == "__main__":
main()

所以同樣的,換到Golang也一樣需要上面三個步驟,中間最麻煩的是GoCV讀取圖片後的type是Mat格式,但TensorFlow要模型推論需要Tensor格式,第二步需要有個型態轉換。

完整的Golang程式碼如下

package main

import (
"fmt"
"image"

tf "github.com/wamuir/graft/tensorflow"
"gocv.io/x/gocv"
)

func main() {

// 1. Load the image and model
img := gocv.IMRead("images/test.jpg", gocv.IMReadColor)
if img.Empty() {
fmt.Println("Error reading image")
return
}
defer img.Close()

model, err := tf.LoadSavedModel("models/face_detection", []string{"serve"}, nil)
if err != nil {
fmt.Printf("error loading saved model: %v\n", err)
return
}
defer model.Session.Close()

// 2. preprocess the image
gocv.Resize(img, &img, image.Pt(300, 300), 0, 0, gocv.InterpolationArea)
gocv.CvtColor(img, &img, gocv.ColorBGRToRGB)
img.ConvertTo(&img, gocv.MatTypeCV32F)
gocv.Normalize(img, &img, -1.0, 1.0, gocv.NormMinMax)

const size = 300 * 300 * 3
floatData, err := img.DataPtrFloat32()
if err != nil {
fmt.Printf("error getting float data pointer: %v\n", err)
return
}
tensor, err := tf.NewTensor([][]float32{floatData[:size]})
if err != nil {
fmt.Printf("error creating tensor: %v\n", err)
return
}
tensor.Reshape([]int64{1, 300, 300, 3})

// 3. inference
results, err := model.Session.Run(
map[tf.Output]*tf.Tensor{
model.Graph.Operation("serving_default_input_1").Output(0): tensor,
},
[]tf.Output{
model.Graph.Operation("StatefulPartitionedCall").Output(0),
},
nil,
)
if err != nil {
fmt.Printf("error running session: %v\n", err)
return
}
output, ok := results[0].Value().([][][]float32)
if !ok {
fmt.Printf("error getting output: %v\n", err)
return
}
fmt.Printf("Output: %v\n", output[0])

}

  1. 我們個別用了 gocv 和 gotf 讀取了圖片和模型
  2. 這邊我們可以盡情的使用OpenCV原有的功能去對圖片做處理,只需要在最後用 GoCV 提供的 DataPtrFloat32 方法和 gotf 的 NewTensor 方法,從Mat格式轉換成Tensor格式
  3. 在tfgo推論模型時,必須要知道 input 和 output的 Operation Name,如果要印出所有Operation Name也可以透過下面的 script 找出來。
for _, op := range model.Graph.Operations() {
fmt.Printf("Operation: %v\n", op.Name())
}

這樣就可以在 Golang 中實現和 Python 幾乎一樣的功能,整個流程比較麻煩的地方大概就是環境安裝,還有中間Data Type的轉換在第一次遇到時會摸不著頭緒。

本次研究大量透過 ChatGPT 4.0 程式輔助,偉哉OpenAI!



Source link

Be the first to comment

Leave a Reply

Your email address will not be published.


*