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
128 lines
2.5 KiB
Go
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
|
|
}
|