暂时使用星下点坐标作为图像左上角坐标

This commit is contained in:
nuknal
2024-05-30 18:11:42 +08:00
parent e4d6b35702
commit 8f2b297a02
25 changed files with 1710 additions and 84 deletions

109
pkg/producer/aux.go Normal file
View File

@@ -0,0 +1,109 @@
package imageproc
import (
"math"
"time"
"github.com/paulmach/orb"
"github.com/paulmach/orb/planar"
"starwiz.cn/sjy01/image-proc/pkg/auxilary"
"starwiz.cn/sjy01/image-proc/pkg/calculator"
)
func (r *Registrator) LoadAuxData() error {
var err error
r.auxHeads, r.auxBoxes, r.AuxPlatforms, err = auxilary.ExtractAux(r.Params.AuxRawFile)
return err
}
func (r *Registrator) SceneImageTime(scene *Scene) (start, center, end time.Time) {
startPosInAux, endPosInAux := r.SceneInAuxIndex(scene)
centerPosInAux := (startPosInAux + endPosInAux) / 2
start = time.Unix(int64(auxilary.ReferenceTime2000)+int64(r.AuxPlatforms[startPosInAux].UTCTimeSec),
int64(r.AuxPlatforms[startPosInAux].Microsecond)*1000)
center = time.Unix(int64(auxilary.ReferenceTime2000)+int64(r.AuxPlatforms[centerPosInAux].UTCTimeSec),
int64(r.AuxPlatforms[centerPosInAux].Microsecond)*1000)
end = time.Unix(int64(auxilary.ReferenceTime2000)+int64(r.AuxPlatforms[endPosInAux].UTCTimeSec),
int64(r.AuxPlatforms[endPosInAux].Microsecond)*1000)
return
}
// FIXME: 位置像元经纬度计算方法,暂时使用星下点替代
func (r *Registrator) ScenePosition(scene *Scene) (topLeft, bottomRight orb.Point) {
// startPosInAux, endPosInAux := r.SceneInAuxIndex(scene)
ap := r.AuxPlatforms[0]
// ap1 := r.AuxPlatforms[endPosInAux]
lat0, lng0, _ := calculator.WGS84XYZtoLatLngH(ap.WGS84PosX, ap.WGS84PosY, ap.WGS84PosZ)
lat, lng := calculator.CalculateDestination(lat0, lng0,
float64(scene.Width)*scene.Meta.Gsd, float64(-scene.Y)*scene.Meta.Gsd)
// lat0, lng0, h0 := calculator.WGS84XYZtoLatLngH(ap1.WGS84PosX, ap1.WGS84PosY, ap1.WGS84PosZ)
// fmt.Println("Scene Position End:", lat0, lng0, h0)
lat1, lng1 := calculator.CalculateDestination(lat, lng,
float64(scene.Width)*scene.Meta.Gsd, float64(-scene.Height)*scene.Meta.Gsd)
poly := orb.Polygon{
{
{lng, lat},
{lng1, lat},
{lng1, lat1},
{lng, lat1},
{lng, lat},
},
}
centroid, _ := planar.CentroidArea(poly)
scene.Meta.CentreLocation.Latitude = centroid.Y()
scene.Meta.CentreLocation.Longitude = centroid.X()
scene.Meta.Corners.UpperLeft.Latitude = lat
scene.Meta.Corners.UpperLeft.Longitude = lng
scene.Meta.Corners.UpperRight.Latitude = lat
scene.Meta.Corners.UpperRight.Longitude = lng1
scene.Meta.Corners.LowerLeft.Latitude = lat1
scene.Meta.Corners.LowerLeft.Longitude = lng
scene.Meta.Corners.LowerRight.Latitude = lat1
scene.Meta.Corners.LowerRight.Longitude = lng1
scene.Meta.SatPosX = ap.WGS84PosX
scene.Meta.SatPosY = ap.WGS84PosY
scene.Meta.SatPosZ = ap.WGS84PosZ
scene.Meta.Yaw = ap.Eular1 * 180 / math.Pi
scene.Meta.Pitch = ap.Eular2 * 180 / math.Pi
scene.Meta.Roll = ap.Eular3 * 180 / math.Pi
// feature := geojson.NewFeature(poly)
// fcs.Features = append(fcs.Features, feature)
// fd, _ := fcs.MarshalJSON()
// fmt.Println(string(fd))
return
}
func (r *Registrator) SceneInAuxIndex(scene *Scene) (int, int) {
var auxForImageRow int
switch scene.Type {
case "MSS":
auxForImageRow = 4
case "PAN":
auxForImageRow = 16
case "FUS":
auxForImageRow = 16
}
startPosInAux := scene.Y / auxForImageRow
if startPosInAux >= len(r.AuxPlatforms) {
startPosInAux = len(r.AuxPlatforms) - 1
}
endPosInAux := (scene.Y + scene.Height) / auxForImageRow
if endPosInAux >= len(r.AuxPlatforms) {
endPosInAux = len(r.AuxPlatforms) - 1
}
return startPosInAux, endPosInAux
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/airbusgeo/godal"
log "github.com/sirupsen/logrus"
"gocv.io/x/gocv"
"starwiz.cn/sjy01/image-proc/pkg/auxilary"
)
type Registrate interface{}
@@ -49,6 +50,10 @@ type Registrator struct {
rgbirImage gocv.Mat
resampleMethod ResampleMethod
auxHeads []*auxilary.AuxFrameHead
auxBoxes []*auxilary.AuxFocalBox
AuxPlatforms []*auxilary.AuxPlatform
}
func NewRegistrator(rsmethod ResampleMethod) *Registrator {

View File

@@ -59,9 +59,7 @@ func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
channels := gocv.Split(img)
for i, ch := range channels {
// 2. 计算图像的最小值和最大值
minVal, maxVal, minLoc, maxLoc := gocv.MinMaxLoc(ch)
log.Printf("Min value: %f, Max value: %f, min location: %v, max location: %v", minVal, maxVal, minLoc, maxLoc)
minVal, maxVal, _, _ := gocv.MinMaxLoc(ch)
// 3. 将16位图像线性拉伸到8位图像
scale := 255.0 / (maxVal - minVal)
shift := -minVal * scale
@@ -85,10 +83,6 @@ func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
0, 0, gocv.InterpolationCubic)
// 7. 应用伽玛校正提升亮度
// avgBrightness := calculateAverageBrightness(img8bit)
// gamma := determineGammaValue(avgBrightness)
// log.Printf("Average Brightness: %f, Determined Gamma: %f", avgBrightness, gamma)
gammaCorrected := applyGammaCorrection(img8bit, 1.6) // 伽玛值
defer gammaCorrected.Close()
ok := gocv.IMWriteWithParams(fjpg, gammaCorrected, []int{gocv.IMWriteJpegOptimize, 1})
@@ -96,9 +90,6 @@ func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
err = fmt.Errorf("error saving %s", fjpg)
return err
}
// gocv.IMWrite(strings.Replace(fjpg, ".jpg", "_8.jpg", 1), img8bit)
return nil
}
@@ -121,24 +112,3 @@ func applyGammaCorrection(src gocv.Mat, gamma float64) gocv.Mat {
gocv.LUT(src, gammaMat, &dst)
return dst
}
// calculateAverageBrightness calculates the average brightness of the image
func calculateAverageBrightness(img gocv.Mat) float64 {
if img.Channels() == 1 {
return img.Mean().Val1
}
gray := gocv.NewMat()
defer gray.Close()
gocv.CvtColor(img, &gray, gocv.ColorBGRToGray)
return gray.Mean().Val1
}
// determineGammaValue determines an appropriate gamma value based on the average brightness
func determineGammaValue(avgBrightness float64) float64 {
targetBrightness := 180.0
if avgBrightness < 0.1 {
return 1.0
}
gamma := math.Log(targetBrightness/255.0) / math.Log(avgBrightness/255.0)
return gamma
}

View File

@@ -3,6 +3,9 @@ package imageproc
import (
"encoding/xml"
"os"
"path/filepath"
"strings"
"time"
)
// 定义与XML结构对应的Go结构体
@@ -55,7 +58,38 @@ type Corners struct {
LowerLeft Location `xml:"LowerLeft"`
}
func WriteProductMeta(productMeta *ProductMeta, filename string) error {
func (r *Registrator) makeProductMeta(scene *Scene) *ProductMeta {
meta := &ProductMeta{
Satellite: "SJY01",
Sensor: "PMS",
OutputFormat: "GTiff",
ProductGenTime: time.Now().Format("2006-01-02T15:04:05"),
Width: scene.Width,
Height: scene.Height,
MapProjection: "GEOGRAPHIC",
EarthEllipsoid: "WGS_84",
ProductLevel: "L1A",
}
switch scene.Type {
case "PAN":
meta.Gsd = 1.25
meta.Bands = 1
case "MSS":
meta.Gsd = 5
meta.Bands = 4
}
meta.ProductID = filepath.Base(strings.TrimSuffix(scene.Tiff, ".tiff"))
startTime, centerTime, endTime := r.SceneImageTime(scene)
meta.StartTime = startTime.Format("2006-01-02T15:04:05.000")
meta.EndTime = endTime.Format("2006-01-02T15:04:05.000")
meta.CentreTime = centerTime.Format("2006-01-02T15:04:05.000")
return meta
}
func (r *Registrator) writeProductMeta(productMeta *ProductMeta, filename string) error {
output, err := xml.MarshalIndent(productMeta, "", " ")
if err != nil {
return err

View File

@@ -13,7 +13,7 @@ import (
)
func (r *Registrator) SaveOriginalPanToGDALGTiff(tiffFile string) error {
err := savePanToGDALGTiff(r.PanImage, tiffFile)
err := savePanToGDALGTiff(r.PanImage, 0, 0, tiffFile)
if err != nil {
return err
}
@@ -21,13 +21,7 @@ func (r *Registrator) SaveOriginalPanToGDALGTiff(tiffFile string) error {
return nil
}
func (r *Registrator) SaveFilteredPanToGDALGTiff(tiffFile string) error {
img := PANFilter(r.PanImage)
img.ConvertTo(&img, gocv.MatTypeCV16U)
return savePanToGDALGTiff(img, tiffFile)
}
func savePanToGDALGTiff(pan gocv.Mat, tiffFile string) error {
func savePanToGDALGTiff(pan gocv.Mat, topLeftX, topLeftY float64, tiffFile string) error {
// log.Println("Saving PAN image to TIFF file:", tiffFile)
width := pan.Cols()
@@ -40,7 +34,7 @@ func savePanToGDALGTiff(pan gocv.Mat, tiffFile string) error {
}
defer ds.Close()
setGeoTransform(ds, 0, 0, float64(width), float64(height), 1.25)
setGeoTransform(ds, topLeftX, topLeftY, 1.25)
ds.SetMetadata("NBITS", "16")
// 将通道的数据转换为uint16数组
@@ -74,7 +68,7 @@ func (r *Registrator) SaveRegisteredMssToGDALGTiff(tiffFile string) error {
gocv.Merge(r.registeredMssImages[:], &r.rgbirImage)
err := SaveBGRToGDALGTiff(r.rgbirImage,
4, 5,
4, 0, 0, 5,
[]godal.ColorInterp{godal.CIBlue, godal.CIGreen, godal.CIRed, godal.CIUndefined},
tiffFile)
if err != nil {
@@ -84,15 +78,11 @@ func (r *Registrator) SaveRegisteredMssToGDALGTiff(tiffFile string) error {
return nil
}
func (r *Registrator) SavePansharpenedToGDALGTiff(tiffFile string) error {
ihsImage := PansharpenIHS(r.PanImage, r.rgbirImage)
return SaveBGRToGDALGTiff(ihsImage,
3, 1.25,
[]godal.ColorInterp{godal.CIRed, godal.CIGreen, godal.CIBlue},
tiffFile)
}
func SaveBGRToGDALGTiff(bgr gocv.Mat, bands int, resolution float64, colorInterps []godal.ColorInterp, tiffFile string) error {
func SaveBGRToGDALGTiff(bgr gocv.Mat,
bands int,
topLeftX, topLeftY float64,
resolution float64,
colorInterps []godal.ColorInterp, tiffFile string) error {
width := bgr.Cols()
height := bgr.Rows()
@@ -123,8 +113,7 @@ func SaveBGRToGDALGTiff(bgr gocv.Mat, bands int, resolution float64, colorInterp
defer ds.Close()
// ds.SetMetadata("NBITS", "16")
setGeoTransform(ds, 0, 0, float64(width), float64(height), resolution)
setGeoTransform(ds, topLeftX, topLeftY, resolution)
for b := 0; b < bands; b++ {
band := ds.Bands()[b]

View File

@@ -75,6 +75,8 @@ func calculateProj(topLeftX, topLeftY, width, height, resolution float64) string
EXTENSION["PROJ4","+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs"],
AUTHORITY["EPSG","3857"]]`)
projections = append(projections, `GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]`)
// 输出投影信息
return projections[0]
return projections[4]
}

View File

@@ -13,12 +13,15 @@ import (
)
type Scene struct {
Width int
Height int
X int // coordinate of the left top corner of the scene
Y int
Mat []gocv.Mat
Tiff string
Type string
Width int
Height int
X int // coordinate of the left top corner of the scene
Y int
Mat []gocv.Mat
Tiff string
Meta *ProductMeta
SceneId string
}
func (s *Scene) Cleanup() {
@@ -43,12 +46,17 @@ func (r *Registrator) SubScenes() (panScenes []*Scene, mssScenes []*Scene, err e
}
scene := &Scene{
Type: "PAN",
Width: 9344,
Height: y1 - i*hPAN,
X: 0,
Y: i * hPAN,
}
name := filepath.Base(r.Params.PanTiffFile)
name = strings.TrimSuffix(name, ".tiff")
scene.SceneId = fmt.Sprintf("%s_%03d", name, i+1)
mat := r.PanImage.Region(image.Rect(0, i*hPAN, 9344, y1))
scene.Mat = append(scene.Mat, mat)
panScenes = append(panScenes, scene)
@@ -64,6 +72,7 @@ func (r *Registrator) SubScenes() (panScenes []*Scene, mssScenes []*Scene, err e
}
scene := &Scene{
Type: "MSS",
Width: 2336,
Height: y1 - i*hMSS,
X: 0,
@@ -75,6 +84,9 @@ func (r *Registrator) SubScenes() (panScenes []*Scene, mssScenes []*Scene, err e
scene.Mat = append(scene.Mat, mat)
}
name := filepath.Base(r.Params.MssTiffFile)
name = strings.TrimSuffix(name, ".tiff")
scene.SceneId = fmt.Sprintf("%s_%03d", name, i+1)
mssScenes = append(mssScenes, scene)
}
@@ -91,16 +103,23 @@ func (r *Registrator) SaveScenesToTiff(panScenes []*Scene, mssScenes []*Scene) e
dir := filepath.Join(r.Params.OutputDir, fmt.Sprintf("%03d", i+1), "PAN")
os.MkdirAll(dir, 0755)
tiff := filepath.Base(r.Params.PanTiffFile)
tiff = strings.TrimSuffix(tiff, ".tiff")
filename := fmt.Sprintf("%s_%03d.tiff", tiff, i+1)
filename := fmt.Sprintf("%s_L1A.tiff", scene.SceneId)
scene.Tiff = filepath.Join(dir, filename)
err := savePanToGDALGTiff(scene.Mat[0], scene.Tiff)
scene.Meta = r.makeProductMeta(scene)
r.ScenePosition(scene)
err := savePanToGDALGTiff(scene.Mat[0],
scene.Meta.Corners.UpperLeft.Longitude,
scene.Meta.Corners.UpperLeft.Latitude,
scene.Tiff)
if err != nil {
log.Errorf("save pan scene %d to tiff failed: %v", i+1, err)
return err
}
scene.Meta.DataSize = sizeOfFile(scene.Tiff)
metaFile := strings.Replace(scene.Tiff, ".tiff", ".meta.xml", 1)
r.writeProductMeta(scene.Meta, metaFile)
GTiffToJPG(scene.Tiff, strings.Replace(scene.Tiff, ".tiff", ".jpg", 1), false)
}
@@ -108,14 +127,17 @@ func (r *Registrator) SaveScenesToTiff(panScenes []*Scene, mssScenes []*Scene) e
dir := filepath.Join(r.Params.OutputDir, fmt.Sprintf("%03d", i+1), "MSS")
os.MkdirAll(dir, 0755)
tiff := filepath.Base(r.Params.MssTiffFile)
tiff = strings.TrimSuffix(tiff, ".tiff")
filename := fmt.Sprintf("%s_%03d.tiff", tiff, i+1)
filename := fmt.Sprintf("%s_L1A.tiff", scene.SceneId)
scene.Tiff = filepath.Join(dir, filename)
scene.Meta = r.makeProductMeta(scene)
r.ScenePosition(scene)
rgbirImage, _ := r.MergeMSSToBGRNIR(scene.Mat)
err := SaveBGRToGDALGTiff(rgbirImage,
4, 5,
4,
scene.Meta.Corners.UpperLeft.Longitude,
scene.Meta.Corners.UpperLeft.Latitude,
5,
[]godal.ColorInterp{godal.CIBlue, godal.CIGreen, godal.CIRed, godal.CIUndefined},
scene.Tiff)
if err != nil {
@@ -123,6 +145,9 @@ func (r *Registrator) SaveScenesToTiff(panScenes []*Scene, mssScenes []*Scene) e
return err
}
scene.Meta.DataSize = sizeOfFile(scene.Tiff)
metaFile := strings.Replace(scene.Tiff, ".tiff", ".meta.xml", 1)
r.writeProductMeta(scene.Meta, metaFile)
GTiffToJPG(scene.Tiff, strings.Replace(scene.Tiff, ".tiff", ".jpg", 1), false)
}

View File

@@ -1,29 +1,47 @@
package imageproc
import (
"os"
"time"
"github.com/airbusgeo/godal"
log "github.com/sirupsen/logrus"
"starwiz.cn/sjy01/image-proc/pkg/calculator"
)
func setGeoTransform(ds *godal.Dataset, topLeftX, topLeftY, width, height, resolution float64) {
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{
0, // top left x
resolution, // w-e pixel resolution
0, // rotation, 0 if image is "north up"
0, // top left y
0, // rotation, 0 if image is "north up"
-resolution, // n-s pixel resolution (negative value)
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
err = ds.SetProjection(calculateProj(topLeftX, topLeftY, width, height, resolution))
// 设置投影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)
}
@@ -32,3 +50,12 @@ func setGeoTransform(ds *godal.Dataset, topLeftX, topLeftY, width, height, resol
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 {
log.Errorf("Failed to get file size: %v", err)
return 0
}
return fileInfo.Size()
}