【问题标题】:C# cut rectangle blocks from imageC# 从图像中剪切矩形块
【发布时间】:2015-08-14 15:58:46
【问题描述】:

我开发了一个屏幕共享应用程序,我想让它尽可能高效,所以我试图只发送屏幕截图之间的差异。 所以,假设我们有这个图像,例如:它是一个 32bpprgba 图像,周围有透明的部分。

我想将此处的每个块作为矩形存储在列表中并获取它们的边界。听起来可能很复杂,但实际上它只需要一点逻辑。 到目前为止,这是我的代码:

    private unsafe List<Rectangle> CodeImage(Bitmap bmp)
    {

        List<Rectangle> rec = new List<Rectangle>();
        Bitmap bmpRes = new Bitmap(bmp.Width, bmp.Height);
        BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
        IntPtr scan0 = bmData.Scan0;
        int stride = bmData.Stride;
        int nWidth = bmp.Width;
        int nHeight = bmp.Height;
        int minX = int.MaxValue; ;
        int minY = int.MaxValue;
        int maxX = 0;
        bool found = false;


        for (int y = 0; y < bmp.Height; y++)
        {
            byte* p = (byte*)scan0.ToPointer();
            p += y * stride;


            for (int x = 0; x < bmp.Width; x++)
            {

                if (p[3] != 0)  //Check if pixel is not transparent;
                {

                    found = true;
                    if (x < minX)
                        minX = x;
                    if (x > maxX)
                        maxX = x;
                    if (y < minY)
                        minY = y;

                }

                else
                {

                    if (found)
                    {

                        int height = getBlockHeight(stride, scan0, maxX, minY);
                        found = false;
                        Rectangle temp = new Rectangle(minX, minY, maxX - minX, height);
                        rec.Add(temp);
                        y += minY;
                        break;

                    }
                }
                p += 4;//add to the pointer 4 bytes;
            }
        }

        return rec;
    }

如您所见,我试图使用高度和宽度扫描图像,当我找到一个像素时,我将其发送到 GetBlockHeight 函数以获取它的高度:

  public unsafe int getBlockHeight(int stride, IntPtr scan, int x, int y1)
    {

        int height = 0; ;
        for (int y = y1; y < 1080; y++)
        {
            byte* p = (byte*)scan.ToPointer();
            p += (y * stride) + (x * 4); 

            if (p[3] != 0)  //Check if pixel is not transparent;
            {

                height++;
            }

        }
        return height;
    }

但我只是没有得到结果......我认为这里的逻辑有些东西......谁能点亮我的眼睛?我知道这需要一些时间和思考,但我非常感谢任何可以提供一点帮助的人。

【问题讨论】:

  • 看来maxX 永远是int.MaxValue。您还需要在匹配块后重置它。此外,从长远来看,您想支持什么?同一垂直位置上的多个矩形?非矩形区域?
  • @C.Evenhui 目前只有矩形区域。关于maxX 是的,你是对的.. 改变了它,但它仍然不是很接近...... 是的,我猜同一行会有多个矩形。
  • @C.Evenhuis 你说的重置是什么意思?打破循环并启动价值观?如果您能多解释一点或举一个简单的例子,我将不胜感激。谢谢。
  • @C.Evenhuis 还在吗?

标签: c# image-processing screenshot


【解决方案1】:

在您当前的算法中,成功匹配一个矩形后,您增加了y 的高度和break 的内部循环。这意味着您只能检测每条水平线一个矩形的数据。

如果我是你,在跳回代码之前,我会考虑以下几点:

  • 将完整图像保存为 PNG 文件,并查看其大小。真的需要进一步处理吗?
  • 这些矩形是否准确?是否会出现您会不断发送整个屏幕内容的情况?
  • 如果您正在为 Windows 开发,您可能能够挂钩使屏幕上的区域无效的过程,在这种情况下您不必自己确定这些矩形。我不知道其他操作系统

另外,我个人不会尝试在“嵌套”for-loop 中解决矩形检测算法,而是使用这样的方法:

public void FindRectangles(Bitmap bitmap, Rectangle searchArea, List<Rectangle> results)
{
    // Find the first non-transparent pixel within the search area. 
    // Ensure that it is the pixel with the lowest y-value possible
    Point p;
    if (!TryFindFirstNonTransparent(bitmap, searchArea, out p))
    {
        // No non-transparent pixels in this area
        return;
    }

    // Find its rectangle within the area
    Rectangle r = GetRectangle(bitmap, p, searchArea);
    results.Add(r);

    // No need to search above the rectangle we just found
    Rectangle left = new Rectangle(searchArea.Left, r.Top, r.Left - searchArea.Left, searchArea.Bottom - r.Top);
    Rectangle right = new Rectangle(r.Right, r.Top, searchArea.Right - r.Right, searchArea.Bottom - r.Top);
    Rectangle bottom = new Rectangle(r.Left, r.Bottom, r.Width, searchArea.Bottom - r.Bottom);

    FindRectangles(bitmap, left, results);
    FindRectangles(bitmap, right, results);
    FindRectangles(bitmap, bottom, results);
}

public Rectangle GetRectangle(Bitmap bitmap, Point p, Rectangle searchArea)
{
    int right = searchArea.Right;
    for (int x = p.X; x < searchArea.Right; x++)
    {
        if (IsTransparent(x, p.Y))
        {
            right = x - 1;
            break;
        }
    }

    int bottom = searchArea.Bottom;
    for (int y = p.Y; y < searchArea.Bottom; y++)
    {
        if (IsTransparent(p.X, y))
        {
            bottom = y - 1;
            break;
        }
    }

    return new Rectangle(p.X, p.Y, right - p.X, bottom - p.Y);
}

这种方法在完全实施后,应该会为您提供一个矩形列表(尽管它偶尔会将一个矩形一分为二)。

(当然不是提供bitmap,而是将指针传递给带有一些元数据的像素数据)

【讨论】:

  • 非常感谢!关于它需要的处理,因为上面的图像只是一个示例,在我的程序中,我将处理 png 格式的屏幕截图,最高可达 1mb+!
  • 还有几个问题 - get rectangle 方法实际上是什么?
  • 我不知道全屏发送 - 但我不认为我会发送全尺寸屏幕截图。 ..只有差异。顺便说一句,在这种情况下,requresion 真的更有效吗?我能感受到代码性能的提升吗?
  • GetRectangle 应该确定矩形的宽度和高度,给定其左上角像素的位置。对不起,我应该提到这一点。
  • 我并没有真正关注性能(我认为它不会比您的循环快/慢得多),而是想展示一种我觉得更容易遵循的不同方法。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-31
  • 2017-06-01
  • 1970-01-01
  • 2018-04-10
相关资源
最近更新 更多