DFT 带阻滤波器

This commit is contained in:
nuknal
2024-06-21 12:47:14 +08:00
parent e13038474a
commit d99b8a9740
16 changed files with 292 additions and 309 deletions

View File

@@ -113,6 +113,13 @@ func (r *Registrator) SetSceneBoundary(scene *Scene) (topLeft, bottomRight orb.P
xResolution := W0 / float64(scene.Width)
yResolution := H0 / float64(scene.Height)
scene.Meta.Gsd = math.Min(xResolution, yResolution)
// FIXME: 临时设置分辨率
if scene.Meta.Gsd < 2 {
scene.Meta.Gsd = 1.3
} else {
scene.Meta.Gsd = 5.2
}
log.Debug("resolution x: ", xResolution)
log.Debug("resolution y: ", yResolution)

View File

@@ -1,78 +0,0 @@
package producer
import (
"image"
"math"
log "github.com/sirupsen/logrus"
"gocv.io/x/gocv"
)
func PANFilter(panImage16UC1 gocv.Mat) gocv.Mat {
log.Println("Applying PAN filter...")
// 转换为浮点数类型进行傅里叶变换
imgFloat := gocv.NewMat()
panImage16UC1.ConvertTo(&imgFloat, gocv.MatTypeCV32F)
// 使用傅里叶变换
planes := gocv.NewMat()
gocv.Merge([]gocv.Mat{imgFloat, gocv.NewMatWithSize(imgFloat.Rows(), imgFloat.Cols(), gocv.MatTypeCV32F)}, &planes)
dft := gocv.NewMat()
gocv.DFT(planes, &dft, gocv.DftComplexOutput)
// 转换DFT图像使低频分量位于中心
dftShifted := shiftDFT(dft)
// 创建掩膜
rows, cols := panImage16UC1.Rows(), panImage16UC1.Cols()
crow, ccol := rows/2, cols/2
mask := gocv.NewMatWithSize(rows, cols, gocv.MatTypeCV32FC2)
mask.SetTo(gocv.NewScalar(1.0, 1.0, 0.0, 0.0))
r := 30 // 调整这个参数来改变滤波效果
for i := 0; i < rows; i++ {
for j := 0; j < cols; j++ {
if math.Abs(float64(i-crow)) <= float64(r) && math.Abs(float64(j-ccol)) <= float64(r) {
mask.SetFloatAt(i, j*2, 0)
mask.SetFloatAt(i, j*2+1, 0)
}
}
}
// 应用掩膜并进行反向DFT
filtered := gocv.NewMat()
gocv.MulSpectrums(dftShifted, mask, &filtered, 0)
filteredShifted := shiftDFT(filtered)
idft := gocv.NewMat()
gocv.DFT(filteredShifted, &idft, gocv.DftInverse|gocv.DftRealOutput)
// 标准化图像到0-65535范围
gocv.Normalize(idft, &idft, 0, 65535, gocv.NormMinMax)
idft.ConvertTo(&idft, gocv.MatTypeCV16U)
return idft
}
// shiftDFT 将DFT结果进行频谱平移
func shiftDFT(src gocv.Mat) gocv.Mat {
rows, cols := src.Rows(), src.Cols()
crow, ccol := rows/2, cols/2
q0 := src.Region(image.Rect(0, 0, ccol, crow))
q1 := src.Region(image.Rect(ccol, 0, cols, crow))
q2 := src.Region(image.Rect(0, crow, ccol, rows))
q3 := src.Region(image.Rect(ccol, crow, cols, rows))
tmp := gocv.NewMatWithSize(crow, ccol, src.Type())
q0.CopyTo(&tmp)
q3.CopyTo(&q0)
tmp.CopyTo(&q3)
q1.CopyTo(&tmp)
q2.CopyTo(&q1)
tmp.CopyTo(&q2)
return src
}

View File

@@ -10,10 +10,11 @@ import (
"github.com/airbusgeo/godal"
log "github.com/sirupsen/logrus"
"gocv.io/x/gocv"
"starwiz.cn/sjy01/image-proc/pkg/utils"
)
func (r *Registrator) SaveOriginalPanToGDALGTiff(tiffFile string) error {
err := savePanToGDALGTiff(r.PanImage, 0, 0, tiffFile, PanResolution)
err := utils.SavePanToGDALGTiff(r.PanImage, 0, 0, tiffFile, PanResolution)
if err != nil {
return err
}
@@ -21,53 +22,11 @@ func (r *Registrator) SaveOriginalPanToGDALGTiff(tiffFile string) error {
return nil
}
func savePanToGDALGTiff(pan gocv.Mat, topLeftX, topLeftY float64, tiffFile string, resolution float64) error {
// log.Println("Saving PAN image to TIFF file:", tiffFile)
width := pan.Cols()
height := pan.Rows()
ds, err := godal.Create(godal.GTiff, tiffFile, 1, godal.UInt16, width, height)
if err != nil {
log.Error("Error creating TIFF file: ", err)
return err
}
defer ds.Close()
setGeoTransform(ds, topLeftX, topLeftY, resolution)
ds.SetMetadata("NBITS", "16")
// 将通道的数据转换为uint16数组
data := make([]uint16, width*height)
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
data[y*width+x] = uint16(pan.GetShortAt(y, x))
}
}
band := ds.Bands()[0]
band.SetColorInterp(godal.CIGray)
err = band.IO(godal.IOWrite,
0, 0,
data,
width, height,
godal.PixelSpacing(2),
godal.LineSpacing(width*2))
if err != nil {
log.Error("Failed to write data to band:", err)
return err
}
log.Info("Saved pan image to ", tiffFile)
return nil
}
func (r *Registrator) SaveRegisteredMssToGDALGTiff(tiffFile string) error {
r.rgbirImage = gocv.NewMat()
gocv.Merge(r.registeredMssImages[:], &r.rgbirImage)
err := SaveBGRToGDALGTiff(r.rgbirImage,
err := utils.SaveBGRToGDALGTiff(r.rgbirImage,
4, 0, 0, MssResolution,
[]godal.ColorInterp{godal.CIBlue, godal.CIGreen, godal.CIRed, godal.CIUndefined},
tiffFile)
@@ -78,63 +37,6 @@ func (r *Registrator) SaveRegisteredMssToGDALGTiff(tiffFile string) error {
return nil
}
func SaveBGRToGDALGTiff(bgr gocv.Mat,
bands int,
topLeftX, topLeftY float64,
resolution float64,
colorInterps []godal.ColorInterp, tiffFile string) error {
width := bgr.Cols()
height := bgr.Rows()
// 创建一个二维切片来存储图像数据
data := make([][]uint16, bands)
for i := range data {
data[i] = make([]uint16, width*height)
}
// 从gocv.Mat中提取数据
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
for b := 0; b < bands; b++ {
data[b][y*width+x] = uint16(bgr.GetShortAt(y, x*bands+b))
}
}
}
ds, err := godal.Create(godal.GTiff,
tiffFile,
bands,
godal.UInt16,
width, height)
if err != nil {
log.Error("Error creating TIFF file: ", err)
return err
}
defer ds.Close()
// ds.SetMetadata("NBITS", "16")
setGeoTransform(ds, topLeftX, topLeftY, resolution)
for b := 0; b < bands; b++ {
band := ds.Bands()[b]
band.SetColorInterp(colorInterps[b])
err := band.IO(godal.IOWrite,
0, 0,
data[b],
width, height,
godal.PixelSpacing(2),
godal.LineSpacing(width*2))
if err != nil {
log.Error("Failed to write data to band:", err)
return err
}
}
log.Info("Saved BGR MSS to ", tiffFile)
return nil
}
func (r *Registrator) BytesToRaw(mssData []byte, filePath string) error {
f, err := os.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0777)
if err != nil {

View File

@@ -13,7 +13,9 @@ import (
"github.com/paulmach/orb/geojson"
log "github.com/sirupsen/logrus"
"gocv.io/x/gocv"
"starwiz.cn/sjy01/image-proc/pkg/config"
"starwiz.cn/sjy01/image-proc/pkg/rrc"
"starwiz.cn/sjy01/image-proc/pkg/utils"
)
type Scene struct {
@@ -34,6 +36,12 @@ func (s *Scene) Cleanup() {
}
}
func CleanScenes(scenes []*Scene) {
for _, scene := range scenes {
scene.Cleanup()
}
}
// 对 PAN 和 配准后的MSS 在 Y 方向进行分景景之间有25%的重叠
// 默认分景大小:
// MSS 2336 * 2336 - 1764
@@ -67,9 +75,18 @@ func (r *Registrator) SubScenes() (panScenes []*Scene, mssScenes []*Scene, err e
scene.SceneId = fmt.Sprintf("%s_%03d", name, i+1)
mat := r.PanImage.Region(image.Rect(0, i*hPAN, 9344, y1))
matFiltered := rrc.HFNoiseFilter(mat, float64(mat.Cols())*0.45)
scene.Mat = append(scene.Mat, matFiltered)
mat.Close()
if config.GCONFIG.Radiation.PANRemoveHfNoise {
log.Println("applying hf noise filter on", scene.SceneId)
matFiltered := rrc.HFNoiseFilter(mat, float64(mat.Cols())*config.GCONFIG.Radiation.HfRadiusRatio)
mat.Close()
mat = matFiltered
}
if config.GCONFIG.Radiation.SceneMomentMatching {
rrc.DoMomentMatching(mat)
}
scene.Mat = append(scene.Mat, mat)
panScenes = append(panScenes, scene)
}
@@ -95,16 +112,26 @@ func (r *Registrator) SubScenes() (panScenes []*Scene, mssScenes []*Scene, err e
continue
}
for band := 0; band < 4; band++ {
mat := r.registeredMssImages[band].Region(image.Rect(0, i*hMSS, 2336, y1))
matFiltered := rrc.HFNoiseFilter(mat, float64(mat.Cols())*0.45)
mat.Close()
scene.Mat = append(scene.Mat, matFiltered)
}
name := filepath.Base(r.Params.MssTiffFile)
name = strings.TrimSuffix(name, ".tiff")
scene.SceneId = fmt.Sprintf("%s_%03d", name, i+1)
for band := 0; band < 4; band++ {
mat := r.registeredMssImages[band].Region(image.Rect(0, i*hMSS, 2336, y1))
if config.GCONFIG.Radiation.MSSRemoveHfNoise {
log.Println("applying hf noise filter on", scene.SceneId)
matFiltered := rrc.HFNoiseFilter(mat, float64(mat.Cols())*config.GCONFIG.Radiation.HfRadiusRatio/4)
mat.Close()
mat = matFiltered
}
if config.GCONFIG.Radiation.SceneMomentMatching {
rrc.DoMomentMatching(mat)
}
scene.Mat = append(scene.Mat, mat)
}
mssScenes = append(mssScenes, scene)
}
@@ -127,7 +154,7 @@ func (r *Registrator) SaveScenesToTiff(panScenes []*Scene, mssScenes []*Scene) e
scene.Meta = r.makeProductMeta(scene)
r.SetSceneBoundary(scene)
err := savePanToGDALGTiff(scene.Mat[0],
err := utils.SavePanToGDALGTiff(scene.Mat[0],
scene.Meta.Corners.UpperLeft.Longitude,
scene.Meta.Corners.UpperLeft.Latitude,
scene.Tiff,
@@ -175,7 +202,7 @@ func (r *Registrator) SaveScenesToTiff(panScenes []*Scene, mssScenes []*Scene) e
r.SetSceneBoundary(scene)
rgbirImage, _ := r.MergeMSSToBGRNIR(scene.Mat)
err := SaveBGRToGDALGTiff(rgbirImage,
err := utils.SaveBGRToGDALGTiff(rgbirImage,
4,
scene.Meta.Corners.UpperLeft.Longitude,
scene.Meta.Corners.UpperLeft.Latitude,

View File

@@ -2,55 +2,10 @@ package producer
import (
"os"
"time"
"github.com/airbusgeo/godal"
log "github.com/sirupsen/logrus"
"starwiz.cn/sjy01/image-proc/pkg/calculator"
)
func setGeoTransform(ds *godal.Dataset, topLeftLng, topLeftLat, resolution float64) {
// 转换分辨率(米到度)
resLat := resolution / calculator.MetersPerDegreeLatitude
resLng := calculator.MetersToDegreesLongitude(resolution, topLeftLat)
// 设置地理变换(假设左上角坐标为(0,0)PAN每个像素分辨率为1.2米)
geotransform := [6]float64{
topLeftLng, // top left x
resLng, // w-e pixel resolution
0, // rotation, 0 if image is "north up"
topLeftLat, // top left y
0, // rotation, 0 if image is "north up"
-resLat, // n-s pixel resolution (negative value)
}
err := ds.SetGeoTransform(geotransform)
if err != nil {
log.Errorf("Failed to set GeoTransform: %v", err)
return
}
// 设置投影为 WGS84
srs, err := godal.NewSpatialRefFromEPSG(4326) // gdal.CreateSpatialReference("")
if err != nil {
log.Errorf("Failed to set spatial reference: %v", err)
return
}
projWKT, err := srs.WKT()
if err != nil {
log.Errorf("Failed to convert spatial reference to WKT: %v", err)
return
}
err = ds.SetProjection(projWKT)
if err != nil {
log.Errorf("Failed to set projection: %v", err)
}
// 设置一些常见的元数据(可选)
ds.SetMetadata("TIFFTAG_DATETIME", time.Now().String())
ds.SetMetadata("TIFFTAG_SOFTWARE", "StarWiz-SJY01-IMAGE-PROC")
}
func sizeOfFile(file string) int64 {
fileInfo, err := os.Stat(file)
if err != nil {