• C#中使用Bitmap 传递图到C++


     bmp.UnlockBits(bmpData); 

    1. System.Drawing.Imaging.BitmapData bmpData = new System.Drawing.Imaging.BitmapData();
    2. int imageWeidth = 0;
    3. int imageHeight = 0;
    4. int imageStride = 0;
    5. //GetImgByte(path, ref bmpData, ref imageWeidth, ref imageHeight, ref imageStride);
    6. Bitmap bmpInitial = (Bitmap)System.Drawing.Image.FromFile(path);
    7. Bitmap bmp = null;
    8. //将图像转换为24位rgb图像
    9. if (bmpInitial.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb)
    10. {
    11. Console.WriteLine("转换8to24:{0}", path);
    12. bmp = new Bitmap(bmpInitial.Width, bmpInitial.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
    13. using (Graphics g = Graphics.FromImage(bmp))
    14. {
    15. g.DrawImage(bmpInitial, new Rectangle(0, 0, bmpInitial.Width, bmpInitial.Height));
    16. }
    17. }
    18. else
    19. {
    20. bmp = bmpInitial;
    21. }
    22. //Console.WriteLine("----****************333-{0}", strTime3);
    23. //设置dpi=96
    24. //bmp.SetResolution(96, 96);
    25. System.Drawing.Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
    26. bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);
    27. //Get the address of the first line.
    28. IntPtr ptr = bmpData.Scan0;
    29. // Declare an array to hold the bytes of the bitmap.
    30. int bytesLength = Math.Abs(bmpData.Stride) * bmp.Height;
    31. imageWeidth = bmp.Width;
    32. imageHeight = bmp.Height;
    33. //图像的Stride
    34. imageStride = bmpData.Stride;
    35. //Console.WriteLine("tuxiang:{0},{1},{2}", imageWeidth, imageHeight, imageStride);
    36. byte[] buffer = new byte[bytesLength];
    37. // Copy the RGB values into the array.
    38. Marshal.Copy(ptr, buffer, 0, bytesLength);
    39. int count = 0;
    40. bool isbool = false;
    41. IntPtr ptr1 = DetectOutByteCls(cls, bmpData.Scan0, imageWeidth, imageHeight, imageStride, ref count, ref isbool, 1.0, false);
    42. bmp.UnlockBits(bmpData); // 在使用之后释放
    43. long a = ptr1.ToInt64();
    44. string retstr = "------thread:" + name + " a:" + a + " count" + count;

    使用lockbits方法处理图像

        许多图像处理任务即时是最简单的文件类型转换,例如从32位深度到8位深度的格式转化,直接获得像素阵列要比使用GetPixel和SetPixel等方法的效率高得多。

        你可能会发现DotNet采用托管机制,大多数情况下微软会推荐你使用托管代码,理由是便捷和安全。实际应用中,直接操作内存中的数据块是很少见的,尽管如此,图像处理恰恰是这类为数不多的情况之一,因为使用托管代码的效率低的难以忍受,特别是对巨幅图像来说,在此,我们讨论一下一种新的方法。

         如何使用非托管代码是因语言而异的,在C#中我们可以通过unsafe关键字来调用指针,从而直接操作内存中的位图数据;VB则使用Marshal类中的方法,它会导致一部分的性能损失,因此效率不如前者。

    锁定比特流

        Bitmap类使用LockBits和UnLockBits方法来将位图的数据矩阵保存在内存中、直接对它进行操作,最后用修改后的数据代替位图中的原始数据。LockBits返回以各BitmapData的类用已描述数据在已锁定的矩阵中的位置和分布。

        BitmapData类包括以下几个重要的属性:

    Scan0:数据矩阵在内存中的地址。

    Stride:数据矩阵中的行宽,以byte为单位。可能会扩展几个Byte,后面会介绍。

    PixelFormat:像素格式,这对矩阵中字节的定位很重要。

    Width:位图的宽度。

    Height:位图的高度。

    stride属性表示位图数据矩阵的行宽,以byte为单位。出于效率考虑,矩阵的行宽并非刚好是每行像素数的整数倍,系统往往会将其封装成4的整数倍。举例来说,对于一幅24位深17像素宽的图像,其stride属性为52;每行的数据量为17*3=51,系统将其自动封装一个字节,所以它的stride为52byte(或13*4byte)。对于一幅17像素宽的4位索引图,其stride为12,其中9byte(准确地说是8.5个byte)用来记录数据信息,每行再自动添加3(3.5)个byte保证其为4的整数倍。

        具体数据的分布因其pixel format而异。24位深的图像每隔3个byte包含一组RGB信息;32位深的图像每隔4个byte包含一组RGBA信息。那些每个字节包含多个像素的pixel format,比如4位索引图像或1位索引图像,必须经过仔细处理,从而保证同一字节中的相邻byte不会混淆。

    指针的准确定位

    32位RGB:假设X、Y为位图中像素的坐标,则其在内存中的地址为scan0+Y*stride+X*4。此时指针指向蓝色,其后分别是绿色、红色,alpha分量。

    24位RGB:scan0+Y*stride+X*3。此时指针指向蓝色,其后分别是绿色和红色。

    8位索引:scan0+Y*stride+X。当前指针指向图像的调色盘。

    4位索引:scan0+Y*stride+(X/2)。当前指针所指的字节包括两个像素,通过高位和低位索引16色调色盘,其中高位表示左边的像素,低位表示右边的像素。

    1位索引:scan0+Y*stride+X/8。当前指针所指的字节中的每一位都表示一个像素的索引颜色,调色盘为两色,最左边的像素为8,最右边的像素为0。

    像素间使用迭代器

        下面这个范例将一幅32位深的图像中所有像素的蓝色分量设为最大(255):

        BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10),   System.Drawing.Imaging.ImageLockMode.ReadOnly, bm.PixelFormat);

          int PixelSize=4;

          for(int y=0; y

          {

            byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);

            for(int x=0; x

            {

              row[x*PixelSize]=255;

            }

          }

          处理4位索引图,高低位应分开处理,代码如下:

          int offset = (y * bmd.Stride) + (x >> 1);

          byte currentByte = ((byte *)bmd.Scan0)[offset];

          if((x&1) == 1)

          {

            currentByte &= 0xF0;

            currentByte |= (byte)(colorIndex & 0x0F);

          }

          else

          {

            currentByte &= 0x0F;

            currentByte |= (byte)(colorIndex << 4);

          }

          ((byte *)bmd.Scan0)[offset]=currentByte;

          处理1位索引的代码:

          byte* p=(byte*)bmd.Scan0.ToPointer();

          int index=y*bmd.Stride+(x>>3);

          byte mask=(byte)(0x80>>(x&0x7));

          if(pixel)

            p[index]|=mask;

          else

            p[index]&=(byte)(mask^0xff);

          最后在进行完所有处理后马不要忘记使用Unlockbits命令解锁。

    (二)
    C#中可以利用非安全指针对位图进行处理,下面利用MSDN的一个例子进行讲解:

    Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");


    //读取本地图像


    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    System.Drawing.Imaging.BitmapData bmpData = 
    bmp.LockBits(rect,    System.Drawing.Imaging.ImageLockMode.ReadWrite,
    bmp.PixelFormat);

    //BitmapData 指定 Bitmap 的属性,如大小、像素格式、像素数据在内存中的始地址以及每个扫描行的长度(步幅)。

    //lockbit将位图锁定到内存中,这样做主要是增加效率,如果速度不是主要因素的话,建议使用bitmap的setpixel和getpixel方法,省去了使用不安全指针的麻烦。

    IntPtr ptr = bmpData.Scan0;
    //Scan0获取或设置位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行。简言之就是把当前指针设置在图像最左上角的位置。 

    int bytes = bmp.Width * bmp.Height * 3;
    byte[] rgbValues = new byte[bytes];

    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
      
    for (int counter = 0; counter < rgbValues.Length; counter+=3)
            rgbValues[counter] = 255;
      
    System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);

    //将数据从托管数组复制到非托管内存指针,或从非托管内存指针复制到托管数组,此处为前者。

    bmp.UnlockBits(bmpData);
    //解锁
      

    e.Graphics.DrawImage(bmp, 0, 150);

    想对lockbit有所了解的同学可以看《Using the LockBits method to access image data》。

    下面附上一个实例,其中原理是凭着研一时的数字图像处理课。

    马赛克范例

    很多图片处理的算法从原理上讲其实非常简单,难点往往在如何去写算法实现它,更加难的就是如何去优化实现的算法。

    马赛克算法很简单,说白了就是把一张图片分割成若干个val * val像素的小区块(可能在边缘有零星的小块,但不影响整体算法),每个小区块的颜色都是相同的。为了方便起见,我们不妨让这个颜色就用该区域最左上角的那个点的颜色。当然还可以有其他方法,比如取区块中间点的颜色,或区块中随机点的颜色作代表等等。

    下面的示意图就是取val=2的结果。

    原图像素
    ABCDEFG
    HIJKLMN   
    OPQRSTU   
    VWXYZ01   
    2345678   

    马赛克处理后
    AACCEEG
    AACCEEG
    OOQQSSU
    OOQQSSU
    2244668

            public static Bitmap KiMosaic(Bitmap b, int val)
            {
                if (b.Equals(null))
                {
                    return null;
                }

                int w = b.Width;
                int h = b.Height;

                int stdR, stdG, stdB;

                stdR = 0;
                stdG = 0;
                stdB = 0;

                BitmapData srcData = b.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

                unsafe
                {
                    byte* p = (byte*)srcData.Scan0.ToPointer();

                    for (int y = 0; y < h; y++)
                    {
                        for (int x = 0; x < w; x++)
                        {
                            if (y % val == 0)
                            {
                                if (x % val == 0)
                                {
                                    stdR = p[2]; stdG = p[1]; stdB = p[0];
                                }
                                else
                                {
                                    p[0] = (byte)stdB;
                                    p[1] = (byte)stdG;
                                    p[2] = (byte)stdR;
                                }
                            }
                            else
                            {
                                // 复制上一行
                                byte * pTemp = p - srcData.Stride;

                                p[0] = (byte)pTemp[0];
                                p[1] = (byte)pTemp[1];
                                p[2] = (byte)pTemp[2];
                            }

                            p += 3;

                        } // end of x

                        p += srcData.Stride - w * 3;

                    } // end of y

                    b.UnlockBits(srcData);
                }

                return b;

            }
     

    C#的Bitmap.LockBits 使用说明_无敌三角猫的博客-CSDN博客_lockbits

    [C#]LockBits使用笔记 - VD630 - 博客园

  • 相关阅读:
    【观察】华为:加速行业智能化,正在“走深向实”
    Mybatis generator
    JavaScript小技能: Ajax
    图片如何加水印?教你几招轻松加
    10.7、华为数通HCIP-DataCom H12-821单选题:121-140
    马铃薯甲虫的成虫和幼虫数据集(YOLO检测)
    Acwing刷题
    .NET6发布项目到腾讯云Windows2012R全网最详细教程
    嵌入式开发:用DSP软件替代模拟组件的5大好处
    明明是旅游小程序却做起了内容电商?
  • 原文地址:https://blog.csdn.net/weixin_37989267/article/details/127639166