70 lines
1.6 KiB
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,
|
|
}
|
|
}
|