C# 使用C检测楼层平面中的门形状

C# 使用C检测楼层平面中的门形状,c#,image-processing,emgucv,C#,Image Processing,Emgucv,我正在绘制光栅图像,所以我的目标是只检测门的形状 我使用的是Emgu C和应用的Haris角点算法,阈值=50 然后检测一个角点矩阵,然后计算两个点之间的距离,以接近这两个点是门形状的起点和终点 问题是: 我无法过滤图像以获得最佳检测效果,比如如何删除所有文本和噪声,只保留醒目的墙壁 [![在此处输入图像描述][1][1] [![在此处输入图像描述][2][2] var img = imgList["Input"].Clone(); var

我正在绘制光栅图像,所以我的目标是只检测门的形状 我使用的是Emgu C和应用的Haris角点算法,阈值=50 然后检测一个角点矩阵,然后计算两个点之间的距离,以接近这两个点是门形状的起点和终点 问题是: 我无法过滤图像以获得最佳检测效果,比如如何删除所有文本和噪声,只保留醒目的墙壁 [![在此处输入图像描述][1][1] [![在此处输入图像描述][2][2]

var img = imgList["Input"].Clone();            
                var gray = img.Convert<Gray, byte>().ThresholdBinaryInv(new Gray(100), new Gray(100)); ;
                imageBoxEx2.Image = gray.ToBitmap();
                var corners = new Mat();
                CvInvoke.CornerHarris(gray, corners,2);
                CvInvoke.Normalize(corners, corners, 255, 0, Emgu.CV.CvEnum.NormType.MinMax);
                Matrix<float> matrix = new Matrix<float>(corners.Rows, corners.Cols);
                corners.CopyTo(matrix);
                dt.Rows.Clear();
                List<Point> LstXpoints = new List<Point>();
                List<Point> LstYpoints = new List<Point>();
                List<PointF> LstF = new List<PointF>();
                for (int i = 0; i < matrix.Rows; i++)
                {
                    for (int j = 0; j < matrix.Cols; j++)
                    {
                        if (matrix[i, j] > threshold)
                        {

                            LstXpoints.Add(new Point ( j, i));
                            LstYpoints.Add(new Point(i, j));
                           // CvInvoke.Circle(img, new Point(j, i), 5, new MCvScalar(0, 0, 255), 3);
                        }
                    }
                }
[编辑-完全扩展的答案以提供完整的解决方案]

前言 我通常不会提供解决方案,因为我觉得它远远超出了有用的、可重用的问答格式。。。但这是一个有趣的问题

答复 以下详细介绍了在楼层平面内检测潜在门洞口的基本算法。除了提供的单个案例外,它没有进行性能优化或测试。由于OP仅将门定义为规定宽度的开口,因此门也容易出现错误指示。该算法只能检测原理正交门

示例结果:

方法 方法如下:

在输入图像中反转和设置阈值,以便将最暗的元素转换为白色全字节值。 计算轮廓检测,以识别现在白色区域的边界。 过滤以仅选择大于选定阈值的区域上的轮廓,从而消除文本元素和噪声。 漫游选定的轮廓以确定角点出现的节点。角点定义为高于阈值的角度变化。 分析检测到的角点是否符合门的配对条件。 [多余渲染]最后,在过滤轮廓的矩形边界内进行光栅渲染,以便将其填充到合成图像中。注意:这不是计算效率或优雅,但是用于轮廓填充的EmguCV方法仅支持凸面轮廓。门也渲染为红色。 算法 角提取 车门检测 轮廓填充渲染工具-不起作用 [编辑-完全扩展的答案以提供完整的解决方案]

前言 我通常不会提供解决方案,因为我觉得它远远超出了有用的、可重用的问答格式。。。但这是一个有趣的问题

答复 以下详细介绍了在楼层平面内检测潜在门洞口的基本算法。除了提供的单个案例外,它没有进行性能优化或测试。由于OP仅将门定义为规定宽度的开口,因此门也容易出现错误指示。该算法只能检测原理正交门

示例结果:

方法 方法如下:

在输入图像中反转和设置阈值,以便将最暗的元素转换为白色全字节值。 计算轮廓检测,以识别现在白色区域的边界。 过滤以仅选择大于选定阈值的区域上的轮廓,从而消除文本元素和噪声。 漫游选定的轮廓以确定角点出现的节点。角点定义为高于阈值的角度变化。 分析检测到的角点是否符合门的配对条件。 [多余渲染]最后,在过滤轮廓的矩形边界内进行光栅渲染,以便将其填充到合成图像中。注意:这不是计算效率或优雅,但是用于轮廓填充的EmguCV方法仅支持凸面轮廓。门也渲染为红色。 算法 角提取 车门检测 轮廓填充渲染工具-不起作用
对于python代码感到抱歉。但也许这将有助于解决你的问题。 见评论

import cv2 

img = cv2.imread('NHoXn.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# convert to binary image
thresh=cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY )[1]

#  Morphological reconstruction (delete labels)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
marker = cv2.dilate(thresh,kernel,iterations = 1)
while True:
    tmp=marker.copy()
    marker=cv2.erode(marker, kernel2)
    marker=cv2.max(thresh, marker)
    difference = cv2.subtract(tmp, marker)
    if cv2.countNonZero(difference) == 0:
        break


# only walls
se=cv2.getStructuringElement(cv2.MORPH_RECT, (4,4))
walls=cv2.morphologyEx(marker, cv2.MORPH_CLOSE, se)
walls=cv2.erode(walls, kernel2,iterations=2)

# other objects
other=cv2.compare(marker,walls, cv2.CMP_GE)
other=cv2.bitwise_not(other)

# find connected components and select by size and area
output = cv2.connectedComponentsWithStats(other, 4, cv2.CV_32S)
num_labels = output[0]
labels = output[1]
stats=output[2]
centroids = output[3]
for i in range(num_labels):
    left,top,width,height,area=stats[i]
    if abs(width-40)<12 and abs(height-40)<12 and area>85:
         cv2.rectangle(img,(left, top), (left+width, top+height), (0,255,0))

cv2.imwrite('doors.png', img)
结果:

图纸中显示的内容:墙、门、窗、家具、文字标签。 被发现的门总是接触墙壁。 墙与其他对象有何不同?粗,这些线条是粗体的。因此,使用所需的结构元件进行膨胀只能留下部分墙体。然后,通过形态重建,首先修复墙壁以及与之相关的元素:门、窗。将清除图纸上不接触墙壁的所有东西。 如果进一步膨胀和侵蚀,那么只会留下墙壁,门窗之类的薄构件就会消失。 从第三阶段减去或逻辑运算,第四阶段我们得到的图片只包含接触墙壁的门、窗和家具。 门的图纸与窗户的图纸有什么不同?事实上,它们的BB几乎是正方形,本图中所有门的尺寸大致相同,它们的长度大约等于r*1+pi/4。 此外,在该准则中还对此类标志进行了选择。在此阶段,您可以添加更多的标志,以便更准确地将门与其他元素分开。 < p> 对于python代码感到抱歉。但也许这将有助于解决你的问题。 见评论

import cv2 

img = cv2.imread('NHoXn.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# convert to binary image
thresh=cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY )[1]

#  Morphological reconstruction (delete labels)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
marker = cv2.dilate(thresh,kernel,iterations = 1)
while True:
    tmp=marker.copy()
    marker=cv2.erode(marker, kernel2)
    marker=cv2.max(thresh, marker)
    difference = cv2.subtract(tmp, marker)
    if cv2.countNonZero(difference) == 0:
        break


# only walls
se=cv2.getStructuringElement(cv2.MORPH_RECT, (4,4))
walls=cv2.morphologyEx(marker, cv2.MORPH_CLOSE, se)
walls=cv2.erode(walls, kernel2,iterations=2)

# other objects
other=cv2.compare(marker,walls, cv2.CMP_GE)
other=cv2.bitwise_not(other)

# find connected components and select by size and area
output = cv2.connectedComponentsWithStats(other, 4, cv2.CV_32S)
num_labels = output[0]
labels = output[1]
stats=output[2]
centroids = output[3]
for i in range(num_labels):
    left,top,width,height,area=stats[i]
    if abs(width-40)<12 and abs(height-40)<12 and area>85:
         cv2.rectangle(img,(left, top), (left+width, top+height), (0,255,0))

cv2.imwrite('doors.png', img)
结果:

图纸中显示的内容:墙、门、窗、家具、文字标签。 被发现的门总是接触墙壁。 墙与其他对象有何不同?粗,这些线条是粗体的。因此,使用所需的结构元件进行膨胀只能留下部分墙体。然后,通过形态重建,首先修复墙壁以及与之相关的元素:门、窗。将清除图纸上不接触墙壁的所有东西。 如果进一步膨胀和侵蚀,那么只会留下墙壁,门窗之类的薄构件就会消失。 从第三阶段减去或逻辑运算,第四阶段我们得到的图片只包含接触墙壁的门、窗和家具。 门的图纸与窗户的图纸有什么不同?事实上,它们的BB几乎是正方形,本图中所有门的尺寸大致相同,它们的长度大约等于r*1+pi/4。 此外,在该准则中还对此类标志进行了选择。在此阶段,您可以添加更多的标志,以便更准确地将门与其他元素分开。

嗨,你能把没有标记的输入图像包括进来吗?我要做一个实验。@GeorgeKerwood是的,我用原始图像更新了帖子。问题:对不起,墙总是正交的吗?仅指水平或垂直?@GeorgeKerwood我在许多图像中工作,因此墙壁将是水平或垂直或混合的,我希望帮助我消除噪音和其他绘图,除了粗体线条墙壁,不工作墙壁这将有助于检测角落,我将预测门形状位置混合?我在问你是否可以期望一堵45度的墙?嗨,你能把没有标记的输入图像包括进来吗?我要做一个实验。@GeorgeKerwood是的,我用原始图像更新了帖子。问题:对不起,墙总是正交的吗?仅指水平或垂直?@GeorgeKerwood我在许多图像中工作,因此墙壁将是水平或垂直或混合的,我希望帮助我消除噪音和其他绘图,除了粗体线条墙壁,不工作墙壁这将有助于检测角落,我将预测门形状位置混合?我是问你是否可以期待45度的墙?不客气。对你有用吗?如果是的话,请标记回答的问题。仍然在尝试预测门的位置,所以我尝试为每面墙获取端点,有没有一个想法只为每面墙获取端点?谢谢你如何从数学上定义门?例如,您希望如何区分门和窗?只有现在通过获取两点之间的距离来检测门,如果相等,那么假设在35和55之间,这应该是门的一个孔。就这样,我只检测每面墙的端点。我可以很容易地做到这一点:。但这并不完美。你会得到双门和一些错误的结果。请问这是干什么用的?学习工业应用?不客气。对你有用吗?如果是的话,请标记回答的问题。仍然在尝试预测门的位置,所以我尝试为每面墙获取端点,有没有一个想法只为每面墙获取端点?谢谢你如何从数学上定义门?例如,您希望如何区分门和窗?只有现在通过获取两点之间的距离来检测门,如果相等,那么假设在35和55之间,这应该是门的一个孔。就这样,我只检测每面墙的端点。我可以很容易地做到这一点:。但这并不完美。你会得到双门和一些错误的结果。请问这是干什么用的?学习工业应用程序?嗨,Alex,我试过用当前图像编写代码。它可以工作,但不能与其他图像一起工作。请尝试此图像:要处理这种类型的图形,需要配置参数。不幸的是,获得了两个错误的门选择。需要调整和补充选择条件,但尺寸和面积除外,例如凸面外壳的面积或其他。@AlexAlex Hello。我真的对你的方法很感兴趣,它看起来很优雅,但是形态学操作对我来说是新的。请你详细说明一下评论/解释好吗?或者引用/参考相关文件。谢谢。@george kerwood你好。非常感谢。我会尽力解释的。英语不是我的母语,所以请原谅我的错误。我认为,卢克·文森特的著作和书籍对数学形态学有很好的解释。数学形态学及其在图像和信号处理中的应用。Bloomberg@AlexAlex用完美的英语进行精彩的解释!我看到你的算法,像我的一样,在任何其他门大小或有BB门的结构上也容易受到错误检测。到你第六节的最后一点,我
想一想,如果您添加了一个最终验证,即您检测到的BBs与墙的顶点/角重合/接触,这将是一个完美的解决方案。如果你仍然遵循这个思路,我会推荐这个解决方案而不是我自己的。这是更优雅。嗨,亚历克斯,我尝试代码与当前图像它的作品,但没有与其他人请尝试这个图像:工作在这种类型的绘图,你需要配置参数。不幸的是,获得了两个错误的门选择。需要调整和补充选择条件,但尺寸和面积除外,例如凸面外壳的面积或其他。@AlexAlex Hello。我真的对你的方法很感兴趣,它看起来很优雅,但是形态学操作对我来说是新的。请你详细说明一下评论/解释好吗?或者引用/参考相关文件。谢谢。@george kerwood你好。非常感谢。我会尽力解释的。英语不是我的母语,所以请原谅我的错误。我认为,卢克·文森特的著作和书籍对数学形态学有很好的解释。数学形态学及其在图像和信号处理中的应用。Bloomberg@AlexAlex用完美的英语进行精彩的解释!我看到你的算法,像我的一样,在任何其他门大小或有BB门的结构上也容易受到错误检测。关于你在6中的最后一点,我认为如果你要添加一个最终验证,即你检测到的BBs与墙的顶点/角重合/接触,这将是一个完美的解决方案。如果你仍然遵循这个思路,我会推荐这个解决方案而不是我自己的。它要优雅得多。
static List<Tuple<Point, Point>> FindDoors(
    List<Point> cornerPoints,
    int inLineTolerance,
    int minDoorWidth,
    int maxDoorWidth)
{
    // Create a resultant list of pairs of points
    List<Tuple<Point, Point>> results = new List<Tuple<Point, Point>>();
    Point p1, p2;
    // For every point in the list
    for (int a = 0; a < cornerPoints.Count; a++)
    {
        p1 = cornerPoints[a];
        // Against every other point in the list
        for (int b = 0; b < cornerPoints.Count; b++)
        {
            // Don't compare a point to it's self...
            if (a == b) continue;
            p2 = cornerPoints[b];

            // If p1 to p2 qualifies as a door:
                // Vertical Doors -     A vertical door will have to points of the same X value, within tolerance, and a Y value delta within the
                //                      min-max limits of a door width.
            if (((Math.Abs(p1.X - p2.X) < inLineTolerance) && (Math.Abs(p1.Y - p2.Y) > minDoorWidth) && (Math.Abs(p1.Y - p2.Y) < maxDoorWidth)) ||
                // Horizontal Doors -   A horizontal door will have to points of the same Y value, within tolerance, and a X value delta within the
                //                      min-max limits of a door width.
                ((Math.Abs(p1.Y - p2.Y) < inLineTolerance) && (Math.Abs(p1.X - p2.X) > minDoorWidth) && (Math.Abs(p1.X - p2.X) < maxDoorWidth)))
            {
                // Add the point pair to the result
                results.Add(new Tuple<Point, Point>(p1, p2));
                // Remove them from further consideration
                cornerPoints.Remove(p1);
                cornerPoints.Remove(p2);
                // Decrement the looping indexes and start over with a new p1
                b--; a--;
                break;
            }
        }
    }
    // Finally return the result
    return results;
}
static void RasterFill(Image<Gray,byte> dstImg, List<VectorOfPoint> contours)
{
    Rectangle contourBounds;
    PointF testPoint;
    // For each contour detected
    foreach(VectorOfPoint contour in contours)
    {
        // Within the bounds of this contour
        contourBounds = CvInvoke.BoundingRectangle(contour);
        for (int u = contourBounds.X; u < contourBounds.X + contourBounds.Width; u++)
        {
            for (int v = contourBounds.Y; v < contourBounds.Y + contourBounds.Height; v++)
            {
                // Test to determine whether the point is within the contour
                testPoint = new PointF(u, v);
                // If it is inside the contour, OR on the contour
                if (CvInvoke.PointPolygonTest(contour, testPoint, false) >= 0)
                {
                    // Set it white
                    dstImg.Data[v, u, 0] = 255;
                }
            }
        }
    }
}
import cv2 

img = cv2.imread('NHoXn.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# convert to binary image
thresh=cv2.threshold(gray, 220, 255, cv2.THRESH_BINARY )[1]

#  Morphological reconstruction (delete labels)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7,7))
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
marker = cv2.dilate(thresh,kernel,iterations = 1)
while True:
    tmp=marker.copy()
    marker=cv2.erode(marker, kernel2)
    marker=cv2.max(thresh, marker)
    difference = cv2.subtract(tmp, marker)
    if cv2.countNonZero(difference) == 0:
        break


# only walls
se=cv2.getStructuringElement(cv2.MORPH_RECT, (4,4))
walls=cv2.morphologyEx(marker, cv2.MORPH_CLOSE, se)
walls=cv2.erode(walls, kernel2,iterations=2)

# other objects
other=cv2.compare(marker,walls, cv2.CMP_GE)
other=cv2.bitwise_not(other)

# find connected components and select by size and area
output = cv2.connectedComponentsWithStats(other, 4, cv2.CV_32S)
num_labels = output[0]
labels = output[1]
stats=output[2]
centroids = output[3]
for i in range(num_labels):
    left,top,width,height,area=stats[i]
    if abs(width-40)<12 and abs(height-40)<12 and area>85:
         cv2.rectangle(img,(left, top), (left+width, top+height), (0,255,0))

cv2.imwrite('doors.png', img)