GDAL RasterIO字节对齐问题

      由于C#版本的GDAL对无法很好的支持中文路径,导致出现很多乱码,使得程序在读取含有中文路径或者名称包含中文字符的文件时不能正常工作。因此采用C++封装需要的GDAL功能(dll),进行底层数据处理,然后采用C# winform做界面并调用封装后的dll文件。

但是在winform中调用封装后的dll文件进行图像数据读取显示的时候出现变形、断层等问题。于是到网上寻求答案,在这位同仁的http://www.cnblogs.com/Romi/archive/2012/03/29/2424073.html看到,在使用RasterIO进行读取栅格数据时,

有时,需要每行进行字节对齐,即每行的字节数为8的倍数,即位数为32的倍数(32位系统一个内存单元是32位),原文中可能是作者笔误的原因bytePerLine=(sizeX*8=31)/32*4,将8后面的加号打成了等号,改正后应该是:bytePerLine=(sizeX*8+31)/32*4。

下面的代码是封装后调用RasterIO进行图像读取的使用实例:

C++封装的代码:

extern "C" __declspec(dllexport) void  CS_RasterIO(
    GDALRasterBand* band, //需要读取数据的波段
    int     eRWFlag,    //读写标志。GF_Read:读取数据到缓存 GF_Write:将缓存中数据写入数据集的波段 
    int     nXOff,   //起始X方向像素位置
    int     nYOff,   //起始Y方向像素位置
    int     nXSize,  //数据的X方向像素大小
    int     nYSize,  //数据的Y方向像素大小     注:以上四个参数制定了要读取数据的位置和大小
    unsigned char* pData,
    int     nBufXSize,  //缓存的X方向像素大小
    int     nBufYSize,  //缓存的Y方向像素大小
    int  nPixelSpace,   //读取每个像素的字节偏移量,即下一个读取的的像素与此时读取的像素的字节距离,默认为0
    int  nLineSpace     //读取每一行像素的字节偏移量,默认为0
    )
{
    GDALRWFlag _eRWFlag;

    if (eRWFlag == 0)
        _eRWFlag = GF_Read;
    else
        _eRWFlag = GF_Write;
    

    band->RasterIO(_eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, GDT_Byte, nPixelSpace, nLineSpace);
}

C#中调用声明:

        [DllImport(@"F:\Project\GDAL\Debug\GDALRasterCharp.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "CS_RasterIO")]
        public static extern void CS_RasterIO(
	                               IntPtr band, //需要读取数据的波段
	                               int eRWFlag,    //读写标志。GF_Read=0:读取数据到缓存 GF_Write=1:将缓存中数据写入数据集的波段 
	                               int     nXOff,   //起始X方向像素位置
	                               int     nYOff,   //起始Y方向像素位置
	                               int     nXSize,  //数据的X方向像素大小
	                               int     nYSize,  //数据的Y方向像素大小     注:以上四个参数制定了要读取数据的位置和大小
                                   byte[] pdata,
	                               int     nBufXSize,  //缓存的X方向像素大小
	                               int     nBufYSize,  //缓存的Y方向像素大小
	                               int  datatype,//缓存数据类型
	                               int  nPixelSpace=0,   //读取每个像素的字节偏移量,即下一个读取的的像素与此时读取的像素的字节距离,默认为0
	                               int  nLineSpace=0     //读取每一行像素的字节偏移量,默认为0
	                               );

  具体使用(调用封装的函数和字节对齐):

 public Bitmap GetImage(Configuration config,IntPtr pDataset)
        {
            if (config == null)
            {
                return null;
            }

            int imgWidth = config.GetRasterRectWidth();   //影像宽
            int imgHeight = config.GetRasterRectHeight();  //影像高

            float ImgRatio = imgWidth / (float)imgHeight;  //影像宽高比

            //获取显示控件大小
            int BoxWidth = config.GetViewAreaRectWidth();
            int BoxHeight = config.GetViewAreaRectHeight();

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

            //计算实际显示区域大小,防止影像畸变显示
            int BufferWidth = 0, BufferHeight = 0;


            if (config.GetZoomRasterRectWidth() == 0)
            {
                if (BoxRatio >= ImgRatio) //判断印象的长宽比
                {
                    BufferHeight = BoxHeight;

                    BufferWidth = (int)(BoxHeight * ImgRatio);
                }
                else
                {
                    BufferWidth = BoxWidth;
                    BufferHeight = (int)(BoxWidth / ImgRatio);
                }
                config.SetZoomRasterRect(0, 0, BufferWidth, BufferHeight);
                config.SetViewDisplayRect(0, 0, BufferWidth, BufferHeight);
            }
            else
            {
                BufferWidth = config.GetZoomRasterRectWidth();
                BufferHeight = config.GetZoomRasterRectHeight();
            }

            int bitmap_X_s = config.GetViewDisplayRectX();//读取图像的起始坐标X
            int bitmap_Y_s = config.GetViewDisplayRectY(); //起始坐标Y
            int bitmap_w = config.GetViewDisplayRectWidth();  //获取显示区域的长宽
            int bitmap_h = config.GetViewDisplayRectHeight();

            int bytePerLine = (bitmap_w * 8 + 31) / 32 * 4;//字节对齐,非常重要,判断需要显示图像的宽度是否是8的倍数

            bitmap_w = bytePerLine;

            float scaleratio = config.GetZoomRasterRectWidth() * 1.0f / config.GetRasterRectWidth();//计算显示区域和图像实际大小的比例

            int orign_x = (int)(bitmap_X_s / scaleratio); //获取缩放后再原始数据中起始位置
            int orign_y = (int)(bitmap_Y_s / scaleratio);

            int orign_width = (int)(bitmap_w / scaleratio);//获取缩放后在原始数据中实际的宽高
            int orign_height = (int)(bitmap_h / scaleratio);

            if (orign_width > config.GetRasterRectWidth())
            {
                orign_width = config.GetRasterRectWidth();
            }

            if (orign_height > config.GetRasterRectHeight())
            {
                orign_height = config.GetRasterRectHeight();
            }

            //构建位图
            Bitmap bitmap = new Bitmap(bitmap_w, bitmap_h,
                                     System.Drawing.Imaging.PixelFormat.Format24bppRgb);


            if (CS_GetGetRasterBandCount(pDataset) >= 3)     //RGB显示
            {
                byte [] _r = new byte[bitmap_w*bitmap_h];
                IntPtr band1 = CS_GetRasterBand(pDataset,1);
                CS_RasterIO(band1, 0, orign_x, orign_y, orign_width, orign_height, _r,bitmap_w, bitmap_h, 0, 0);  //读取图像到内存

                byte[] _g = new byte[bitmap_w * bitmap_h];
                IntPtr band2 = CS_GetRasterBand(pDataset, 2);
                CS_RasterIO(band2, 0, orign_x, orign_y, orign_width, orign_height, _g, bitmap_w, bitmap_h, 0, 0);  //读取图像到内存

                byte[] _b = new byte[bitmap_w * bitmap_h];
                IntPtr band3 = CS_GetRasterBand(pDataset, 3);
                CS_RasterIO(band3, 0, orign_x, orign_y, orign_width, orign_height, _b,bitmap_w, bitmap_h, 0, 0);  //读取图像到内存;

                //要操作的图片区域 操作方式 已经以多少位的方式操作
                BitmapData bmpData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                byte[] byColorInfoSrc = new byte[bmpData.Height * bmpData.Stride];
                for (int i = 0; i < bitmap_w; i++)
                {
                    for (int j = 0; j < bitmap_h; j++)
                    {
                        byColorInfoSrc[(i + j * bitmap_w)*3] =_b[i + j * bitmap_w];
                        byColorInfoSrc[(i + j * bitmap_w) * 3+1] = _g[i + j * bitmap_w];
                        byColorInfoSrc[(i + j * bitmap_w) * 3+2] = _r[i + j * bitmap_w];
                    }
                }

                //操作完之后 数据拷贝回去
                Marshal.Copy(byColorInfoSrc, 0, bmpData.Scan0, byColorInfoSrc.Length);
                bitmap.UnlockBits(bmpData);//解除锁定
            }
            else               //灰度显示
            {
                byte[] _r = new byte[bitmap_w * bitmap_h];
                IntPtr band1 = CS_GetRasterBand(pDataset, 1);
                CS_RasterIO(band1, 0, orign_x, orign_y, orign_width, orign_height, _r, bitmap_w, bitmap_h, 0, 0);  //读取图像到内存

                byte[] byColorInfoSrc = new byte[bitmap_w * bitmap_h * 3];

                //要操作的图片区域 操作方式 已经以多少位的方式操作
                BitmapData bmpData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                for (int i = 0; i < bitmap_w; i++)
                {
                    for (int j = 0; j < bitmap_h; j++)
                    {
                        byColorInfoSrc[(i + j * bitmap_w) * 3] = _r[i + j * bitmap_w];
                        byColorInfoSrc[(i + j * bitmap_w) * 3 + 1] = _r[i + j * bitmap_w];
                        byColorInfoSrc[(i + j * bitmap_w) * 3 + 2] = _r[i + j * bitmap_w];
                    }
                }

                //操作完之后 数据拷贝回去
                Marshal.Copy(byColorInfoSrc, 0, bmpData.Scan0, byColorInfoSrc.Length);
                bitmap.UnlockBits(bmpData);//解除锁定
            }

            return bitmap;
        }

未字节对齐之前的图像显示:

字节对齐之后的图像显示:

 

很神奇是不是!

第一次写博客,可能有许多表述不清的地方,希望能够谅解!同时欢迎留言讨论交流!!

posted @ 2016-10-12 11:37  氕氘钏  阅读(618)  评论(0编辑  收藏  举报