GDI+下Bitmap逐像素快速读写性能测试

写在前面的话:

本文针对GDI+下Bitmap操作(Get/SetPixel)进行测试,而非寻求最快速的位图处理方式。如果你需要速度上的提升,请使用GDI+以外的技术,如并行计算、调用MMX/SSE指令、CUDA等。

这是一个古老的技巧:
使用Bitmap类时经常会用到GetPixel和SetPixel,但是这两个方法直接使用都比较慢,所以一般都会使用LockBits/UnlockBits将位图在内存中锁定,以加快操作速度。
MSDN上的标准参考是这样的:

MSDN示例:锁定内存后拷贝

用指针法会更快,所以你看到的实际代码,一般是类似这样的:

 1 unsafe public Color GetPixel(int x, int y) 2 { 3     if (this.bmpData.PixelFormat == PixelFormat.Format32bppArgb) 4     { 5         byte* numPtr = (byte*) ((((void*) this.bmpData.Scan0) + (y * this.bmpData.Stride)) + (x * 4)); 6         return Color.FromArgb(numPtr[3], numPtr[2], numPtr[1], numPtr[0]); 7     } 8     if (this.bmpData.PixelFormat == PixelFormat.Format24bppRgb) 9     {10         byte* numPtr2 = (byte*) ((((void*) this.bmpData.Scan0) + (y * this.bmpData.Stride)) + (x * 3));11         return Color.FromArgb(numPtr2[2], numPtr2[1], numPtr2[0]);12     }13     return Color.Empty;14 }

因为我比较闲,所以我在想这样的问题:加快之后到底有多快?

为此,我稍微调整了下之前用过的BitmapEx类(记得应该是人脸识别还是什么代码里用过),改成FastBitmap,然后创建了测试程序,搜集了一系列测试用例。(点击左上图片框打开图片文件,无异常处理)

测试机配置如下:

测试用例如下:

为了保证不受文件格式影响,统一使用24bpp的bmp格式。(感谢科技发展,内存白菜价,不然单个文件将近200MB可真要让我麻烦一番。)

考察分为GetPixel和SetPixel两个部分,把读写分开。测试代码(以GetPixel为例)非常简单,遍历位图上每个像素点,如下:

1 for (int y = 0; y < h; y++)2 {3     for (int x = 0; x < w; x++)4     {5         tmp = bmp.GetPixel(x, y);6     }7 }

其中bmp分别为Bitmap和FastBitmap。

为了专注于对比结果,虽然逐像素遍历图像非常耗费时间,但并没有刻意使用并行计算,使用单个CPU内核完成。所以如果你打算用这个程序对特别巨大的图片(10000×10000数量级以上)进行测试,还请慎重。

最后,得到了这样的测试记录:

从测试结果来看,使用指针后,平均提升效率在90%~95%,也就说性能提高了10~20倍。

这个结果,虽然还不算很快,但我觉得基本到了GDI+的极限了(剩下的就是机器性能的提升了),如果再要提升,可以试试并行计算、C++ native、直接调用MMX/SSE指令、CUDA之类的技术。

我不知道现在技术发展下还有多少用到Bitmap的场合,只是觉得:追求开发效率和性能平衡的时候,Bitmap也能成为一个不错的选择。(总比绞尽脑汁写Win32 ASM来得轻松)

测试程序:点击下载



后记

有朋友指出:

GDI+这种 LockBits是临时性的把图像的数据读到内存,是不适合于做专业的图像处理软件的,专业做的话一个图像加载后在内存中的格式应该是固定的,这样做算法也就是直接访问这段内存的数据。GetPixel之类的函数的存在也不是为了专业的图像处理的,而是对类似于屏幕取色或DC取色这样小批量数据时方便处理。

要玩速度,图像处理方面的算法先是用普通语言写出来,对算法的核心尽心优化,如果速度还不行,考虑用汇编进一步优化,越简单的算法,用汇编优化的速度能提高的倍数越高,比如,最简单的反色算法,3000*4000*24的图像,一般的语言要100ms左右的处理时间,用汇编的话20ms够了,不过复杂的算法,一般汇编能提升的档次不会有这么明显。

尽管本文的目的并非追求速度,仅仅是「测试」速度,我还是尝试着优化了一下代码,就用上述「反色」操作为例进行了测试。

测试用例选择#7(4096x4096 @ 24bpp),用时299ms,截图如下:

随后再次改进算法,得到了52ms的速度提升(约17%)。

这个结果,尽管较一般操作方式快了不少,但和「一般的语言100ms左右」比起来,还是「右」得多了一点(笑)。和汇编的「20ms」(无实验数据)比,差得更远了。

优化代码似乎比较有趣,我就继续试着优化一下。通过调整调用结构,改进算法,使用多线程并行计算,总算是进入50ms了。

仍旧基于Bitmap类的LockBits/UnlockBits。

语言:C#、C#指针

测试机:i3 380M @2.53GHz,2.92G DDR3-1333,Windows 7 32位

速度:约50ms



网友测试结果对比

以下是部分热心网友给出的测试结果数据,加以对比,供诸君参考。

测试项目统一为对规格为4096x4096x24bpp的位图图像进行反色处理。

测试1

ImageWizard(作者:laviewpbt)

实现:汇编+VB.NET

配置:i3 380M @2.53GHz,2.92G DDR3-1333,Windows 7 32位

用时:25ms

测试2

临时测试(作者:兰征鹏)

实现:VC++.NET调用SSE指令

配置:i7 [email protected],12G PC1333内存,Windows7 64位

用时:12~19ms

测试3

GebImage(作者:xiaotie)

实现:C#重写全部图像库、unsafe指针

配置:优于测试1

用时:33ms

测试4

本文(作者:野比)

实现:GDI+、unsafe指针

配置:同测试1

用时:46ms

(完)

时间: 2024-08-27 06:06:44

GDI+下Bitmap逐像素快速读写性能测试的相关文章

InfluxDB读写性能测试

今天进行了InfluxDB和MySQL的对比测试,这里记录下结果,也方便我以后查阅. 操作系统: CentOS6.5_x64InfluxDB版本 : v1.1.0MySQL版本:v5.1.73CPU : Intel(R) Core(TM) i5-2320 CPU @ 3.00GHz内存 :12G硬盘 :SSD 一.MySQL读写测试 测试准备 初始化SQL语句: CREATE DATABASE testMysql; CREATE TABLE `monitorStatus` ( `system_n

Qt中快速读写Excel方法封装

#include "RwExcel.h"/*快速读写的机制是实现获取有效区域只调用一次dynamicCall("Value");或setProperty("Value", var);即可, *而不是在循环里多次被用 */ RwExcel::RwExcel(){} RwExcel::~RwExcel(){}void RwExcel::castVariant2ListListVariant(const QVariant &var, QList

[评测]低配环境下,PostgresQL和Mysql读写性能简单对比

[评测]低配环境下,PostgresQL和Mysql读写性能简单对比 原文链接:https://www.cnblogs.com/blog5277/p/10658426.html 原文作者:博客园--曲高终和寡 *******************如果你看到这一行,说明爬虫在本人还没有发布完成的时候就抓走了我的文章,导致内容不完整,请去上述的原文链接查看原文**************** 由于最近经过朋友启发,又有了一个写个人项目的小想法,在这次个人项目中准备学习并使用一些之前自己没有掌握的新

怎么使用瓦特平台下面的“代码工厂”快速生成BS程序代码

这里说一下怎么使用瓦特平台下面的“代码工厂”快速生成程序代码 使用平台:windows+"visual studio 2010"+"SqlServer2000+" 结构:B/S 生成的结构:前端Html和JS.数据库sql,底层ajax,底层数据交互! 免费下载客户端:(永久有效)----------此版本不附带权限控制! http://pan.baidu.com/s/1hqtGGlA 代码生成介绍 1:打开瓦特平台的设计器: 点击提交:出现---> 在你的机

002 bitmap海量数据的快速查找和去重

题目描述 给你一个文件,里面包含40亿个整数,写一个算法找出该文件中不包含的一个整数, 假设你有1GB内存可用. 如果你只有10MB的内存呢? 对于40亿个整数,如果直接用int数组来表示的大约要用40*10^8*4B=16GB,超出了内存要求,这里 我们可以用bitmap来解决,bitmap基本思想是一位表示一个整数,比如我们有6个数据: 7   3  1  5  6  4 假设bitmap容量为8,当插入7时 bit[7]=1,一次类推 bit[3]=1 bit[1]=1 bit[5]=1

实现快速读写配置文件的内容,可以用于读取*.exe.config文件或者Web.Config文件的内容,或者可以读取指定文件的配置项.

形如: <?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="dataConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Data.Configuration.DatabaseSettings, Microso

Unity shader学习之逐像素漫反射光照模型

shader如下: Shader "Custom/Diffuse Fragment-Level" { Properties { _Diffuse ("Diffuse", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { Tags { "LightMode"="ForwardBase"

linux下如何查看文件大小 快速找到最大文件的方法

查看当前目录的大小 du -sh . 查看当前目录下所有文件或者目录的大小 du -sh * 显示前10个占用空间最大的目录 du -s * | sort -nr | head 显示前10个占用空间最大的文件,使用MB为单位 du * --block-size=MB |sort -nr | head 或者也可写成 du * -B MB | sort -nr | head linux下如何查看文件大小 快速找到最大文件的方法

java对比IO和NIO的文件读写性能测试

原文:java对比IO和NIO的文件读写性能测试 源代码下载地址:http://www.zuidaima.com/share/1550463508466688.htm 1. NIO采用更接近操作系统执行IO的方式:通道和缓存器:顾名思义,数据源的数据由缓存器通过通道进行传输. 2. 在JDK5之后,原始IO系统底层用NIO进行了优化,这可以通过sun公布的源码中找到,但是,NIO系统性还是比IO强. 3. 在稍微研究了IO源码以及部分sun源码,我觉得IO系统的性能瓶颈主要是由于原始的IO架构因