WebNN是一个非常好用的推理工具,但如果遇到一些非端到端的模型,依然还是需要自行编写算法。原生Javascript的类型系统过于逆天,根本没法跑一些科学程序。好在我们有号称互联网C语言的Golang,它甚至原生支持WebAssembly。
现在以一个计算交并比的函数为例:
package main
import (
"syscall/js"
)
func Area(box []float32) float32 {
w := box[2] - box[0]
h := box[3] - box[1]
return w * h
}
func MaxFloat32(a float32, b float32) float32 {
if a > b {
return a
} else {
return b
}
}
func MinFloat32(a float32, b float32) float32 {
if a > b {
return b
} else {
return a
}
}
func Intersection(box1 []float32, box2 []float32) []float32 {
x1 := MaxFloat32(box1[0], box2[0])
y1 := MaxFloat32(box1[1], box2[1])
x2 := MinFloat32(box1[2], box2[2])
y2 := MinFloat32(box1[3], box2[3])
if x2 < x1 || y2 < y1 {
return []float32{0, 0, 0, 0}
}
return []float32{x1, y1, x2, y2}
}
func Union(box1 []float32, box2 []float32) float32 {
return Area(box1) + Area(box2) - Area(Intersection(box1, box2))
}
func IoU(box1 []float32, box2 []float32) float32 {
inter := Area(Intersection(box1, box2))
union := Union(box1, box2)
if union == 0 {
return 0
}
return inter / (union + 1e-6)
}
func GIoU(box1 []float32, box2 []float32) float32 {
iou := IoU(box1, box2)
union := Union(box1, box2)
cx1 := MinFloat32(box1[0], box2[0])
cy1 := MinFloat32(box1[1], box2[1])
cx2 := MaxFloat32(box1[2], box2[2])
cy2 := MaxFloat32(box1[3], box2[3])
cArea := MaxFloat32((cx2-cx1), 0.0) * MaxFloat32((cy2-cy1), 0.0)
giou := iou - (cArea-union)/(cArea+1e-6)
return giou
}
// Wrapper function for javascript.
func Calculate_IoU(this js.Value, args []js.Value) any {
box1 := []float32{float32(args[0].Index(0).Float()), float32(args[0].Index(1).Float()), float32(args[0].Index(2).Float()), float32(args[0].Index(3).Float())}
box2 := []float32{float32(args[1].Index(0).Float()), float32(args[1].Index(1).Float()), float32(args[1].Index(2).Float()), float32(args[1].Index(3).Float())}
iou := IoU(box1, box2)
return js.ValueOf(iou)
}
// Wrapper function for javascript.
func Calculate_GIoU(this js.Value, args []js.Value) any {
box1 := []float32{float32(args[0].Index(0).Float()), float32(args[0].Index(1).Float()), float32(args[0].Index(2).Float()), float32(args[0].Index(3).Float())}
box2 := []float32{float32(args[1].Index(0).Float()), float32(args[1].Index(1).Float()), float32(args[1].Index(2).Float()), float32(args[1].Index(3).Float())}
giou := GIoU(box1, box2)
return js.ValueOf(giou)
}
func main() {
// a, b := []float32{10, 10, 50, 50}, []float32{30, 30, 70, 70}
// iou := IoU(a, b)
// giou := GIoU(a, b)
// fmt.Printf("IoU is %.5f, GIoU is %.5f", iou, giou)
done := make(chan int, 0)
js.Global().Set("calculate_iou", js.FuncOf(Calculate_IoU))
js.Global().Set("calculate_giou", js.FuncOf(Calculate_GIoU))
<-done
}
配置如下:
GOARCH=wasm
GOOS=js
CGO_ENABLED=1 前端调用代码如下(wasm_exec.js在GOROOT/misc/wasm文件夹):
<!DOCTYPE html>
<html>
<head>
<title>Golang Web Assembly</title>
<script src="./wasm_exec.js"></script>
</head>
<body>
<script>
function main() {
var a = new Float32Array([10, 10, 50, 50]);
var b = new Float32Array([30, 30, 70, 70]);
var iou = calculate_iou(a, b);
var giou = calculate_giou(a, b);
console.log(`IoU:${iou}, GIoU:${giou}`);
}
window.onload = function() {
const go = new Go();
WebAssembly.instantiateStreaming(fetch('./main.wasm'), go.importObject).then((result)=> {
go.run(result.instance);
main();
});
}
</script>
</body>
</html> 