2025-09-21 03:51:25 +08:00

104 lines
2.6 KiB
Go

package radarfetch
import (
"fmt"
"image"
"image/color"
"image/png"
"os"
"path/filepath"
)
// AttachClusterPNGs renders a tiny PNG for each cluster by flood-filling
// from its centroid on the thresholded mask, cropping to the cluster bbox.
// It writes files to outDir/clusters/cluster-<id>.png and returns updated clusters
// with PNG field filled.
func AttachClusterPNGs(grid [][]*float64, thr float64, clusters []Cluster, outDir string) ([]Cluster, error) {
const W, H = 256, 256
if len(grid) != H || (len(grid) > 0 && len(grid[0]) != W) {
return clusters, nil
}
// precompute threshold mask
mask := make([][]bool, H)
for r := 0; r < H; r++ {
mask[r] = make([]bool, W)
for c := 0; c < W; c++ {
if grid[r][c] == nil {
continue
}
if *grid[r][c] >= thr {
mask[r][c] = true
}
}
}
outDir = filepath.Join(outDir, "clusters")
_ = os.MkdirAll(outDir, 0o755)
for i := range clusters {
cl := &clusters[i]
// BFS from (Row,Col) within mask to reconstruct membership
r0, c0 := cl.Row, cl.Col
if r0 < 0 || r0 >= H || c0 < 0 || c0 >= W || !mask[r0][c0] {
// skip if centroid not on mask
continue
}
minR, minC := cl.MinRow, cl.MinCol
maxR, maxC := cl.MaxRow, cl.MaxCol
w := maxC - minC + 1
h := maxR - minR + 1
if w <= 0 || h <= 0 || w > W || h > H {
continue
}
img := image.NewRGBA(image.Rect(0, 0, w, h))
// init transparent
for y := 0; y < h; y++ {
for x := 0; x < w; x++ {
img.SetRGBA(x, y, color.RGBA{0, 0, 0, 0})
}
}
// flood fill within bbox
vis := make([][]bool, H)
for r := 0; r < H; r++ {
vis[r] = make([]bool, W)
}
stack := [][2]int{{r0, c0}}
vis[r0][c0] = true
dirs := [][2]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}}
for len(stack) > 0 {
cur := stack[len(stack)-1]
stack = stack[:len(stack)-1]
rr, cc := cur[0], cur[1]
if rr < minR || rr > maxR || cc < minC || cc > maxC {
continue
}
// paint
dbz := grid[rr][cc]
if dbz != nil {
col := colorForDBZ(*dbz)
img.SetRGBA(cc-minC, rr-minR, col)
}
for _, d := range dirs {
nr, nc := rr+d[0], cc+d[1]
if nr < 0 || nr >= H || nc < 0 || nc >= W {
continue
}
if vis[nr][nc] || !mask[nr][nc] {
continue
}
vis[nr][nc] = true
stack = append(stack, [2]int{nr, nc})
}
}
// write file
name := fmt.Sprintf("cluster-%d.png", cl.ID)
p := filepath.Join(outDir, name)
f, err := os.Create(p)
if err != nil {
continue
}
_ = png.Encode(f, img)
_ = f.Close()
cl.PNG = filepath.Join(filepath.Base(outDir), name)
}
return clusters, nil
}