| | |
| | | package controller |
| | | |
| | | import ( |
| | | "encoding/base64" |
| | | "gat1400Exchange/config" |
| | | "gat1400Exchange/pkg" |
| | | "gat1400Exchange/service" |
| | | "net/http" |
| | | "time" |
| | |
| | | |
| | | c.JSON(http.StatusOK, gin.H{"ResponseStatusObject": rspMsg}) |
| | | } |
| | | |
| | | func (a CaptureController) VideoLabels(c *gin.Context) { |
| | | var req vo.RequestVideoLabelList |
| | | if err := c.BindJSON(&req); err != nil { |
| | |
| | | face.FaceAppearTime = videoLabel.BehaviorAnalysisObject.BehaviorBeginTime |
| | | face.FaceDisAppearTime = videoLabel.BehaviorAnalysisObject.BehaviorEndTime |
| | | |
| | | for idx, _ := range videoLabel.SubImageList.SubImageInfoObject { |
| | | var hasTargetImage bool |
| | | var bgImageWith, bgImageHeight int |
| | | var bgImage *vo.SubImageInfoObject |
| | | for idx, img := range videoLabel.SubImageList.SubImageInfoObject { |
| | | videoLabel.SubImageList.SubImageInfoObject[idx].EventSort = 10 |
| | | |
| | | // 判断是否有小图, 如果没有切一张 |
| | | if img.Type == "14" { |
| | | bgImageWith, bgImageHeight = img.Width, img.Height |
| | | } else { |
| | | hasTargetImage = true |
| | | bgImage = &videoLabel.SubImageList.SubImageInfoObject[idx] |
| | | } |
| | | |
| | | face.SubImageList.SubImageInfoObject = videoLabel.SubImageList.SubImageInfoObject |
| | | face.SubImageList.SubImageInfoObject = append( |
| | | face.SubImageList.SubImageInfoObject, |
| | | videoLabel.SubImageList.SubImageInfoObject[idx], |
| | | ) |
| | | } |
| | | |
| | | if !hasTargetImage && bgImage != nil { |
| | | imgData, err := base64.StdEncoding.DecodeString(bgImage.Data) |
| | | if err != nil { |
| | | c.AbortWithStatus(http.StatusBadRequest) |
| | | return |
| | | } |
| | | |
| | | var subRect pkg.Rect |
| | | subRect.Left = bgImageWith / 3 |
| | | subRect.Top = bgImageHeight / 3 |
| | | subRect.Right = subRect.Left * 2 |
| | | subRect.Bottom = subRect.Top * 2 |
| | | |
| | | subImage, err := pkg.SubCutImage(imgData, &subRect, 0) |
| | | if err != nil { |
| | | c.AbortWithStatus(http.StatusBadRequest) |
| | | return |
| | | } |
| | | |
| | | var subImageInfo = vo.SubImageInfoObject{ |
| | | ImageID: bgImage.ImageID + "1", |
| | | EventSort: 10, |
| | | DeviceID: bgImage.DeviceID, |
| | | StoragePath: "", |
| | | Type: "11", |
| | | FileFormat: "Jpeg", |
| | | ShotTime: bgImage.ShotTime, |
| | | Width: subRect.Right - subRect.Left, |
| | | Height: subRect.Top - subRect.Bottom, |
| | | Data: base64.StdEncoding.EncodeToString(subImage), |
| | | } |
| | | face.SubImageList.SubImageInfoObject = append( |
| | | face.SubImageList.SubImageInfoObject, |
| | | subImageInfo, |
| | | ) |
| | | } |
| | | |
| | | // 如果开启了下级, 身份应该是消息代理, 不再转发到服务器 |
| | | if config.ClientConf.Enable && config.ServeConf.Role == "agent" { |
| | | //go a.Repository.VIIDMsgForward(&req) |
| | | var faceObjList vo.RequestFaceList |
| | | faceObjList.FaceListObject.FaceObject = append(faceObjList.FaceListObject.FaceObject, face) |
| | | go a.Repository.VIIDMsgForward(&faceObjList) |
| | | } else if config.ServeConf.Role == "cascade" { |
| | | go service.AddFaceNotification(&face) |
| | | } |
New file |
| | |
| | | package pkg |
| | | |
| | | import ( |
| | | "bufio" |
| | | "bytes" |
| | | "encoding/base64" |
| | | "image" |
| | | "image/color" |
| | | "image/jpeg" |
| | | "log" |
| | | ) |
| | | |
| | | type Image struct { |
| | | Data []byte |
| | | Width int |
| | | Height int |
| | | } |
| | | |
| | | type Rect struct { |
| | | Left int |
| | | Top int |
| | | Right int |
| | | Bottom int |
| | | } |
| | | |
| | | func SubCutImage(srcImage []byte, rect *Rect, enlarge int) ([]byte, error) { |
| | | img, err := ReadImageData(srcImage) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | bkImg, _ := DataToImage(img.Data, img.Width, img.Height) |
| | | |
| | | cutRect := image.Rect(rect.Left, rect.Top, rect.Right, rect.Bottom) |
| | | |
| | | if enlarge > 0 { |
| | | enlargeRect := EnlargeCut(rect, img.Width, img.Height, enlarge) |
| | | squareRect := LetterBox(enlargeRect, img.Width, img.Height) |
| | | cutRect = image.Rect(squareRect.Left, squareRect.Top, squareRect.Right, squareRect.Bottom) |
| | | } |
| | | |
| | | subImg := bkImg.(*image.RGBA).SubImage(cutRect) |
| | | |
| | | dstImage, err := ImageToJpeg(subImg, nil) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | return dstImage, nil |
| | | } |
| | | |
| | | func ReadImageData(srcImage []byte) (*Image, error) { |
| | | bt := bytes.NewBuffer(srcImage) |
| | | img, _, err := image.Decode(bt) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | |
| | | bgr := Image2BGR(img) |
| | | |
| | | return &Image{ |
| | | Width: img.Bounds().Dx(), |
| | | Height: img.Bounds().Dy(), |
| | | Data: bgr, |
| | | }, nil |
| | | } |
| | | |
| | | // letterbox 按照宽边修改目标框为正方形 |
| | | func LetterBox(rect *Rect, maxW, maxH int) *Rect { |
| | | width := rect.Right - rect.Left |
| | | height := rect.Bottom - rect.Top |
| | | |
| | | // 计算目标框的中心点坐标 |
| | | centerX := (rect.Left + rect.Right) / 2 |
| | | centerY := (rect.Top + rect.Bottom) / 2 |
| | | |
| | | // 如果宽度大于高度,则以高度为基准调整宽度,使其成为正方形 |
| | | if width > height { |
| | | newWidth := height |
| | | newLeft := centerX - newWidth/2 |
| | | newRight := centerX + newWidth/2 |
| | | |
| | | // 调整后的正方形框超出图像边界时的处理 |
| | | if newLeft < 0 { |
| | | // 如果左边界超出,向右移动右边界 |
| | | newRight += -newLeft |
| | | newLeft = 0 |
| | | } |
| | | if newRight > maxW { |
| | | // 如果右边界超出,向左移动左边界 |
| | | newLeft -= newRight - maxW |
| | | newRight = maxW |
| | | } |
| | | |
| | | return &Rect{ |
| | | Left: newLeft, |
| | | Top: rect.Top, |
| | | Right: newRight, |
| | | Bottom: rect.Bottom, |
| | | } |
| | | } |
| | | |
| | | // 如果高度大于宽度,则以宽度为基准调整高度,使其成为正方形 |
| | | if height > width { |
| | | newHeight := width |
| | | newTop := centerY - newHeight/2 |
| | | newBottom := centerY + newHeight/2 |
| | | |
| | | // 调整后的正方形框超出图像边界时的处理 |
| | | if newTop < 0 { |
| | | // 如果上边界超出,向下移动下边界 |
| | | newBottom += -newTop |
| | | newTop = 0 |
| | | } |
| | | if newBottom > maxH { |
| | | // 如果下边界超出,向上移动上边界 |
| | | newTop -= newBottom - maxH |
| | | newBottom = maxH |
| | | } |
| | | |
| | | return &Rect{ |
| | | Left: rect.Left, |
| | | Top: newTop, |
| | | Right: rect.Right, |
| | | Bottom: newBottom, |
| | | } |
| | | } |
| | | |
| | | // 如果宽度和高度相等,则已经是正方形,直接返回原始矩形 |
| | | return rect |
| | | } |
| | | |
| | | // 长宽变为一比一,每边各扩百分之... |
| | | func EnlargeCut(rect *Rect, maxW, maxH int, enlargePer int) *Rect { |
| | | // 先把长宽变为一比一 |
| | | x0 := rect.Left |
| | | y0 := rect.Top |
| | | x1 := rect.Right |
| | | y1 := rect.Bottom |
| | | |
| | | chaZhi := (y1 - y0) - (x1 - x0) |
| | | if chaZhi > 0 { |
| | | x0 = x0 - chaZhi/2 |
| | | if x0 < 0 { |
| | | x0 = 0 |
| | | } |
| | | x1 = x1 + chaZhi/2 |
| | | if x1 > maxW { |
| | | x1 = maxW |
| | | } |
| | | } else { |
| | | y0 = y0 + chaZhi/2 |
| | | if y0 < 0 { |
| | | y0 = 0 |
| | | } |
| | | y1 = y1 - chaZhi/2 |
| | | if y1 > maxH { |
| | | y1 = maxH |
| | | } |
| | | } |
| | | |
| | | // 再把每边各扩大百分之... |
| | | enlarge := float32(enlargePer) / 100 |
| | | x0New := int((1+enlarge)*float32(x0) - enlarge*float32(x1)) |
| | | if x0New < 0 { |
| | | x0New = 0 |
| | | } |
| | | |
| | | x1New := int((1+enlarge)*float32(x1) - enlarge*float32(x0)) |
| | | if x1New > maxW { |
| | | x1New = maxW |
| | | } |
| | | |
| | | y0New := int((1+enlarge)*float32(y0) - enlarge*float32(y1)) |
| | | if y0New < 0 { |
| | | y0New = 0 |
| | | } |
| | | |
| | | y1New := int((1+enlarge)*float32(y1) - enlarge*float32(y0)) |
| | | if y1New > maxH { |
| | | y1New = maxH |
| | | } |
| | | |
| | | return &Rect{ |
| | | Left: x0New, |
| | | Top: y0New, |
| | | Right: x1New, |
| | | Bottom: y1New, |
| | | } |
| | | } |
| | | |
| | | func DataToImage(data []byte, w, h int) (image.Image, error) { |
| | | width := w |
| | | height := h |
| | | step := w * 3 |
| | | channels := 3 |
| | | |
| | | img := image.NewRGBA(image.Rect(0, 0, width, height)) |
| | | c := color.RGBA{ |
| | | R: uint8(0), |
| | | G: uint8(0), |
| | | B: uint8(0), |
| | | A: uint8(255), |
| | | } |
| | | |
| | | for y := 0; y < height; y++ { |
| | | for x := 0; x < step; x = x + channels { |
| | | c.B = data[y*step+x] |
| | | c.G = data[y*step+x+1] |
| | | c.R = data[y*step+x+2] |
| | | //if channels == 4 { |
| | | // c.A = data[y*step+x+3] |
| | | //} |
| | | img.SetRGBA(x/channels, y, c) |
| | | } |
| | | } |
| | | |
| | | return img, nil |
| | | } |
| | | |
| | | // ImageToJpeg save to jpeg data |
| | | func ImageToJpeg(img image.Image, quality *int) ([]byte, error) { |
| | | var o *jpeg.Options |
| | | |
| | | if quality != nil { |
| | | o = &jpeg.Options{Quality: *quality} |
| | | } |
| | | var b bytes.Buffer |
| | | w := bufio.NewWriter(&b) |
| | | err := jpeg.Encode(w, img, o) |
| | | if err != nil { |
| | | return nil, err |
| | | } |
| | | return b.Bytes(), err |
| | | } |
| | | |
| | | // Image2BGR img->bgr |
| | | func Image2BGR(img image.Image) []byte { |
| | | bounds := img.Bounds() |
| | | x := bounds.Dx() |
| | | y := bounds.Dy() |
| | | data := make([]byte, 0, x*y*3) |
| | | for j := bounds.Min.Y; j < bounds.Max.Y; j++ { |
| | | for i := bounds.Min.X; i < bounds.Max.X; i++ { |
| | | r, g, b, _ := img.At(i, j).RGBA() |
| | | data = append(data, byte(b>>8), byte(g>>8), byte(r>>8)) |
| | | } |
| | | } |
| | | return data |
| | | } |
| | | |
| | | func DecodeBase64ToImage(data string) image.Image { |
| | | imgData, err := base64.StdEncoding.DecodeString(data) |
| | | if err != nil { |
| | | log.Fatal(err) |
| | | } |
| | | |
| | | img, _, err := image.Decode(bytes.NewReader(imgData)) |
| | | if err != nil { |
| | | log.Fatal(err) |
| | | } |
| | | |
| | | return img |
| | | } |