DFT 带阻滤波器
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package rrc
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/airbusgeo/godal"
|
||||
@@ -9,11 +10,14 @@ import (
|
||||
)
|
||||
|
||||
func TestFilter(t *testing.T) {
|
||||
os.MkdirAll("/Users/lan/workspace/sjy01/image-proc/data/fft", 0755)
|
||||
godal.RegisterAll()
|
||||
tif := "/Users/lan/workspace/sjy01/image-proc/data/052022-no-rrc/010/PAN/SJY01_PAN_20240520_115428_052022_103_010_L1A.tiff"
|
||||
tif = "/Users/lan/workspace/sjy01/image-proc/data/051622/007/PAN/SJY01_PAN_20240516_101236_051622_096_007_L1A.tiff"
|
||||
// tif = "/Users/lan/workspace/sjy01/image-proc/data/051622/007/PAN/SJY01_PAN_20240516_101236_051622_096_007_L1A.tiff"
|
||||
// tif = "/Users/lan/workspace/sjy01/image-proc/data/060622-mm/003/PAN/SJY01_PAN_20240606_012004_060622_065_003_L1A.tiff"
|
||||
|
||||
// tif = "/Users/lan/workspace/sjy01/image-proc/data/052022-no-rrc/010/MSS/SJY01_MSS_20240520_115428_052022_103_010_L1A.tiff"
|
||||
tif = "/Users/lan/workspace/sjy01/image-proc/data/052022-no-rrc/006/PAN/SJY01_PAN_20240520_115428_052022_103_006_L1A.tiff"
|
||||
tif = "/Users/lan/workspace/sjy01/image-proc/data/060622-RRC/002/PAN/SJY01_PAN_20240606_012004_060622_065_002_L1A.tiff"
|
||||
ds, err := godal.Open(tif)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -37,7 +41,23 @@ func TestFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
m0 := HFNoiseFilter(img, float64(img.Cols())*0.45)
|
||||
m0 := HFNoiseFilter(img, float64(img.Cols())*0.49)
|
||||
defer m0.Close()
|
||||
utils.SavePanToGDALGTiff(m0, 0, 0, "/Users/lan/workspace/sjy01/image-proc/data/fft/filtered.tiff", 1.3)
|
||||
}
|
||||
|
||||
func TestMomentMatching(t *testing.T) {
|
||||
raw := "/Users/lan/workspace/sjy01/preprocessing/demo/output/051622/SJY01_PAN_20240516_101236_051622_096.RAW"
|
||||
data, err := os.ReadFile(raw)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
height := len(data) / (9344 * 2)
|
||||
img, err := gocv.NewMatFromBytes(height, 9344, gocv.MatTypeCV16U, data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
DoMomentMatching(img)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
package rrc
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func searchVL(V []float64, Sik float64) int {
|
||||
left, right := 0, len(V)-2
|
||||
|
||||
@@ -31,16 +24,3 @@ func searchVL(V []float64, Sik float64) int {
|
||||
// Return -1 if no such position is found
|
||||
return -1
|
||||
}
|
||||
|
||||
var lastTotalFreed uint64
|
||||
|
||||
func printMemStats() {
|
||||
var m runtime.MemStats
|
||||
runtime.ReadMemStats(&m)
|
||||
log.Printf("[Memory] Alloc = %v TotalAlloc=%v Just Freed = %v Sys = %v NumGc=%v",
|
||||
humanize.Bytes(m.Alloc),
|
||||
humanize.Bytes(m.TotalAlloc),
|
||||
humanize.Bytes(((m.TotalAlloc - m.Alloc) - lastTotalFreed)),
|
||||
humanize.Bytes(m.Sys), m.NumGC)
|
||||
lastTotalFreed = m.TotalAlloc - m.Alloc
|
||||
}
|
||||
|
||||
@@ -4,69 +4,74 @@ import (
|
||||
"image"
|
||||
"math"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gocv.io/x/gocv"
|
||||
"starwiz.cn/sjy01/image-proc/pkg/config"
|
||||
)
|
||||
|
||||
// filter high frequence noise
|
||||
func HFNoiseFilter(img gocv.Mat, radius float64) gocv.Mat {
|
||||
logrus.Println("applying hf noise filter...")
|
||||
imgFloat := gocv.NewMat()
|
||||
img.ConvertTo(&imgFloat, gocv.MatTypeCV32F)
|
||||
|
||||
// rows, cols := img.Rows(), img.Cols()
|
||||
// utils.SavePanToGDALGTiff(img, 0, 0, "/Users/lan/workspace/sjy01/image-proc/data/fft/original.tiff", 1.3)
|
||||
minGrayVal, maxGrayVal, _, _ := gocv.MinMaxLoc(img)
|
||||
log.Println("minGrayVal:", minGrayVal, "maxGrayVal:", maxGrayVal)
|
||||
|
||||
// 将图像转换为32位浮点型
|
||||
img32f := gocv.NewMat()
|
||||
img.ConvertTo(&img32f, gocv.MatTypeCV32F)
|
||||
defer img32f.Close()
|
||||
|
||||
// 扩展图像尺寸,使其宽高为2的幂次方
|
||||
// optRows := gocv.GetOptimalDFTSize(img32f.Rows())
|
||||
// optCols := gocv.GetOptimalDFTSize(img32f.Cols())
|
||||
// padded := gocv.NewMatWithSize(optRows, optCols, gocv.MatTypeCV32F)
|
||||
// defer padded.Close()
|
||||
// gocv.CopyMakeBorder(img32f, &padded,
|
||||
// 0, optRows-img32f.Rows(), 0, optCols-img32f.Cols(),
|
||||
// gocv.BorderConstant, color.RGBA{0, 0, 0, 0})
|
||||
|
||||
// 获取图像尺寸
|
||||
rows, cols := img32f.Rows(), img32f.Cols()
|
||||
|
||||
// zeros := gocv.NewMatFromScalar(gocv.NewScalar(0, 0, 0, 0), gocv.MatTypeCV32F)
|
||||
|
||||
// 执行 DFT
|
||||
DFT := gocv.NewMat()
|
||||
defer DFT.Close()
|
||||
|
||||
planes := gocv.NewMatWithSize(rows, cols, gocv.MatTypeCV32F)
|
||||
img32f.ConvertTo(&planes, gocv.MatTypeCV32F)
|
||||
gocv.Merge([]gocv.Mat{planes, gocv.Zeros(rows, cols, gocv.MatTypeCV32F)}, &DFT)
|
||||
gocv.Merge([]gocv.Mat{img32f, gocv.Zeros(rows, cols, gocv.MatTypeCV32F)}, &DFT)
|
||||
gocv.DFT(DFT, &DFT, gocv.DftComplexOutput)
|
||||
fftshift(&DFT)
|
||||
// 将复数部分分离并计算振幅光谱
|
||||
pss := gocv.Split(DFT)
|
||||
// SaveDFT(DFT, "/Users/lan/workspace/sjy01/image-proc/data/fft/dft.tiff")
|
||||
|
||||
// 应用滤波器
|
||||
for y := 0; y < rows; y++ {
|
||||
for x := 0; x < cols; x++ {
|
||||
if math.Sqrt(math.Pow(float64(x-cols/2), 2)+math.Pow(float64(y-rows/2), 2)) > float64(radius) {
|
||||
// if math.Abs(float64(x-cols/2)) < float64(radius) || math.Abs(float64(y-rows/2)) < float64(radius) {
|
||||
pss[0].SetFloatAt(y, x, 0.0)
|
||||
pss[1].SetFloatAt(y, x, 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
gocv.Merge(pss, &DFT)
|
||||
// SaveDFT(DFT, "/Users/lan/workspace/sjy01/image-proc/data/fft/dft_filtered.tiff")
|
||||
// 中心化频谱
|
||||
fftshift(&DFT)
|
||||
|
||||
// visualizeDFT(DFT, "/Users/lan/workspace/sjy01/image-proc/data/fft/dft.jpg")
|
||||
|
||||
// 低通滤波器
|
||||
pss := gocv.Split(DFT)
|
||||
defer pss[0].Close()
|
||||
defer pss[1].Close()
|
||||
filter := gocv.NewMatWithSize(rows, cols, gocv.MatTypeCV32F)
|
||||
defer filter.Close()
|
||||
// createLowPassFilter(&filter, radius)
|
||||
// createBandBlockFilter(&filter, radius, radius+0.015*float64(cols))
|
||||
createFixedBlockFilter(&filter, config.GCONFIG.Radiation.HFBandStopWidth)
|
||||
gocv.Multiply(pss[0], filter, &pss[0])
|
||||
gocv.Multiply(pss[1], filter, &pss[1])
|
||||
gocv.Merge(pss, &DFT)
|
||||
|
||||
// visualizeDFT(DFT, "/Users/lan/workspace/sjy01/image-proc/data/fft/dft_filtered.jpg")
|
||||
|
||||
// 频谱位置还原
|
||||
fftshift(&DFT)
|
||||
|
||||
// 执行 IDFT
|
||||
IDFT := gocv.NewMat()
|
||||
gocv.DFT(DFT, &IDFT, gocv.DftInverse|gocv.DftRealOutput)
|
||||
|
||||
// 归一化到0-65535
|
||||
gocv.Normalize(IDFT, &IDFT, 0, 8192, gocv.NormMinMax)
|
||||
// 归一化到原始图像范围
|
||||
gocv.Normalize(IDFT, &IDFT, float64(minGrayVal), float64(maxGrayVal), gocv.NormMinMax)
|
||||
IDFT.ConvertTo(&IDFT, gocv.MatTypeCV16U)
|
||||
|
||||
// 保存 IDFT 结果
|
||||
return IDFT
|
||||
}
|
||||
|
||||
// 创建低通滤波器
|
||||
func createLowPassFilter(filter *gocv.Mat, radius int) {
|
||||
func createLowPassFilter(filter *gocv.Mat, radius float64) {
|
||||
rows, cols := filter.Rows(), filter.Cols()
|
||||
center := image.Pt(cols/2, rows/2)
|
||||
for y := 0; y < rows; y++ {
|
||||
@@ -81,6 +86,38 @@ func createLowPassFilter(filter *gocv.Mat, radius int) {
|
||||
}
|
||||
}
|
||||
|
||||
// 创建带阻滤波器
|
||||
func createBandBlockFilter(filter *gocv.Mat, radius0, radius1 float64) {
|
||||
rows, cols := filter.Rows(), filter.Cols()
|
||||
center := image.Pt(cols/2, rows/2)
|
||||
for y := 0; y < rows; y++ {
|
||||
for x := 0; x < cols; x++ {
|
||||
dist := math.Sqrt(math.Pow(float64(x-center.X), 2) + math.Pow(float64(y-center.Y), 2))
|
||||
if dist <= float64(radius0) || dist >= float64(radius1) {
|
||||
filter.SetFloatAt(y, x, 1.0)
|
||||
} else {
|
||||
filter.SetFloatAt(y, x, 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createFixedBlockFilter(filter *gocv.Mat, ksize int) {
|
||||
rows, cols := filter.Rows(), filter.Cols()
|
||||
center := image.Pt(cols/2, rows/2)
|
||||
for y := 0; y < rows; y++ {
|
||||
for x := 0; x < cols; x++ {
|
||||
if (x >= center.X-ksize/2 && x <= center.X+ksize/2 &&
|
||||
(y < int(ksize) || y >= rows-int(ksize))) ||
|
||||
((x < ksize || x > cols-ksize) && (y >= center.Y-ksize/2 && y <= center.Y+ksize/2)) {
|
||||
filter.SetFloatAt(y, x, 0.0)
|
||||
} else {
|
||||
filter.SetFloatAt(y, x, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fftshift 将频率直流分量移动到图像中心
|
||||
func fftshift(input *gocv.Mat) {
|
||||
cx, cy := input.Cols()/2, input.Rows()/2
|
||||
@@ -100,19 +137,25 @@ func fftshift(input *gocv.Mat) {
|
||||
tmp.CopyTo(&q2)
|
||||
}
|
||||
|
||||
func SaveDFT(DFT gocv.Mat, file string) {
|
||||
func visualizeDFT(DFT gocv.Mat, file string) {
|
||||
pss := gocv.Split(DFT)
|
||||
planes := gocv.NewMat()
|
||||
defer planes.Close()
|
||||
|
||||
gocv.Magnitude(pss[0], pss[1], &planes)
|
||||
// 进行对数尺度变换,便于可视化
|
||||
ones := gocv.NewMatFromScalar(gocv.NewScalar(1, 0, 0, 0), gocv.MatTypeCV64F)
|
||||
defer ones.Close()
|
||||
gocv.Add(planes, ones, &planes)
|
||||
gocv.Log(planes, &planes)
|
||||
|
||||
// 归一化
|
||||
gocv.Normalize(planes, &planes, 0, 1, gocv.NormMinMax)
|
||||
gocv.Normalize(planes, &planes, 0, 255, gocv.NormMinMax)
|
||||
planes.ConvertTo(&planes, gocv.MatTypeCV8U)
|
||||
out := gocv.NewMatWithSize(planes.Rows()/4, planes.Cols()/4, gocv.MatTypeCV8U)
|
||||
defer out.Close()
|
||||
gocv.Resize(planes, &out, image.Point{planes.Cols() / 4, planes.Rows() / 4}, 0, 0, gocv.InterpolationLinear)
|
||||
|
||||
// 保存DFT结果
|
||||
gocv.IMWrite(file, planes)
|
||||
|
||||
gocv.IMWrite(file, out)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"gocv.io/x/gocv"
|
||||
)
|
||||
|
||||
// FIXME: still need to implement the HFNoiseFilterFFT function
|
||||
|
||||
func HFNoiseFilterFFT(input gocv.Mat, radius int) gocv.Mat {
|
||||
// 将灰度图像转换为浮点数数组
|
||||
height := input.Rows()
|
||||
|
||||
@@ -10,10 +10,11 @@ import (
|
||||
// Destriping multisensor imagery with moment matching [Gadallah, 2000]
|
||||
func DoMomentMatching(originalImg gocv.Mat) {
|
||||
probes := originalImg.Cols()
|
||||
log.Printf("do moment matching for %d probes", probes)
|
||||
log.Printf("do moment matching for %d probes, %d rows", probes, originalImg.Rows())
|
||||
// 第i个探元的像元均值和标准差
|
||||
means := make([]float64, probes)
|
||||
stds := make([]float64, probes)
|
||||
|
||||
// 计算每个探元的均值和标准差
|
||||
for x := 0; x < originalImg.Cols(); x++ {
|
||||
var total int64
|
||||
@@ -34,16 +35,17 @@ func DoMomentMatching(originalImg gocv.Mat) {
|
||||
}
|
||||
|
||||
// 列参考值和列参考标准差
|
||||
var mu float64
|
||||
var sig float64
|
||||
var mu, sig float64
|
||||
for x := 0; x < probes; x++ {
|
||||
mu += means[x]
|
||||
sig += stds[x]
|
||||
}
|
||||
|
||||
mu = mu / float64(probes)
|
||||
sig = sig / float64(probes)
|
||||
|
||||
log.Printf("mean reference value: %f, std reference value: %f", mu, sig)
|
||||
|
||||
// 修正 DN_adjusted[i] = (DN[i] - means[i]) *sig/stds[i]+mu
|
||||
for x := 0; x < originalImg.Cols(); x++ {
|
||||
for y := 0; y < originalImg.Rows(); y++ {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gocv.io/x/gocv"
|
||||
"starwiz.cn/sjy01/image-proc/pkg/utils"
|
||||
)
|
||||
|
||||
// Relative Radiation Correction
|
||||
@@ -49,7 +50,7 @@ func (rrc *RRC) Close() {}
|
||||
|
||||
// 统计探元灰度的累积概率密度
|
||||
func (rrc *RRC) StatisticalPAN(dsfile string) error {
|
||||
printMemStats()
|
||||
utils.PrintMemStats()
|
||||
f, err := os.Open(dsfile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -100,7 +101,7 @@ func (rrc *RRC) StatisticalPAN(dsfile string) error {
|
||||
rrc.Histograms[0].sum([]*ProbeHistogram{&hist})
|
||||
hist.free()
|
||||
runtime.GC()
|
||||
printMemStats()
|
||||
utils.PrintMemStats()
|
||||
|
||||
mutex.Unlock()
|
||||
|
||||
@@ -118,7 +119,7 @@ func (rrc *RRC) StatisticalPAN(dsfile string) error {
|
||||
}
|
||||
|
||||
func (rrc *RRC) StatisticalMSS(dsfile string) error {
|
||||
printMemStats()
|
||||
utils.PrintMemStats()
|
||||
f, err := os.Open(dsfile)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -179,7 +180,7 @@ func (rrc *RRC) StatisticalMSS(dsfile string) error {
|
||||
rrc.Histograms[i+1].sum([]*ProbeHistogram{&hist})
|
||||
hist.free()
|
||||
runtime.GC()
|
||||
printMemStats()
|
||||
utils.PrintMemStats()
|
||||
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user