我正在处理光栅图像绘制,所以我的目标是仅检测门形状我正在使用Emgu C#并应用Haris Corner算法,阈值= 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给出为“指定宽度的开口”,因此也容易出现错误的指示。该算法只能检测正交的门原理。
示例结果:
方法如下:
// Open the image Image<Gray, byte> baseImage = new Image<Gray, byte>(@"TestLayout.jpg"); // Invert the image Image<Gray, byte> invBaseImage = baseImage.Not(); // Threshold the image so as "close to white" is maintained, all else is black Image<Gray, byte> blackOnlyImage = invBaseImage.ThresholdBinary(new Gray(200), new Gray(255)); // An output image of the same size to contain the walls Image<Gray, byte> wallsOnlyImage = new Image<Gray, byte>(blackOnlyImage.Size); // A set of dected contours VectorOfVectorOfPoint inputContours = new VectorOfVectorOfPoint(); // A set of validated contours List<VectorOfPoint> validContours = new List<VectorOfPoint>(); // Perform contour detection Mat hierarchy = new Mat(); CvInvoke.FindContours(blackOnlyImage, inputContours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxSimple); // Filter out to select only contours bounding more that 500 pixels int areaThreshold = 500; for (int c = 0; c < inputContours.Size; c++) { if (CvInvoke.ContourArea(inputContours[c]) >= areaThreshold) { validContours.Add(inputContours[c]); } } // Find all the corner points in the valid contours List<Point> contourCorners = new List<Point>(); foreach(VectorOfPoint contour in validContours) { contourCorners.AddRange(CornerWalk(contour, 80)); } // Sort the contour corners by proximity to origin in order to optimise following loops contourCorners.OrderBy(p => Math.Sqrt(Math.Pow(p.X, 2) + Math.Pow(p.Y, 2))); // Extract all door candidate point pairs from all detected corners List<Tuple<Point, Point>> doorCandidates = FindDoors(contourCorners, 2, 30, 45); // Pixels contained within the filtered contours are walls, fill them white RasterFill(wallsOnlyImage, validContours); // Output Image Image<Rgb, byte> outputImage = new Image<Rgb, byte>(wallsOnlyImage.Size); CvInvoke.CvtColor(wallsOnlyImage, outputImage, ColorConversion.Gray2Rgb); // Draw the doors foreach (Tuple<Point,Point> door in doorCandidates) { outputImage.Draw(new LineSegment2D(door.Item1, door.Item2), new Rgb(255,0,0), 1); } // Display generated output and save it to file CvInvoke.NamedWindow("TestOutput"); CvInvoke.Imshow("TestOutput", outputImage); CvInvoke.WaitKey(); outputImage.Save(@"OutputImage.bmp");
角提取
static List<Point> CornerWalk(VectorOfPoint contour, int threshold) { // Create a resultant list of points List<Point> result = new List<Point>(); // Points are used to store 2D vectors as dx,dy (i,j) Point reverseVector, forwardVector; double theta; // For each point on the contour for(int p = 1; p < contour.Size; p++) { // Determine the vector to the prior point reverseVector = new Point() { X = contour[p].X - contour[p - 1].X, Y = contour[p].Y - contour[p - 1].Y, }; // Determine the vector to the next point forwardVector = p == contour.Size - 1 ? new Point() { X = contour[0].X - contour[p].X, Y = contour[0].Y - contour[p].Y, } : new Point() { X = contour[p + 1].X - contour[p].X, Y = contour[p + 1].Y - contour[p].Y, }; // Compute the angular delta between the two vectors (Radians) theta = Math.Acos(((reverseVector.X * forwardVector.X) + (reverseVector.Y * forwardVector.Y)) / (Math.Sqrt(Math.Pow(reverseVector.X, 2) + Math.Pow(reverseVector.Y, 2)) * Math.Sqrt(Math.Pow(forwardVector.X, 2) + Math.Pow(forwardVector.Y, 2)))); // Convert the angle to degrees theta *= 180 / Math.PI; // If the angle is above or equal the threshold, the point is a corner if (theta >= threshold) result.Add(contour[p]); } // Return the result return result; }
门检测
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;
}
}
}
}
}
对不起,python代码。但这也许会帮助您解决问题。查看评论。