精易论坛

标题: 秒杀乐玩极速找图 最新opencv4.11模板匹配 GO开发通用调用API [打印本页]

作者: callme大大    时间: 2025-5-27 13:59
标题: 秒杀乐玩极速找图 最新opencv4.11模板匹配 GO开发通用调用API
本帖最后由 callme大大 于 2025-5-27 14:08 编辑

运行效果图:



调用C++编译的DLL 原版 GO多线程加持  带来无与伦比的速度
可以手动测试:http://127.0.0.1:8080/

估计大多数人是无法完成这个opencv的原版编译和调用,众所周知WIN环境下编译gocv的环境搭建非常麻烦,坑巨TM的多。
我这里给出了编译好的包,直接使用即可,不带任何广告。最后附上了源码,想改可以自己改一下。


附上GO源码(用这个源码编译坑多别问懒得回,直接用我编译好的成品):
package main

import (
    "encoding/base64"
    "encoding/json"
    "fmt"
    "gocv.io/x/gocv"
    "html/template"
    "io"
    "log"
    "net/http"
    "strconv"
)

// MatchRequest 请求结构体
type MatchRequest struct {
    BigImage   string  `json:"big_image"`
    SmallImage string  `json:"small_image"`
    MinSim     float64 `json:"min_similarity"`
}

// MatchResponse 响应结构体
type MatchResponse struct {
    Found      bool    `json:"found"`
    X          int     `json:"x"`
    Y          int     `json:"y"`
    Similarity float32 `json:"similarity"`
    Message    string  `json:"message,omitempty"`
}

// decodeBase64Image Base64字符串解码为gocv.Mat
func decodeBase64Image(data string) (gocv.Mat, error) {
    imgBytes, err := base64.StdEncoding.DecodeString(data)
    if err != nil {
       return gocv.NewMat(), fmt.Errorf("Base64解码错误: %v", err)
    }
    mat, err := gocv.IMDecode(imgBytes, gocv.IMReadColor)
    if err != nil {
       return gocv.NewMat(), fmt.Errorf("IMDecode错误: %v", err)
    }
    if mat.Empty() {
       return gocv.NewMat(), fmt.Errorf("解码后的Mat为空")
    }
    return mat, nil
}

// matchHandler 处理匹配请求的HTTP处理函数
func matchHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
       http.Error(w, "仅支持POST方法", http.StatusMethodNotAllowed)
       return
    }

    var req MatchRequest
    body, err := io.ReadAll(r.Body)
    if err != nil {
       respond(w, false, 0, 0, 0, "读取请求体失败: "+err.Error())
       return
    }
    defer r.Body.Close()

    if err := json.Unmarshal(body, &req); err != nil {
       respond(w, false, 0, 0, 0, "JSON解析失败: "+err.Error())
       return
    }

    minSim := 0.8
    if req.MinSim > 0 && req.MinSim < 1.0 {
       minSim = req.MinSim
    }

    // 解码大图
    bigMat, err := decodeBase64Image(req.BigImage)
    if err != nil {
       respond(w, false, 0, 0, 0, "大图解码失败: "+err.Error())
       return
    }
    defer bigMat.Close()

    // 解码小图
    smallMat, err := decodeBase64Image(req.SmallImage)
    if err != nil {
       respond(w, false, 0, 0, 0, "小图解码失败: "+err.Error())
       return
    }
    defer smallMat.Close()

    // 检查尺寸
    if bigMat.Cols() < smallMat.Cols() || bigMat.Rows() < smallMat.Rows() {
       respond(w, false, 0, 0, 0, "小图尺寸大于大图")
       return
    }

    // 模板匹配
    result := gocv.NewMat()
    defer result.Close()

    gocv.MatchTemplate(bigMat, smallMat, &result, gocv.TmCcoeffNormed, gocv.NewMat())
    _, maxVal, _, maxLoc := gocv.MinMaxLoc(result)

    // 判断是否匹配成功
    if float64(maxVal) >= minSim {
       respond(w, true, maxLoc.X, maxLoc.Y, maxVal, "")
    } else {
       respond(w, false, 0, 0, maxVal, "未找到符合相似度要求的匹配")
    }
}

// respond 统一响应函数
func respond(w http.ResponseWriter, found bool, x, y int, sim float32, msg string) {
    resp := MatchResponse{
       Found:      found,
       X:          x,
       Y:          y,
       Similarity: sim,
       Message:    msg,
    }
    data, err := json.MarshalIndent(resp, "", "  ")
    if err != nil {
       http.Error(w, "响应编码失败: "+err.Error(), http.StatusInternalServerError)
       return
    }
    w.Header().Set("Content-Type", "application/json")
    w.Write(data)
}

// HTML主页模板
const indexHTML = `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>图片模板匹配测试</title>
<style>
    body { font-family: Arial, sans-serif; margin: 40px; }
    input[type="file"] { margin-bottom: 15px; }
    #resp { width: 100%; height: 180px; }
    .img-preview { max-width: 300px; max-height: 200px; margin: 10px 0; }
</style>
</head>
<body>
<h2>图片模板匹配手动测试</h2>
<form id="form">
    <label>大图: <input type="file" id="big" accept="image/*" required></label><br>
    <img id="bigPreview" class="img-preview"><br>
    <label>小图: <input type="file" id="small" accept="image/*" required></label><br>
    <img id="smallPreview" class="img-preview"><br>
    <label>最小相似度(0~1): <input type="number" id="minSim" step="0.01" value="0.8" min="0" max="1"></label><br><br>
    <button type="submit">查找小图位置</button>
</form>
<br>
<label>接口响应信息:</label><br>
<textarea id="resp" readonly></textarea>

<script>
function fileToBase64(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = e => resolve(e.target.result.split(',')[1]);
        reader.onerror = e => reject(e);
        reader.readAsDataURL(file);
    });
}

function showPreview(input, imgId) {
    input.addEventListener('change', function() {
        const file = input.files[0];
        if (file) {
            const url = URL.createObjectURL(file);
            document.getElementById(imgId).src = url;
        }
    });
}
showPreview(document.getElementById('big'), 'bigPreview');
showPreview(document.getElementById('small'), 'smallPreview');

document.getElementById('form').onsubmit = async function(e) {
    e.preventDefault();
    const bigFile = document.getElementById('big').files[0];
    const smallFile = document.getElementById('small').files[0];
    const minSim = parseFloat(document.getElementById('minSim').value);

    if (!bigFile || !smallFile) {
        alert("请上传两张图片");
        return;
    }

    try {
        document.getElementById('resp').value = "图片编码中...";
        const [bigB64, smallB64] = await Promise.all([
            fileToBase64(bigFile),
            fileToBase64(smallFile)
        ]);
        document.getElementById('resp').value = "请求中...";
        const resp = await fetch('/match', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({
                big_image: bigB64,
                small_image: smallB64,
                min_similarity: minSim
            })
        });
        const text = await resp.text();
        document.getElementById('resp').value = text;
    } catch (err) {
        document.getElementById('resp').value = "请求失败: " + err;
    }
};
</script>
</body>
</html>
`

// indexHandler 返回主页
func indexHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/html; charset=utf-8")
    tmpl := template.Must(template.New("index").Parse(indexHTML))
    tmpl.Execute(w, nil)
}

func main() {
    http.HandleFunc("/", indexHandler)
    http.HandleFunc("/match", matchHandler)
    port := 8080
    log.Printf("服务启动,监听端口: %d", port)
    log.Fatal(http.ListenAndServe(":"+strconv.Itoa(port), nil))
}





这里下载完整的编译好的:

通过网盘分享的文件:GO_opencv找图.rar
链接: https://pan.baidu.com/s/19Dm3r7eidWu5XxulQoTAKw 提取码: schm
--来自百度网盘超级会员v7的分享
使用方法:使用post直接本地调用api 传json数据。


地址:http://127.0.0.1:8080/match

图片要base64编码传输。注意自己传数据,简易用e2ee
{
  "big_image": "(大图的Base64字符串)",
  "small_image": "(小图的Base64字符串)",
  "min_similarity": 0.8
}


















作者: 天泽A    时间: 2025-5-27 14:26
图片要base64编码传输。注意自己传数据,简易用e2ee
作者: pipicool    时间: 2025-5-27 14:48
学习一下
作者: 无极T    时间: 2025-5-27 14:50
学习一下
作者: zifeiyu    时间: 2025-5-27 14:54
666666666666666666666
作者: 弱鸡第一步    时间: 2025-5-27 15:01
区别是不是一个可以本地 一个需要联网
作者: callme大大    时间: 2025-5-27 15:35
弱鸡第一步 发表于 2025-5-27 15:01
区别是不是一个可以本地 一个需要联网

我这个可以本地也可以联网
作者: geren    时间: 2025-5-27 17:50
看看效果如何~
作者: 五花牛    时间: 2025-5-27 17:57
打打打打打不开.......
作者: 卡卡1111    时间: 2025-5-27 18:15
支持开源~!感谢分享
作者: 回不到的过去    时间: 2025-5-27 19:40

支持开源~!感谢分享
作者: callme大大    时间: 2025-5-27 20:00
五花牛 发表于 2025-5-27 17:57
打打打打打不开.......

提示什么
作者: 1184798949    时间: 2025-5-27 21:35
感谢分享
作者: 杨明煜    时间: 2025-5-28 07:36
学习看看..!....
作者: 五花牛    时间: 2025-5-28 07:53
callme大大 发表于 2025-5-27 20:00
提示什么

没有提示,没反应...win7x64
作者: Fate    时间: 2025-5-28 08:28
感谢分享
作者: 一指温柔    时间: 2025-5-28 09:11
感谢分享
作者: 3082    时间: 2025-5-28 09:39
支持一下,感谢分享
作者: 小虎来了    时间: 2025-5-28 09:46
感谢分享啊
作者: bianyuan456    时间: 2025-5-28 13:55
已经顶贴,感谢您对论坛的支持!
作者: realpc    时间: 2025-5-28 14:49
端口可以修改吗》?
作者: ccyc    时间: 2025-5-29 17:28
我也来试试看

作者: kiss0459    时间: 2025-5-29 23:42
为啥不直接调用dll,非要搞web服务呢,web服务调用速度比直接调用dll快?

作者: 李商隐2    时间: 2025-5-30 20:47
网络通信不说识别速度,光是转base64再传输,人家调用dll的结果图都绘完了
作者: ccyc    时间: 2025-6-2 11:34
有例子吗

作者: aosheng    时间: 2025-6-7 21:32
6666666666666666666666666666
作者: sky122    时间: 2025-6-8 00:09
66666666666666
作者: huanjushidai    时间: 2025-6-21 12:39
端口可以修改吗?能不能搞一个易语言的服务端
作者: callme大大    时间: 2025-6-22 15:49
kiss0459 发表于 2025-5-29 23:42
为啥不直接调用dll,非要搞web服务呢,web服务调用速度比直接调用dll快?

可以本地调用啊





欢迎光临 精易论坛 (https://125.confly.eu.org/) Powered by Discuz! X3.4