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 }