Skip to content

Instantly share code, notes, and snippets.

@hpcslag
Last active March 21, 2021 10:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hpcslag/3e13f48b61ff5869863633d32fc5c25d to your computer and use it in GitHub Desktop.
Save hpcslag/3e13f48b61ff5869863633d32fc5c25d to your computer and use it in GitHub Desktop.
Image Recognize in Golang
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<form action="/recognize" method="post" enctype="multipart/form-data">
<input type="file" name="image" id="">
<input type="submit">
</form>
</body>
</html>
//this code is referenced by https://outcrawl.com/image-recognition-api-go-tensorflow/
//執行程式後會跑在 port 8080 端口,從 localhost:8080 進入測試
package main
import (
"bufio"
"bytes"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"sort"
"strings"
"github.com/gin-gonic/gin"
tf "github.com/tensorflow/tensorflow/tensorflow/go"
"github.com/tensorflow/tensorflow/tensorflow/go/op"
)
//先在全域變數定義神經網路模型的 Graph(圖) 和 對應標籤 (labels)
var (
graph *tf.Graph
labels []string
)
func main() {
//載入模型
if err := loadModel(); err != nil {
log.Fatal(err)
return
}
//製作成 web server 方便測試
r := gin.Default()
r.POST("/recognize", recognizeHandler)
r.Static("/", "./public")
log.Fatal(http.ListenAndServe(":8080", r))
r.Run()
}
//載入模型
func loadModel() error {
//載入模型
model, err := ioutil.ReadFile("./model/inception_v3_2016_08_28_frozen.pb")
if err != nil {
log.Fatal("模型檔案找不到 (.pb) 檔案")
return err
}
//把模型指定到全域變數的 graph (圖)
graph = tf.NewGraph()
if err := graph.Import(model, ""); err != nil {
return err
}
// 載入標籤文字檔案
labelsFile, err := os.Open("./model/imagenet_slim_labels.txt")
if err != nil {
log.Fatal("標籤文字檔案找不到 .txt")
return err
}
//讀取玩文字後就把 os.Open 讀取關閉
defer labelsFile.Close()
//分割 txt 每一行文字,依序 append 儲存到全域變數 labels 陣列中
scanner := bufio.NewScanner(labelsFile)
for scanner.Scan() {
labels = append(labels, scanner.Text())
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
//http 輸入進來的函式,同時也執行神經網路的輸入、輸出
func recognizeHandler(c *gin.Context) {
//HTML Form multipart/file 輸入進來、讀取圖片、變成 buffer
imageFile, err := c.FormFile("image")
imageName := strings.Split(imageFile.Filename, ".")
if err != nil {
log.Fatal("無法讀取圖片")
return
}
imf, err := imageFile.Open()
if err != nil {
log.Fatal("read image failed.")
return
}
defer imf.Close()
var imageBuffer bytes.Buffer
// Copy image data to a buffer
io.Copy(&imageBuffer, imf)
//把輸入進來的圖片製作變成 tensor
tensor, _ := makeTensorFromImage(&imageBuffer, imageName[:1][0])
//開啟一個神經網路模型的會話
session, err := tf.NewSession(graph, nil)
if err != nil {
log.Fatal(err)
}
//推遲在最後才關閉會話
defer session.Close()
//開始把資料丟進去 input layer 端,跑完後資料會跑出來到 output。
output, err := session.Run(
map[tf.Output]*tf.Tensor{
graph.Operation("input").Output(0): tensor,
},
[]tf.Output{
graph.Operation("InceptionV3/Predictions/Reshape_1").Output(0),
},
nil)
if err != nil {
log.Fatal(err)
}
//把輸出的資料 interface{} 當成 json 丟回去
c.JSON(200, ClassifyResult{
Filename: imageFile.Filename,
Labels: findBestLabels(output[0].Value().([][]float32)[0]),
})
}
//輸入進來的圖片變成一個 tensor
func makeTensorFromImage(imgFile *bytes.Buffer, imgFormat string) (*tf.Tensor, error) {
//資料放進 tensor
tensor, _ := tf.NewTensor(imgFile.String())
//先製作一個圖 graph,然後把 tensor資料和 graph 標準化變成一個標準的 tensor
graph, input, output, _ := makeTransformImageGraph(imgFormat)
session, _ := tf.NewSession(graph, nil)
defer session.Close()
normalized, _ := session.Run(
map[tf.Output]*tf.Tensor{input: tensor},
[]tf.Output{output},
nil)
return normalized[0], nil
}
//製作一個圖 graph
func makeTransformImageGraph(imgFormat string) (graph *tf.Graph, input, output tf.Output, err error) {
//定義輸入向量的調整參數,每個模型使用的都不一樣
const (
H, W = 299, 299
Mean = float32(0)
Scale = float32(255)
)
//製作成圖、把資料做伸縮
s := op.NewScope()
input = op.Placeholder(s, tf.String)
// Decode PNG or JPEG
var decode tf.Output
if imgFormat == "png" {
decode = op.DecodePng(s, input, op.DecodePngChannels(3))
} else {
decode = op.DecodeJpeg(s, input, op.DecodeJpegChannels(3))
}
// Div and Sub perform (value-Mean)/Scale for each pixel
output = op.Div(s,
op.Sub(s,
// Resize to 224x224 with bilinear interpolation
op.ResizeBilinear(s,
// Create a batch containing a single image
op.ExpandDims(s,
// Use decoded pixel values
op.Cast(s, decode, tf.Float),
op.Const(s.SubScope("make_batch"), int32(0))),
op.Const(s.SubScope("size"), []int32{H, W})),
op.Const(s.SubScope("mean"), Mean)),
op.Const(s.SubScope("scale"), Scale))
graph, err = s.Finalize()
return graph, input, output, err
}
//定義一個分類結果的結構 (最後輸出)
type ClassifyResult struct {
Filename string `json:"filename"`
Labels []LabelResult `json:"labels"`
}
//定義一個標籤結果的結構
type LabelResult struct {
Label string `json:"label"`
Probability float32 `json:"probability"`
}
type ByProbability []LabelResult
func (a ByProbability) Len() int { return len(a) }
func (a ByProbability) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByProbability) Less(i, j int) bool { return a[i].Probability > a[j].Probability }
//從神經網路最後輸出的機率(概率),在標籤檔案中找出最佳的 5 個結果回傳
func findBestLabels(probabilities []float32) []LabelResult {
// Make a list of label/probability pairs
var resultLabels []LabelResult
for i, p := range probabilities {
if i >= len(labels) {
break
}
resultLabels = append(resultLabels, LabelResult{Label: labels[i], Probability: p})
}
// Sort by probability
sort.Sort(ByProbability(resultLabels))
// Return top 5 labels
return resultLabels[:5]
}
//this code is referenced by https://outcrawl.com/image-recognition-api-go-tensorflow/
//執行程式後會跑在 port 8080 端口,從 localhost:8080 進入測試
package main
import (
"bufio"
"bytes"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"sort"
"strings"
"github.com/gin-gonic/gin"
tf "github.com/tensorflow/tensorflow/tensorflow/go"
"github.com/tensorflow/tensorflow/tensorflow/go/op"
)
//先在全域變數定義神經網路模型的 Graph(圖) 和 對應標籤 (labels)
var (
graph *tf.Graph
labels []string
)
func main() {
//載入模型
if err := loadModel(); err != nil {
log.Fatal(err)
return
}
//製作成 web server 方便測試
r := gin.Default()
r.POST("/recognize", recognizeHandler)
r.Static("/", "./public")
log.Fatal(http.ListenAndServe(":8080", r))
r.Run()
}
//載入模型
func loadModel() error {
//載入模型
model, err := ioutil.ReadFile("./model/tensorflow_inception_graph.pb")
if err != nil {
log.Fatal("模型檔案找不到 (.pb) 檔案")
return err
}
//把模型指定到全域變數的 graph (圖)
graph = tf.NewGraph()
if err := graph.Import(model, ""); err != nil {
return err
}
// 載入標籤文字檔案
labelsFile, err := os.Open("./model/imagenet_comp_graph_label_strings.txt")
if err != nil {
log.Fatal("標籤文字檔案找不到 .txt")
return err
}
//讀取玩文字後就把 os.Open 讀取關閉
defer labelsFile.Close()
//分割 txt 每一行文字,依序 append 儲存到全域變數 labels 陣列中
scanner := bufio.NewScanner(labelsFile)
for scanner.Scan() {
labels = append(labels, scanner.Text())
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}
//http 輸入進來的函式,同時也執行神經網路的輸入、輸出
func recognizeHandler(c *gin.Context) {
//HTML Form multipart/file 輸入進來、讀取圖片、變成 buffer
imageFile, err := c.FormFile("image")
imageName := strings.Split(imageFile.Filename, ".")
if err != nil {
log.Fatal("無法讀取圖片")
return
}
imf, err := imageFile.Open()
if err != nil {
log.Fatal("read image failed.")
return
}
defer imf.Close()
var imageBuffer bytes.Buffer
// Copy image data to a buffer
io.Copy(&imageBuffer, imf)
//把輸入進來的圖片製作變成 tensor
tensor, _ := makeTensorFromImage(&imageBuffer, imageName[:1][0])
//開啟一個神經網路模型的會話
session, err := tf.NewSession(graph, nil)
if err != nil {
log.Fatal(err)
}
//推遲在最後才關閉會話
defer session.Close()
//開始把資料丟進去 input layer 端,跑完後資料會跑出來到 output。
output, err := session.Run(
map[tf.Output]*tf.Tensor{
graph.Operation("input").Output(0): tensor,
},
[]tf.Output{
graph.Operation("InceptionV3/Predictions/Reshape_1").Output(0),
},
nil)
if err != nil {
log.Fatal(err)
}
//把輸出的資料 interface{} 當成 json 丟回去
c.JSON(200, ClassifyResult{
Filename: imageFile.Filename,
Labels: findBestLabels(output[0].Value().([][]float32)[0]),
})
}
//輸入進來的圖片變成一個 tensor
func makeTensorFromImage(imgFile *bytes.Buffer, imgFormat string) (*tf.Tensor, error) {
//資料放進 tensor
tensor, _ := tf.NewTensor(imgFile.String())
//先製作一個圖 graph,然後把 tensor資料和 graph 標準化變成一個標準的 tensor
graph, input, output, _ := makeTransformImageGraph(imgFormat)
session, _ := tf.NewSession(graph, nil)
defer session.Close()
normalized, _ := session.Run(
map[tf.Output]*tf.Tensor{input: tensor},
[]tf.Output{output},
nil)
return normalized[0], nil
}
//製作一個圖 graph
func makeTransformImageGraph(imgFormat string) (graph *tf.Graph, input, output tf.Output, err error) {
//定義輸入向量的調整參數,每個模型使用的都不一樣
const (
H, W = 224, 224
Mean = float32(116)
Scale = float32(1)
)
//製作成圖、把資料做伸縮
s := op.NewScope()
input = op.Placeholder(s, tf.String)
// Decode PNG or JPEG
var decode tf.Output
if imgFormat == "png" {
decode = op.DecodePng(s, input, op.DecodePngChannels(3))
} else {
decode = op.DecodeJpeg(s, input, op.DecodeJpegChannels(3))
}
// Div and Sub perform (value-Mean)/Scale for each pixel
output = op.Div(s,
op.Sub(s,
// Resize to 224x224 with bilinear interpolation
op.ResizeBilinear(s,
// Create a batch containing a single image
op.ExpandDims(s,
// Use decoded pixel values
op.Cast(s, decode, tf.Float),
op.Const(s.SubScope("make_batch"), int32(0))),
op.Const(s.SubScope("size"), []int32{H, W})),
op.Const(s.SubScope("mean"), Mean)),
op.Const(s.SubScope("scale"), Scale))
graph, err = s.Finalize()
return graph, input, output, err
}
//定義一個分類結果的結構 (最後輸出)
type ClassifyResult struct {
Filename string `json:"filename"`
Labels []LabelResult `json:"labels"`
}
//定義一個標籤結果的結構
type LabelResult struct {
Label string `json:"label"`
Probability float32 `json:"probability"`
}
type ByProbability []LabelResult
func (a ByProbability) Len() int { return len(a) }
func (a ByProbability) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByProbability) Less(i, j int) bool { return a[i].Probability > a[j].Probability }
//從神經網路最後輸出的機率(概率),在標籤檔案中找出最佳的 5 個結果回傳
func findBestLabels(probabilities []float32) []LabelResult {
// Make a list of label/probability pairs
var resultLabels []LabelResult
for i, p := range probabilities {
if i >= len(labels) {
break
}
resultLabels = append(resultLabels, LabelResult{Label: labels[i], Probability: p})
}
// Sort by probability
sort.Sort(ByProbability(resultLabels))
// Return top 5 labels
return resultLabels[:5]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment