在 C# 中快速使用位图
我需要访问位图的每个像素,使用它们,然后将它们保存到位图。
使用 Bitmap.GetPixel()
和 Bitmap.SetPixel()
,我的程序运行缓慢。
如何快速将 Bitmap
与 byte[]
相互转换?
我需要一个 byte[]
,其中 length = (4 * width * height)
,包含每个像素的 RGBA 数据。
I need to access each pixel of a Bitmap, work with them, then save them to a Bitmap.
Using Bitmap.GetPixel()
and Bitmap.SetPixel()
, my program runs slowly.
How can I quickly convert Bitmap
to byte[]
and back?
I need a byte[]
with length = (4 * width * height)
, containing RGBA data of each pixel.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您可以通过几种不同的方式来做到这一点。您可以使用
unsafe
直接访问数据,也可以使用封送处理来回复制数据。不安全代码速度更快,但封送不需要不安全代码。这是性能比较 我不久前做过。这是使用锁位的完整示例:
这是相同的事情,但使用了封送处理:
You can do it a couple of different ways. You can use
unsafe
to get direct access to the data, or you can use marshaling to copy the data back and forth. The unsafe code is faster, but marshaling doesn't require unsafe code. Here's a performance comparison I did a while back.Here's a complete sample using lockbits:
Here's the same thing, but with marshaling:
如果您使用 C# 8.0,我建议使用新的
Span
来提高效率。这是一个粗略的实现
If you're on C# 8.0 I'll suggest to use the new
Span<T>
for higher efficiency.Here's a rough implementation
您可以使用 Bitmap.LockBits 方法。另外,如果您想使用并行任务执行,可以使用 System.Threading.Tasks 命名空间中的 Parallel 类。以下链接有一些示例和解释。
You can use Bitmap.LockBits method. Also if you want to use parallel task execution, you can use the Parallel class in System.Threading.Tasks namespace. Following links have some samples and explanations.
您需要 LockBits。然后,您可以从它提供的 BitmapData 对象中提取所需的字节。
You want LockBits. You can then extract the bytes you want from the BitmapData object it gives you.
还有另一种方法更快、更方便。如果您查看 Bitmap 构造函数,您会发现它采用 IntPtr 作为最后一个参数。 IntPtr 用于保存像素数据。那么如何使用它呢?
您现在拥有的是一个简单的整数数组和一个引用相同内存的位图。对整数数组所做的任何更改都将直接影响位图。让我们尝试一下简单的亮度变换。
您可能会注意到,我使用了一个小技巧来避免在循环内进行浮点数学运算。这大大提高了性能。
当你完成后,你当然需要清理一下......
我在这里忽略了 alpha 组件,但你也可以自由使用它。我通过这种方式整合了很多位图编辑工具。它比 Bitmap.LockBits() 更快、更可靠,最重要的是,它需要零内存复制才能开始编辑位图。
There is another way that is way faster and much more convenient. If you have a look at the Bitmap constructors you will find one that takes and IntPtr as the last parameter. That IntPtr is for holding pixel data. So how do you use it?
What you have now is a simple Integer array and a Bitmap referencing the same memory. Any changes you make to the Integer array will be directly affecting the Bitmap. Let us try this with a simple brightness transform.
You may notice that I have used a little trick to avoid doing floating point math within the loop. This improves performance quite a bit.
And when you are done you need to clean up a little of course...
I have ignored the alpha component here but you are free to use that as well. I have thrown together a lot of bitmap editing tools this way. It is much faster and more reliable than Bitmap.LockBits() and best of all, it requires zero memory copying to start editing your bitmap.
建立在 @notJim 答案的基础上(并在 https 的帮助下: //web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx),我开发了以下内容,这使我的生活变得更加轻松,因为我最终得到了一个数组数组这允许我通过 x 和 y 坐标跳转到像素。当然,x 坐标需要根据每个像素的字节数进行校正,但这是一个简单的扩展。
Building on @notJim answer (and with help from https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx), I developed the following that makes my life a lot easier in that I end up with an array of arrays that allows me to jump to a pixel by its
x
andy
coordinates. Of course, thex
coordinate needs to be corrected for by the number of bytes per pixel, but that is an easy extension.尝试这个 C# 解决方案。
创建一个 winforms 应用程序进行测试。
添加一个按钮和一个图片框,以及一个单击事件和一个窗体关闭事件。
在您的表单中使用以下代码:
现在,您对数组所做的任何编辑都会自动更新位图。
您需要调用图片框上的刷新方法才能看到这些更改。
Try this C# solution.
Create a winforms app for testing.
Add a Button and a PictureBox, and a click event and a form closing event.
Use the following code for your form:
Now, any edits you make to the array will automatically update the Bitmap.
You will need to call the refresh method on the picturebox to see these changes.