分享源码
界面截图:
是否带模块:
-
备注说明:
-
本帖最后由 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
}