Files
zmkgC/api/v1/common/coryCommon/baiduOCR.go
2025-07-07 20:11:59 +08:00

492 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package coryCommon
import (
"encoding/json"
"errors"
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gclient"
"github.com/gogf/gf/v2/os/gctx"
"github.com/tiger1103/gfast-cache/cache"
commonService "github.com/tiger1103/gfast/v3/internal/app/common/service"
"github.com/tiger1103/gfast/v3/library/liberr"
tool "github.com/tiger1103/gfast/v3/utility/coryUtils"
"golang.org/x/net/context"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
)
/**
功能:
文字识别百度API识别身份证、银行卡内容、人脸检测、人脸对比
*/
// OcrReq 请求体参数 身份证和银行卡都是此结构体,
type OcrReq struct {
Image string `json:"image"` // 二选一图像数据base64编码后进行urlencode需去掉编码头data:image/jpeg;base64, 要求base64编码和urlencode后大小不超过4M最短边至少15px最长边最大4096px,支持jpg/jpeg/png/bmp格式
Url string `json:"url"` // 二选一图片完整URLURL长度不超过1024字节URL对应的图片base64编码后大小不超过4M最短边至少15px最长边最大4096px,支持jpg/jpeg/png/bmp格式当image字段存在时url字段失效 请注意关闭URL防盗链
IdCardSide string `json:"id_card_side"` // 身份证需要此参数人头面填写front国徽面填写back 银行卡不需要
DetectPhoto bool `json:"detect_photo"` // 是否检测身份证进行裁剪默认不检测。可选值true-检测身份证并返回证照的 base64 编码及位置信息
}
/*
1、clientId 必须参数应用的APIKey
2、clientSecret 必须参数应用的Secret Key
3、存在redis的数据前缀
*/
var clientId = ""
var clientSecret = ""
var cacheRedis = "zmGoBaiDuOcrAccessToken"
func init() {
one, err := g.Cfg().Get(gctx.New(), "baiDuYun.clientId")
if err != nil {
fmt.Println("百度云API未找到")
}
two, err := g.Cfg().Get(gctx.New(), "baiDuYun.clientSecret")
if err != nil {
fmt.Println("百度云Secret未找到")
}
clientId = one.String()
clientSecret = two.String()
}
/*
IDCardInfo 获取身份证相关数据
*/
type IDCardInfo struct {
WordsResult WordsResult `json:"words_result"`
WordsResultNum int `json:"words_result_num"`
IDCardNumberType int `json:"idcard_number_type"`
ImageStatus string `json:"image_status"`
LogID int64 `json:"log_id"`
Photo string `json:"photo"`
}
type WordsResult struct {
Name Field `json:"姓名"`
Nation Field `json:"民族"`
Address Field `json:"住址"`
CitizenIdentification Field `json:"公民身份号码"`
Birth Field `json:"出生"`
Gender Field `json:"性别"`
ExpirationDate Field `json:"失效日期"`
IssuingAuthority Field `json:"签发机关"`
IssueDate Field `json:"签发日期"`
}
type Field struct {
Location Location `json:"location"`
Words string `json:"words"`
}
type Location struct {
Top int `json:"top"`
Left int `json:"left"`
Width int `json:"width"`
Height int `json:"height"`
}
func ImgOCR(vr OcrReq) (m map[string]interface{}) {
//请求路径+token
baseUrl := "https://aip.baidubce.com/rest/2.0/ocr/v1/idcard"
//先从缓存里面捞取token,如果没得就重新获取token
var atStr = redisCacheStr()
// 构造 URL 参数
params := url.Values{}
params.Set("access_token", atStr)
// 构造完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseUrl, params.Encode())
response, err := gclient.New().ContentJson().ContentType("application/x-www-form-urlencoded").Post(gctx.New(), requestURL, vr)
if err != nil {
return
}
var dataInfo = strings.ReplaceAll(response.ReadAllString(), " ", "")
//解析数据
bodyData := []byte(dataInfo)
var idCardInfo IDCardInfo
err = json.Unmarshal(bodyData, &idCardInfo)
if err != nil {
fmt.Println("Failed to parse JSON data:", err)
return
}
m = make(map[string]interface{})
//身份证正反面颠倒了,直接返回空
if idCardInfo.ImageStatus == "reversed_side" {
return
}
result := idCardInfo.WordsResult
//if idCardInfo.Photo != "" {
// m["pacePhoto"] = idCardInfo.Photo
//}
if result.Name.Words != "" {
m["userName"] = result.Name.Words
}
if result.Nation.Words != "" {
m["sfzNation"] = result.Nation.Words
}
if result.Address.Words != "" {
m["sfzSite"] = result.Address.Words
}
if result.CitizenIdentification.Words != "" {
m["sfzNumber"] = result.CitizenIdentification.Words
}
if result.Birth.Words != "" {
str, _ := tool.New().TimeCycle(result.Birth.Words)
m["sfzBirth"] = str
}
if result.Gender.Words != "" {
var se = result.Gender.Words
if se == "男" {
se = "1"
} else if se == "女" {
se = "2"
} else {
se = "3"
}
m["sex"] = se
}
if result.ExpirationDate.Words != "" {
str, err := tool.New().TimeCycle(result.ExpirationDate.Words)
if err == nil {
m["sfzEnd"] = str
} else {
str := result.ExpirationDate.Words
m["sfzEnd"] = str
}
}
if result.IssuingAuthority.Words != "" {
m["IssuingAuthority"] = result.IssuingAuthority.Words
}
if result.IssueDate.Words != "" {
str, _ := tool.New().TimeCycle(result.IssueDate.Words)
m["sfzStart"] = str
}
return m
}
/*
BankData 获取银行卡相关数据
针对卡号、有效期、发卡行、卡片类型、持卡人5个关键字段进行结构化识别识别准确率超过99%
*/
type BankData struct {
ValidDate string `json:"valid_date"` //有效期
BankCardNumber string `json:"bank_card_number"` //银行卡卡号
BankName string `json:"bank_name"` //银行名,不能识别时为空
BankCardType int `json:"bank_card_type"` //银行卡类型0不能识别; 1借记卡; 2贷记卡原信用卡大部分为贷记卡; 3准贷记卡; 4预付费卡
HolderName string `json:"holder_name"` //持卡人姓名,不能识别时为空
}
type Result struct {
Res BankData `json:"result"` //具体数据
Direction int `json:"direction"` //图像方向。 - - 1未定义; - 0正向; - 1逆时针90度; - 2逆时针180度; - 3逆时针270度
LogID int64 `json:"log_id"` //请求标识码,随机数,唯一。
}
func ImgYhkOCR(vr OcrReq) (m map[string]interface{}) {
m = make(map[string]interface{})
//请求路径+token
baseUrl := "https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard"
//先从缓存里面捞取token
var atStr = redisCacheStr()
// 构造 URL 参数
params := url.Values{}
params.Set("access_token", atStr)
// 构造完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseUrl, params.Encode())
response, err := gclient.New().ContentJson().ContentType("application/x-www-form-urlencoded").Post(gctx.New(), requestURL, vr)
if err != nil {
return
}
//解析数据
allString := response.ReadAllString()
bodyData := []byte(allString)
var result Result
err = json.Unmarshal(bodyData, &result)
if err != nil {
fmt.Println("Failed to parse JSON data:", err)
return
}
if result.Res.ValidDate != "" {
m["ValidDate"] = result.Res.ValidDate
}
if result.Res.BankCardNumber != "" {
m["yhkNumber"] = strings.ReplaceAll(result.Res.BankCardNumber, " ", "")
}
if result.Res.BankName != "" {
m["yhkOpeningBank"] = result.Res.BankName
}
if result.Res.BankCardType >= 0 {
m["BankCardType"] = result.Res.BankCardType
}
if result.Res.HolderName != "" {
m["yhkCardholder"] = result.Res.HolderName
}
return m
}
/*
HumanFaceReq 请求参数 人脸识别+人脸检测
*/
type HumanFaceReq struct {
Image string `json:"image"` //图片此处填写base64
ImageType string `json:"image_type" gf:"default:BASE64" ` //图片类型-BASE64-URL-BASE64此封装固定用base64
FaceField string `json:"face_field" gf:"default:face_type,quality"` //包括age,expression,face_shape,gender,glasses,landmark,landmark150, quality,eye_status,emotion,face_type,mask,spoofing信息 逗号分隔. 默认只返回face_token、人脸框、概率和旋转角度
}
/*
HumanFaceRep 返回参数 人脸识别
*/
type HumanFaceRep struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
LogId int `json:"log_id"`
Timestamp int `json:"timestamp"`
Cached int `json:"cached"`
Result struct {
FaceNum int `json:"face_num"`
FaceList []struct {
FaceToken string `json:"face_token"`
Location struct {
Left float64 `json:"left"`
Top float64 `json:"top"`
Width int `json:"width"`
Height int `json:"height"`
Rotation int `json:"rotation"`
} `json:"location"`
FaceProbability float64 `json:"face_probability"`
Angle struct {
Yaw float64 `json:"yaw"`
Pitch float64 `json:"pitch"`
Roll float64 `json:"roll"`
} `json:"angle"`
FaceType struct {
Type string `json:"type"`
Probability float64 `json:"probability"`
} `json:"face_type"`
Quality struct {
Occlusion struct {
LeftEye float64 `json:"left_eye"`
RightEye float64 `json:"right_eye"`
Nose float64 `json:"nose"`
Mouth float64 `json:"mouth"`
LeftCheek float64 `json:"left_cheek"`
RightCheek float64 `json:"right_cheek"`
ChinContour float64 `json:"chin_contour"`
} `json:"occlusion"`
Blur float64 `json:"blur"`
Illumination float64 `json:"illumination"`
Completeness int64 `json:"completeness"`
} `json:"quality"`
} `json:"face_list"`
} `json:"result"`
}
func HumanFace(hf *HumanFaceReq) (err error) {
ctx := gctx.New()
err = g.Try(ctx, func(ctx context.Context) {
err = nil
//1、请求地址+token
url := "https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token=" + redisCacheStr()
marshal, err := json.Marshal(hf)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
//3、准备请求
payload := strings.NewReader(string(marshal))
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
//4、设置请求头
req.Header.Add("Content-Type", "application/json")
//5、发送请求
res, err := client.Do(req)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
defer res.Body.Close()
//6、返回数据
body, err := io.ReadAll(res.Body)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
var aaa = body
//解析数据
var result HumanFaceRep
err = json.Unmarshal(aaa, &result)
if err != nil {
liberr.ErrIsNil(ctx, err)
return
}
if result.ErrorCode != 0 {
if result.ErrorMsg == "pic not has face" {
err = errors.New("这张照片没有人脸")
liberr.ErrIsNil(ctx, err)
} else {
err = errors.New(result.ErrorMsg)
liberr.ErrIsNil(ctx, err)
}
return
}
//1、人脸置信度范围【0~1】代表这是一张人脸的概率0最小、1最大。其中返回0或1时数据类型为Integer
dataInfo := result.Result.FaceList[0]
reliabilityOne := dataInfo.FaceProbability
if reliabilityOne != 1.0 {
err = errors.New("识别不清晰!")
}
//2、判断是否真是人脸 human: 真实人脸 cartoon: 卡通人脸
reliabilityTwo := dataInfo.FaceType.Type
if reliabilityTwo == "cartoon" {
err = errors.New("请传入真实人脸!")
} else {
//判断可信度 置信度范围0~1
reliabilityThree := dataInfo.FaceType.Probability
if reliabilityThree < 0.8 {
err = errors.New("请勿化妆太过夸张!")
}
}
//3、人脸模糊程度范围[0~1]0表示清晰1表示模糊
reliabilityFour := dataInfo.Quality.Blur
if reliabilityFour >= 0.1 {
err = errors.New("人脸过于模糊!")
}
//4、光线太暗 0~255 值越大光线越好
reliabilityFive := dataInfo.Quality.Illumination
if reliabilityFive < 80.0 {
err = errors.New("光线太暗!")
}
//5、人脸是否完整 1完整 0不完整
reliabilitySix := dataInfo.Quality.Completeness
if reliabilitySix != 1 {
err = errors.New("请确定人脸在图框内!")
}
return
})
return
}
// ComparisonRep 人脸检测返回数据
type ComparisonRep struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
LogId int `json:"log_id"`
Timestamp int `json:"timestamp"`
Cached int `json:"cached"`
Result struct {
Score float64 `json:"score"` //人脸相似度得分推荐阈值80分
FaceList []struct {
FaceToken string `json:"face_token"`
} `json:"face_list"`
} `json:"result"`
}
func Comparison(arrObject []*HumanFaceReq) (score float64, err error) {
//1、请求地址+token
url := "https://aip.baidubce.com/rest/2.0/face/v3/match?access_token=" + redisCacheStr()
//2、请求参数转字符串json
marshal, err := json.Marshal(arrObject)
if err != nil {
return
}
payload := strings.NewReader(string(marshal))
//3、准备post请求
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)
if err != nil {
fmt.Println(err)
return
}
//4、设置请求头
req.Header.Add("Content-Type", "application/json")
//5、发送请求关闭连接
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
//6、数据结果
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
//7、解析数据
var result ComparisonRep
err = json.Unmarshal(body, &result)
if err != nil {
fmt.Println("Failed to parse JSON data:", err)
return
}
score = result.Result.Score
return score, err
}
/*
AccessTokenResponse 获取Access_token,有效期(秒为单位有效期30天)
*/
type AccessTokenResponse struct {
AccessToken string `json:"access_token"`
}
func AccessTokenFunc() (str string) {
url := "https://aip.baidubce.com/oauth/2.0/token?client_id=" + clientId + "&client_secret=" + clientSecret + "&grant_type=client_credentials"
payload := strings.NewReader(``)
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var tokenResponse AccessTokenResponse
json.Unmarshal(body, &tokenResponse)
if err != nil {
fmt.Println(err)
return
}
return tokenResponse.AccessToken
}
// 缓存捞取token,如果没得就重新获取token
func redisCacheStr() (atStr string) {
atStr = ""
ctx := gctx.New()
prefix := g.Cfg().MustGet(ctx, "system.cache.prefix").String()
gfCache := cache.New(prefix)
at := commonService.Cache().Get(ctx, gfCache.CachePrefix+cacheRedis)
if at == nil || at.String() == "" {
atStr = AccessTokenFunc()
//存储到redis时间为29天
commonService.Cache().Set(ctx, cacheRedis, atStr, time.Hour*24*29)
} else {
atStr = at.String()
}
//atStr = "24.c8814d3fc7961820f0e23ee9d80cf96c.2592000.1696671067.282335-38777216"
return atStr
}