我怎样才能加快这个程序
本文关键字:程序 | 更新日期: 2023-09-27 18:27:57
我有以下代码,需要以25fps或更好的速度运行,我们现在可以这样做。最终,我们将使用高清视频,因此需要对其进行更多优化以适应。
有什么方法可以优化这个方法吗?
public unsafe void OverlayImage(Bitmap overlay, Bitmap background, Bitmap output)
{
Rectangle lrEntire = new Rectangle(new Point(), background.Size);
BitmapData bdBack = background.LockBits(lrEntire, ImageLockMode.ReadOnly, background.PixelFormat);
BitmapData bdOverlay = overlay.LockBits(lrEntire, ImageLockMode.ReadOnly, overlay.PixelFormat);
BitmapData bdOut = output.LockBits(lrEntire, ImageLockMode.WriteOnly, output.PixelFormat);
uint* pBack = (uint*) bdBack.Scan0;
uint* pOverlay = (uint*) bdOverlay.Scan0;
uint* pOut = (uint*) bdOut.Scan0;
for (int luiToProcess = (bdBack.Height*bdBack.Stride) >> 2; luiToProcess != 0; luiToProcess--)
{
//get each pixel component
uint red = (*pBack & 0x00ff0000) >> 16; // red color component
uint green = (*pBack & 0x0000ff00) >> 8; // green color component
uint blue = *pBack & 0x000000ff; // blue color component
uint oalpha = (*pOverlay & 0xff000000) >> 24;
uint ored = (*pOverlay & 0x00ff0000) >> 16; // red color component
uint ogreen = (*pOverlay & 0x0000ff00) >> 8; // green color component
uint oblue = *pOverlay & 0x000000ff; // blue color component
//get each pixel color component
uint rOut = (red*(255 - oalpha) + (ored*oalpha))/255;
uint gOut = (green*(255 - oalpha) + (ogreen*oalpha))/255;
uint bOut = (blue*(255 - oalpha) + (oblue*oalpha))/255;
*pOut = bOut | gOut << 8 | rOut << 16 | 0x00 << 24;
//move to the next pixel
pBack++;
pOverlay++;
pOut++;
}
overlay.UnlockBits(bdOverlay);
background.UnlockBits(bdBack);
output.UnlockBits(bdOut);
}
警告:答案很长,有很多数字。
短版本:下面的代码是否会使帧速率几乎翻倍取决于您的覆盖层。。
看到张贴的代码,脑海中浮现出几件事:
-
由于颜色通道是字节,因此将它们视为字节似乎更自然,而不是所有的掩蔽和移位,尽管可能很便宜。
-
你用
oalpha
做了不少计算;除非您期望它大部分不相等255或0,否则额外的分支将节省一些乘法运算。。(每个像素6个) -
由于它并没有显示你是如何调用例程的,你可能已经在做了,但这种事情需要并行处理;如果你在单核高清上获得25fps,那么在多核机器上应该不会有问题,即使是像
Parallel.For
这样简单的东西也会使你的输出成倍增加。。 -
此外,还可以选择使用
Lockbits & Mashalling
而不是unsafe
;不确定这是否会更快,但我想我会写一个基准来做一些测试。。
BTW:你的代码有一个错误,阿法克斯,我认为你需要更改这个
*pOut = bOut | gOut << 8 | rOut << 16 | 0x00 << 24;
否则输出具有α通道=0
*pOut = (bOut | gOut << 8 | rOut << 16 ) | 0xff000000;
或者,您可能需要计算最终的alpha。。
更新1:第一次测试显示您的代码比Lockbits&混搭版,除非我把它搞砸了。)所以从现在起我将忽略#4。
更新2:
初步编号:
在i7-3770T 2.5GHz、W8.1 64 的UI线程(!)上运行代码
- QVGA_size(320x240)666,7 fps
- NTSC_size(720x480)161,3帧/秒
- HR_size(1280x720)64,1帧/秒
- HD_size(1920x1080)每秒29.2帧
更新3:
改为运行DrawImage:
- QVGA_size(320x240)641,0帧/秒
- NTSC_size(720x480)194,2帧/秒
- HR_size(1280x720)77.2帧/秒
- HD_size(1920x1080)每秒33.4帧
使用此代码:
public void DrawImage(Bitmap overlay, Bitmap background, Bitmap output)
{
overlay.SetResolution(96, 96);
background.SetResolution(96, 96);
output.SetResolution(96, 96);
using (Graphics G = Graphics.FromImage(output) )
{
G.DrawImage(background, 0, 0);
G.CompositingMode = CompositingMode.SourceOver;
G.DrawImage(overlay, 0, 0);
}
}
更新4:
我现在已经尝试了更多的东西,可以说
- 使用字节而不是int32可以使代码更干净imo,但不会改变其速度,因此第1点并不重要
- 如果你所有的像素都有阿尔法混合,并且你总是这样混合,那么使用DrawImage只会稍微快一点
- 至于#2:根据使用alpha混合的像素百分比(即0>alpha<255的像素),针对alpha=0和alpha=255进行优化可能会产生巨大差异,因此除非大多数像素都使用alpha混合,否则这种优化几乎会使帧速率翻倍:
public unsafe void OverlayImage3(Bitmap overlay, Bitmap background, Bitmap output)
{
Rectangle lrEntire = new Rectangle(new Point(), background.Size);
BitmapData bdBack = background.LockBits(lrEntire,
ImageLockMode.ReadOnly, background.PixelFormat);
BitmapData bdOverlay = overlay.LockBits(lrEntire,
ImageLockMode.ReadOnly, overlay.PixelFormat);
BitmapData bdOut = output.LockBits(lrEntire,
ImageLockMode.WriteOnly, output.PixelFormat);
byte* pBack = (byte*)bdBack.Scan0;
byte* pOverlay = (byte*)bdOverlay.Scan0;
byte* pOut = (byte*)bdOut.Scan0;
for (int luiToProcess = (bdBack.Height * bdBack.Stride) >> 2;
luiToProcess > 0; luiToProcess--)
{
//get each pixel component
byte red = *(pBack + 2);
byte green = *(pBack + 1);
byte blue = *(pBack + 0);
byte oalpha = *(pOverlay + 3);
byte ored = *(pOverlay + 2);
byte ogreen = *(pOverlay + 1);
byte oblue = *(pOverlay + 0);
//get each pixel color component
byte rOut, gOut, bOut;
if (oalpha == 255)
{ rOut = ored; gOut = ogreen; bOut = oblue; }
else if (oalpha == 0)
{ rOut = red; gOut = green; bOut = blue; }
else
{
rOut = (byte)((red * (255 - oalpha) + (ored * oalpha)) / 255);
gOut = (byte)((green * (255 - oalpha) + (ogreen * oalpha)) / 255);
bOut = (byte)((blue * (255 - oalpha) + (oblue * oalpha)) / 255);
}
*(pOut + 3) = 0xff;
*(pOut + 2) = rOut;
*(pOut + 1) = gOut;
*(pOut + 0) = bOut;
//move to the next pixel
pBack += 4; pOverlay += 4; pOut += 4;
}
更多数字:
- 覆盖Image3,其中5%的像素具有alpha混合
- QVGA_size(320x240)1.282,1帧/秒
- NTSC_size(720x480)320,5帧/秒
- HR_size(1280x720)114,3帧/秒
HD_size(1920x1080)52.1 fps
覆盖Image3,60%的像素具有阿尔法混合
- QVGA_size(320x240)917,4帧/秒
- NTSC_size(720x480)256,4帧/秒
- HR_size(1280x720)98.5帧/秒
HD_size(1920x1080)46.7 fps
覆盖Image3,95%的像素具有阿尔法混合
- QVGA_size(320x240)714,3帧/秒
- NTSC_size(720x480)220,8帧/秒
- HR_size(1280x720)84.2帧/秒
- HD_size(1920x1080)每秒36,6帧
DrawImage也从缺乏阿尔法混合中获利:
- DrawImage,所有像素的5%具有alpha混合
- QVGA_size(320x240)584.8帧/秒
- NTSC_size(720x480)220,8帧/秒
- HR_size(1280x720)100,0帧/秒
HD_size(1920x1080)41.8 fps
DrawImage,95%的像素具有阿尔法混合
- QVGA_size(320x240)534.8 fps
- NTSC_size(720x480)200,4帧/秒
- HR_size(1280x720)73.3帧/秒
- HD_size(1920x1080)每秒33.6帧
第3点,并行处理显然会有帮助,这取决于您的硬件。
结论:我不知道你目前的分辨率,但在所有测试中,从SD到HD需要5到6倍的时间,所以如果你现在只能达到25fps,你需要的不仅仅是上面的代码;我认为你需要并行处理。。