Files
lone-services/pkg/facepp/yolo.go
T
zzw 747ab25325
CI / changes (push) Successful in 1m31s
CI / ad (push) Successful in 26s
CI / admin (push) Successful in 17s
CI / analysis (push) Successful in 24s
CI / bff (push) Successful in 16s
CI / chore (push) Successful in 14s
CI / equipment (push) Successful in 15s
CI / express (push) Successful in 13s
CI / factory (push) Successful in 13s
CI / message (push) Successful in 10s
CI / order (push) Successful in 10s
CI / payment (push) Successful in 11s
CI / percutaneous (push) Successful in 11s
CI / product (push) Successful in 12s
CI / record (push) Successful in 12s
CI / sale (push) Successful in 11s
CI / store (push) Successful in 11s
CI / support (push) Successful in 11s
CI / task (push) Successful in 10s
CI / user (push) Successful in 18s
CI / water (push) Successful in 13s
CI / wecom (push) Successful in 13s
CI / ws (push) Successful in 6s
feat: add facepp
2026-10-27 17:17:43 +08:00

128 lines
2.5 KiB
Go

package facepp
import (
"encoding/json"
"fmt"
"strings"
)
const (
YOLOClassAcne = 0
YOLOClassMole = 1
YOLOClassBrownSpot = 2
)
type yoloField struct {
Key string
ClassID int
}
var yoloRectangleFields = []yoloField{
{Key: "acne_pustule", ClassID: YOLOClassAcne},
{Key: "closed_comedones", ClassID: YOLOClassAcne},
{Key: "acne_mark", ClassID: YOLOClassAcne},
{Key: "acne", ClassID: YOLOClassAcne},
{Key: "acne_nodule", ClassID: YOLOClassAcne},
{Key: "mole", ClassID: YOLOClassMole},
{Key: "brown_spot", ClassID: YOLOClassBrownSpot},
}
type yoloBox struct {
ClassID int
XC float64
YC float64
W float64
H float64
}
type yoloRect struct {
Top float64 `json:"top"`
Left float64 `json:"left"`
Width float64 `json:"width"`
Height float64 `json:"height"`
}
type yoloLocated struct {
Rectangle []yoloRect `json:"rectangle"`
Rect []yoloRect `json:"rect"`
}
func ResultToYOLO(result json.RawMessage, imgW, imgH int) (string, error) {
if len(result) == 0 || string(result) == "null" {
return "", nil
}
if imgW <= 0 || imgH <= 0 {
return "", fmt.Errorf("facepp: invalid image size %dx%d", imgW, imgH)
}
var root map[string]json.RawMessage
if err := json.Unmarshal(result, &root); err != nil {
return "", err
}
imgWf := float64(imgW)
imgHf := float64(imgH)
var boxes []yoloBox
for _, field := range yoloRectangleFields {
raw, ok := root[field.Key]
if !ok {
continue
}
for _, rect := range locatedRectangles(raw) {
box, ok := pixelToYOLO(field.ClassID, rect.Left, rect.Top, rect.Width, rect.Height, imgWf, imgHf)
if !ok {
continue
}
boxes = append(boxes, box)
}
}
var b strings.Builder
for _, box := range boxes {
fmt.Fprintf(&b, "%d %.6f %.6f %.6f %.6f\n", box.ClassID, box.XC, box.YC, box.W, box.H)
}
return b.String(), nil
}
func locatedRectangles(raw json.RawMessage) []yoloRect {
var obj yoloLocated
if err := json.Unmarshal(raw, &obj); err != nil {
return nil
}
if len(obj.Rectangle) > 0 {
return obj.Rectangle
}
return obj.Rect
}
func pixelToYOLO(classID int, xMin, yMin, boxW, boxH, imgW, imgH float64) (yoloBox, bool) {
xMax := xMin + boxW
yMax := yMin + boxH
if xMin < 0 {
xMin = 0
}
if yMin < 0 {
yMin = 0
}
if xMax > imgW {
xMax = imgW
}
if yMax > imgH {
yMax = imgH
}
boxW = xMax - xMin
boxH = yMax - yMin
if boxW <= 0 || boxH <= 0 {
return yoloBox{}, false
}
return yoloBox{
ClassID: classID,
XC: (xMin + boxW/2) / imgW,
YC: (yMin + boxH/2) / imgH,
W: boxW / imgW,
H: boxH / imgH,
}, true
}