package rrc import ( "bytes" "encoding/binary" "fmt" "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") } } 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]) } return nil } const ( CheckPointProbe = 1000 CheckPointGray = 15000 ) 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] } } fmt.Println("Hist.M:", hist.M) fmt.Println("Hist.probes:", hist.probes) }