增加图片识别后端接口

master
linlnf 3 months ago
parent 8758733bc9
commit 6507272923

@ -191,7 +191,7 @@ Authorization|Headers|string|必填|Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
参数字段|参数属性|类型|选项|默认值
---|---|---|---|---
pic|form-data|string|必填|""
pic|form-data|string|必填|"" (示例内容在 storage/app/test/文字识别.txt 中)
> 返回示例:
```json
@ -202,4 +202,22 @@ pic|form-data|string|必填|""
},
"msg": "Success"
}
```
#### 请求语音识别
> <font color=#FF4500>*post*/admin/ai_recognition/voc_recognition</font>
参数字段|参数属性|类型|选项|默认值
---|---|---|---|---
voc|form-data|string|必填|"" (示例内容在 storage/app/test/A13_221.txt 中)
> 返回示例:
```json
{
"code": 200,
"data": {
"words": "韩国的基本目标是射箭三块金牌只到三块金牌羽毛球两块金牌以及举重等12块金牌。\n"
},
"msg": "Success"
}
```

@ -81,4 +81,6 @@ const (
// 模型功能按相关
PicRecognitionFailMsg string = "图片文字识别失败"
PicRecognitionFailCode int = -400450
VocRecognitionFailMsg string = "语音识别失败"
VocRecognitionFailCode int = -400451
)

@ -72,4 +72,5 @@ const (
ErrorBaiduCEGetTokenFail string = "访问百度智能云时获得access_token 出现问题:"
ErrorBaiduCEUseOCRFail string = "使用百度智能云OCR接口失败"
ErrorBaiduCEPostFail string = "使用百度智能云接口失败(POST通用)"
ErrorBaiduCEUseVOPFail string = "使用百度智能云VOP接口失败"
)

@ -21,3 +21,12 @@ func (u *AiRecognition) PicRecognition(context *gin.Context) {
response.Fail(context, consts.PicRecognitionFailCode, consts.PicRecognitionFailMsg, "")
}
}
// 语音文字识别
func (u *AiRecognition) VocRecognition(context *gin.Context) {
if r, recogWords := ai_model_cli.RequestVOP(context); r {
response.Success(context, consts.CurdStatusOkMsg, recogWords)
} else {
response.Fail(context, consts.VocRecognitionFailCode, consts.VocRecognitionFailMsg, "")
}
}

@ -51,4 +51,6 @@ func WebRegisterValidator() {
// ai 识别功能相关
key = consts.ValidatorPrefix + "PicRecognition"
containers.Set(key, ai_recognition.PicRecognition{})
key = consts.ValidatorPrefix + "VocRecognition"
containers.Set(key, ai_recognition.VocRecognition{})
}

@ -1,10 +1,14 @@
package ai_recognition
import (
"bytes"
"encoding/base64"
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"image"
"math"
"github.com/gin-gonic/gin"
)
@ -25,37 +29,36 @@ func (p PicRecognition) CheckParams(context *gin.Context) {
response.ErrorSystem(context, "PicRecognition表单参数验证器json化失败", "")
return
}
// TODO:linlnf
// pic := p.Pic
// // 先进行 URL 解码
pic := p.Pic
// 先进行 URL 解码
// decodedPic, err := url.QueryUnescape(pic)
// if err != nil {
// response.ErrorSystem(context, "PicRecognition表单参数验证器URL解码失败", "")
// return
// }
// // 再进行 Base64 解码
// decodedData, err := base64.StdEncoding.DecodeString(decodedPic)
// if err != nil {
// response.ErrorSystem(context, "PicRecognition表单参数验证器Base64解码失败", "")
// return
// }
// // 检查大小不超过 10M10 * 1024 * 1024 字节)
// if len(decodedData) > 10*1024*1024 {
// response.ErrorSystem(context, "PicRecognition表单参数验证器图片大小超过 10M", "")
// return
// }
// // 将字节数据转换为图像以检查格式和尺寸
// img, _, err := image.Decode(bytes.NewReader(decodedData))
// if err != nil {
// return
// }
// bounds := img.Bounds()
// minSide := math.Min(float64(bounds.Dx()), float64(bounds.Dy()))
// maxSide := math.Max(float64(bounds.Dx()), float64(bounds.Dy()))
// // 最短边至少 15px最长边最大 8192px
// if minSide < 15 || maxSide > 8192 {
// response.ErrorSystem(context, "PicRecognition表单参数验证器图片尺寸过大或过小", "")
// return
// }
// 再进行 Base64 解码
decodedData, err := base64.StdEncoding.DecodeString(pic)
if err != nil {
response.ErrorSystem(context, "PicRecognition表单参数验证器Base64解码失败", "")
return
}
// 检查大小不超过 10M10 * 1024 * 1024 字节)
if len(decodedData) > 10*1024*1024 {
response.ErrorSystem(context, "PicRecognition表单参数验证器图片大小超过 10M", "")
return
}
// 将字节数据转换为图像以检查格式和尺寸
img, _, err := image.Decode(bytes.NewReader(decodedData))
if err != nil {
return
}
bounds := img.Bounds()
minSide := math.Min(float64(bounds.Dx()), float64(bounds.Dy()))
maxSide := math.Max(float64(bounds.Dx()), float64(bounds.Dy()))
// 最短边至少 15px最长边最大 8192px
if minSide < 15 || maxSide > 8192 {
response.ErrorSystem(context, "PicRecognition表单参数验证器图片尺寸过大或过小", "")
return
}
(&web.AiRecognition{}).PicRecognition(extraAddBindDataContext)
}

@ -0,0 +1,43 @@
package ai_recognition
import (
"encoding/base64"
"goskeleton/app/global/consts"
"goskeleton/app/http/controller/web"
"goskeleton/app/http/validator/core/data_transfer"
"goskeleton/app/utils/response"
"github.com/gin-gonic/gin"
)
type VocRecognition struct {
Voc string `form:"voc" json:"voc" binding:"required"` // 必填、对于文本,表示它的长度>=1
}
func (v VocRecognition) CheckParams(context *gin.Context) {
if err := context.ShouldBind(&v); err != nil {
// 将表单参数验证器出现的错误直接交给错误翻译器统一处理即可
response.ValidatorError(context, err)
return
}
// 该函数主要是将本结构体的字段(成员)按照 consts.ValidatorPrefix+ json标签对应的 键 => 值 形式绑定在上下文,便于下一步(控制器)可以直接通过 context.Get(键) 获取相关值
extraAddBindDataContext := data_transfer.DataAddContext(v, consts.ValidatorPrefix, context)
if extraAddBindDataContext == nil {
response.ErrorSystem(context, "VocRecognition表单参数验证器json化失败", "")
return
}
voc := v.Voc
// 先进行 URL 解码
// decodedVoc, err := url.QueryUnescape(voc)
// if err != nil {
// response.ErrorSystem(context, "VocRecognition表单参数验证器URL解码失败", "")
// return
// }
// 再进行 Base64 解码
_, err := base64.StdEncoding.DecodeString(voc)
if err != nil {
response.ErrorSystem(context, "VocRecognition表单参数验证器Base64解码失败", "")
return
}
(&web.AiRecognition{}).VocRecognition(extraAddBindDataContext)
}

@ -4,7 +4,6 @@ import (
"goskeleton/app/global/my_errors"
"goskeleton/app/global/variable"
"goskeleton/app/utils/baidubce"
"goskeleton/app/utils/files"
"net/http"
"net/url"
"strings"
@ -19,8 +18,9 @@ import (
func RequestOCR(context *gin.Context) (r bool, c interface{}) {
path := "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token=" + baidubce.GetAccessToken()
// 获得图片数据,需要 base64 和urlencode处理
// linlnf: test
content, _ := files.GetFileBase64("storage/app/test/文字识别.png")
// TODO:linlnf
content := context.PostForm("pic")
// 组装 body 数据参数在该网址https://cloud.baidu.com/doc/OCR/s/1k3h7y3db
data := make(url.Values)
data.Set("detect_direction", "false")
@ -52,7 +52,7 @@ func RequestOCR(context *gin.Context) (r bool, c interface{}) {
return false, nil
}
return true, gin.H{
"words": decodeBody2Str(mbody),
"words": decodeOCRBody2Str(mbody),
}
}
@ -60,7 +60,7 @@ func RequestOCR(context *gin.Context) (r bool, c interface{}) {
*
* @return
*/
func decodeBody2Str(mbody map[string]interface{}) (str string) {
func decodeOCRBody2Str(mbody map[string]interface{}) (str string) {
words, ok := mbody["words_result"]
if ok {
sliceWords, ok := words.([]interface{})

@ -0,0 +1,89 @@
package ai_model_cli
import (
"bytes"
"encoding/base64"
"encoding/json"
"goskeleton/app/global/my_errors"
"goskeleton/app/global/variable"
"goskeleton/app/utils/baidubce"
"net/http"
"github.com/gin-gonic/gin"
)
type BaiduBCEVOP struct {
Format string `json:"format"`
Rate int `json:"rate"`
Channel int `json:"channel"`
Cuid string `json:"cuid"`
DevPid int `json:"dev_pid"`
Speech string `json:"speech"`
Len int `json:"len"`
Token string `json:"token"`
}
/**
* ocr(),
* @return
*/
func RequestVOP(context *gin.Context) (r bool, c interface{}) {
path := "https://vop.baidu.com/pro_api"
// 获得语音数据,需要 base64 和urlencode处理
// TODO:linlnf
content := context.PostForm("voc")
// 再进行 Base64 解码
decodedVoc, _ := base64.StdEncoding.DecodeString(content)
// 组装 body 数据参数在该网址https://cloud.baidu.com/doc/SPEECH/s/4lbxdz34z
jsonData, _ := json.Marshal(BaiduBCEVOP{
Format: "pcm",
Rate: 16000,
Channel: 1,
Cuid: "kRxqyFGTgJOtBU4b5LnhWEvX6g8EU4Z7",
DevPid: 80001,
Speech: content,
Len: len(decodedVoc),
Token: baidubce.GetAccessToken(),
})
client := &http.Client{}
// 请求数据
req, err := http.NewRequest("POST", path, bytes.NewBuffer(jsonData))
if err != nil {
variable.ZapLog.Error(my_errors.ErrorBaiduCEUseVOPFail + err.Error())
return false, nil
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
res, err := client.Do(req)
if err != nil {
variable.ZapLog.Error(my_errors.ErrorBaiduCEUseVOPFail + err.Error())
return false, nil
}
defer res.Body.Close()
//从res中提取识别出的信息
mbody, err := baidubce.DecodeResBody(res, "vop")
if err != nil {
variable.ZapLog.Error(my_errors.ErrorBaiduCEUseVOPFail + err.Error())
return false, nil
}
return true, gin.H{
"words": decodeBody2Str(mbody),
}
}
/*
*
* @return
*/
func decodeBody2Str(mbody map[string]interface{}) (str string) {
words, ok := mbody["result"].([]interface{})
if ok {
for _, word := range words {
str += word.(string) + "\n"
}
return str
}
return ""
}

@ -51,7 +51,25 @@ func DecodeResBody(res *http.Response, flag string) (mbody map[string]interface{
return nil, err
}
mbody = jbody.(map[string]interface{})
err_str, _ := mbody["error_msg"].(string)
var err_str string
switch flag {
case "vop":
err_str1, _ := mbody["err_msg"].(string)
if err_str1 == "success." {
err_str = ""
} else {
err_str = err_str1
}
default:
err_str2, _ := mbody["error_msg"].(string)
if err_str2 == "" {
err_str = ""
} else {
err_str = err_str2
}
}
if err_str != "" {
variable.ZapLog.Error(my_errors.ErrorBaiduCEPostFail + flag + ", " + err_str)
err = errors.New(err_str)

@ -50,10 +50,10 @@ func GetFilesMimeByFp(fp multipart.File) string {
}
// 读取本地文件进行Base64编码
func GetFileBase64(filePath string) (string, error) {
func GetFileBase64(filePath string) (int, string, error) {
data, err := os.ReadFile(filePath)
if err != nil {
return "", err
return 0, "", err
}
return base64.StdEncoding.EncodeToString(data), nil
return len(data), base64.StdEncoding.EncodeToString(data), nil
}

@ -67,6 +67,8 @@ func InitWebRouter_Co() *gin.Engine {
{
// 请求图片文字识别
aiRecognition.POST("pic_recognition", validatorFactory.Create(consts.ValidatorPrefix+"PicRecognition"))
// 请求语音识别
aiRecognition.POST("voc_recognition", validatorFactory.Create(consts.ValidatorPrefix+"VocRecognition"))
}
// 【需要token】中间件验证的路由

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save