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
|
}
|