我怎样才能加快这个程序

本文关键字:程序 | 更新日期: 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);
    }

我怎样才能加快这个程序

警告:答案很长,有很多数字。

短版本:下面的代码是否会使帧速率几乎翻倍取决于您的覆盖层。。

看到张贴的代码,脑海中浮现出几件事:

  1. 由于颜色通道是字节,因此将它们视为字节似乎更自然,而不是所有的掩蔽和移位,尽管可能很便宜。

  2. 你用oalpha做了不少计算;除非您期望它大部分不相等255或0,否则额外的分支将节省一些乘法运算。。(每个像素6个)

  3. 由于它并没有显示你是如何调用例程的,你可能已经在做了,但这种事情需要并行处理;如果你在单核高清上获得25fps,那么在多核机器上应该不会有问题,即使是像Parallel.For这样简单的东西也会使你的输出成倍增加。。

  4. 此外,还可以选择使用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,你需要的不仅仅是上面的代码;我认为你需要并行处理。。