First commit
This commit is contained in:
1 parent
4f70c7f8d5
commit
dc8dcca75e
17 files changed
+4382
-2
No files matched your search
@@ -0,0 +1,241 @@
|
||||
package drawing
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"gocv.io/x/gocv"
|
||||
)
|
||||
|
||||
const (
|
||||
CrossFlashFrames = 12
|
||||
PopupLifetime = 20
|
||||
LinePulseFrames = 12
|
||||
CountPulseFrames = 15
|
||||
BatchPulseFrames = 20
|
||||
)
|
||||
|
||||
var skeleton = [][2]int{{0, 1}, {4, 3}, {1, 2}, {3, 2}, {2, 6}, {2, 5}, {2, 7}, {7, 8}}
|
||||
var skColors = []color.RGBA{
|
||||
{0, 255, 255, 255},
|
||||
{0, 255, 255, 255},
|
||||
{255, 0, 255, 255},
|
||||
{255, 0, 255, 255},
|
||||
{0, 255, 0, 255},
|
||||
{255, 255, 0, 255},
|
||||
{0, 0, 255, 255},
|
||||
{200, 200, 0, 255},
|
||||
}
|
||||
|
||||
var cPanel = color.RGBA{28, 24, 18, 255}
|
||||
var cBorder = color.RGBA{90, 85, 75, 255}
|
||||
var cAccent = color.RGBA{255, 200, 60, 255}
|
||||
var cGreen = color.RGBA{80, 220, 100, 255}
|
||||
var cText = color.RGBA{220, 220, 220, 255}
|
||||
var cMuted = color.RGBA{150, 150, 150, 255}
|
||||
var cAyamBox = color.RGBA{0, 165, 255, 255}
|
||||
var cTalenanBox = color.RGBA{220, 120, 60, 255}
|
||||
var cLineCore = color.RGBA{180, 220, 255, 255}
|
||||
var cLineGlow = color.RGBA{100, 160, 220, 255}
|
||||
|
||||
type Popup struct {
|
||||
X, Y int
|
||||
Born int
|
||||
Text string
|
||||
}
|
||||
|
||||
func ResolveLineX(w int, lineX *int, lineXFrac float64) int {
|
||||
if lineX != nil {
|
||||
return *lineX
|
||||
}
|
||||
if lineXFrac != 0.5 {
|
||||
return int(float64(w) * lineXFrac)
|
||||
}
|
||||
return w / 2
|
||||
}
|
||||
|
||||
func CrossedLine(prevCX, cx float64, lineX int, direction string) bool {
|
||||
lx := float64(lineX)
|
||||
switch direction {
|
||||
case "ltr":
|
||||
return prevCX < lx && lx <= cx
|
||||
case "both":
|
||||
return (prevCX > lx && lx >= cx) || (prevCX < lx && lx <= cx)
|
||||
default:
|
||||
return prevCX > lx && lx >= cx
|
||||
}
|
||||
}
|
||||
|
||||
func OverlayRect(img *gocv.Mat, x1, y1, x2, y2 int, clr color.RGBA, alpha float64) {
|
||||
x1 = clampI(x1, 0, img.Cols())
|
||||
y1 = clampI(y1, 0, img.Rows())
|
||||
x2 = clampI(x2, 0, img.Cols())
|
||||
y2 = clampI(y2, 0, img.Rows())
|
||||
if x2 <= x1 || y2 <= y1 {
|
||||
return
|
||||
}
|
||||
roi := img.Region(image.Rect(x1, y1, x2, y2))
|
||||
defer roi.Close()
|
||||
overlay := gocv.NewMatWithSize(y2-y1, x2-x1, gocv.MatTypeCV8UC3)
|
||||
defer overlay.Close()
|
||||
overlay.SetTo(gocv.NewScalar(float64(clr.B), float64(clr.G), float64(clr.R), 0))
|
||||
gocv.AddWeighted(overlay, alpha, roi, 1-alpha, 0, &roi)
|
||||
}
|
||||
|
||||
func DrawPill(img *gocv.Mat, text string, x, y int, bg color.RGBA) {
|
||||
fontScale := 0.45
|
||||
size := gocv.GetTextSize(text, gocv.FontHersheySimplex, fontScale, 1)
|
||||
padX, padY := 6, 4
|
||||
|
||||
x1 := x
|
||||
y1 := y - size.Y - padY
|
||||
x2 := x + size.X + padX*2
|
||||
y2 := y + padY - 1
|
||||
|
||||
gocv.Rectangle(img, image.Rect(x1, y1, x2, y2), bg, -1)
|
||||
gocv.Rectangle(img, image.Rect(x1, y1, x2, y2), cBorder, 1)
|
||||
gocv.PutText(img, text, image.Point{X: x + padX, Y: y}, gocv.FontHersheySimplex, fontScale, cText, 1)
|
||||
}
|
||||
|
||||
func DrawCountingLine(img *gocv.Mat, lineX, h, pulse int) {
|
||||
strength := float64(pulse) / float64(max(LinePulseFrames, 1))
|
||||
glowAlpha := 0.12 + 0.18*strength
|
||||
for _, offset := range []int{14, 9, 5} {
|
||||
alpha := uint8(glowAlpha * 255)
|
||||
clr := color.RGBA{cLineGlow.R, cLineGlow.G, cLineGlow.B, alpha}
|
||||
gocv.Line(img, image.Point{X: lineX - offset, Y: 0}, image.Point{X: lineX - offset, Y: h}, clr, 1)
|
||||
gocv.Line(img, image.Point{X: lineX + offset, Y: 0}, image.Point{X: lineX + offset, Y: h}, clr, 1)
|
||||
}
|
||||
|
||||
dashLen, gap := 18, 12
|
||||
for y := 0; y < h; y += dashLen + gap {
|
||||
yEnd := y + dashLen
|
||||
if yEnd > h {
|
||||
yEnd = h
|
||||
}
|
||||
gocv.Line(img, image.Point{X: lineX, Y: y}, image.Point{X: lineX, Y: yEnd}, cLineCore, 2)
|
||||
}
|
||||
|
||||
gocv.PutText(img, "COUNT LINE", image.Point{X: lineX - 46, Y: 24}, gocv.FontHersheySimplex, 0.42, cLineCore, 1)
|
||||
}
|
||||
|
||||
func DrawHeroCount(img *gocv.Mat, lineX, h, count, pulse int) {
|
||||
text := fmt.Sprintf("%d", count)
|
||||
boost := 0.35 * float64(pulse) / float64(max(CountPulseFrames, 1))
|
||||
fontScale := 1.6 + boost
|
||||
thickness := 3
|
||||
|
||||
size := gocv.GetTextSize(text, gocv.FontHersheySimplex, fontScale, thickness)
|
||||
pad := 14
|
||||
tx := lineX - size.X/2
|
||||
ty := h/2 + size.Y/2
|
||||
|
||||
OverlayRect(img, tx-pad, ty-size.Y-pad, tx+size.X+pad, ty+pad/2, cPanel, 0.78)
|
||||
gocv.Rectangle(img, image.Rect(tx-pad, ty-size.Y-pad, tx+size.X+pad, ty+pad/2), cLineCore, 2)
|
||||
gocv.PutText(img, text, image.Point{X: tx, Y: ty}, gocv.FontHersheySimplex, fontScale, cGreen, thickness)
|
||||
}
|
||||
|
||||
func DrawHUD(img *gocv.Mat, w int, batchNum, batchCount, totalAyam int, elapsedSec float64, rate float64, cameraID string) {
|
||||
barH := 52
|
||||
OverlayRect(img, 0, 0, w, barH, cPanel, 0.72)
|
||||
gocv.Line(img, image.Point{X: 0, Y: barH}, image.Point{X: w, Y: barH}, cBorder, 1)
|
||||
|
||||
gocv.PutText(img, "BATCH", image.Point{X: 16, Y: 20}, gocv.FontHersheySimplex, 0.45, cMuted, 1)
|
||||
batchLabel := "--"
|
||||
if batchNum > 0 {
|
||||
batchLabel = fmt.Sprintf("%d", batchNum)
|
||||
}
|
||||
gocv.PutText(img, batchLabel, image.Point{X: 16, Y: 44}, gocv.FontHersheySimplex, 0.9, cAccent, 2)
|
||||
|
||||
gocv.PutText(img, "COUNT", image.Point{X: 100, Y: 20}, gocv.FontHersheySimplex, 0.45, cMuted, 1)
|
||||
gocv.PutText(img, fmt.Sprintf("%d", batchCount), image.Point{X: 100, Y: 44}, gocv.FontHersheySimplex, 0.9, cGreen, 2)
|
||||
|
||||
gocv.PutText(img, "TOTAL", image.Point{X: 190, Y: 20}, gocv.FontHersheySimplex, 0.45, cMuted, 1)
|
||||
gocv.PutText(img, fmt.Sprintf("%d", totalAyam), image.Point{X: 190, Y: 44}, gocv.FontHersheySimplex, 0.7, cText, 1)
|
||||
|
||||
gocv.PutText(img, "UPTIME", image.Point{X: 280, Y: 20}, gocv.FontHersheySimplex, 0.45, cMuted, 1)
|
||||
gocv.PutText(img, fmt.Sprintf("%.1fh", elapsedSec/3600), image.Point{X: 280, Y: 44}, gocv.FontHersheySimplex, 0.7, cText, 1)
|
||||
|
||||
gocv.PutText(img, "RATE", image.Point{X: 380, Y: 20}, gocv.FontHersheySimplex, 0.45, cMuted, 1)
|
||||
gocv.PutText(img, fmt.Sprintf("%.1f/min", rate), image.Point{X: 380, Y: 44}, gocv.FontHersheySimplex, 0.7, cAccent, 1)
|
||||
|
||||
gocv.PutText(img, fmt.Sprintf("CAM %s", cameraID), image.Point{X: w - 180, Y: 20}, gocv.FontHersheySimplex, 0.45, cMuted, 1)
|
||||
}
|
||||
|
||||
func DrawFooter(img *gocv.Mat, w, h, frameIdx int, liveTag string) {
|
||||
barH := 28
|
||||
OverlayRect(img, 0, h-barH, w, h, cPanel, 0.55)
|
||||
gocv.PutText(img, fmt.Sprintf("%s | Frame %d", liveTag, frameIdx), image.Point{X: 12, Y: h - 9}, gocv.FontHersheySimplex, 0.45, cMuted, 1)
|
||||
}
|
||||
|
||||
func DrawSkeleton(img *gocv.Mat, kpts [][2]float64) {
|
||||
for i, pair := range skeleton {
|
||||
a, b := pair[0], pair[1]
|
||||
if a >= len(kpts) || b >= len(kpts) {
|
||||
continue
|
||||
}
|
||||
xa, ya := int(kpts[a][0]), int(kpts[a][1])
|
||||
xb, yb := int(kpts[b][0]), int(kpts[b][1])
|
||||
if xa > 0 && ya > 0 && xb > 0 && yb > 0 {
|
||||
clr := cGreen
|
||||
if i < len(skColors) {
|
||||
clr = skColors[i]
|
||||
}
|
||||
gocv.Line(img, image.Point{X: xa, Y: ya}, image.Point{X: xb, Y: yb}, clr, 3)
|
||||
}
|
||||
}
|
||||
for _, kp := range kpts {
|
||||
x, y := int(kp[0]), int(kp[1])
|
||||
if x > 0 && y > 0 {
|
||||
gocv.Circle(img, image.Point{X: x, Y: y}, 6, color.RGBA{255, 255, 255, 255}, -1)
|
||||
gocv.Circle(img, image.Point{X: x, Y: y}, 6, color.RGBA{40, 40, 40, 255}, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func DrawPopups(img *gocv.Mat, popups []Popup, frameIdx int) []Popup {
|
||||
var alive []Popup
|
||||
for _, pop := range popups {
|
||||
age := frameIdx - pop.Born
|
||||
if age > PopupLifetime {
|
||||
continue
|
||||
}
|
||||
alive = append(alive, pop)
|
||||
fade := 1.0 - float64(age)/PopupLifetime
|
||||
yy := pop.Y - int(float64(age)*1.8)
|
||||
clr := color.RGBA{
|
||||
uint8(float64(cGreen.R) * fade),
|
||||
uint8(float64(cGreen.G) * fade),
|
||||
uint8(float64(cGreen.B) * fade),
|
||||
255,
|
||||
}
|
||||
gocv.PutText(img, pop.Text, image.Point{X: pop.X, Y: yy}, gocv.FontHersheySimplex, 0.7, clr, 2)
|
||||
}
|
||||
return alive
|
||||
}
|
||||
|
||||
func DrawBatchBanner(img *gocv.Mat, w, batchNum, pulse int) {
|
||||
if pulse <= 0 {
|
||||
return
|
||||
}
|
||||
text := fmt.Sprintf("NEW BATCH %d", batchNum)
|
||||
size := gocv.GetTextSize(text, gocv.FontHersheySimplex, 0.8, 2)
|
||||
x1 := w/2 - size.X/2 - 16
|
||||
y1 := 62
|
||||
x2 := w/2 + size.X/2 + 16
|
||||
y2 := 62 + size.Y + 20
|
||||
OverlayRect(img, x1, y1, x2, y2, cPanel, 0.7)
|
||||
gocv.Rectangle(img, image.Rect(x1, y1, x2, y2), cAccent, 2)
|
||||
gocv.PutText(img, text, image.Point{X: w/2 - size.X/2, Y: 62 + size.Y + 4}, gocv.FontHersheySimplex, 0.8, cAccent, 2)
|
||||
}
|
||||
|
||||
func NowStr() string {
|
||||
return time.Now().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
func clampI(v, lo, hi int) int {
|
||||
return int(math.Max(float64(lo), math.Min(float64(hi), float64(v))))
|
||||
}
|
||||
Reference in new issue
Block a user