首页 文章

改进图像合成算法c#.NET

提问于
浏览
0

我想知道是否有人可以说明我可以做的改进,使这个合成算法更快 . 做什么是将3个图像分割成第一个图像红色通道,第二个图像绿色通道和第三个图像蓝色通道,并将它们合成为一个新图像 . 现在它起作用但速度极慢 . 我想到它必须对所有图像组件进行逐像素处理的原因 .

过程是:

对于所有图像:提取相应的R G和B值 - >合成为1个图像 - >保存新图像 .

foreach (Image[] QRE2ImgComp in QRE2IMGArray)
{
    Globals.updProgress = "Processing frames: " + k + " of " + QRE2IMGArray.Count + " frames done.";
    QRMProgressUpd(EventArgs.Empty);

    Image RedLayer = GetRedImage(QRE2ImgComp[0]);
    QRE2ImgComp[0] = RedLayer;

    Image GreenLayer = GetGreenImage(QRE2ImgComp[1]);
    QRE2ImgComp[1] = GreenLayer;

    Image BlueLayer = GetBlueImage(QRE2ImgComp[2]);
    QRE2ImgComp[2] = BlueLayer;


    Bitmap composite = new Bitmap(QRE2ImgComp[0].Height, QRE2ImgComp[0].Width);

    Color Rlayer,Glayer,Blayer;
    byte R, G, B;

    for (int y = 0; y < composite.Height; y++)
    {
        for (int x = 0; x < composite.Width; x++)
        {
            //pixelColorAlpha = composite.GetPixel(x, y);

            Bitmap Rcomp = new Bitmap(QRE2ImgComp[0]);
            Bitmap Gcomp = new Bitmap(QRE2ImgComp[1]);
            Bitmap Bcomp = new Bitmap(QRE2ImgComp[2]);

            Rlayer = Rcomp.GetPixel(x, y);
            Glayer = Gcomp.GetPixel(x, y);
            Blayer = Bcomp.GetPixel(x, y);

            R = (byte)(Rlayer.R);
            G = (byte)(Glayer.G);
            B = (byte)(Blayer.B);
            composite.SetPixel(x, y, Color.FromArgb((int)R, (int)G, (int)B));
        }
    }


    Globals.updProgress = "Saving frame...";
    QRMProgressUpd(EventArgs.Empty);
    Image tosave = composite;
    Globals.QRFrame = tosave;
    tosave.Save("C:\\QRItest\\E" + k + ".png", ImageFormat.Png);
    k++;

}

这里参考红色通道滤波器方法对蓝色和绿色相对相同:

public Image GetRedImage(Image sourceImage)
{
    Bitmap bmp = new Bitmap(sourceImage);
    Bitmap redBmp = new Bitmap(sourceImage.Width, sourceImage.Height);

        for (int x = 0; x < bmp.Width; x++)
        {
            for (int y = 0; y < bmp.Height; y++)
            {
                Color pxl = bmp.GetPixel(x, y);
                Color redPxl = Color.FromArgb((int)pxl.R, 0, 0);

                redBmp.SetPixel(x, y, redPxl);
            }
        }
        Image tout = (Image)redBmp;

        return tout;
}

2 回答

  • 3

    我完全同意Yahia在提高绩效的答案中列出的观点 . 我想再补充一点关于性能的观点 . 您可以使用.Net Framework的 Parallel 类来并行执行for循环 . 以下示例使用 LockBits 方法和 Parallel 类来提高性能(假设每像素32位(PixelFormat.Format32bppArgb)):

    public unsafe static Bitmap GetBlueImagePerf(Image sourceImage)
    {
      int width = sourceImage.Width;
      int height = sourceImage.Height;
    
      Bitmap bmp = new Bitmap(sourceImage);
      Bitmap redBmp = new Bitmap(width, height, bmp.PixelFormat);
    
      BitmapData bd = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
      BitmapData bd2 = redBmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
      byte* source = (byte*)bd.Scan0.ToPointer();
      byte* target = (byte*)bd2.Scan0.ToPointer();
    
      int stride = bd.Stride;
    
      Parallel.For(0, height, (y1) =>
      {
        byte* s = source + (y1 * stride);
        byte* t = target + (y1 * stride);
        for (int x = 0; x < width; x++)
        {
          // use t[1], s[1] to access green channel
          // use t[2], s[2] to access red channel
          t[0] = s[0]; 
          t += 4;       // Add bytes per pixel to current position.
          s += 4;       // For other pixel formats this value is different.
        }
      });
    
      bmp.UnlockBits(bd);
      redBmp.UnlockBits(bd2);
    
    
      return redBmp;
    }
    
    public unsafe static void DoImageConversion()
    {
      Bitmap RedLayer   = GetRedImagePerf(Image.FromFile("image_path1"));
      Bitmap GreenLayer = GetGreenImagePerf(Image.FromFile("image_path2"));
      Bitmap BlueLayer  = GetBlueImagePerf(Image.FromFile("image_path3"));
    
      Bitmap composite =
        new Bitmap(RedLayer.Width, RedLayer.Height, RedLayer.PixelFormat);      
    
      BitmapData bd = composite.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
      byte* comp = (byte*)bd.Scan0.ToPointer();
    
      BitmapData bdRed = RedLayer.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
      BitmapData bdGreen = GreenLayer.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
      BitmapData bdBlue = BlueLayer.LockBits(new Rectangle(0, 0, RedLayer.Width, RedLayer.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    
      byte* red = (byte*)bdRed.Scan0.ToPointer();
      byte* green = (byte*)bdGreen.Scan0.ToPointer();
      byte* blue = (byte*)bdBlue.Scan0.ToPointer();
    
      int stride = bdRed.Stride;
    
      Parallel.For(0, bdRed.Height, (y1) =>
      {
        byte* r = red + (y1 * stride);
        byte* g = green + (y1 * stride);
        byte* b = blue + (y1 * stride);
        byte* c = comp + (y1 * stride);
    
        for (int x = 0; x < bdRed.Width; x++)
        {
          c[0] = b[0];
          c[1] = g[1];
          c[2] = r[2];
    
          r += 4; // Add bytes per pixel to current position.
          g += 4; // For other pixel formats this value is different.
          b += 4; // Use Image.GetPixelFormatSize to get number of bits per pixel
          c += 4;
        }
      });
    
      composite.Save("save_image_path", ImageFormat.Jpeg);
    }
    

    希望,这个答案为您提供了改进代码的起点 .

  • 1

    Move these

    Bitmap Rcomp = new Bitmap(QRE2ImgComp[0]);
        Bitmap Gcomp = new Bitmap(QRE2ImgComp[1]);
        Bitmap Bcomp = new Bitmap(QRE2ImgComp[2]);
    

    outside the for-loops!

    其他非常重要的一点:

    • avoid using GetPixel - it is VERY SLOW!

    • Checkout LockBits等 - 这就是通常在.NET中进行像素级访问的方式

    • 考虑使用第三方库(免费或商业)......有几个内置了一些优化方法来做你想要实现的...

相关问题