开发者

Improving Image compositing Algorithm c# .NET

开发者 https://www.devze.com 2023-04-07 20:23 出处:网络
I was wondering if anyone could shed some light on improvements I can do in making this compositing algorithm faster. What is does is takes 3 images splits them up to get the 1st Images Red Channel, 2

I was wondering if anyone could shed some light on improvements I can do in making this compositing algorithm faster. What is does is takes 3 images splits them up to get the 1st Images Red Channel, 2nd Images Green channel and the 3rd Images Blue channel and composites them together into 1 new image. Now开发者_运维技巧 it works but at an excruciatingly slow pace. The reason i think down to the pixel by pixel processing it has to do on all image components.

The process is to :

For all images: Extract respective R G and B values -> composite into 1 image -> Save new Image.

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

}

For reference here is the red channel filter method relatively the same for blue and green:

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


Move these

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

outside the for-loops!

Other very important points:

  • avoid using GetPixel - it is VERY SLOW!

  • Checkout LockBits etc. - this is how pixel-level access is usually done in .NET

  • Consider using a 3rd-party library (free or commercial)... several have some optimized method built-in to do what you are trying to achieve...


I totally agree with the points Yahia listed in his answer to improve performance. I'd like to add one more point regarding performance. You could use the Parallel class of the .Net Framework to parallelize the execution of your for loops. The following example makes use of the LockBits method and the Parallel class to improve performance (assuming 32 bits per pixel (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);
}

Hope, this answer gives you a starting point for improving your code.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号