改进图像合成算法

本文关键字:算法 图像 | 更新日期: 2023-09-27 18:09:44

我想知道是否有人可以透露一些我可以做的改进,使这个合成算法更快。它所做的是将3张图像分开,得到第一张图像的红色通道,第二张图像的绿色通道和第三张图像的蓝色通道,并将它们合成为一张新图像。现在它起作用了,但速度慢得令人难以忍受。我认为原因在于它必须对所有图像组件进行逐像素处理。

过程是:

所有图片:分别提取R G和B值->合成为一张图像->保存新图像

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;
}

改进图像合成算法

移动这些

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

在for循环之外!

其他非常重要的要点:

  • 避免使用GetPixel -它非常慢!

  • Checkout LockBits等-这是在。net中像素级访问通常是如何完成的

  • 考虑使用第三方库(免费或商业)…

我完全同意Yahia在他的回答中列出的提高性能的要点。关于性能,我想再补充一点。你可以使用。net框架的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);
}

希望这个答案能给你一个改进代码的起点