实战PaddleOCR自动识别车位坐标并渲染可点击按钮

需求

先说需求,客户会给我们一张平面图,然后我们需要通过js渲染出可以点击的车位按钮,用来帮助选择车位进行操作,之前我们是通过手动拖拽,但是面对上百上千的量人工成本太高了

最终效果

先上效果图,这是一张有600个车位车位平面图的部分展示效果,在设置精度必须满0.9的情况下能正确的识别出来500多个,因为图比较大,所以这里显示部分 通过识别

准备工作

API文档地址: ai.baidu.com/ai-doc/AIST... 从右上角控制台获取token

获取文字错标

PaddleOcr 响应结构体

go 复制代码
// PaddleOcr 响应结构体
type ResPaddleOCR struct {
	LogId  string `json:"logId"` // 日志ID
	Result struct {
		OcrResults []struct {
			PrunedResult struct {
				RecTexts []string `json:"rec_texts"` // 识别文本
				RecBoxes [][]int  `json:"rec_boxes"` // 识别框坐标
			} `json:"prunedResult"`
		} `json:"ocrResults"`
		DataInfo struct {
			Width  int    `json:"width"`  // 图片宽度
			Height int    `json:"height"` // 图片高度
			Type   string `json:"type"`   // 图片类型
		} `json:"dataInfo"`
	} `json:"result"`
	ErrorCode int    `json:"errorCode"` // 错误码
	ErrorMsg  string `json:"errorMsg"`  // 错误信息
}

// 配置信息
const (
	API_URL = "https://wfy6da03m00fd1w1.aistudio-app.com/ocr"
	TOKEN   = ""
)

获取远程图片

golang 复制代码
// 获取远程图片base64编码
func GetOssImg64(fileURL string) (fileData string, err error) {
	resp, err := http.Get("https://" + global.Conf.Oss.BucketName + "." + global.Conf.Oss.Endpoint + "/" + fileURL)
	if err != nil {
		err = fmt.Errorf("获取图片失败: %w", err)
		return
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		err = fmt.Errorf("读取图片失败: %w", err)
		return
	}
	fileData = base64.StdEncoding.EncodeToString(body)
	return
}

请求PaddleOCR服务api

golang 复制代码
func PaddleOCR(fileUrl string) (respData ResPaddleOCR, err error) {
	fileData, err := GetOssImg64(fileUrl)
	if err != nil {
		return
	}

	// 构建请求参数
	payload := map[string]interface{}{
		"file":                      fileData, // 文件内容的Base64编码结果
		"fileType":                  1,        // 文件类型。0表示PDF文件,1表示图像文件
		"useDocOrientationClassify": false,    // 是否在推理时使用文档方向分类模块
		"useDocUnwarping":           false,    // 是否在推理时使用文本图像矫正模块
		"useTextlineOrientation":    false,    // 是否在推理时使用文本行方向分类模块
		"textDetLimitSideLen":       64,       // 文本检测的图像边长限制
		"textDetLimitType":          "min",    // 文本检测的边长度限制类型
		"textDetThresh":             0.3,      // 文本检测像素阈值
		"textDetBoxThresh":          0.6,      // 文本检测框阈值
		"textDetUnclipRatio":        1.5,      // 文本检测扩张系数
		"textRecScoreThresh":        0.8,      // 文本识别阈值
		"visualize":                 false,    // 是否可视化识别结果
	}
	payloadBytes, _ := json.Marshal(payload)
	client := &http.Client{}
	req, _ := http.NewRequest("POST", API_URL, bytes.NewBuffer(payloadBytes))
	// 设置请求头
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", fmt.Sprintf("token %s", TOKEN))

	res, err := client.Do(req)
	if err != nil {
		err = fmt.Errorf("发送请求失败: %w", err)
		return
	}
	defer res.Body.Close()

	if res.StatusCode != http.StatusOK {
		err = fmt.Errorf("返回状态码错误: %d", res.StatusCode)
		return
	}

	body, err := io.ReadAll(res.Body)
	if err != nil {
		err = fmt.Errorf("读取响应体失败: %w", err)
		return
	}
	fmt.Println(string(body))
	if err = json.Unmarshal(body, &respData); err != nil {
		err = fmt.Errorf("解析响应体失败: %w", err)
		return
	}
	return
}

对接现有系统

我们系统前端存储每个坐标点的格式是,其中name对应图中的车位号

json 复制代码
{
    "room_id": 1,
    "name": "001"
    "style": "....",
    "x":0,
    "y":0
}

过滤数据

比如我们系统是识别车位号,就把Ocr不符合格式的数据全部过滤,并存储好x,y坐标

golang 复制代码
// 处理数据
	var (
		xMap      = make(map[string]int) // 横坐标
		yMap      = make(map[string]int) // 纵坐标
		OcrStrs   = make([]string, 0)    // 有效数据
		RecTexts  = ocrData.Result.OcrResults[0].PrunedResult.RecTexts // Ocr文字数据
		RecBoxes  = ocrData.Result.OcrResults[0].PrunedResult.RecBoxes // Ocr坐标数据
		imgWidth  = ocrData.Result.DataInfo.Width // 图片宽
		imgHeight = ocrData.Result.DataInfo.Height // 图片高
	)
	// 处理Ocr结果
	re := regexp.MustCompile(`[\p{Han}\s]+`)
	for index, v := range RecBoxes {
                // 过滤数据正则
		result := re.ReplaceAllString(RecTexts[index], "")
		// 排除空字符串
		if result == "" {
			continue
		}
		OcrStrs = append(OcrStrs, result)
		// 计算文本中心坐标并存储
		xMap[result] = (v[0]+v[2])/2 - imgWidth/2
		yMap[result] = -((v[1]+v[3])/2 - imgHeight/2)
	}

渲染数据拼接

golang 复制代码
   sqlData := GetMapData()
	// 渲染数据
	for index, v := range sqlData {
		sqlData[index].X = float64(xMap[v.Name])
		sqlData[index].Y = float64(yMap[v.Name])
	}

这样就完成了AI图片定点

后续改进

  1. 我保存了文件的md5值和处理结果,所以同样的图片不会处理第二次
  2. 使用异步通知客户ai标记任务完成,这个接口还是挺慢的,和我本地跑这个模型没有太大区别
相关推荐
tyung11 小时前
Go 手写 Wait-Free MPSC 无界队列:SwapPointer 实现多生产者无锁入队
后端·go
陈明勇18 小时前
Go 1.26 新特性回顾:语言增强、工具升级与 Green Tea GC 默认启用
后端·go
妙码生花2 天前
从 PHP 到 AI + Golang,程序员自救转型手记(二):目录结构、初始化 GIT、设计并开发配置系统
前端·后端·go
leeyi2 天前
Deer-Go:字节 Deer-Flow 的 Go 移植,深度研究 Agent 全拆解
go·aigc·agent
Bolt2 天前
TypeScript 7.0 来了:当 tsc 用 Go 重写之后
javascript·typescript·go
Go_error3 天前
Datatypes:Go 轻松支持数据库JSON类型
后端·go
任沫3 天前
Agent之Function Call
javascript·人工智能·go
唐青枫4 天前
别再把 interface 当万能盒子:Go 接口从隐式实现到项目解耦
go
tyung6 天前
Go 手写有界 SPSC 环形队列:无 CAS、无锁、Cache 友好的无锁模型
后端·go
喵个咪7 天前
技术复盘:基于 go-wind-cms 的官网+商城双业务渐进拆分实战
后端·架构·go