C# 扫描图像以查找矩形

C# 扫描图像以查找矩形,c#,algorithm,image-processing,computer-vision,C#,Algorithm,Image Processing,Computer Vision,我正在尝试扫描一个恒定大小的图像,并找到其中绘制的矩形。 矩形可以有任何大小,但只有红色 这不是问题开始的地方 我将使用一个已经编写好的函数,稍后在代码逻辑中将其用作伪代码调用 矩形定位(矩形扫描区域)//扫描给定扫描区域中的矩形。 如果未找到Rectage,则返回null 我的逻辑是这样的: 使用带有完整图像大小作为参数的Locate()函数查找第一个初始红色矩形。 现在,划分其余区域,并继续递归扫描。 该算法逻辑的要点是,您从不检查已检查的区域,并且您不必使用任何条件,因为scanArea参

我正在尝试扫描一个恒定大小的图像,并找到其中绘制的矩形。 矩形可以有任何大小,但只有红色

这不是问题开始的地方

我将使用一个已经编写好的函数,稍后在代码逻辑中将其用作伪代码调用

矩形定位(矩形扫描区域)//扫描给定扫描区域中的矩形。
如果未找到Rectage,则返回null

我的逻辑是这样的:

使用带有完整图像大小作为参数的
Locate()
函数查找第一个初始红色矩形。 现在,划分其余区域,并继续递归扫描。 该算法逻辑的要点是,您从不检查已检查的区域,并且您不必使用任何条件,因为
scanArea
参数始终是一个您以前从未扫描过的新区域(这要感谢分割技术)。 分割过程如下:当前找到的矩形的右侧区域、底部区域和左侧区域

这里有一张图片说明了这个过程。 (白色虚线矩形和黄色箭头不是图像的一部分,我添加它们只是为了演示。) 如您所见,一旦找到一个红色矩形,我会一直扫描它的右侧、底部和左侧。递归地

下面是该方法的代码:

List<Rectangle> difList=new List<Rectangle>();

private void LocateDifferences(Rectangle scanArea)
{
    Rectangle foundRect = Locate(scanArea);
    if (foundRect == null)
        return; // stop the recursion.

    Rectangle rightArea = new Rectangle(foundRect.X + foundRect.Width, foundRect.Y, (scanArea.X + scanArea.Width) - (foundRect.X + foundRect.Width), (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); // define right area.
    Rectangle bottomArea = new Rectangle(foundRect.X, foundRect.Y + foundRect.Height, foundRect.Width, (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); // define bottom area.
    Rectangle leftArea = new Rectangle(scanArea.X, foundRect.Y, (foundRect.X - scanArea.X), (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); // define left area.

    difList.Add(rectFound);

    LocateDifferences(rightArea);
    LocateDifferences(bottomArea);
    LocateDifferences(leftArea);
}
List difList=new List();
专用空心位置差异(矩形扫描区域)
{
矩形foundRect=定位(扫描区域);
if(foundRect==null)
return;//停止递归。
矩形rightArea=新矩形(foundRect.X+foundRect.Width,foundRect.Y,(scanArea.X+scanArea.Width)-(foundRect.X+foundRect.Width),(scanArea.Y+scanArea.Height)-(foundRect.Y+foundRect.Height));//定义右侧区域。
矩形底部区域=新矩形(foundRect.X,foundRect.Y+foundRect.Height,foundRect.Width,(scanArea.Y+scanArea.Height)-(foundRect.Y+foundRect.Height));//定义底部区域。
矩形左区域=新矩形(scanArea.X,foundRect.Y,(foundRect.X-scanArea.X),(scanArea.Y+scanArea.Height)-(foundRect.Y+foundRect.Height));//定义左区域。
difList.Add(rectFound);
位置差异(右侧区域);
位置差异(底部区域);
位置差异(左侧区域);
}
到目前为止,一切正常,它确实找到了每个红色矩形。 但有时,矩形保存为几个矩形。原因对我来说是显而易见的: 重叠矩形的大小写。

一个有问题的案例,例如:

现在,在程序中,程序按计划找到第一个红色区域,但是,由于右区域只在第二个区域的中间开始,所以它不会从第二个红色矩形开始扫描。p> 以类似的方式,我可以划分区域,以便底部区域从

scanArea
的开始一直延伸到结尾,如下所示: 但是现在,我们在扫描
foundRect
矩形的右侧和左侧的重叠矩形时会遇到问题,例如,在这种情况下:

我只需要得到一个矩形。 我希望结合我的代码逻辑得到任何帮助或建议,因为它工作得很好,但我认为在递归方法中只需要一个或两个附加条件。我不知道该做什么,我真的很感激任何帮助

如果有什么不清楚的地方,就告诉我,我会尽可能地解释! 谢谢


当然,这不是我面临的真正问题,它只是一个小演示,可以帮助我解决我正在处理的真正问题(这是一个实时互联网项目)。

我很抱歉,但我没有阅读你的解决方案,因为我不确定你是否想要一个好的解决方案,或者用这个解决方案解决问题

使用现有构建块(如OpenCV,我不知道是否有c#的端口)的简单解决方案是:

  • 以红色通道为例(因为您说过只想检测红色矩形)
  • findContours
  • 对于每个轮廓 3.1以其边界框为例 3.2通过将轮廓的总面积与边框的总面积进行比较,检查轮廓是否为矩形
  • 解决方案将根据输入图像的不同而变化。
    我希望我能帮助你。如果没有,请告诉我您需要什么样的帮助。

    按照您的标准,不更改Locate()函数,只扩展现有逻辑,我们需要在扫描后加入任何rects。试试这个:

    首先稍微修改LocateDifferences()函数,以跟踪可能需要连接的矩形

    private void LocateDifferences(Rectangle scanArea)
    {
        Rectangle foundRect = Locate(scanArea);
        if (foundRect == null)
            return; // stop the recursion.
    
        Rectangle rightArea = new Rectangle(foundRect.X + foundRect.Width, foundRect.Y, (scanArea.X + scanArea.Width) - (foundRect.X + foundRect.Width), (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); //define right area.
        Rectangle bottomArea = new Rectangle(foundRect.X, foundRect.Y + foundRect.Height, foundRect.Width, (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); // define bottom area.
        Rectangle leftArea = new Rectangle(scanArea.X, foundRect.Y, (foundRect.X - scanArea.X), (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); // define left area.
    
        if (foundRect.X == scanArea.X || foundRect.Y == scanArea.Y || (foundRect.X + foundRect.Width == scanArea.X + scanArea.Width) || (foundRect.Y + foundRect.Height == scanArea.Y + scanArea.Height)) 
        {
            // edge may extend scanArea
            difList.Add(Tuple.Create(foundRect, false));
        } else {
            difList.Add(Tuple.Create(foundRect, true));
        }
    
        LocateDifferences(rightArea);
        LocateDifferences(bottomArea);
        LocateDifferences(leftArea);
    }
    
    我还添加了以下两种使用方法:

    // JoinRects: will return a rectangle composed of r1 and r2.
    private Rectangle JoinRects(Rectangle r1, Rectangle r2)
    {
        return new Rectangle(Math.Min(r1.X, r2.X), 
                        Math.Min(r1.Y, r2.Y), 
                        Math.Max(r1.Y + r1.Width, r2.Y + r2.Width), 
                        Math.Max(r1.X + r1.Height, r2.X + r2.Height));
    }
    
    // ShouldJoinRects: determines if the rectangles are connected and the height or width matches.
    private bool ShouldJoinRects(Rectangle r1, Rectangle r2)
    {
        if ((r1.X + r1.Width + 1 == r2.X && r1.Y == r2.Y && r1.Height == r2.Height)
         || (r1.X - 1 == r2.x + r2.Width && r1.Y == r2.Y && r1.Height == r2.Height)
         || (r1.Y + r1.Height + 1 == r2.Y && r1.X == r2.X && r1.Width == r2.Width)
         || (r1.Y - 1 == r2.Y + r2.Height && r1.X == r2.X && r1.Width == r2.Width))
        {
            return true;
        } 
        else 
        {
            return false;
        }
    }
    
    最后是启动扫描的主要功能

    List<Tuple<Rectangle, Bool>> difList = new List<Tuple<Rectangle, Bool>();
    
    // HERE: fill our list by calling LocateDifferences
    LocateDifferences();
    
    var allGood = difList.Where(t => t.Item2 == true).ToList();
    var checkThese = difList.Where(t => t.Item2 == false).ToArray();
    
    for (int i = 0; i < checkThese.Length - 1; i++)
    {
        // check that its not an empty Rectangle
        if (checkThese[i].IsEmpty == false) 
        {
            for (int j = i; j < checkThese.Length; j++)
            {
                // check that its not an empty Rectangle
                if (checkThese[j].IsEmpty == false) 
                {
                    if (ShouldJoinRects(checkThese[i], checkThese[j])
                    {
                        checkThese[i] = JoinRects(checkThese[i], checkThese[j]);
                        checkThese[j] = new Rectangle(0,0,0,0);
                        j = i // restart the inner loop in case we are dealing with a rect that crosses 3 scan areas
                    }
                }
            }
            allGood.Add(checkThese[i]);
        }
    }
    
    //Now 'allGood' contains all the rects joined where needed.
    
    List difList=new List t.Item2==true).ToList();
    var checkthises=difList.Where(t=>t.Item2==false).ToArray();
    对于(int i=0;i
    使用以下简单算法的最简单方法:

    function find(Image): Collection of Rects
       core_rect = FindRects(Image)
       split(core_rect) -> 4 rectangles (left-top, left-bottom, right-top, right-bottom)
       return Merge of (find(leftTop), find(leftBottom), ...)
    
    function findAll(Image): Collection of Rects
       rects <- find(Image)
       sort rectangles by X, Y
       merge rectangles
       sort rectangles by Y, X
       merge rectangles
       return merged set
    
    函数查找(图像):矩形集合
    core_rect=FindRects(图像)
    拆分(核心矩形)->4个矩形(左上、左下、右
    
    internal enum Side : byte 
    {
        Left,
        Bottom,
        Right
    }
    
    internal class RectangleInfo
    {
        public RectangleInfo(Rectangle rect, bool leftOverlap, bool rightOverlap)
        {
            Rectangle = rect;
            LeftOverlap = leftOverlap;
            RightOverlap = rightOverlap;
        }
        public Rectangle Rectangle { get; set; }
        public bool LeftOverlap { get; set; }
        public bool RightOverlap { get; set; }
    }
    
    List<Rectangle> difList = new List<Rectangle>();
    
    List<Rectangle> leftList = new List<Rectangle>();
    List<RectangleInfo> bottomList = new List<RectangleInfo>();
    List<Rectangle> rightList = new List<Rectangle>();
    
    private void AccumulateDifferences(Rectangle scanArea, Side direction)
    {
        Rectangle foundRect = Locate(scanArea);
        if (foundRect == null)
            return; // stop the recursion.
    
        switch (direction)
        {
            case Side.Left:
                if (foundRect.X + foundRect.Width == scanArea.X + scanArea.Width)
                    leftList.Add(foundRect);
                else difList.Add(foundRect);
                break;
    
            case Side.Bottom:
                bottomList.Add(new RectangleInfo(foundRect, foundRect.X == scanArea.X, foundRect.X + foundRect.Width == scanArea.X + scanArea.Width));
                break;
    
            case Side.Right:
                if (foundRect.X == scanArea.X)
                    rightList.Add(foundRect);
                else difList.Add(foundRect);
                break;
        }
        Rectangle leftArea = new Rectangle(scanArea.X, foundRect.Y, (foundRect.X - scanArea.X), (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); // define left area.
        Rectangle bottomArea = new Rectangle(foundRect.X, foundRect.Y + foundRect.Height, foundRect.Width, (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); // define bottom area.
        Rectangle rightArea = new Rectangle(foundRect.X + foundRect.Width, foundRect.Y, (scanArea.X + scanArea.Width) - (foundRect.X + foundRect.Width), (scanArea.Y + scanArea.Height) - (foundRect.Y + foundRect.Height)); //define right area.
    
        AccumulateDifferences(leftArea, Side.Left);
        AccumulateDifferences(bottomArea, Side.Bottom);
        AccumulateDifferences(rightArea, Side.Right);
    }
    
    private void ProcessDifferences()
    {
        foreach (RectangleInfo rectInfo in bottomList)
        {
            if (rectInfo.LeftOverlap)
            {
                Rectangle leftPart =
                    leftList.Find(r => r.X + r.Width == rectInfo.Rectangle.X
                                       && r.Y == rectInfo.Rectangle.Y
                                       && r.Height == rectInfo.Rectangle.Height
                                 );
                if (leftPart != null)
                {
                    rectInfo.Rectangle.X = leftPart.X;
                    leftList.Remove(leftPart);
                }
            }
    
            if (rectInfo.RightOverlap)
            {
                Rectangle rightPart =
                    rightList.Find(r => r.X == rectInfo.Rectangle.X + rectInfo.Rectangle.Width
                                        && r.Y == rectInfo.Rectangle.Y
                                        && r.Height == rectInfo.Rectangle.Height
                                  );
                if (rightPart != null)
                {
                    rectInfo.Rectangle.X += rightPart.Width;
                    rightList.Remove(rightPart);
                }
            }
    
            difList.Add(rectInfo.Rectangle);
        }
    
        difList.AddRange(leftList);
        difList.AddRange(rightList);
    }
    
    private void LocateDifferences(Rectangle scanArea)
    {
        AccumulateDifferences(scanArea, Side.Left);
        ProcessDifferences();
    
        leftList.Clear();
        bottomList.Clear();
        rightList.Clear();
    }
    
    unfinished: {10,13,1}  
    
    unfinished: {10,13,1},{16,18,2}  
    
    unfinished: {1,4,3},{6,7,3},{10,13,1},{16,18,2},{21,214}  
    
    unfinished: {1,4,3},{6,7,3},{16,18,2},{21,21,4}  
    finished: {10,13,1,5}  
    
    unfinished: {12,16,7}  
    finished: {10,13,1,5},{16,18,2,5},{1,4,3,6},{6,7,3,8},{21,21,4,8}  
    
    unfinished:  
    finished: {10,13,1,5},{16,18,2,5},{1,4,3,6},{6,7,3,8},{21,21,4,8},  
              {12,16,7,10},{3,10,10,13},{13,17,13,14},{19,22,11,14}