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 } // 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 }