使用gamma校正提升jpg亮度

This commit is contained in:
nuknal
2024-05-30 11:22:34 +08:00
parent 7d9ec46750
commit 07ee4d88d4
18 changed files with 174 additions and 98 deletions

View File

@@ -8,7 +8,7 @@ import (
"github.com/airbusgeo/godal"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
producer "starwiz.cn/sjy01/image-proc/producer"
producer "starwiz.cn/sjy01/image-proc/pkg/producer"
)
var (

8
pkg/config/config.go Normal file
View File

@@ -0,0 +1,8 @@
package config
type CoRegistrationConfig struct {
CRBlockNW int `yaml:"cr_block_nw" mapstructure:"cr_block_nw"`
CRBlockNH int `yaml:"cr_block_nh" mapstructure:"cr_block_nh"`
CRResampleMethod string `yaml:"cr_resample_method" mapstructure:"cr_resample_method"`
FUSBandOrder string `yaml:"fu_band_order" mapstructure:"fu_band_order"`
}

View File

@@ -4,6 +4,7 @@ import (
"image"
"os"
"os/exec"
"path/filepath"
"strings"
log "github.com/sirupsen/logrus"
@@ -83,7 +84,8 @@ func PansharpenIHS(panImage, mssImage gocv.Mat) gocv.Mat {
}
func GDALPansharpen(panTiff, mssTiff string) (string, error) {
fusedTIff := strings.Replace(mssTiff, "MSS", "FUS", 1)
fusedTIff := strings.Replace(mssTiff, "MSS", "FUS", -1)
os.MkdirAll(filepath.Dir(fusedTIff), 0755)
// pansharpenCmd := exec.Command("gdal_pansharpen.py", "-w 0.6 -w 0.1 -w 0.3 -w 0 -b 3 -b 2 -b 1", panTiff, mssTiff, fusedTIff, "-r cubic", "-of GTiff")
pansharpenCmd := exec.Command("gdal_pansharpen.py",
"-w", "0.4", "-w", "0.2", "-w", "0.4", "-w", "0",

View File

@@ -18,22 +18,22 @@ func TestGDALPansharpen(t *testing.T) {
func TestGTiffToJPG(t *testing.T) {
godal.RegisterAll()
tiff := "../data/051622/006/SJY01_MSS_20240516_101236_051622_096_006.tiff"
jpg := "../data/051622/006/SJY01_MSS_20240516_101236_051622_096_006.jpg"
tiff := "../data/051622/006/MSS/SJY01_MSS_20240516_101236_051622_096_006.tiff"
jpg := "../data/051622/006/MSS/SJY01_MSS_20240516_101236_051622_096_006.jpg"
err := GTiffToJPG(tiff, jpg, false)
if err != nil {
t.Error(err)
}
// tiff = "../data/051622/006/SJY01_PAN_20240516_101236_051622_096_006.tiff"
// jpg = "../data/051622/006/SJY01_PAN_20240516_101236_051622_096_006.jpg"
// err = GTiffToJPG(tiff, jpg, false)
// if err != nil {
// t.Error(err)
// }
tiff = "../data/051622/006/PAN/SJY01_PAN_20240516_101236_051622_096_006.tiff"
jpg = "../data/051622/006/PAN/SJY01_PAN_20240516_101236_051622_096_006.jpg"
err = GTiffToJPG(tiff, jpg, false)
if err != nil {
t.Error(err)
}
tiff = "../data/051622/006/SJY01_FUS_20240516_101236_051622_096_006.tiff"
jpg = "../data/051622/006/SJY01_FUS_20240516_101236_051622_096_006.jpg"
tiff = "../data/051622/006/FUS/SJY01_FUS_20240516_101236_051622_096_006.tiff"
jpg = "../data/051622/006/FUS/SJY01_FUS_20240516_101236_051622_096_006.jpg"
err = GTiffToJPG(tiff, jpg, true)
if err != nil {
t.Error(err)

150
pkg/producer/jpg.go Normal file
View File

@@ -0,0 +1,150 @@
package imageproc
import (
"fmt"
"image"
"math"
"github.com/airbusgeo/godal"
log "github.com/sirupsen/logrus"
"gocv.io/x/gocv"
)
func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
// 打开 TIFF 文件
ds, err := godal.Open(ftiff)
if err != nil {
log.Printf("Error opening TIFF file %s: %v", ftiff, err)
return err
}
defer ds.Close()
bands := ds.Bands()
log.Infof("creating JPG for TIFF file %s.", ftiff)
// 获取图像大小
width := bands[0].Structure().SizeX
height := bands[0].Structure().SizeY
bandsCnt := len(bands)
// 创建 16 位图像矩阵
var img gocv.Mat
defer img.Close()
if bandsCnt == 1 {
img = gocv.NewMatWithSize(height, width, gocv.MatTypeCV16UC1)
} else {
bandsCnt = 3 // 只取前三个波段
img = gocv.NewMatWithSize(height, width, gocv.MatTypeCV16UC3)
}
// 读取每个波段并存储到图像矩阵中
for i := 0; i < bandsCnt; i++ {
band := bands[i]
data := make([]uint16, width*height)
err = band.Read(0, 0, data, width, height)
if err != nil {
fmt.Printf("Error reading band %d: %v\n", i, err)
return err
}
// 将 16 位数据存储到图像的相应通道
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
value := data[y*width+x]
img.SetShortAt(y, x*bandsCnt+i, int16(value))
}
}
}
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)
// 3. 将16位图像线性拉伸到8位图像
scale := 255.0 / (maxVal - minVal)
shift := -minVal * scale
ch.ConvertToWithParams(&channels[i], gocv.MatTypeCV8U, scale, shift)
}
img8bit := gocv.NewMat()
defer img8bit.Close()
if reversed {
gocv.Merge([]gocv.Mat{channels[2], channels[1], channels[0]}, &img8bit)
} else {
gocv.Merge(channels, &img8bit)
}
for i := range channels {
channels[i].Close()
}
gocv.Resize(img8bit, &img8bit,
image.Point{X: img8bit.Cols() / 2, Y: img8bit.Rows() / 2},
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})
if !ok {
err = fmt.Errorf("error saving %s", fjpg)
return err
}
// gocv.IMWrite(strings.Replace(fjpg, ".jpg", "_8.jpg", 1), img8bit)
return nil
}
// applyGammaCorrection applies gamma correction to the image
func applyGammaCorrection(src gocv.Mat, gamma float64) gocv.Mat {
lookupTable := make([]uint8, 256)
invGamma := 1.0 / gamma
for i := 0; i < 256; i++ {
lookupTable[i] = uint8(math.Pow(float64(i)/255.0, invGamma) * 255.0)
}
dst := gocv.NewMat()
gammaMat, err := gocv.NewMatFromBytes(1, 256, gocv.MatTypeCV8UC1, lookupTable)
if err != nil {
log.Printf("Error creating gamma correction matrix: %v", err)
return src
}
defer gammaMat.Close()
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
// mean := gocv.NewMat()
// dstStdDev := gocv.NewMat()
// defer mean.Close()
// defer dstStdDev.Close()
// gocv.MeanStdDev(gray, &mean, &dstStdDev)
// return float64(mean.GetFloatAt(0, 0))
}
// 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

@@ -88,7 +88,7 @@ func (r *Registrator) SubScenes() (panScenes []*Scene, mssScenes []*Scene, err e
func (r *Registrator) SaveScenesToTiff(panScenes []*Scene, mssScenes []*Scene) error {
for i, scene := range panScenes {
dir := filepath.Join(r.Params.OutputDir, fmt.Sprintf("%03d", i+1))
dir := filepath.Join(r.Params.OutputDir, fmt.Sprintf("%03d", i+1), "PAN")
os.MkdirAll(dir, 0755)
tiff := filepath.Base(r.Params.PanTiffFile)
@@ -105,7 +105,7 @@ func (r *Registrator) SaveScenesToTiff(panScenes []*Scene, mssScenes []*Scene) e
}
for i, scene := range mssScenes {
dir := filepath.Join(r.Params.OutputDir, fmt.Sprintf("%03d", i+1))
dir := filepath.Join(r.Params.OutputDir, fmt.Sprintf("%03d", i+1), "MSS")
os.MkdirAll(dir, 0755)
tiff := filepath.Base(r.Params.MssTiffFile)

View File

@@ -1,84 +0,0 @@
package imageproc
import (
"fmt"
"image"
"github.com/airbusgeo/godal"
log "github.com/sirupsen/logrus"
"gocv.io/x/gocv"
)
func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
// 打开 TIFF 文件
ds, err := godal.Open(ftiff)
if err != nil {
log.Printf("Error opening TIFF file %s: %v", ftiff, err)
return err
}
defer ds.Close()
bands := ds.Bands()
log.Infof("creating JPG for TIFF file %s.", ftiff)
// 获取图像大小
width := bands[0].Structure().SizeX
height := bands[0].Structure().SizeY
bandsCnt := len(bands)
// 创建 16 位图像矩阵
var img gocv.Mat
defer img.Close()
if bandsCnt == 1 {
img = gocv.NewMatWithSize(height, width, gocv.MatTypeCV16UC1)
} else {
bandsCnt = 3 // 只取前三个波段
img = gocv.NewMatWithSize(height, width, gocv.MatTypeCV16UC3)
}
// 读取每个波段并存储到图像矩阵中
for i := 0; i < bandsCnt; i++ {
band := bands[i]
data := make([]uint16, width*height)
err = band.Read(0, 0, data, width, height)
if err != nil {
fmt.Printf("Error reading band %d: %v\n", i, err)
return err
}
// 将 16 位数据存储到图像的相应通道
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
value := data[y*width+x]
img.SetShortAt(y, x*bandsCnt+i, int16(value))
}
}
}
// 重采样图像
gocv.Resize(img, &img, image.Point{X: img.Cols() / 2, Y: img.Rows() / 2}, 0, 0, gocv.InterpolationCubic)
// 创建 8 位图像矩阵
img8 := gocv.NewMat()
defer img8.Close()
if bandsCnt == 1 {
img.ConvertToWithParams(&img8, gocv.MatTypeCV8UC1, 1.0/256.0*1.5, 0)
} else {
img.ConvertToWithParams(&img8, gocv.MatTypeCV8UC3, 1.0/256.0*1.5, 0)
if reversed {
channels := gocv.Split(img8)
gocv.Merge([]gocv.Mat{channels[2], channels[1], channels[0]}, &img8)
for _, ch := range channels {
ch.Close()
}
}
}
ok := gocv.IMWrite(fjpg, img8)
if !ok {
err = fmt.Errorf("error saving %s", fjpg)
return err
}
return nil
}