cloud cover by kmeans
This commit is contained in:
152
pkg/cloud-cover/k_means.go
Normal file
152
pkg/cloud-cover/k_means.go
Normal file
@@ -0,0 +1,152 @@
|
||||
package cloudcover
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
|
||||
"math"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
k = 2 // 聚类数目,2表示云和非云
|
||||
maxIters = 100 // 最大迭代次数
|
||||
tolerance = 1.0 // 聚类中心变化容忍度
|
||||
)
|
||||
|
||||
type Pixel struct {
|
||||
r, g, b float64
|
||||
}
|
||||
|
||||
func CloudPercentByKMeans(imagePath string) float64 {
|
||||
file, err := os.Open(imagePath)
|
||||
if err != nil {
|
||||
log.Errorf("无法打开图像: %v", err)
|
||||
return 0.0
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
img, _, err := image.Decode(file)
|
||||
if err != nil {
|
||||
log.Errorf("无法解码图像: %v", err)
|
||||
return 0.0
|
||||
}
|
||||
|
||||
bounds := img.Bounds()
|
||||
width, height := bounds.Max.X, bounds.Max.Y
|
||||
pixels := make([]Pixel, 0, width*height)
|
||||
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
r, g, b, _ := img.At(x, y).RGBA()
|
||||
pixels = append(pixels, Pixel{
|
||||
r: float64(r >> 8),
|
||||
g: float64(g >> 8),
|
||||
b: float64(b >> 8),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
centroids := initializeCentroids(pixels)
|
||||
assignments := make([]int, len(pixels))
|
||||
|
||||
for iter := 0; iter < maxIters; iter++ {
|
||||
for i, pixel := range pixels {
|
||||
assignments[i] = nearestCentroid(pixel, centroids)
|
||||
}
|
||||
|
||||
newCentroids := updateCentroids(pixels, assignments)
|
||||
if hasConverged(centroids, newCentroids) {
|
||||
log.Printf("在第 %d 次迭代后收敛\n", iter+1)
|
||||
break
|
||||
}
|
||||
centroids = newCentroids
|
||||
}
|
||||
|
||||
cloudPixels := 0
|
||||
totalPixels := width * height
|
||||
|
||||
// 新增:检查哪个聚类的平均亮度更高,确保白色表示云
|
||||
cloudCluster := 0
|
||||
if centroids[1].r+centroids[1].g+centroids[1].b > centroids[0].r+centroids[0].g+centroids[0].b {
|
||||
cloudCluster = 1
|
||||
}
|
||||
|
||||
outputImg := image.NewGray(bounds)
|
||||
for y := 0; y < height; y++ {
|
||||
for x := 0; x < width; x++ {
|
||||
i := y*width + x
|
||||
if assignments[i] == cloudCluster {
|
||||
outputImg.SetGray(x, y, color.Gray{Y: 255}) // 云区域
|
||||
cloudPixels++
|
||||
} else {
|
||||
outputImg.SetGray(x, y, color.Gray{Y: 0}) // 非云区域
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cloudRatio := float64(cloudPixels) / float64(totalPixels)
|
||||
return cloudRatio
|
||||
}
|
||||
|
||||
func initializeCentroids(pixels []Pixel) []Pixel {
|
||||
centroids := make([]Pixel, k)
|
||||
for i := range centroids {
|
||||
centroids[i] = pixels[rand.Intn(len(pixels))]
|
||||
}
|
||||
return centroids
|
||||
}
|
||||
|
||||
func nearestCentroid(pixel Pixel, centroids []Pixel) int {
|
||||
minDist := math.Inf(1)
|
||||
bestIndex := 0
|
||||
for i, c := range centroids {
|
||||
dist := math.Sqrt(math.Pow(pixel.r-c.r, 2) + math.Pow(pixel.g-c.g, 2) + math.Pow(pixel.b-c.b, 2))
|
||||
if dist < minDist {
|
||||
minDist = dist
|
||||
bestIndex = i
|
||||
}
|
||||
}
|
||||
return bestIndex
|
||||
}
|
||||
|
||||
func updateCentroids(pixels []Pixel, assignments []int) []Pixel {
|
||||
sums := make([]Pixel, k)
|
||||
counts := make([]int, k)
|
||||
|
||||
for i, assignment := range assignments {
|
||||
sums[assignment].r += pixels[i].r
|
||||
sums[assignment].g += pixels[i].g
|
||||
sums[assignment].b += pixels[i].b
|
||||
counts[assignment]++
|
||||
}
|
||||
|
||||
newCentroids := make([]Pixel, k)
|
||||
for i := range newCentroids {
|
||||
if counts[i] > 0 {
|
||||
newCentroids[i].r = sums[i].r / float64(counts[i])
|
||||
newCentroids[i].g = sums[i].g / float64(counts[i])
|
||||
newCentroids[i].b = sums[i].b / float64(counts[i])
|
||||
} else {
|
||||
newCentroids[i] = sums[i]
|
||||
}
|
||||
}
|
||||
|
||||
return newCentroids
|
||||
}
|
||||
|
||||
func hasConverged(oldCentroids, newCentroids []Pixel) bool {
|
||||
for i := range oldCentroids {
|
||||
if math.Abs(oldCentroids[i].r-newCentroids[i].r) > tolerance ||
|
||||
math.Abs(oldCentroids[i].g-newCentroids[i].g) > tolerance ||
|
||||
math.Abs(oldCentroids[i].b-newCentroids[i].b) > tolerance {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -24,6 +24,10 @@ const (
|
||||
GDALPansharpen
|
||||
IHS
|
||||
GIHS
|
||||
WeightR = 1
|
||||
WeightG = 0.75
|
||||
WeightB = 0.55
|
||||
WeightNIR = 0.85
|
||||
)
|
||||
|
||||
func Pansharpen(panTif, mssTif, fusTif string, method PansharpenMethod, weight float32) error {
|
||||
|
||||
Reference in New Issue
Block a user