C# BitmapData和Marshal.Copy()用法

//此函数用法例子如下:

public static byte[] GetGrayArray(Bitmap srcBmp, Rectangle rect)
{
//将Bitmap锁定到系统内存中,获得BitmapData
//这里的第三个参数确定了该图像信息时rgb存储还是Argb存储
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
IntPtr srcPtr = srcBmpData.Scan0;
//将Bitmap对象的信息存放到byte数组中
int scanWidth = srcBmpData.Width * 3;
int src_bytes = scanWidth * rect.Height;
//int srcStride = srcBmpData.Stride;
byte[] srcValues = new byte[src_bytes];
byte[] grayValues = new byte[rect.Width * rect.Height];
//RGB[] rgb = new RGB[srcBmp.Width * rows];
//复制GRB信息到byte数组
Marshal.Copy(srcPtr, srcValues, 0, src_bytes);
//LogHelper.OutputArray(srcValues, rect.Width * 3, rect.Height, true);
//Marshal.Copy(dstPtr, dstValues, 0, dst_bytes);
//解锁位图
srcBmp.UnlockBits(srcBmpData);
//灰度化处理
int m = 0, j = 0;
int k = 0;
byte gray;
//根据Y = 0.299*R + 0.587*G + 0.114*B,intensity为亮度
for (int i = 0; i < rect.Height; i++) //行
{
for (j = 0; j < rect.Width; j++) //列
{
//注意位图结构中RGB按BGR的顺序存储
k = 3 * j;
gray = (byte)(srcValues[i * scanWidth + k + 2] * 0.299
+ srcValues[i * scanWidth + k + 1] * 0.587
+ srcValues[i * scanWidth + k + 0] * 0.114);
grayValues[m] = gray; //将灰度值存到byte一维数组中
m++;
}
}
return grayValues;
}

/*
以上方法,主要是实现一个图像指定矩形区域的信息进行处理,
以一维数组形式返回指定矩形区域的灰度值,
此方法会遇到一个很棘手的问题
假设一张图像的大小256*256,想要处理图像的上下部分区域为256*10,左右部分区域10*256的信息,
则创建矩形区域为:
*/
// 上部分区域:
Rectangle rectTop = new Rectangle(0,0,256,10);
// 下部分区域:
Rectangle rectBottom = new Rectangle(0,256-10,256,10);
// 左部分区域:
Rectangle rectLeft = new Rectangle(0,0,10,256);
// 有部分区域:
Rectangle rectRight = new Rectangle(256-10,0,10,256);

/*
此时上下部分没有问题,左右部分则会出现问题,原因很简单
阐述如下:
是BitmapData,Marshal.Copy()函数造成的
C#处理图像时使用BitmapData,这个是将处理的图像锁定到内存中,为了提高效率,将图像锁定要内存
IntPtr srcPtr = srcBmpData.Scan0;这个是指向锁定图像的第一个地址,然后按照顺序读取像素数据
,这时,左右部分区域的宽度只有10,如果这一行读取完之后,应该此时换行读取下一行像素数据,
但是实际上它没有,而是一直按照顺序读取,没有换行,此时问题就出现了,
因为左区域是图像的一部分,整个图像比方如下:
“-”表示左区域,即要处理的部分;“+”表示图像的剩余部分
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
---+++++++++++++++++++++++
扫描时第一行读取到第三个“-”号之后,应该换行,但是没有,它继续读取“++++”,一直读取到行尾,
这才换行,问题就是这么产生的,
上图如果没有理解的,接下来我画一个图如下:

思考一下,两种办法如下
[A]首先,想到的最好的办法我要锁定哪一部分区域就要取得相应区域的像素数据,但是目前这个
我没有实现
[B]其次,我用了两外一种方法解决了,就是将源图像的要计算的区域或者要处理的区域图像裁切好,
因为源图像后面是要用的,所以裁切后得到的图像另保存之,传递给方法,然后将上述方法修改为
*/

///这里的srcBmp是裁切后要处理的区域的图像
public static byte[] GetGrayArray(Bitmap srcBmp)
{
//将Bitmap锁定到系统内存中,获得BitmapData
//这里的第三个参数确定了该图像信息时rgb存储还是Argb存储
Rectangle rect = new Rectangle(0,0,srcBmp.Width,srcBmp.Height); //表示要锁定全图
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
IntPtr srcPtr = srcBmpData.Scan0;
//将Bitmap对象的信息存放到byte数组中
int scanWidth = srcBmpData.Width * 3;
int src_bytes = scanWidth * rect.Height;
//int srcStride = srcBmpData.Stride;
byte[] srcValues = new byte[src_bytes];
byte[] grayValues = new byte[rect.Width * rect.Height];
//RGB[] rgb = new RGB[srcBmp.Width * rows];
//复制GRB信息到byte数组
Marshal.Copy(srcPtr, srcValues, 0, src_bytes);
//LogHelper.OutputArray(srcValues, rect.Width * 3, rect.Height, true);
//Marshal.Copy(dstPtr, dstValues, 0, dst_bytes);
//解锁位图
srcBmp.UnlockBits(srcBmpData);
//灰度化处理
int m = 0, j = 0;
int k = 0;
byte gray;
//根据Y = 0.299*R + 0.587*G + 0.114*B,intensity为亮度
for (int i = 0; i < rect.Height; i++) //行
{
for (j = 0; j < rect.Width; j++) //列
{
//注意位图结构中RGB按BGR的顺序存储
k = 3 * j;
gray = (byte)(srcValues[i * scanWidth + k + 2] * 0.299
+ srcValues[i * scanWidth + k + 1] * 0.587
+ srcValues[i * scanWidth + k + 0] * 0.114);
grayValues[m] = gray; //将灰度值存到byte一维数组中
m++;
}
}
return grayValues;
}

  

//这样也能解决上述问题,求哪位大神赐教方法[A]的实现,谢谢,这个问题先记录到此。

//修改部分:
//今天又发现一个问题,左右部分图像裁切后处理的时候自动才每一行后面加上两个0,0
//这就导致计算出现问题了,为了解决此问题,可以讲上面的方法再进行修改
//只需要将上述方法中的
int scanWidth = srcBmpData.Width * 3;
//修改为
int scanWidth = srcBmpData.Stride;

//为什么用srcBmpData.Width * 3作为扫描行宽度我当时是这么理解的,上面的
BitmapData srcBmpData = srcBmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//用的是PixelFormat.Format24bppRgb这个,这个表示每个像素是24为,红色,绿色,蓝色各使用8位
//那么byte[]数组用来存储的时候相当于宽度*3,其实这里如果是截图图像的水平区域的话没有问题
//要是截取的是图像的左右两侧部分区域则会出现上述问题,即每一行后面会多出两个字节,值为0,0
//至此测试通过,问题也算完美的解决了

C# BitmapData和Marshal.Copy()用法的更多相关文章

  1. Marshal.Copy将指针拷贝给数组

    lpStatuss是一个UNITSTATUS*的指针类型实例,并包含SensorDust字段 //定义一个数组类型 byte[] SensorDust = new byte[30] //将指针类型拷贝 ...

  2. 使用Marshal.Copy把Txt行数据转为Struct类型值

    添加重要的命名空间: using System.Runtime.InteropServices; 先建立结构相同(char长度相同)的Struct类型用于转换: [StructLayout(Layou ...

  3. C#调用C++ memcpy实现各种参数类型的内存拷贝 VS marshal.copy的实现 效率对比

    using System; using System.Runtime.InteropServices; using System.IO; namespace tx { struct ST { publ ...

  4. c#中Marshal.Copy()方法的使用

    c#中Marshal.Copy方法的使用 Marshal.copy()方法用来在托管对象(数组)和非托管对象(IntPtr)之间进行内容的复制 函数有很多重载,如下所示: Copy(array< ...

  5. Golang(Go语言)内置函数之copy用法

    该函数主要是切片(slice)的拷贝,不支持数组 将第二个slice里的元素拷贝到第一个slice里,拷贝的长度为两个slice中长度较小的长度值 示例: s := []int{1,2,3} fmt. ...

  6. python shutil.copy()用法

    shutil.copyfile(src, dst):复制文件内容(不包含元数据)从src到dst. DST必须是完整的目标文件名; 如果src和dst是同一文件,就会引发错误shutil.Error. ...

  7. MonoGame 3.2 下,截屏与 Texture2D 的保存

    10月20日注:后来发现了这篇博文(英文),XNA 中的 Color 实际上是与 Alpha 值自左乘(premultiplied)的,这也解释了直接用 0xARGB 转译而颜色异常的原因. 注意,由 ...

  8. Base64 字符串转图片 问题整理汇总

    前言 最近碰到了一些base64字符串转图片的开发任务,开始觉得没啥难度,但随着开发的进展还是发现有些东西需要记录下. Base64 转二进制 这个在net有现有方法调用: Convert.FromB ...

  9. 基于GDI和D3D的抓屏技术

    GDI32Api.Direct3D屏幕截图 最近因为工作需要,认真研究了一下屏幕截图的方法. 最主要的方法有两种,一.调用windows GDI32 API函数.二.使用DirectX9.0来实现. ...

随机推荐

  1. Simple calculations

    Description 有一个包括n+2个元素的数列a0, a1, ..., an+1 (n <= 3000, -1000 <= ai <=1000).它们之间满足ai = (ai- ...

  2. VS2008转VS2013时遇到的问题

    最近我们要把DPM进行行人检测嵌入到我们的项目里,需要一个高级版本的VS,于是我们要把2008转换成2013,至于为什么没有换成最高级的版本,可能担心会遇到有更多的麻烦吧,毕竟我们的DPM源码是在20 ...

  3. TestNG – Dependency Test

    转自:http://www.mkyong.com/unittest/testng-tutorial-7-dependency-test/ In TestNG, we use dependOnMetho ...

  4. (转载)display:inline、block、inline-block的区别

    display:block就是将元素显示为块级元素. block元素的特点是: 总是在新行上开始: 高度,行高以及顶和底边距都可控制: 宽度缺省是它的容器的100%,除非设定一个宽度 <div& ...

  5. ElasticSearch远程随意代码运行漏洞(CVE-2014-3120)分析

    原理 这个漏洞实际上非常easy,ElasticSearch有脚本运行(scripting)的功能,能够非常方便地对查询出来的数据再加工处理. ElasticSearch用的脚本引擎是MVEL,这个引 ...

  6. luogu3384 【模板】 树链剖分

    题目大意 已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作:操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z操作2: 格式: 2 x ...

  7. 新建web项目时css注意事项

    初始化css ,如设置body的margin,padding值,button:hover的pointer手型,li dd的list-style,a的下划线等. 最好将常用的初始化css文件整合在一起, ...

  8. js遍历map

    //火狐控制台打印输出: Object { fileNumber="文件编号", fileName="文件名称"} console.log(map); for( ...

  9. 总结 <stdlib.h>头文件 在算法中可能会用到的一些函数

    头文件<stdlib.>具有一定的总结性. 它定义了类型.宏和各种函数,这些函数用于:内存管理.排序和查找.整形运算.字符串到数字的转换.伪随机数序列.与环境的接口.把多字节字符串和字符转 ...

  10. Jackson序列化和反序列化

    1,下载Jackson工具包(jackson-core-2.2.3.jar  jackson-annotations-2.2.3.jar  jackson-databind-2.2.3.jar ) j ...