C# GDAL 数字图像处理Part2 Band合成BMP

        在成功读取dataset之后,我们应该进行图像的显示,究竟如何把三个Band读取出来合成为标准的BMP图像呢?(读取dataset见:C# GDAL 数字图像处理Part1 安装以及读取Dataset_A_RSswjtuer的博客-CSDN博客

        首先,我们知道,读取的图像尺寸和PictureBox的尺寸总是不一样的,我们应该保持图像的原高宽比进行显示,才不至于导致图像的畸变。

        比如出现这样的情况:

        

         我们应该做的是,把Image的宽设置为Box的宽,然后根据Image原始的高宽比,求Image新的高(若Image是比Box高的也同理)。结果如下:

        

         我们将保持高宽比显示后新的Image的宽和高称为BufferWidth和BufferHeight(Buffer:缓冲,缓冲区),它们就是我们创建BMP图像的宽和高。

        之后,我们使用Band中的ReadRaster函数读取Band里的每一个像素的数据,此函数的参数如下:       

 ReadRaster(int xOff, int yOff,      //读取的起始位置

                            int xSize, int ySize,  //在原图中要读取的尺寸

                            int[] buffer,  //接收数据的数组

                            int buf_xSize, int buf_ySize,  //从原图中读取出来后变成的尺寸

                            int pixelSpace, int lineSpace) //读取时x和y方向上的间隔,即是否需要隔行读取

        这里,如果从原图中读取数据的尺寸和最后Buffer的尺寸不一样,Gdal的函数会自动进行插值,最后把长度为 buf_xSize*buf_ySize 的数塞到buffer数组里,所以在定义buffer数组时,要仔细检查它的长度和 buf_xSize*buf_ySize 一致。

        获取该波段的数据之后,把它逐个读取到Bitmap的R/G/B通道中即可,因为有三个通道,所以我们在显示彩色图像时需要三个波段,在显示单波段图像时把RGB都设为相同的值即可。

        But!在原始的TIF或IMG数据中,读取出来的数值可能是超过了8Bit的数值,我们还需要将它缩放到0-255之间,这样才能顺利地合成BMP图像。这里作者提供两种简单的拉伸方法:

        一、简单截断:

        对于大于255的数值,我们全部设置为255;对于小于0的数值,全部设置为0。为了使这一函数更为灵活,我们把最大阈值和最小阈值变量化。函数如下:

    private int clip_array(int min, int max,int Val) //截断
        {
            if (Val> max)
                Val= max;
            if (Val< min)
                Val= min;
            return Val;
        }

        这样,就是一个简单的截断函数。

        二、简单拉伸:

        简单拉伸是最基本的拉伸方法,它的公式如下:

        Val = \tfrac{Val - Min}{Max - Min} \times (255 - 0)

        这里的(255-0)也可以换成其他的想要变换成的范围的最大值减最小值。这个公式是计算原值在原范围内的百分比数,然后乘以新的范围,获得在新范围内的相应数值,如此即可缩放到新的范围内。在函数中,我们将“255”和“0”也变量化,使得函数更灵活。函数如下:

private int Linear_strentch(int Val,int[] Pminmax,int[] Nminmax)
        {
            //Pminmax是长度为2的数组,第一个数是原范围的Min,第二个数是原范围的Max
            //Nminmax同理
            return (int)((double)(Val - Pminmax[0]) / (double)(Pminmax[1] - Pminmax[0]) * (double)(Nminmax[1] - Nminmax[0]));
        }

       获得了合适的像素值之后,我们便可以通过BMP类的SetPixel函数将像素值设到某个像素的R/G/B通道中。

        讲完了基本流程,我们来看看函数实现。

        public System.Drawing.Bitmap Bands2BMP(OSGeo.GDAL.Dataset dt,int[] bands_idxs, int BoxWidth, int BoxHeight, int stretch = 0)
        {
            /*Paras:
                dt: 图像的Dataset
                bands_idxs: 要显示的波段的索引组成的数组,比如[1,2,3]就是从dt里抽取这三个波段进行合成图像
                BoxWidth: 显示框的宽
                BoxHeight: 显示框的高
                stretch: 指定拉伸的方式
            */
            foreach(int idx in bands_idxs)
            {
                if(idx > dt.RasterCount || idx < 1)
                {
                    MessageBox.Show("这是一个编程错误,在三波段融合BMP中传入的波段值超出了Dataset的范围!", "错误!");
                    return null;
                }
            }

            int ImgWidth = dt.RasterXSize;
            int ImgHeight = dt.RasterYSize;

            float ImgRatio = ImgWidth / (float)ImgHeight;  //影像宽高比

            float BoxRatio = BoxWidth / (float)BoxHeight;  //显示控件宽高比


            int BufferWidth, BufferHeight;
            if (BoxRatio >= ImgRatio) //相同高度下Box更宽或一样宽,相同宽度下Box更矮
            {
                BufferHeight = BoxHeight;
                BufferWidth = (int)(BufferHeight * ImgRatio);
            }
            else //相同高度下Box更窄,相同宽度下Box更高
            {
                BufferWidth = BoxWidth;
                BufferHeight = (int)(BufferWidth / ImgRatio);
            }
            //这里进行的就是计算Buffer的尺寸

            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
                BufferWidth,
                BufferHeight,
                System.Drawing.Imaging.PixelFormat.Format24bppRgb
                );//定义一定尺寸的BMP图

            int[] R_data = new int[BufferWidth * BufferHeight];//存储在R通道显示的数据
            OSGeo.GDAL.Band band1 = dt.GetRasterBand(bands_idxs[0]);//读取对应的波段
            band1.ReadRaster(0, 0, ImgWidth, ImgHeight, R_data, BufferWidth, BufferHeight, 0, 0);//将波段的数据塞入存储数据的数组中

            int[] G_data = new int[BufferWidth * BufferHeight];
            OSGeo.GDAL.Band band2 = dt.GetRasterBand(bands_idxs[1]);
            band2.ReadRaster(0, 0, ImgWidth, ImgHeight, G_data, BufferWidth, BufferHeight, 0, 0);

            int[] B_data = new int[BufferWidth * BufferHeight];
            OSGeo.GDAL.Band band3 = dt.GetRasterBand(bands_idxs[2]);
            band3.ReadRaster(0, 0, ImgWidth, ImgHeight, B_data, BufferWidth, BufferHeight, 0, 0);

            int i, j;

            if(stretch == 0)//不拉伸
            {
                for (i = 0; i < BufferWidth; i++)
                {
                    for (j = 0; j < BufferHeight; j++)
                    {
                        int rVal = this.clip_array(0, 255, System.Convert.ToInt32(R_data[i + j * BufferWidth])); //获得对应位置的数值

                        int gVal = this.clip_array(0, 255, System.Convert.ToInt32(G_data[i + j * BufferWidth]));

                        int bVal = this.clip_array(0, 255, System.Convert.ToInt32(B_data[i + j * BufferWidth]));

                        System.Drawing.Color newColor = System.Drawing.Color.FromArgb(rVal, gVal, bVal); 
                        //Color类对象通过指定RGB的值构造
                        bitmap.SetPixel(i, j, newColor);
                        //使用SetPixel函数将指定位置的像素设置为指定Color
                    }
                }
            }
            else if(stretch == 1) //简单拉伸
            {
                double[] maxandmin1 = { 0, 0 };
                band1.ComputeRasterMinMax(maxandmin1, 0);
                //ComputeRasterMinMax是Gdal的Band中自带的函数,参数一是需要我们定义的存储最大最小值的数组,参数二可以不用,设为0.

                double[] maxandmin2 = { 0, 0 };
                band2.ComputeRasterMinMax(maxandmin2, 0);

                double[] maxandmin3 = { 0, 0 };
                band3.ComputeRasterMinMax(maxandmin3, 0);

                for (i = 0; i < BufferWidth; i++)
                {
                    for (j = 0; j < BufferHeight; j++)
                    {
                        int rVal = System.Convert.ToInt32(R_data[i + j * BufferWidth]);
                        rVal = this.Linear_strentch(rVal, new int[2] { (int)maxandmin1[0], (int)maxandmin1[1] }, new int[2] { 0, 255 });
                        //将原范围拉伸至0-255之间。

                        int gVal = System.Convert.ToInt32(G_data[i + j * BufferWidth]);
                        gVal = this.Linear_strentch(gVal, new int[2] { (int)maxandmin2[0], (int)maxandmin2[1] }, new int[2] { 0, 255 });

                        int bVal = System.Convert.ToInt32(B_data[i + j * BufferWidth]);
                        bVal = this.Linear_strentch(bVal, new int[2] { (int)maxandmin3[0], (int)maxandmin3[1] }, new int[2] { 0, 255 });


                        System.Drawing.Color newColor = System.Drawing.Color.FromArgb(rVal, gVal, bVal);
                        bitmap.SetPixel(i, j, newColor);
                    }
                }
            }
            return bitmap;
        }

        

        至此。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值