constrast enhancement for browser image

This commit is contained in:
nuknal
2024-07-17 12:45:29 +08:00
parent 63f995ccfe
commit cab03827f0
3 changed files with 164 additions and 13 deletions

View File

@@ -8,6 +8,7 @@ import (
type Config struct { type Config struct {
CoRegistration CoRegistrationConfig `yaml:"coregistration" mapstructure:"coregistration"` CoRegistration CoRegistrationConfig `yaml:"coregistration" mapstructure:"coregistration"`
Radiation RadiationConfig `yaml:"radiation" mapstructure:"radiation"` Radiation RadiationConfig `yaml:"radiation" mapstructure:"radiation"`
BrowserImg BrowserImgConfig `yaml:"browser_img" mapstructure:"browser_img"`
LogLevel logrus.Level `yaml:"log_level" mapstructure:"log_level"` LogLevel logrus.Level `yaml:"log_level" mapstructure:"log_level"`
} }
@@ -33,6 +34,12 @@ type RadiationConfig struct {
HFBandStopWidth int `yaml:"hf_band_stop_width" mapstructure:"hf_band_stop_width"` HFBandStopWidth int `yaml:"hf_band_stop_width" mapstructure:"hf_band_stop_width"`
} }
type BrowserImgConfig struct {
Enhancement string `yaml:"enhancement" mapstructure:"enhancement"`
CumulativeCutLower float64 `yaml:"cumulative_cut_lower" mapstructure:"cumulative_cut_lower"`
CumulativeCutUpper float64 `yaml:"cumulative_cut_upper" mapstructure:"cumulative_cut_upper"`
}
var GCONFIG Config var GCONFIG Config
func init() { func init() {
@@ -58,5 +65,10 @@ func init() {
HfRadiusRatio: 0.49, HfRadiusRatio: 0.49,
HFBandStopWidth: 24, HFBandStopWidth: 24,
}, },
BrowserImg: BrowserImgConfig{
Enhancement: "StretchToCumulativeCutMinMax",
CumulativeCutLower: 0.01,
CumulativeCutUpper: 0.99,
},
} }
} }

124
pkg/producer/enhancement.go Normal file
View File

@@ -0,0 +1,124 @@
package producer
import (
"gocv.io/x/gocv"
)
const (
NoEnhancement = "NoEnhancement"
StretchToMinimumMaximum = "StretchToMinimumMaximum"
StretchToCumulativeCutMinMax = "StretchToCumulativeCutMinMax"
)
type ContrastEnhancement struct {
minimumValue int
maximumValue int
lookupTableOffset int
lookupTable []int
mRasterDataTypeRange int
enhancementType string
//!< Range is [ min + cumulativeCutLower() * (max - min), min + cumulativeCutUpper() * (max - min) ]
cumulativeCutLower float64
cumulativeCutUpper float64
}
func NewContrastEnhancement(minimumValue, maximumValue int) *ContrastEnhancement {
ce := &ContrastEnhancement{
enhancementType: StretchToMinimumMaximum,
cumulativeCutLower: 0,
cumulativeCutUpper: 1,
}
ce.minimumValue = minimumValue + int(ce.cumulativeCutLower)*(maximumValue-minimumValue)
ce.maximumValue = minimumValue + int(ce.cumulativeCutUpper)*(maximumValue-minimumValue)
ce.mRasterDataTypeRange = ce.maximumValue - ce.minimumValue
ce.lookupTableOffset = -1 * ce.minimumValue
ce.lookupTable = make([]int, ce.mRasterDataTypeRange+1)
ce.generateLookupTable()
return ce
}
func (ce ContrastEnhancement) enhance(value int) int {
if ce.enhancementType != NoEnhancement {
shiftedValue := value + ce.lookupTableOffset
if shiftedValue >= 0 && shiftedValue < ce.mRasterDataTypeRange+1 {
return ce.lookupTable[shiftedValue]
}
return 0
}
return ce.linearMinMaxEnhancement(value)
}
func (ce *ContrastEnhancement) generateLookupTable() {
for i := range ce.lookupTable {
ce.lookupTable[i] = ce.linearMinMaxEnhancement(i - ce.lookupTableOffset)
}
}
func (ce ContrastEnhancement) linearMinMaxEnhancement(value int) int {
stretchedValue := (float64(value-ce.minimumValue) / float64(ce.mRasterDataTypeRange)) * 255.0
if stretchedValue < 0 {
return 0
} else if stretchedValue > 255 {
return 255
}
return int(stretchedValue)
}
func cumulativeCountCutEnhancement(img gocv.Mat, cumulativeCutLower, cumulativeCutUpper float64) gocv.Mat {
dataTypeMax := 65536
// 计算直方图
hist := make([]int, dataTypeMax)
for y := 0; y < img.Rows(); y++ {
for x := 0; x < img.Cols(); x++ {
hist[uint16(img.GetShortAt(y, x))]++
}
}
// 计算累积直方图
cdf := make([]int, dataTypeMax)
cdf[0] = hist[0]
for i := 1; i < dataTypeMax; i++ {
cdf[i] = cdf[i-1] + hist[i]
}
// 计算低高阈值
totalPixels := img.Rows() * img.Cols()
lowCutoff := int(float64(totalPixels) * cumulativeCutLower)
highCutoff := int(float64(totalPixels) * cumulativeCutUpper)
var lowerBound, upperBound int
for i, v := range cdf {
if v > lowCutoff {
lowerBound = i
break
}
}
for i := dataTypeMax - 1; i >= 0; i-- {
if cdf[i] < highCutoff {
upperBound = i
break
}
}
// 重新映射灰度值
enhancedImg := gocv.NewMatWithSize(img.Rows(), img.Cols(), gocv.MatTypeCV8U)
for y := 0; y < img.Rows(); y++ {
for x := 0; x < img.Cols(); x++ {
oldVal := uint16(img.GetShortAt(y, x))
newVal := (int(oldVal) - lowerBound) * 255 / (upperBound - lowerBound)
if newVal > 255 {
newVal = 255
}
if newVal < 0 {
newVal = 0
}
enhancedImg.SetUCharAt(y, x, uint8(newVal))
}
}
return enhancedImg
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/airbusgeo/godal" "github.com/airbusgeo/godal"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"gocv.io/x/gocv" "gocv.io/x/gocv"
"starwiz.cn/sjy01/image-proc/pkg/config"
) )
func GTiffToJPG(ftiff, fjpg string, reversed bool) error { func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
@@ -58,12 +59,24 @@ func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
channels := gocv.Split(img) channels := gocv.Split(img)
for i, ch := range channels { for i, ch := range channels {
// 2. 计算图像的最小值和最大值 switch config.GCONFIG.BrowserImg.Enhancement {
minVal, maxVal, _, _ := gocv.MinMaxLoc(ch) case NoEnhancement:
// 3. 将16位图像线性拉伸到8位图像 case StretchToMinimumMaximum:
scale := 255.0 / (maxVal - minVal) minVal, maxVal, _, _ := gocv.MinMaxLoc(ch)
shift := -minVal * scale ce := NewContrastEnhancement(int(minVal), int(maxVal))
ch.ConvertToWithParams(&channels[i], gocv.MatTypeCV8U, scale, shift) for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
value := int(uint16(ch.GetShortAt(y, x)))
value = ce.enhance(value)
ch.SetShortAt(y, x, int16(value))
}
}
case StretchToCumulativeCutMinMax:
channels[i] = cumulativeCountCutEnhancement(ch,
config.GCONFIG.BrowserImg.CumulativeCutLower,
config.GCONFIG.BrowserImg.CumulativeCutUpper)
ch.Close()
}
} }
img8bit := gocv.NewMat() img8bit := gocv.NewMat()
@@ -82,14 +95,16 @@ func GTiffToJPG(ftiff, fjpg string, reversed bool) error {
image.Point{X: img8bit.Cols() / 2, Y: img8bit.Rows() / 2}, image.Point{X: img8bit.Cols() / 2, Y: img8bit.Rows() / 2},
0, 0, gocv.InterpolationCubic) 0, 0, gocv.InterpolationCubic)
gocv.IMWrite(fjpg, img8bit)
// 7. 应用伽玛校正提升亮度 // 7. 应用伽玛校正提升亮度
gammaCorrected := applyGammaCorrection(img8bit, 1.6) // 伽玛值 // gammaCorrected := applyGammaCorrection(img8bit, 1.6) // 伽玛值
defer gammaCorrected.Close() // defer gammaCorrected.Close()
ok := gocv.IMWriteWithParams(fjpg, gammaCorrected, []int{gocv.IMWriteJpegOptimize, 1}) // ok := gocv.IMWriteWithParams(fjpg, gammaCorrected, []int{gocv.IMWriteJpegOptimize, 1})
if !ok { // if !ok {
err = fmt.Errorf("error saving %s", fjpg) // err = fmt.Errorf("error saving %s", fjpg)
return err // return err
} // }
return nil return nil
} }