C#扫描图像代码改进

时间:2015-08-24 13:38:42

标签: c# performance image-processing

我正在开发一个屏幕共享应用程序,它运行循环并使用GDI方法获取快速截图。 example here

当然我也使用泛光填充算法来查找2幅图像之间的变化区域(前一个截图和当前图片)。

我使用另一个小技巧 - 我将快照分辨率缩减为10,因为非常频繁地处理1920 * 1080 = 2073600像素效率不高。

然而,当我找到矩形边界时 - 我将它应用于原始的全尺寸位图,我只是将尺寸乘以10(包括顶部,左边,宽度,高度)。

这是扫描码:

    unsafe bool ArePixelsEqual(byte* p1, byte* p2, int bytesPerPixel)
    {
        for (int i = 0; i < bytesPerPixel; ++i)
            if (p1[i] != p2[i])
                return false;
        return true;
    }

    private unsafe List<Rectangle> CodeImage(Bitmap bmp, Bitmap bmp2)
    {
       List<Rectangle> rec = new List<Rectangle>();
        var bmData1 = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
        var bmData2 = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);

        int bytesPerPixel = 4;
        IntPtr scan01 = bmData1.Scan0;
        IntPtr scan02 = bmData2.Scan0;
        int stride1 = bmData1.Stride;
        int stride2 = bmData2.Stride;
        int nWidth = bmp.Width;
        int nHeight = bmp.Height;
        bool[] visited = new bool[nWidth * nHeight];

        byte* base1 = (byte*)scan01.ToPointer();
        byte* base2 = (byte*)scan02.ToPointer();

        for (int y = 0; y < nHeight; y ++)
        {
            byte* p1 = base1;
            byte* p2 = base2;

            for (int x = 0; x < nWidth; ++x)
            {
                if (!ArePixelsEqual(p1, p2, bytesPerPixel) && !(visited[x + nWidth * y]))
                {
                    // fill the different area
                    int minX = x;
                    int maxX = x;
                    int minY = y;
                    int maxY = y;

                    var pt = new Point(x, y);

                    Stack<Point> toBeProcessed = new Stack<Point>();
                    visited[x + nWidth * y] = true;
                    toBeProcessed.Push(pt);
                    while (toBeProcessed.Count > 0)
                    {
                        var process = toBeProcessed.Pop();
                        var ptr1 = (byte*)scan01.ToPointer() + process.Y * stride1 + process.X * bytesPerPixel;
                        var ptr2 = (byte*)scan02.ToPointer() + process.Y * stride2 + process.X * bytesPerPixel;
                        //Check pixel equality
                        if (ArePixelsEqual(ptr1, ptr2, bytesPerPixel))
                            continue;

                        //This pixel is different
                        //Update the rectangle
                        if (process.X < minX) minX = process.X;
                        if (process.X > maxX) maxX = process.X;
                        if (process.Y < minY) minY = process.Y;
                        if (process.Y > maxY) maxY = process.Y;

                        Point n; int idx;
                        //Put neighbors in stack
                        if (process.X - 1 >= 0)
                        {
                            n = new Point(process.X - 1, process.Y); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }

                        if (process.X + 1 < nWidth)
                        {
                            n = new Point(process.X + 1, process.Y); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }

                        if (process.Y - 1 >= 0)
                        {
                            n = new Point(process.X, process.Y - 1); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }

                        if (process.Y + 1 < nHeight)
                        {
                            n = new Point(process.X, process.Y + 1); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }
                    }

                       //finaly set a rectangle.
                             Rectangle r = new Rectangle(minX * 10, minY * 10, (maxX - minX + 1) * 10, (maxY - minY + 1) * 10);
                            rec.Add(r);

                            //got the rectangle now i'll do whatever i want with that.
                            //notify i scaled everything by x10 becuse i want to apply the changes on the originl 1920x1080 image.

                }

            p1 += bytesPerPixel;
            p2 += bytesPerPixel;
        }

        base1 += stride1;
        base2 += stride2;
    }


        bmp.UnlockBits(bmData1);
        bmp2.UnlockBits(bmData2);

        return rec;
    }

这是我的电话:

  private void Start()
  {     
     full1 = GetDesktopImage();//the first,intial screen.

     while (true)
     {
         full2 = GetDesktopImage();
         a = new Bitmap(full1, 192, 108);//resizing for faster processing the images.
         b = new Bitmap(full2, 192, 108);  // resizing for faster processing the images.
         CodeImage(a, b);

         count++;          // counter for the performance.
         full1 = full2;    // assign old to current bitmap.
      }
    }

然而,在我使用的所有技巧和技术之后,算法运行得非常慢...在我的机器上 - 英特尔i5 4670k 3.4ghz - 它只运行20次(最多!它可能会降低)!它可能听起来很快(不要忘记我必须通过网络发送每个更改的区域),但我希望每秒可以获得更多处理过的图像。我认为主要的瓶颈在于调整2张图像的大小 - 但我只是认为在调整大小后它会更快 - 因为它必须循环通过更少的像素... 192 * 108 = 200,000 只有..

我会感激任何帮助,任何进步。感谢。

0 个答案:

没有答案