constrast enhancement for browser image
This commit is contained in:
@@ -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
124
pkg/producer/enhancement.go
Normal 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
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user