C# GDAL 数字图像处理Part3 获得统计信息与直方图数组

        在作业中,我们收到的第一个任务便是自己写获得Band的统计信息的函数,当然这部分函数不在此多做讲解,因为Gdal中自带有这样的“轮子”,我们不应该重复造“轮子”。然而在BMP图像中,就没有获得统计信息的轮子,所以我们得自己书写。这篇文章写的就是如何获得Band的统计信息,以及如何对BMP进行统计。

        一、Gdal中获取Band的统计信息

        首先,如何获得Band的最大值,最小值,均值,方差?我们使用这个函数: 

Band.ComputeStatistics(bool approx_ok,

                                   out double min, out double max, out double mean, out double stddev, 

                                        Gdal.GDALProgressFuncDelegate callback, string callback_data)
    /*Paras:
        approx_ok:在第一次统计时,会生成一个记录统计结果的文件。在之后的运行中,若这个值为true,则会直接从文件里提取统计信息,速度较快;或为false,则会重新进行统计,速度较慢
        min,max,mean,stddev:将在函数前定义的用于接收最小值、最大值、均值、标准差的变量传入,在运行函数后这四个值会输出到这几个变量中
        callback,callback_data:获得统计进度的参数,可以全为null
    */

               

    //使用示例
    double mymin,mymax,mymean,mystddev;

    //My_Band是自己从dataset中取得的Band
    My_Band.ComputeStatistics(true, out mymin, out mymax, out mymean, out mystddev, null, null)
    //注意,这里获得的是标准差,并不是方差
    

        如此便可以获得波段的最小值最大值均值方差。接下来介绍从Band里获得画直方图所需的灰度值统计信息的函数。

        Band.GetHistogram(double min, double max, 
                int buckets, int[] panHistogram, 
                int include_out_of_range, int approx_ok, 
                Gdal.GDALProgressFuncDelegate callback, string callback_data);
    /*Paras:
        min,max: 函数对波段中值在 (min,max) 范围内的数据进行统计,表示统计范围
        buckets: 统计的份数。比如统计8bit的图像分为256份,这个值便是256
        panHistogram: 用于接收统计结果的数组,需要在函数前定义,数组的长度应该等于buckets
        include_out_of_range: 是否统计 (min,max) 范围外的值
        approx_ok: 是否从文件中统计(上面解释过)
        callback,callback_data: 返回统计的进度,可以为null
    */

    //使用实例如下,假设要统计一个8bit的波段获得统计直方图信息
    //My_Band是自己从dataset中取得的Band

    double[] Histogram = new double[256];
    My_Band.GetHistogram(min - 0.5, max + 0.5, 256, Histogram, 0, 1, null, null);
    //min与max是从Band中获得的最小最大值,这里也可以 -/+ 0.1

        这样获得Histogram数组,Histogram[0]便是Band里值为0的个数,Histogram[128]便是Band里值为128的个数

        二、获取BMP的统计信息

        对于BMP,我们需要自己写统计的函数,其实没什么难度,统计最小值、最大值、均值、方差的函数很好写。但在这之前,我们思考一个问题:

        当我们通过Bitmap.GetPixel(x,y)获取一个Color时,我们需要通过Color.R/Color.G/Color.B获得R/G/B通道的值。当统计三个通道时,我们如果去写三遍代码,如:

        统计R波段

        统计G波段

        统计B波段

        的话,未免也太傻了,不符合程序员“重复的事绝对不做”的特点。所以我们希望构造一个函数,这个函数的功能是传入一个Color和一个为 1/2/3 的整数,就可以获取 R/G/B 的值。大概意思是:

        for(int i = 1; i <= 3; i++)

                统计i波段

        这样来看,是不是好很多呢?这个函数其实很简单,意在让同学们体会一下编程思想,在写代码中构造重用性鲁棒性灵活性更高的函数,这能很好地减轻我们的工作量,并增强我们的抽象思维。

        如下:

        private int Get_rgb(System.Drawing.Color color,int channel)//获得RGB
        {
            switch(channel)
            {
                case 1:
                    return color.R;
                case 2:
                    return color.G;
                case 3:
                    return color.B;
            }
            return 0;
        }

        这样,我们就可以写出统计BMP某个通道统计信息的函数:

private double[] BMP_Statistics(System.Drawing.Bitmap bitmap,int BandSelect)
        {
            if(BandSelect<1||BandSelect>3)
            {
                MessageBox.Show("这是一个编程错误,在BMP统计函数中要统计的波段索引超出了范围", "错误!");
                return null;
            }

            double[] data = new double[4] { 255,0,0,0 };
            //最小值、最大值、均值、方差
            
            for(int y = 0;y data[1])//大于最大值则替换最大值
                        data[1] = this.Get_rgb(bitmap.GetPixel(x, y), BandSelect);

                    data[2] += this.Get_rgb(bitmap.GetPixel(x, y), BandSelect);//值的总和
                }

            data[2] = data[2] / (bitmap.Height * bitmap.Width); //获得平均值

            for (int y = 0; y < bitmap.Height; y++)
                for (int x = 0; x < bitmap.Width; x++)
                {
                    data[3] += System.Math.Pow(this.Get_rgb(bitmap.GetPixel(x, y), BandSelect) - data[2], 2.0); //计算方差
                }

            data[3] = data[3] / (bitmap.Height * bitmap.Width); //获得方差

            return data; //返回数组
        }

        如果不用Get_rgb这一函数,是否又有什么简洁的统计三个波段的方法呢?

        接下来我们想要获得Bitmap的灰度统计信息,其实也很简单,就是在遍历的同时在数组的相应索引上+1。函数如下:

        private int[] BMP_Histogram(System.Drawing.Bitmap bitmap, int BandSelect)//BMP直方图统计信息
        {
            if (BandSelect < 1 || BandSelect > 3)
            {
                MessageBox.Show("这是一个编程错误,在BMP统计函数中要统计的波段索引超出了范围", "错误!");
                return null;
            }
            int[] his = new int[256]; //定义接收数据的数组

            for (int y = 0; y < bitmap.Height; y++)
                for (int x = 0; x < bitmap.Width; x++)
                {
                    his[this.Get_rgb(bitmap.GetPixel(x, y), BandSelect)]++;
                } 
            //由于Histogram[n]表示像素值为n的像素个数,所以在这里反过来用像素值作为数组的索引。

            return his;
        }

        三、灰度统计直方图变为累计直方图

        在直方图均衡化中,我们需要获得相对累计直方图,既然在上面我们已经获得了灰度直方图,那我们就可以制作一个函数,输入灰度直方图的数组,输出累计直方图/相对累计直方图的数组。先来说思想:

        假设直方图数组: int[] His = new int[256]

        累计直方图:

                for(int i = 1; i < His.Length; i++) //注意是从1开始到最后一个结束

                        His[i] += His[i-1] ;

        若需要相对累计直方图,则再遍历一次His数组,每个数都除以全部像素值的总和即可。函数如下:

        private double[] Cumulate_Histogram(int[] Histogram, bool ToPercent = false, bool include_zero = true)//将直方图变换成累计直方图
        {
            double[] CuHis = new double[Histogram.Length]; //定义相同长度的double类型数组

            for (int i = 0; i < Histogram.Length; i++)
            {
                CuHis[i] = (double)Histogram[i]; //数据转为double类型
            }

            if (include_zero == false) //是否统计0
                CuHis[0] = 0.0;

            for (int i = 1; i < CuHis.Length; i++) //获得累计的直方图
            {
                CuHis[i] += CuHis[i - 1];
            }

            if (ToPercent)   //若要获得相对累计直方图
            {
                for (int i = 0; i < CuHis.Length; i++)
                {
                    CuHis[i] = CuHis[i] / CuHis[CuHis.Length - 1];
                }
            }
            return CuHis;
        }

        至此,便是统计信息与统计直方图的内容

你可能感兴趣的:(数字图像处理,1024程序员节,c#,图像处理)