Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/322.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#图像泛洪填充算法改进_C#_Performance_Image Processing - Fatal编程技术网

C#图像泛洪填充算法改进

C#图像泛洪填充算法改进,c#,performance,image-processing,C#,Performance,Image Processing,我正在开发一个屏幕共享应用程序,它使用GDI方法运行循环并抓取快速屏幕截图 当然,我也使用泛洪填充算法来查找两幅图像之间的变化区域(上一张截图和当前截图) 我使用了另一个小技巧-我将快照分辨率降低到10,因为非常持续地处理1920*1080=2073600像素不是非常有效 但是,当我找到矩形边界时,我将其应用于原始的全尺寸位图,然后将尺寸乘以10(包括顶部、左侧、宽度和高度) 这是扫描代码: unsafe bool ArePixelsEqual(byte* p1, byte* p2, i

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

当然,我也使用泛洪填充算法来查找两幅图像之间的变化区域(上一张截图和当前截图)

我使用了另一个小技巧-我将快照分辨率降低到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;
    }
然而,在我使用了所有的技巧和技巧之后,算法运行得相当慢。。。在我的机器上-英特尔i5 4670k 3.4ghz-它只运行20次意味着
CodeImage
方法执行时间50ms(最大值!可能会降低)!这听起来可能很快(别忘了我必须在之后通过网络发送每个更改的区域),但我希望每秒处理更多的图像。我认为主要的瓶颈是在调整2幅图像的大小-但我只是认为它在调整大小后会更快-因为它必须循环通过更少的像素192*108=200000仅限

如果有任何帮助和改进,我将不胜感激


如果有什么不清楚的地方,我会编辑并重写它。

编辑:我已经做了一些分析,耗时最长的不是你的代码或调整大小,而是屏幕截图本身。我已使用用于测试的代码更新了示例代码:

    public static void ResizeImage(Bitmap image, Bitmap destImage)
    {
        var destRect = new Rectangle(0, 0, destImage.Width, destImage.Height);

        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CompositingMode = CompositingMode.SourceCopy;
            graphics.CompositingQuality = CompositingQuality.HighSpeed;
            graphics.InterpolationMode = InterpolationMode.Low;
            graphics.SmoothingMode = SmoothingMode.None;
            graphics.PixelOffsetMode = PixelOffsetMode.Default;

            using (var wrapMode = new ImageAttributes())
            {
                wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
            }
        }
    }

    private void CaptureScreen(Bitmap destImage)
    {
        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CopyFromScreen(0, 0, 0, 0, new Size(destImage.Width, destImage.Height), CopyPixelOperation.SourceCopy);
        }

    }

    public void Start()
    {
        var size = new Size(1920, 1080);
        var fullSizeImgs = new Bitmap[2]
        {
            new Bitmap(size.Width, size.Height),
            new Bitmap(size.Width, size.Height)
        };

        var resizedImgs = new Bitmap[2]
        {
            new Bitmap((int)(size.Width / 10.0f), (int)(size.Height / 10.0f)),
            new Bitmap((int)(size.Width / 10.0f), (int)(size.Height / 10.0f))
        };

        var globalWatch = new Stopwatch();
        var codeWatch = new Stopwatch();

        int current = 0;
        int counter = 0;
        CaptureScreen(fullSizeImgs[current]);
        ResizeImage(fullSizeImgs[current], resizedImgs[current]);

        globalWatch.Start();
        while (true)
        {
            var next = (current + 1) % 2;
            long totalFrameTime = 0;

            {
                codeWatch.Reset();
                codeWatch.Start();
                CaptureScreen(fullSizeImgs[next]);
                codeWatch.Stop();
                var elapsed = codeWatch.ElapsedMilliseconds;
                Console.WriteLine("Capture : {0} ms", elapsed);
                totalFrameTime += elapsed;
            }

            {
                codeWatch.Reset();
                codeWatch.Start();
                ResizeImage(fullSizeImgs[next], resizedImgs[next]);
                codeWatch.Stop();
                var elapsed = codeWatch.ElapsedMilliseconds;
                Console.WriteLine("Resize : {0} ms", elapsed);
                totalFrameTime += elapsed;
            }

            {
                codeWatch.Reset();
                codeWatch.Start();
                var rects = CodeImage(resizedImgs[current], resizedImgs[next]);
                codeWatch.Stop();
                var elapsed = codeWatch.ElapsedMilliseconds;
                Console.WriteLine("Code : {0} ms", elapsed);
                totalFrameTime += elapsed;
            }
            counter++;
            Console.WriteLine("Total : {0} ms\nFPS : {1}\n", totalFrameTime, counter / ((double)globalWatch.ElapsedMilliseconds / 1000.0));
            current = (current + 1) % 2;
        }
        globalWatch.Stop();
    }
不管我是否使用较小的图像,我的电脑平均每秒20帧。考虑到屏幕截图本身已经需要30-40毫秒,你几乎没有回旋的余地

您可以评估其他技术(DirectX、镜像驱动程序等)。
您可以查看和寻找每种技术的比较方法。

如果您的代码正在运行,并且您只是想提高性能,您应该将问题转移到。不要猜测什么是慢的,因为它通常不是您所想的。永远是个人资料!非常感谢您所做的一切,但我不明白您这句话的意思-
GetDesktopImage(fullSizeImgs[current])
当然不起作用了,因为
GetDesktopImage
没有参数,但我试着使用它。。。我不明白该怎么写。再多一点帮助就可以解决这个问题了:)谢谢,好吧,但实际上你还没有回答我的问题。我的代码本身也以每秒20帧的速度运行。。这里的改进是什么?谢谢!没有进步,这才是重点。评测后,您的代码不是瓶颈;你需要找到一种更快的方法来捕捉屏幕,仅此而已。不!当您仅使用捕获屏幕代码时,它的工作速度为30fps!
    public static void ResizeImage(Bitmap image, Bitmap destImage)
    {
        var destRect = new Rectangle(0, 0, destImage.Width, destImage.Height);

        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CompositingMode = CompositingMode.SourceCopy;
            graphics.CompositingQuality = CompositingQuality.HighSpeed;
            graphics.InterpolationMode = InterpolationMode.Low;
            graphics.SmoothingMode = SmoothingMode.None;
            graphics.PixelOffsetMode = PixelOffsetMode.Default;

            using (var wrapMode = new ImageAttributes())
            {
                wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
            }
        }
    }

    private void CaptureScreen(Bitmap destImage)
    {
        using (var graphics = Graphics.FromImage(destImage))
        {
            graphics.CopyFromScreen(0, 0, 0, 0, new Size(destImage.Width, destImage.Height), CopyPixelOperation.SourceCopy);
        }

    }

    public void Start()
    {
        var size = new Size(1920, 1080);
        var fullSizeImgs = new Bitmap[2]
        {
            new Bitmap(size.Width, size.Height),
            new Bitmap(size.Width, size.Height)
        };

        var resizedImgs = new Bitmap[2]
        {
            new Bitmap((int)(size.Width / 10.0f), (int)(size.Height / 10.0f)),
            new Bitmap((int)(size.Width / 10.0f), (int)(size.Height / 10.0f))
        };

        var globalWatch = new Stopwatch();
        var codeWatch = new Stopwatch();

        int current = 0;
        int counter = 0;
        CaptureScreen(fullSizeImgs[current]);
        ResizeImage(fullSizeImgs[current], resizedImgs[current]);

        globalWatch.Start();
        while (true)
        {
            var next = (current + 1) % 2;
            long totalFrameTime = 0;

            {
                codeWatch.Reset();
                codeWatch.Start();
                CaptureScreen(fullSizeImgs[next]);
                codeWatch.Stop();
                var elapsed = codeWatch.ElapsedMilliseconds;
                Console.WriteLine("Capture : {0} ms", elapsed);
                totalFrameTime += elapsed;
            }

            {
                codeWatch.Reset();
                codeWatch.Start();
                ResizeImage(fullSizeImgs[next], resizedImgs[next]);
                codeWatch.Stop();
                var elapsed = codeWatch.ElapsedMilliseconds;
                Console.WriteLine("Resize : {0} ms", elapsed);
                totalFrameTime += elapsed;
            }

            {
                codeWatch.Reset();
                codeWatch.Start();
                var rects = CodeImage(resizedImgs[current], resizedImgs[next]);
                codeWatch.Stop();
                var elapsed = codeWatch.ElapsedMilliseconds;
                Console.WriteLine("Code : {0} ms", elapsed);
                totalFrameTime += elapsed;
            }
            counter++;
            Console.WriteLine("Total : {0} ms\nFPS : {1}\n", totalFrameTime, counter / ((double)globalWatch.ElapsedMilliseconds / 1000.0));
            current = (current + 1) % 2;
        }
        globalWatch.Stop();
    }