我怎样才能加快这个程序的速度?

时间:2014-10-25 10:28:36

标签: c# image-processing unsafe

我有以下代码需要以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 个答案:

答案 0 :(得分:1)

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

简短版本:这取决于您的叠加层,以下代码是否会使您的帧速率几乎翻倍。

查看发布的代码会想到几件事:

  1. 由于颜色通道是字节似乎更自然地对待它们而不是所有的屏蔽和移位,尽可能便宜..

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

  3. 因为没有显示你如何调用你可能已经在做的例程,但这种事情需要并行处理;如果你在一个核心HD上获得25fps不应该是多核机器上的一个问题,即使是Parallel.For简单的声音也会使你的输出倍增..

  4. 此外,您可以选择使用Lockbits & Mashalling代替unsafe;不确定这是否会更快,但我想我会写一个基准来做一些测试..

  5. BTW:您的代码中有错误,afaiks,我认为您需要更改此

    *pOut = bOut | gOut << 8 | rOut << 16 | 0x00 << 24;
    

    ,否则输出的alpha通道= 0

    *pOut = (bOut | gOut << 8 | rOut << 16 ) | 0xff000000;
    

    或者您可能想要计算最终的alpha ..

    更新1:首次测试显示您的代码比Lockbits&amp;更快(~2x)。 Mashalling`版本,除非我搞砸了..)所以我从现在开始忽略#4 ..

    更新2:

    初步数字:

    在i7-3770T 2.5GHz,W8.1 64的UI线程(!)上运行代码

    • QVGA_size(320x240)666,7 fps
    • NTSC_size(720x480)161,3 fps
    • HR_size(1280x720)64,1 fps
    • HD_size(1920x1080)29,2 fps

    更新3:

    改为运行DrawImage:

    • QVGA_size(320x240)641,0 fps
    • NTSC_size(720x480)194,2 fps
    • HR_size(1280x720)77,2 fps
    • HD_size(1920x1080)33,4 fps

    使用此代码:

    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:

    我现在尝试了更多的东西,可以说

    • 使用bytes而不是int32使代码更清晰,但不会改变它的速度所以#1点并不重要
    • 如果你的所有像素都有alpha混合并且你总是会进行这种混合,那么使用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;
        }
    

    还有一些数字:

    • OverlayImage3,其中5%的所有像素都具有Alpha混合
    • QVGA_size(320x240)1.282,1 fps
    • NTSC_size(720x480)320,5 fps
    • HR_size(1280x720)114,3 fps
    • HD_size(1920x1080)52,1 fps

    • OverlayImage3,其中60%的像素都有alpha混合

    • QVGA_size(320x240)917,4 fps
    • NTSC_size(720x480)256,4 fps
    • HR_size(1280x720)98,5 fps
    • HD_size(1920x1080)46,7 fps

    • OverlayImage3,95%的所有像素都有alpha混合

    • QVGA_size(320x240)714,3 fps
    • NTSC_size(720x480)220,8 fps
    • HR_size(1280x720)84,2 fps
    • HD_size(1920x1080)36,6 fps

    DrawImage也可以从缺乏alpha混合中获益:

    • 具有alpha混合的所有像素的5%的DrawImage
    • QVGA_size(320x240)584,8 fps
    • NTSC_size(720x480)220,8 fps
    • HR_size(1280x720)100,0 fps
    • HD_size(1920x1080)41,8 fps

    • DrawImage,95%的所有像素都有alpha混合

    • QVGA_size(320x240)534,8 fps
    • NTSC_size(720x480)200,4 fps
    • HR_size(1280x720)73,3 fps
    • HD_size(1920x1080)33,6 fps

    第3点,并行处理显然会有所帮助,具体取决于您的硬件。

    结论:我不知道你当前的分辨率,但是从SD到HD将在所有测试中花费5-6倍的时间,所以如果你现在只需要25fps,你需要的不仅仅是上面的代码;你需要并行处理,我要说..