更快的c#图像处理

Faster c# image processing

本文关键字:图像处理      更新时间:2023-10-16

我有一个相当广泛的c++图像处理库,我一直在做一个c#项目,但我似乎无法让c#的速度接近c++。我的c++需要149ms来处理将整个图像设置为白色的过程,而c#也需要1071ms。

这是我的c++代码

for (int i = 0; i < 100; i++)
{
for (int y = 0; y < image->height; y++)
{
unsigned char * srcPixel = (unsigned char*)image->mImageData + (y * image->width);
for (int x = 0; x < image->width; x++)
{
srcPixel[0] = 255;
srcPixel[1] = 255;
srcPixel[2] = 255;
srcPixel[3] = 255;
srcPixel += 4;
}
}
}

mImageData是无符号字符的结构

struct mImageData
{
unsigned char alpha;
unsigned char red;
unsigned char green;
unsigned char blue;
}

这就是我正在使用的c代码。这是我能拿到的最快的一个。

frame = new Bitmap(3840, 2160);
BitmapData bitmapData12 = frame.LockBits(new Rectangle(0, 0,
frame.Width, frame.Height),
ImageLockMode.ReadWrite,
PixelFormat.Format32bppArgb);

var stopwatch = Stopwatch.StartNew();
unsafe
{
int pixelBytes = 4;
int paddingBytes = bitmapData12.Stride % pixelBytes;
byte* location1 = (byte*)bitmapData12.Scan0;
for (int i = 0; i < 100; i++)
{
location1 = (byte*)bitmapData12.Scan0;
for (int y = 0; y < bitmapData12.Height; ++y)
{
for (int x = 0; x < bitmapData12.Width; ++x)
{
location1[0] = 255;
location1[1] = 255;
location1[2] = 255;
location1[3] = 255;
location1 += pixelBytes;
}
location1 += paddingBytes;
}
}
}
stopwatch.Stop();
var miliseconds = stopwatch.ElapsedMilliseconds;
frame.UnlockBits(bitmapData12);

我用ResW=3000和ResH=3000运行了您的代码,以获得900ms的处理时间。我在发布模式下运行它,调试器是分离的。

请注意,此图像包含900万像素,每个像素长4字节。这需要36 MB。我们已经填充了100次,所以总共需要设置36亿字节。我的CPU运行频率为4.5 GHz,所以它在40亿个时钟周期内设置了36亿个字节。

我想说,这对任何语言来说都不算太寒酸。如果我关闭我的开发机器上的所有虚拟机、后台进程和服务器(目前消耗的CPU在5%到20%之间)来运行一个更干净的测量,那么每个时钟周期我会得到几乎正好一个字节的设置。当然,CPU可以做得更好——如果您要求它们执行正确的操作。一次设置一个字节肯定会使速度变慢。

因此,C#确实在不修改算法的情况下尽可能快地完成了这项工作。只是C#拒绝优化代码的字面翻译,而C++可以也会这样做。在我自己的测试中,简单地按照AdamF的建议(使用uint)已经将时间缩短到了300ms。

我不认为你已经指定了你的ResW/ResH是什么(或者我是盲人),所以你仍然有可能没有以最快的方式运行代码,有些东西干扰了测量。

这是一个有点脏的解决方案,但如果图像结构的大小始终相同,那么也许您可以尝试以这种方式进行优化:

在我的测试中,速度从大约1100毫秒提高到750毫秒

unsafe
{
int width = bitmapData12.Width;
for (int y = 0; y < bitmapData12.Height; ++y)
{
UInt32* location1 = (UInt32*) (bitmapData12.Scan0 + y*bitmapData12.Stride);
for (int x = 0; x < width; ++x)
location1[x] = UInt32.MaxValue;
}
}