104 lines
2.6 KiB
Go
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
|
|
}
|