package controllers import ( "basic.com/fileServer/WeedFSClient.git" esApi "basic.com/pubsub/esutil.git" "basic.com/pubsub/protomsg.git" "basic.com/valib/bhomeclient.git" "basic.com/valib/bhomedbapi.git" "basic.com/valib/godraw.git" "basic.com/valib/logger.git" "encoding/base64" "encoding/json" "fmt" "image" "image/color" "sort" "strconv" "strings" "time" "vamicro/config" "vamicro/extend/util" "vamicro/search-service/models" "vamicro/search-service/service" ) type FileController struct { } // weedfs 访问路径 //var picIp = "http://192.168.1.182:6080/" var picIp = "http://" var picUrlField = "fileUrl" type FaceExtract struct { Url string `json:"url"` FaceBytes []byte `json:"faceBytes"` } var faceExtractedMap = make(map[string]FaceExtract,0) // @Security ApiKeyAuth // @Summary 人脸提取 // @Description 人脸提取 // @Accept multipart/form-data // @Produce json // @Tags 以图搜图 // @Param file formData file true "人员图片" // @Success 200 {string} json "{"code":200, msg:"", data:"", success:true}" // @Failure 500 {string} json "{"code":500, msg:"", data:"", success:false}" // @Router /data/api-v/dbperson/faceExtract [POST] func (sc *EsSearchController) FaceExtract(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { file, err := c.FormFile() //image这个是uplaodify参数定义中的 'fileObjName':'image' if err != nil || file.Name == "" || len(file.Bytes) == 0 { return &bhomeclient.Reply{Success:false,Msg:"参数有误"} } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" ||localConf.AlarmPort ==0 { logger.Debug("localConf err") return &bhomeclient.Reply{Success:false, Msg: "localConf err"} } var weedfsUri = "http://"+localConf.WebPicIp+":"+strconv.Itoa(int(localConf.WebPicPort))+"/submit?collection=persistent" logger.Debug("weedfsUri:",weedfsUri) resultMap :=make(map[string]interface{},0) //将上传的图片交人脸检测和人脸提取,获得特征 faceArr, err, pI := service.GetFaceFeaFromSdk(file.Bytes, time.Second*60) if err ==nil && len(faceArr) >0 { //1.提取每一张人脸小图 urlArr := make([]string,0) for _,r := range faceArr { rcFace := r.Pos.RcFace cutFaceImgData,cutErr := util.SubCutImg(pI, rcFace, 20) if cutErr != nil { logger.Debug("util.SubCutImg err:", cutErr) continue } weedFilePath, e := WeedFSClient.UploadFile(weedfsUri, "FaceUrl", cutFaceImgData, 5*time.Second) if e == nil{ faceExtractedMap[weedFilePath] = FaceExtract{ Url:weedFilePath, FaceBytes:r.Feats, } urlArr = append(urlArr, weedFilePath) } } //2.大图画框,标识人脸位置 drawedB, _ := drawPolygonOnImg(pI, &faceArr) originFilePath, _ := WeedFSClient.UploadFile(weedfsUri, "FaceUrl", *drawedB, 5*time.Second) resultMap["uploadImage"] = originFilePath resultMap["smImage"] = urlArr return &bhomeclient.Reply{Success:true,Data:resultMap} } else { return &bhomeclient.Reply{Success:false, Msg:"未提取到人脸"} } } func drawPolygonOnImg(i *protomsg.Image,faceArr *[]*protomsg.ResultFaceDetect) (*[]byte,error) { img,err := godraw.ToImage(i.Data, int(i.Width), int(i.Height)) if err != nil { return nil,err } red := color.RGBA{255, 0, 0, 0} for _,faceResult := range *faceArr { util.DrawRect(img.(*image.RGBA),int(faceResult.Pos.RcFace.Left),int(faceResult.Pos.RcFace.Top),int(faceResult.Pos.RcFace.Right), int(faceResult.Pos.RcFace.Bottom), red) } quantity := 100 jpgData, err := godraw.ImageToJpeg(img, &quantity) return &jpgData,err } type CompareResult struct { Id string `json:"id"` CompareScore float64 `json:"compareScore"` CameraId string `json:"cameraId"` CameraName string `json:"cameraName"` CameraAddr string `json:"cameraAddr"` PicDate string `json:"picDate"` Content string `json:"content"` IsAlarm bool `json:"isAlarm"` PicMaxUrl []string `json:"picMaxUrl"` TargetInfo []TargetInfo `json:"targetInfo"` TaskId string `json:"taskId"` TaskName string `json:"taskName"` BaseInfo []DbPersonVo `json:"baseInfo"` VideoUrl string `json:"videoUrl"` SdkName string `json:"sdkName"` AlarmRules []AlarmRuleVo `json:"alarmRules"` } type TargetInfo struct { TargetId string `json:"targetId"` TargetType string `json:"targetType"` TargetScore float64 `json:"targetScore"` Feature string `json:"feature"` PicSmUrl string `json:"picSmUrl"` TargetLocation TargetLocation `json:"targetLocation"` } type TargetLocation struct { TopLeft protomsg.Location `json:"topLeft"` BottomRight protomsg.Location `json:"bottomRight"` } type DbPersonVo struct { TableId string `json:"tableId"` TableName string `json:"tableName"` BwType string `json:"bwType"` CompareScore float64 `json:"compareScore"` TargetId string `json:"targetId"` TargetName string `json:"targetName"` TargetPicUrl string `json:"targetPicUrl"` MonitorLevel string `json:"monitorLevel"` Labels string `json:"labels"` Content string `json:"content"` Enable int32 `json:"enable"` } type ScoreIndex struct { CompareScore float64 Index int } type AlarmRuleVo struct { GroupId string `json:"groupId"` AlarmLevel string `json:"alarmLevel"` RuleText string `json:"ruleText"` DefenceState bool `json:"defenceState"` IsLink bool `json:"isLink"` LinkInfo string `json:"linkInfo"` } //填充向前端返回的数据 func FillDataToCompareResult(compResult *protomsg.SdkCompareResult) []CompareResult { var resultList = make([]CompareResult, len(compResult.CompareResult)) dbPersonM := make(map[string]ScoreIndex, 0) captureM := make(map[string]ScoreIndex, 0) personIds := make([]string,0) captureIds := make([]string,0) for idx,v :=range compResult.CompareResult{ if v.Tableid == service.CaptureTable { captureM[v.Id] = ScoreIndex{ Index: idx, CompareScore: util.ParseScore64(float64(v.CompareScore)), } captureIds = append(captureIds,v.Id) } else { dbPersonM[v.Id] = ScoreIndex{ Index: idx, CompareScore: util.ParseScore64(float64(v.CompareScore)), } personIds = append(personIds,v.Id) } } logger.Debug("comp len(personIds):", len(personIds)) logger.Debug("comp len(captureIds):", len(captureIds)) var dbpersons []protomsg.Dbperson if len(personIds) >0 { var dbpApi bhomedbapi.DbPersonApi dbpersons, _ = dbpApi.Dbpersoninfosbyid(personIds) } if len(dbpersons) >0 { var dtApi bhomedbapi.DbTableApi for _,p :=range dbpersons { var dbP = DbPersonVo { TargetId: p.Id, CompareScore: dbPersonM[p.Id].CompareScore, MonitorLevel: p.MonitorLevel, TargetName: p.PersonName, TargetPicUrl: p.PersonPicUrl, Labels: p.PhoneNum+"/"+p.Sex+"/"+p.IdCard, TableId: p.TableId, Content: p.Reserved, Enable: p.Enable, } dbTableInfos, _ := dtApi.DbtablesById([]string{ p.TableId }) if dbTableInfos !=nil{ dbP.BwType = dbTableInfos[0].BwType dbP.TableName = dbTableInfos[0].TableName } resultList[dbPersonM[p.Id].Index] = CompareResult{ BaseInfo:[]DbPersonVo{ dbP }, } } } if len(captureIds) >0 { var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" ||localConf.AlarmPort ==0 { logger.Debug("localConf err") return nil } logger.Debug("captureIds:",strings.Join(captureIds,",")) aiTargets, _ := esApi.AIOceaninfosbyid(captureIds, config.EsInfo.EsIndex.AiOcean.IndexName, localConf.AlarmIp, strconv.Itoa(int(localConf.AlarmPort))) logger.Debug("comp aiTargets.len:",len(aiTargets)) for _,vp :=range aiTargets { var bi []DbPersonVo for _,p :=range vp.BaseInfo { bi = append(bi, DbPersonVo{ TargetId: p.TargetId, CompareScore: util.ParseScore64(p.CompareScore), MonitorLevel: p.MonitorLevel, TargetName: p.TargetName, TargetPicUrl: p.TargetPicUrl, Labels: p.Labels, TableId: p.TableId, BwType: p.BwType, TableName: p.TableName, }) } var alarmRules []AlarmRuleVo if vp.AlarmRules !=nil && len(vp.AlarmRules) >0 { for _,ar :=range vp.AlarmRules { alarmRules = append(alarmRules, AlarmRuleVo{ GroupId: ar.GroupId, AlarmLevel: ar.AlarmLevel, RuleText: ar.RuleText, DefenceState: ar.DefenceState, IsLink: ar.IsLink, LinkInfo: ar.LinkInfo, }) } } var ti = make([]TargetInfo,0) if vp.TargetInfo !=nil { for _,vti :=range vp.TargetInfo { tl := protomsg.Location{ X: vti.TargetLocation.TopLeft.X, Y: vti.TargetLocation.TopLeft.Y, } br := protomsg.Location{ X: vti.TargetLocation.BottomRight.X, Y: vti.TargetLocation.BottomRight.Y, } tInfo := TargetInfo{ TargetId: vti.TargetId, TargetType: vti.TargetType, TargetScore: vti.TargetScore, Feature: vti.Feature, PicSmUrl: vti.PicSmUrl, TargetLocation: TargetLocation{ TopLeft:tl, BottomRight:br, }, } ti = append(ti, tInfo) } } vpE := CompareResult{ Id: vp.Id, CompareScore: captureM[vp.Id].CompareScore, CameraId: vp.CameraId, CameraName: vp.CameraName, CameraAddr: vp.CameraAddr, PicDate: vp.PicDate, PicMaxUrl: vp.PicMaxUrl, TargetInfo: ti, IsAlarm: vp.IsAlarm, TaskName: vp.TaskName, TaskId: vp.TaskId, VideoUrl: vp.VideoUrl, BaseInfo: bi, SdkName: "人脸", AlarmRules: alarmRules, } resultList[captureM[vp.Id].Index] = vpE } } return resultList } func parseMonitorLevel(level string) string { if level == "1" { return "一级" } if level == "2" { return "二级" } if level == "3" { return "三级" } return level } // @Security ApiKeyAuth // @Summary 以图搜图 // @Description 以图搜图 // @Accept json // @Produce json // @Tags 以图搜图 // @Param condition body models.EsSearch true "搜索参数" // @Success 200 {string} json "{"code":200, msg:"", data:"", success:true}" // @Failure 500 {string} json "{"code":500, msg:"", data:"", success:false}" // @Router /data/api-v/dbperson/searchByPhoto [POST] func (sc *EsSearchController) SearchByPhoto(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { var searchBody models.EsSearch err := c.BindJSON(&searchBody) if err !=nil{ return &bhomeclient.Reply{Success:false, Msg:"参数有误"} } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" ||localConf.AlarmPort ==0 { logger.Debug("localConf err") return &bhomeclient.Reply{Success:false, Msg: "localConf err"} } var faceB []byte logger.Debug("compTargetId:",searchBody.CompTargetId,",compTargetType:",searchBody.CompTargetType) if searchBody.CompTargetId != "" {//做查找此人,按抓拍的人脸或者底库的人脸以图搜图 if searchBody.CompTargetType == 0 {//本张人脸是底库人脸 var dbpApi bhomedbapi.DbPersonApi dbPersons, e := dbpApi.Dbpersoninfosbyid([]string{searchBody.CompTargetId}) if e ==nil && dbPersons !=nil && len(dbPersons) ==1 { searchBase64Fea := dbPersons[0].FaceFeature if searchBase64Fea != "" { decodeF, err := base64.StdEncoding.DecodeString(dbPersons[0].FaceFeature) if err !=nil { return &bhomeclient.Reply{Success:false, Msg:"本条底库人员特征不是base64,请检查"} } faceB = decodeF } else { return &bhomeclient.Reply{Success:false, Msg:"本条底库人员特征为空,请检查"} } } else { return &bhomeclient.Reply{Success:false, Msg:"底库人员查询失败,请检查"} } } else { searchBase64Fea, err := esApi.GetVideoPersonFaceFeatureById(searchBody.CompTargetId, config.EsInfo.EsIndex.AiOcean.IndexName, localConf.AlarmIp, strconv.Itoa(int(localConf.AlarmPort))) if err !=nil { return &bhomeclient.Reply{Success:false, Msg:"抓拍数据不存在,请检查"} } if searchBase64Fea !=""{ decodeF, err := base64.StdEncoding.DecodeString(searchBase64Fea) if err !=nil { return &bhomeclient.Reply{Success:false, Msg:"本条抓拍特征不是base64,请检查"} } faceB = decodeF } else { return &bhomeclient.Reply{Success:false, Msg:"本条人脸抓拍特征为空,请检查"} } } } else {//做以图搜图 if searchBody.PicUrl == "" || len(searchBody.DataBases) == 0 { return &bhomeclient.Reply{Success:false, Msg:"以图搜图PicUrl不能为空"} } if face,ok := faceExtractedMap[searchBody.PicUrl];!ok{ return &bhomeclient.Reply{Success:false, Msg:"人脸特征未检测,请重新上传图片"} } else { faceB = face.FaceBytes } } if faceB == nil { return &bhomeclient.Reply{Success:false, Msg:"请重新上传图片"} } arg := protomsg.CompareArgs{ FaceFeature: faceB, CompareThreshold: searchBody.Threshold, } var hasCompEsPerson = false if searchBody.DataBases !=nil { for idx,tableId :=range searchBody.DataBases { if tableId == "esData" { searchBody.DataBases = append(searchBody.DataBases[:idx], searchBody.DataBases[idx+1:]...) hasCompEsPerson = true break } } arg.TableIds = searchBody.DataBases } else { arg.TableIds = []string{} } alarmLevelTypes := strings.Replace(strings.Trim(fmt.Sprint(getAlarmLevel(searchBody.AlarmLevel)), "[]"), " ", "\",\"", -1) arg.Source = true // 标识来源是web arg.AlarmLevel = alarmLevelTypes arg.Tasks = searchBody.Tasks arg.TreeNodes = searchBody.TreeNodes arg.Tabs = searchBody.Tabs arg.SearchTime = searchBody.SearchTime arg.InputValue = searchBody.InputValue arg.Collection = searchBody.Collection if !searchBody.IsAll { arg.AnalyServerId = localConf.ServerId } logger.Debug("arg.TableIds:", arg.TableIds, ",alarmLevel:",arg.AlarmLevel,",treeNodes:",arg.TreeNodes,",searchTime:",arg.SearchTime, ",inputValue:",arg.InputValue,",tasks:",arg.Tasks,",compThreshold:",arg.CompareThreshold) timeStart := time.Now() compareService := service.NewFaceCompareService(arg) var totalData service.CompareList if len(arg.TableIds) >0 {//有比对底库 dbPersonTargets := compareService.CompareDbPersons() if dbPersonTargets !=nil { totalData = append(totalData,*dbPersonTargets...) } } if hasCompEsPerson {//有比对Es抓拍 esPersons := compareService.CompareVideoPersons() if esPersons !=nil { totalData = append(totalData, *esPersons...) } } logger.Debug("comp 比对结束,用时:",time.Since(timeStart)) service.SetCompResultByNum(&service.CompareOnce{ CompareNum: compareService.CompareNum, CompareData: &totalData, }) m := make(map[string]interface{},3) if totalData != nil && totalData.Len() > 0{ sort.Sort(totalData) total := totalData.Len() m["compareNum"] = compareService.CompareNum m["total"] = total var sCompResult protomsg.SdkCompareResult if total <= searchBody.Size { sCompResult.CompareResult = totalData } else { sCompResult.CompareResult = totalData[0:searchBody.Size] } resultList := FillDataToCompareResult(&sCompResult) m["totalList"] = resultList logger.Debug("comp 比对加排序返回用时:", time.Since(timeStart)) } else { m["total"] = 0 m["compareNum"] = compareService.CompareNum m["totalList"] = []CompareResult{} } return &bhomeclient.Reply{Success:true, Data:m} } // @Security ApiKeyAuth // @Summary 比对数据查询 // @Description 比对数据查询 // @Accept json // @Produce json // @Tags es // @Param reqMap body models.EsSearch true "collection 为空" // @Success 200 {string} json "{"code":200, msg:"", success:true}" // @Failure 500 {string} json "{"code":500, msg:"", success:false}" // @Router /data/api-v/es/queryEsCompareData [POST] func (sc *EsSearchController) PostEsCompareData(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { searchBody := new(models.EsSearch) err := c.BindJSON(&searchBody) if err != nil || searchBody.PicUrl == "" || len(searchBody.DataBases) == 0 { return &bhomeclient.Reply{Success:false, Msg:"参数有误"} } if searchBody.CompareNum != "" { //二次搜索,不需要再比对了 co := service.GetCompResultByNum(searchBody.CompareNum) if co != nil { //二次搜索和排序 twiceM := GetCompareDataTwice(co, searchBody) return &bhomeclient.Reply{Success:true, Data:twiceM} } else { m := make(map[string]interface{}, 0) m["compareNum"] = searchBody.CompareNum m["total"] = 0 m["totalList"] = []CompareResult{} return &bhomeclient.Reply{Success:false, Msg:"上次比对已失效,请重新比对"} } } m := make(map[string]interface{}, 0) m["compareNum"] = searchBody.CompareNum m["total"] = 0 m["totalList"] = []CompareResult{} return &bhomeclient.Reply{Success:false, Msg:"上次比对已失效,请重新比对", Data:m} } type PersonId struct { Id string `json:"id"` } func GetCompareDataTwice(co *service.CompareOnce,searchBody *models.EsSearch) map[string]interface{} { m := make(map[string]interface{},0) from := (searchBody.Page-1)*searchBody.Size to := from + searchBody.Size var hasCompEsPerson = false if searchBody.DataBases !=nil { for idx,tableId :=range searchBody.DataBases { if tableId == "esData" { hasCompEsPerson = true searchBody.DataBases = append(searchBody.DataBases[:idx], searchBody.DataBases[idx+1:]...) break } } } if hasCompEsPerson {//二次检索ES比对结果 arg := protomsg.CompareArgs{ CompareThreshold: searchBody.Threshold, } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if flag && localConf.AlarmIp != "" { alarmLevelTypes := strings.Replace(strings.Trim(fmt.Sprint(getAlarmLevel(searchBody.AlarmLevel)), "[]"), " ", "\",\"", -1) arg.Source = true // 标识来源是web arg.AlarmLevel = alarmLevelTypes arg.Tasks = searchBody.Tasks arg.TreeNodes = searchBody.TreeNodes arg.Tabs = searchBody.Tabs arg.SearchTime = searchBody.SearchTime arg.InputValue = searchBody.InputValue arg.Collection = searchBody.Collection if !searchBody.IsAll { arg.AnalyServerId = localConf.ServerId } ct := time.Now() captureIds := esApi.GetAllLocalVideopersonsId(arg, config.EsInfo.EsIndex.AiOcean.IndexName, localConf.AlarmIp, strconv.Itoa(int(localConf.AlarmPort)), alarmLevelTypes) ut := time.Since(ct) logger.Debug("searchPhoto first Result.len:",len(*co.CompareData),"twice len(captureIds):",len(captureIds), "useTime:", ut) if captureIds !=nil { var aResult protomsg.SdkCompareResult aList := getTwiceSearchResult(co, &captureIds, searchBody) aTotal := aList.Len() if aTotal <= searchBody.Size { aResult.CompareResult = *aList } else { if from >= aTotal { aResult.CompareResult = make(service.CompareList, 0) } else { if (aTotal-from) > searchBody.Size { aResult.CompareResult = (*aList)[from:to] } else { aResult.CompareResult = (*aList)[from:] } } } out := FillDataToCompareResult(&aResult) m["total"] = aTotal m["compareNum"] = searchBody.CompareNum m["totalList"] = out return m } else { m["total"] = 0 m["compareNum"] = searchBody.CompareNum m["totalList"] = []interface{}{} return m } } } else {//底库数据的二次检索 var dbpApi bhomedbapi.DbPersonApi personIds, _ := dbpApi.FindLikePersonIds(searchBody.DataBases, searchBody.InputValue) logger.Debug("searchPhoto first Result.len:",len(*co.CompareData),"personIds:",personIds) if personIds !=nil { var pIds []PersonId b, _ := json.Marshal(personIds) json.Unmarshal(b, &pIds) if len(pIds) >0 { var personIdArr []string for _,pid :=range pIds{ personIdArr = append(personIdArr, pid.Id) } var aResult protomsg.SdkCompareResult aList := getTwiceSearchResult(co, &personIdArr, searchBody) aTotal := aList.Len() if aTotal <= searchBody.Size { aResult.CompareResult = *aList } else { if from >= aTotal { aResult.CompareResult = make(service.CompareList, 0) } else { if (aTotal-from) > searchBody.Size { aResult.CompareResult = (*aList)[from:to] } else { aResult.CompareResult = (*aList)[from:] } } } out := FillDataToCompareResult(&aResult) m["total"] = aTotal m["compareNum"] = searchBody.CompareNum m["totalList"] = out return m } } else { m["total"] = 0 m["compareNum"] = searchBody.CompareNum m["totalList"] = []interface{}{} return m } } m["total"] = 0 m["compareNum"] = searchBody.CompareNum m["totalList"] = []interface{}{} return m } func getTwiceSearchResult(co *service.CompareOnce, scopeIds *[]string, searchBody *models.EsSearch) *service.CompareList{ m := make(map[string]string) for _,capId :=range *scopeIds { m[capId] = capId } var totalData service.CompareList for _,each :=range *co.CompareData { if _,ok :=m[each.Id];ok && each.CompareScore >= searchBody.Threshold { totalData = append(totalData, each) } } if totalData != nil && totalData.Len() > 0{ sort.Sort(totalData) } return &totalData } type EsPersonSave struct { FaceFeature string `json:"faceFeature"` TableId string `json:"tableId"` Id string `json:"id"` PersonPicUrl string `json:"personPicUrl"` IdCard string `json:"idCard"` }