vision/recognize.go

70 lines
1.6 KiB
Go

package vision
import (
"image"
"math"
)
// MatchResult 模板匹配结果
type MatchResult struct {
Point image.Point
Score float64 // 相似度分数 (0.0 - 1.0)
}
// FindTemplate 在当前画布中查找子图 (模板匹配)
// 使用简单的平方差和 (Sum of Squared Differences) 算法
func (c *Canvas) FindTemplate(template *Canvas) MatchResult {
src := c.dc.Image()
tpl := template.dc.Image()
srcBounds := src.Bounds()
tplBounds := tpl.Bounds()
sw, sh := srcBounds.Dx(), srcBounds.Dy()
tw, th := tplBounds.Dx(), tplBounds.Dy()
if tw > sw || th > sh {
return MatchResult{Score: 0}
}
bestPoint := image.Point{}
minDiff := math.MaxFloat64
// 为了性能,在大图中进行步长采样
step := 1
if sw > 500 || sh > 500 { step = 2 }
for y := 0; y <= sh-th; y += step {
for x := 0; x <= sw-tw; x += step {
diff := 0.0
// 简单的像素比较
for ty := 0; ty < th; ty += 2 {
for tx := 0; tx < tw; tx += 2 {
sr, sg, sb, _ := src.At(x+tx, y+ty).RGBA()
tr, tg, tb, _ := tpl.At(tx, ty).RGBA()
dr := float64(sr>>8) - float64(tr>>8)
dg := float64(sg>>8) - float64(tg>>8)
db := float64(sb>>8) - float64(tb>>8)
diff += dr*dr + dg*dg + db*db
}
}
if diff < minDiff {
minDiff = diff
bestPoint = image.Point{X: x, Y: y}
}
}
}
// 归一化分数 (1.0 为完美匹配)
// 最大可能差异:(255*255 * 3) * (tw/2 * th/2)
maxPossibleDiff := (255.0 * 255.0 * 3.0) * (float64(tw) / 2.0 * float64(th) / 2.0)
score := 1.0 - (minDiff / maxPossibleDiff)
if score < 0 { score = 0 }
return MatchResult{
Point: bestPoint,
Score: score,
}
}