132 lines
3.4 KiB
Go
132 lines
3.4 KiB
Go
|
|
package vision
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"image"
|
|||
|
|
"image/color"
|
|||
|
|
"math"
|
|||
|
|
|
|||
|
|
"github.com/fogleman/gg"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// WarpPerspective 执行透视变换(4点变换)
|
|||
|
|
// srcPoints: 源图像中的 4 个点 [TL, TR, BR, BL]
|
|||
|
|
// dstWidth, dstHeight: 目标图像的尺寸
|
|||
|
|
func (c *Canvas) WarpPerspective(srcPoints [4]image.Point, dstWidth, dstHeight int) {
|
|||
|
|
src := c.dc.Image()
|
|||
|
|
dst := image.NewRGBA(image.Rect(0, 0, dstWidth, dstHeight))
|
|||
|
|
|
|||
|
|
// 计算透视变换矩阵 (3x3)
|
|||
|
|
// 这里使用简化的线性方程求解
|
|||
|
|
matrix := getPerspectiveTransform(srcPoints, dstWidth, dstHeight)
|
|||
|
|
|
|||
|
|
// 应用反向映射
|
|||
|
|
for y := 0; y < dstHeight; y++ {
|
|||
|
|
for x := 0; x < dstWidth; x++ {
|
|||
|
|
// 计算源坐标
|
|||
|
|
tmpX := matrix[0]*float64(x) + matrix[1]*float64(y) + matrix[2]
|
|||
|
|
tmpY := matrix[3]*float64(x) + matrix[4]*float64(y) + matrix[5]
|
|||
|
|
tmpW := matrix[6]*float64(x) + matrix[7]*float64(y) + matrix[8]
|
|||
|
|
|
|||
|
|
sx := tmpX / tmpW
|
|||
|
|
sy := tmpY / tmpW
|
|||
|
|
|
|||
|
|
// 双线性插值
|
|||
|
|
dst.Set(x, y, bilinearInterpolation(src, sx, sy))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
c.dc = gg.NewContextForImage(dst)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 辅助函数:计算透视变换矩阵的逆矩阵 (用于目标到源的映射)
|
|||
|
|
func getPerspectiveTransform(src [4]image.Point, dw, dh int) [9]float64 {
|
|||
|
|
// 这里实现一个基础的矩阵求解逻辑 (Dlt 算法简化版)
|
|||
|
|
// 为了保持精简,我们直接计算从目标到源的映射矩阵
|
|||
|
|
dst := [4]image.Point{
|
|||
|
|
{0, 0}, {dw, 0}, {dw, dh}, {0, dh},
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var a [8][8]float64
|
|||
|
|
var b [8]float64
|
|||
|
|
|
|||
|
|
for i := 0; i < 4; i++ {
|
|||
|
|
a[i][0] = float64(dst[i].X)
|
|||
|
|
a[i][1] = float64(dst[i].Y)
|
|||
|
|
a[i][2] = 1
|
|||
|
|
a[i][6] = -float64(dst[i].X) * float64(src[i].X)
|
|||
|
|
a[i][7] = -float64(dst[i].Y) * float64(src[i].X)
|
|||
|
|
b[i] = float64(src[i].X)
|
|||
|
|
|
|||
|
|
a[i+4][3] = float64(dst[i].X)
|
|||
|
|
a[i+4][4] = float64(dst[i].Y)
|
|||
|
|
a[i+4][5] = 1
|
|||
|
|
a[i+4][6] = -float64(dst[i].X) * float64(src[i].Y)
|
|||
|
|
a[i+4][7] = -float64(dst[i].Y) * float64(src[i].Y)
|
|||
|
|
b[i+4] = float64(src[i].Y)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 简单的正向消元求解 (假设非奇异)
|
|||
|
|
res := solveLinearSystem(a, b)
|
|||
|
|
return [9]float64{res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], 1.0}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func solveLinearSystem(a [8][8]float64, b [8]float64) [8]float64 {
|
|||
|
|
// 高斯消元
|
|||
|
|
for i := 0; i < 8; i++ {
|
|||
|
|
pivot := a[i][i]
|
|||
|
|
for j := i + 1; j < 8; j++ {
|
|||
|
|
factor := a[j][i] / pivot
|
|||
|
|
for k := i; k < 8; k++ {
|
|||
|
|
a[j][k] -= a[i][k] * factor
|
|||
|
|
}
|
|||
|
|
b[j] -= b[i] * factor
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var x [8]float64
|
|||
|
|
for i := 7; i >= 0; i-- {
|
|||
|
|
sum := 0.0
|
|||
|
|
for j := i + 1; j < 8; j++ {
|
|||
|
|
sum += a[i][j] * x[j]
|
|||
|
|
}
|
|||
|
|
x[i] = (b[i] - sum) / a[i][i]
|
|||
|
|
}
|
|||
|
|
return x
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func bilinearInterpolation(img image.Image, x, y float64) color.Color {
|
|||
|
|
x0, y0 := int(math.Floor(x)), int(math.Floor(y))
|
|||
|
|
x1, y1 := x0+1, y0+1
|
|||
|
|
|
|||
|
|
bounds := img.Bounds()
|
|||
|
|
if x0 < bounds.Min.X || x1 >= bounds.Max.X || y0 < bounds.Min.Y || y1 >= bounds.Max.Y {
|
|||
|
|
return img.At(int(x), int(y)) // 越界直接返回
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
dx := x - float64(x0)
|
|||
|
|
dy := y - float64(y0)
|
|||
|
|
|
|||
|
|
c00 := img.At(x0, y0)
|
|||
|
|
c01 := img.At(x0, y1)
|
|||
|
|
c10 := img.At(x1, y0)
|
|||
|
|
c11 := img.At(x1, y1)
|
|||
|
|
|
|||
|
|
r00, g00, b00, a00 := c00.RGBA()
|
|||
|
|
r01, g01, b01, a01 := c01.RGBA()
|
|||
|
|
r10, g10, b10, a10 := c10.RGBA()
|
|||
|
|
r11, g11, b11, a11 := c11.RGBA()
|
|||
|
|
|
|||
|
|
lerp := func(v00, v01, v10, v11 uint32) uint8 {
|
|||
|
|
v0 := float64(v00)*(1-dx) + float64(v10)*dx
|
|||
|
|
v1 := float64(v01)*(1-dx) + float64(v11)*dx
|
|||
|
|
return uint8(uint32(v0*(1-dy)+v1*dy) >> 8)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return color.RGBA{
|
|||
|
|
R: lerp(r00, r01, r10, r11),
|
|||
|
|
G: lerp(g00, g01, g10, g11),
|
|||
|
|
B: lerp(b00, b01, b10, b11),
|
|||
|
|
A: lerp(a00, a01, a10, a11),
|
|||
|
|
}
|
|||
|
|
}
|