Files
likedream/internal/application/recognizer/slider/slider_width.go
T
sugarBecomer 120e690795 🐛 cas滑块验证码
2025-11-12 13:31:54 +08:00

193 lines
4.8 KiB
Go

package slider
import (
"encoding/base64"
"os"
"gocv.io/x/gocv"
)
func VerifySliderWidth(bgImgPath, sliderImgPath string) int {
// 检查背景图片文件是否存在
if _, err := os.Stat(bgImgPath); os.IsNotExist(err) {
return -1
}
// 检查滑块图片文件是否存在
if _, err := os.Stat(sliderImgPath); os.IsNotExist(err) {
return -1
}
// 读取背景图片
bgImg := gocv.IMRead(bgImgPath, gocv.IMReadColor)
if bgImg.Empty() {
return -1
}
defer bgImg.Close()
// 读取滑块图片(包含 alpha 通道)
sliderImgWithAlpha := gocv.IMRead(sliderImgPath, gocv.IMReadUnchanged)
if sliderImgWithAlpha.Empty() {
return -1
}
defer sliderImgWithAlpha.Close()
// 分离滑块图片的通道
channels := gocv.Split(sliderImgWithAlpha)
defer func() {
for i := range channels {
channels[i].Close()
}
}()
var sliderImg gocv.Mat
var mask gocv.Mat
// 如果有 alpha 通道(4通道),提取 mask
if len(channels) == 4 {
// 前3个通道是 BGR
sliderImg = gocv.NewMat()
defer sliderImg.Close()
gocv.Merge(channels[:3], &sliderImg)
// 使用 alpha 通道作为 mask
mask = channels[3].Clone()
defer mask.Close()
} else if len(channels) == 3 {
// 没有透明通道,直接使用原图
sliderImg = sliderImgWithAlpha.Clone()
defer sliderImg.Close()
mask = gocv.NewMat() // 空 mask
defer mask.Close()
} else {
return -1
}
// 创建结果矩阵用于存储匹配结果
result := gocv.NewMat()
defer result.Close()
// 直接在原图上使用模板匹配找到滑块位置
if mask.Empty() {
gocv.MatchTemplate(bgImg, sliderImg, &result, gocv.TmCcoeffNormed, gocv.NewMat())
} else {
// 带 mask 的模板匹配
gocv.MatchTemplate(bgImg, sliderImg, &result, gocv.TmCcoeffNormed, mask)
}
// 找到最佳匹配位置
_, maxVal, _, maxLoc := gocv.MinMaxLoc(result)
// 如果匹配度太低,返回 -1 表示失败
if maxVal < 0.6 {
return -1
}
// 获取原图上的 x 坐标,然后按比例缩放到目标尺寸
originalWidth := bgImg.Cols()
targetWidth := 280
scale := float64(targetWidth) / float64(originalWidth)
scaledX := int(float64(maxLoc.X) * scale)
// 返回缩放后的 x 坐标(滑块需要移动的距离)
return scaledX
}
// VerifySliderWidthByBase64 通过 base64 编码的图片计算滑块验证码需要移动的距离
// bgImgBase64: 背景图片的 base64 编码
// sliderImgBase64: 滑块图片的 base64 编码
// 返回: 滑块需要移动的 x 距离(已缩放到 280 宽度),失败返回 -1
func VerifySliderWidthByBase64(bgImgBase64, sliderImgBase64 string) int {
// 解码背景图片的 base64
bgImgData, err := base64.StdEncoding.DecodeString(bgImgBase64)
if err != nil {
return -1
}
// 解码滑块图片的 base64
sliderImgData, err := base64.StdEncoding.DecodeString(sliderImgBase64)
if err != nil {
return -1
}
// 从内存中读取背景图片
bgImg, err := gocv.IMDecode(bgImgData, gocv.IMReadColor)
if err != nil {
return -1
}
if bgImg.Empty() {
return -1
}
defer bgImg.Close()
// 从内存中读取滑块图片(包含 alpha 通道)
sliderImgWithAlpha, err := gocv.IMDecode(sliderImgData, gocv.IMReadUnchanged)
if err != nil {
return -1
}
if sliderImgWithAlpha.Empty() {
return -1
}
defer sliderImgWithAlpha.Close()
// 分离滑块图片的通道
channels := gocv.Split(sliderImgWithAlpha)
defer func() {
for i := range channels {
channels[i].Close()
}
}()
var sliderImg gocv.Mat
var mask gocv.Mat
// 如果有 alpha 通道(4通道),提取 mask
if len(channels) == 4 {
// 前3个通道是 BGR
sliderImg = gocv.NewMat()
defer sliderImg.Close()
gocv.Merge(channels[:3], &sliderImg)
// 使用 alpha 通道作为 mask
mask = channels[3].Clone()
defer mask.Close()
} else if len(channels) == 3 {
// 没有透明通道,直接使用原图
sliderImg = sliderImgWithAlpha.Clone()
defer sliderImg.Close()
mask = gocv.NewMat() // 空 mask
defer mask.Close()
} else {
return -1
}
// 创建结果矩阵用于存储匹配结果
result := gocv.NewMat()
defer result.Close()
// 直接在原图上使用模板匹配找到滑块位置
if mask.Empty() {
gocv.MatchTemplate(bgImg, sliderImg, &result, gocv.TmCcoeffNormed, gocv.NewMat())
} else {
// 带 mask 的模板匹配
gocv.MatchTemplate(bgImg, sliderImg, &result, gocv.TmCcoeffNormed, mask)
}
// 找到最佳匹配位置
_, maxVal, _, maxLoc := gocv.MinMaxLoc(result)
// 如果匹配度太低,返回 -1 表示失败
if maxVal < 0.6 {
return -1
}
// 获取原图上的 x 坐标,然后按比例缩放到目标尺寸
originalWidth := bgImg.Cols()
targetWidth := 280
scale := float64(targetWidth) / float64(originalWidth)
scaledX := int(float64(maxLoc.X) * scale)
// 返回缩放后的 x 坐标(滑块需要移动的距离)
return scaledX
}