-
-
Save jampajeen/04564ca759e4e3f0b1b966418261885d to your computer and use it in GitHub Desktop.
Image Recognize in Golang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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