Files
sjy01-image-proc/pkg/rrc/histogram.go
2024-06-15 13:05:42 +08:00

232 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package rrc
import (
"bytes"
"encoding/binary"
"math"
"os"
log "github.com/sirupsen/logrus"
"gocv.io/x/gocv"
"gonum.org/v1/plot"
"gonum.org/v1/plot/plotter"
"gonum.org/v1/plot/plotutil"
"gonum.org/v1/plot/vg"
)
type ProbeHistogram struct {
probes int // 探元数
N_i []int64 // N_i 探元像素总数 PAN 0-9343 MSS 0-2335
n_ik [][]int64 // n_ik 第i探元灰度等级为k的像素数统计 PAN 9343 x 65536 MSS 2335 x 65536
p_ik [][]float64 // p_ik = n_ik / N_i 探元灰度概率密度 PAN 9343 x 65536 MSS 2335 x 65536
m_l []int64 // 具有灰度等级l的像素总数 l = 0-65535
M int64 // 参与直方图统计的总像素数
P_l []float64 // P_l = m_l/M 所有探元的期望直方图灰度等级为l的概率密度
S_ik [][]float64 // S_ik = sum(p_ij),j=0..k 第i个探元直方图灰度等级k的累积概率密度
V_l []float64 // V_l = sum(P_j),j=0..l // 期望直方图灰度级l对应的累积概率密度
Tmat [][]uint16 // 第i个像元的j灰度等级对应的新的灰度值用于修正图像
}
func (hist *ProbeHistogram) init(width int) {
hist.probes = width
hist.M = 0
hist.N_i = make([]int64, width)
hist.n_ik = make([][]int64, width)
hist.p_ik = make([][]float64, width)
hist.m_l = make([]int64, MaxGrayLevel)
hist.P_l = make([]float64, MaxGrayLevel)
hist.S_ik = make([][]float64, width)
hist.V_l = make([]float64, MaxGrayLevel)
hist.Tmat = make([][]uint16, width)
for i := 0; i < width; i++ {
hist.n_ik[i] = make([]int64, MaxGrayLevel)
hist.p_ik[i] = make([]float64, MaxGrayLevel)
hist.S_ik[i] = make([]float64, MaxGrayLevel)
hist.Tmat[i] = make([]uint16, MaxGrayLevel)
}
}
func (hist *ProbeHistogram) init0(width int) {
hist.probes = width
hist.M = 0
hist.N_i = make([]int64, width)
hist.n_ik = make([][]int64, width)
hist.m_l = make([]int64, MaxGrayLevel)
for i := 0; i < width; i++ {
hist.n_ik[i] = make([]int64, MaxGrayLevel)
}
}
func (hist *ProbeHistogram) statistical(img gocv.Mat) error {
hist.M += int64(img.Rows() * img.Cols())
// 探元i像素总数
for i := 0; i < hist.probes; i++ {
hist.N_i[i] += int64(img.Rows())
}
// 探元i灰度等级k的像素数统计
for y := 0; y < img.Rows(); y++ {
for x := 0; x < img.Cols(); x++ {
gray := uint16(img.GetShortAt(x, y))
hist.n_ik[x][gray]++
}
}
// 灰度等级l的像素总数
for gray := 0; gray < MaxGrayLevel; gray++ {
for i := 0; i < hist.probes; i++ {
hist.m_l[gray] += int64(hist.n_ik[i][gray])
}
}
return nil
}
func (hist *ProbeHistogram) compute() {
// 探元i灰度k概率密度
log.Info("computing p_ik")
for i := 0; i < hist.probes; i++ {
for k := 0; k < MaxGrayLevel; k++ {
hist.p_ik[i][k] = float64(hist.n_ik[i][k]) / float64(hist.N_i[i])
}
}
points := plotter.XYs{}
var maxY int64
// 所有探元的期望直方图灰度等级为l的概率密度
log.Info("computing p_l")
for gray := 0; gray < MaxGrayLevel; gray++ {
hist.P_l[gray] = float64(hist.m_l[gray]) / float64(hist.M)
points = append(points, plotter.XY{X: float64(gray), Y: float64(hist.m_l[gray])})
if hist.m_l[gray] > maxY {
maxY = hist.m_l[gray]
}
}
plt := plot.New()
plt.Title.Text = "Gray Level Histogram"
plt.X.Label.Text = "Gray Level"
plt.Y.Label.Text = "Pixels"
plt.Y.Min, plt.X.Min, plt.Y.Max, plt.X.Max = 0, 0, float64(maxY), 65536
plotutil.AddLines(plt, points)
plt.Save(5*vg.Inch, 5*vg.Inch, "data/rrc/01-gray-level-histogram.png")
// 第i个探元直方图灰度等级k的累积概率密度
log.Info("computing s_ik")
for i := 0; i < hist.probes; i++ {
for k := 0; k < MaxGrayLevel; k++ {
if k == 0 {
hist.S_ik[i][k] = hist.p_ik[i][0]
} else {
hist.S_ik[i][k] = hist.S_ik[i][k-1] + hist.p_ik[i][k]
}
}
}
// 期望直方图灰度级l对应的累积概率密度
log.Info("computing v_l")
for gray := 0; gray < MaxGrayLevel; gray++ {
hist.V_l[gray] = 0
if gray == 0 {
hist.V_l[gray] = hist.P_l[0]
} else {
hist.V_l[gray] = hist.V_l[gray-1] + hist.P_l[gray]
}
}
// 生成查找表
log.Info("computing Tij table")
var nT int64
for i := 0; i < hist.probes; i++ {
for k := 0; k < MaxGrayLevel; k++ {
l := searchVL(hist.V_l, hist.S_ik[i][k])
if l == -1 {
continue
}
nT++
if math.Abs(hist.S_ik[i][k]-hist.V_l[l]) <= math.Abs(hist.S_ik[i][k]-hist.V_l[l+1]) {
hist.Tmat[i][k] = uint16(l)
} else {
hist.Tmat[i][k] = uint16(l + 1)
}
}
}
log.Info("total Tij table entries:", nT)
if nT != int64(hist.probes*MaxGrayLevel) {
log.Warn("error in computing Tij table, some values are not satisfied, nT:", nT,
"probes*MaxGrayLevel:", hist.probes*MaxGrayLevel)
}
}
const (
CheckPointProbe = 1000
CheckPointGray = 15000
)
func (hist *ProbeHistogram) saveLUT(fLUT string) error {
file, err := os.Create(fLUT)
if err != nil {
return err
}
defer file.Close()
for i := 0; i < hist.probes; i++ {
binary.Write(file, binary.LittleEndian, hist.Tmat[i])
if i == CheckPointProbe {
log.Infof("Probes *, LUT check point [%d][%d]: %d", i, CheckPointGray, hist.Tmat[i][CheckPointGray])
}
}
return nil
}
func LoadLUT(fLUT string, probes int) ([][]uint16, error) {
data, err := os.ReadFile(fLUT)
if err != nil {
return nil, err
}
Tmat := make([][]uint16, probes)
for i := 0; i < probes; i++ {
Tmat[i] = make([]uint16, MaxGrayLevel)
binary.Read(bytes.NewReader(data[i*MaxGrayLevel*2:i*MaxGrayLevel*2+MaxGrayLevel*2]), binary.LittleEndian, &Tmat[i])
if i == CheckPointProbe {
log.Infof("Probes[%d], LUT check point [%d][%d]: %d", probes, i, CheckPointGray, Tmat[i][CheckPointGray])
}
}
return Tmat, nil
}
func (hist *ProbeHistogram) sum(hists []*ProbeHistogram) {
for _, h := range hists {
hist.M += h.M
for i := 0; i < hist.probes; i++ {
hist.N_i[i] += h.N_i[i]
for k := 0; k < MaxGrayLevel; k++ {
hist.n_ik[i][k] += h.n_ik[i][k]
}
}
for gray := 0; gray < MaxGrayLevel; gray++ {
hist.m_l[gray] += h.m_l[gray]
}
}
}
func (hist *ProbeHistogram) free() {
hist.N_i = nil
hist.n_ik = nil
hist.p_ik = nil
hist.m_l = nil
hist.P_l = nil
hist.S_ik = nil
hist.V_l = nil
hist.Tmat = nil
}