【问题标题】:emgucv: pan card improper skew detection in C#emgucv:C# 中的泛卡不正确倾斜检测
【发布时间】:2020-10-14 10:40:45
【问题描述】:

我有三个泛卡图像,用于使用 emgucv 和 c# 测试图像的倾斜。

顶部的第一张图像检测到 180 度正常工作。

中间的第二张图像检测到 90 度应检测为 180 度。

检测到 180 度的第三张图像应检测为 90 度。

我想在这里分享的一个观察结果是,当我使用画笔从平移卡的上下侧裁剪不需要的图像部分时,它使用下面提到的代码给了我预期的结果。

现在我想了解如何使用编程删除不需要的部分。 我玩过轮廓和投资回报率,但我无法弄清楚如何适应它们。我无法理解 emgucv 本身是选择轮廓还是我必须做些什么。

请推荐任何合适的代码示例。

请检查下面的代码以进行角度检测,请帮助我。提前致谢。

imgInput = new Image<Bgr, byte>(impath);
          Image<Gray, Byte> img2 = imgInput.Convert<Gray, Byte>();
          Bitmap imgs;
          Image<Gray, byte> imgout = imgInput.Convert<Gray, byte>().Not().ThresholdBinary(new Gray(50), new Gray(125));
          VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
          Emgu.CV.Mat hier = new Emgu.CV.Mat();
          var blurredImage = imgInput.SmoothGaussian(5, 5, 0 , 0);
          CvInvoke.AdaptiveThreshold(imgout, imgout, 255, Emgu.CV.CvEnum.AdaptiveThresholdType.GaussianC, Emgu.CV.CvEnum.ThresholdType.Binary, 5, 45);

          CvInvoke.FindContours(imgout, contours, hier, Emgu.CV.CvEnum.RetrType.External, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
          if (contours.Size >= 1)
          {
              for (int i = 0; i <= contours.Size; i++)
              {

                  Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
                  RotatedRect box = CvInvoke.MinAreaRect(contours[i]);
                  PointF[] Vertices = box.GetVertices();
                  PointF point = box.Center;
                  PointF edge1 = new PointF(Vertices[1].X - Vertices[0].X, Vertices[1].Y - Vertices[0].Y);
                  PointF edge2 = new PointF(Vertices[2].X - Vertices[1].X, Vertices[2].Y - Vertices[1].Y);
                  double r = edge1.X + edge1.Y;
                  double edge1Magnitude = Math.Sqrt(Math.Pow(edge1.X, 2) + Math.Pow(edge1.Y, 2));
                  double edge2Magnitude = Math.Sqrt(Math.Pow(edge2.X, 2) + Math.Pow(edge2.Y, 2));
                  PointF primaryEdge = edge1Magnitude > edge2Magnitude ? edge1 : edge2;
                  double primaryMagnitude = edge1Magnitude > edge2Magnitude ? edge1Magnitude : edge2Magnitude;
                  PointF reference = new PointF(1, 0);
                  double refMagnitude = 1;
                  double thetaRads = Math.Acos(((primaryEdge.X * reference.X) + (primaryEdge.Y * reference.Y)) / (primaryMagnitude * refMagnitude));
                  double thetaDeg = thetaRads * 180 / Math.PI;
                  imgInput = imgInput.Rotate(thetaDeg, new Bgr());
                  imgout = imgout.Rotate(box.Angle, new Gray());
                  Bitmap bmp = imgout.Bitmap;
                  break;
              }

          }

【问题讨论】:

  • 您是否测试过您检测到的唯一轮廓是否是卡的外部?
  • 另外,RotatedRect 有一个属性,Angle,所以如果你使用它,所有其他代码都是多余的。 emgu.com/wiki/files/3.0.0/document/html/…
  • 在当前代码之前,我测试了 RotatedRect 的角度检测,但它没有给出预期的结果。
  • 轮廓没有画在卡片外面,而是画在图像的边界上。
  • 有什么建议吗?

标签: c# opencv ocr emgucv pan


【解决方案1】:

问题

让我们从解决之前的问题开始:

您的代码

当您提交代码、寻求帮助时,至少要努力“清理”它。帮助人们帮助你!这里有很多代码行什么都不做。您声明从未使用过的变量。添加一些 cmets,让人们知道你认为你的代码应该做什么。

Bitmap imgs;
var blurredImage = imgInput.SmoothGaussian(5, 5, 0, 0);
Rectangle rect = CvInvoke.BoundingRectangle(contours[i]);
PointF point = box.Center;
double r = edge1.X + edge1.Y;
// Etc

自适应阈值

以下代码行生成以下图像:

 CvInvoke.AdaptiveThreshold(imgout, imgout, 255, Emgu.CV.CvEnum.AdaptiveThresholdType.GaussianC, Emgu.CV.CvEnum.ThresholdType.Binary, 5, 45);

图片 1

图片 2

图 3

显然,这不是您的目标,因为主要轮廓(即卡片边缘)已完全丢失。作为提示,您始终可以使用以下代码在运行时显示图像以帮助您进行调试。

CvInvoke.NamedWindow("Output");
CvInvoke.Imshow("Output", imgout);
CvInvoke.WaitKey();

解决方案

由于您在示例图像中的卡片主要是与背景相似的值(在 HSV 意义上)。在这种情况下,我不认为简单的灰度阈值是正确的方法。我的目的如下:

算法

  1. 使用 Canny Edge Detection 提取图像中的边缘。

  2. 扩大边缘以便卡片内容结合。

  3. 使用轮廓检测​​过滤具有最大边界的组合边缘。

  4. 用一个旋转的矩形拟合这个主要轮廓,以便提取角点。

  5. 使用角点定义要使用WarpAffine 应用的变换矩阵。

  6. 扭曲和裁剪图像。

代码

您不妨尝试一下 Canny 检测和扩张的参数。

// Working Images
Image<Bgr, byte> imgInput = new Image<Bgr, byte>("Test1.jpg");
Image<Gray, byte> imgEdges = new Image<Gray, byte>(imgInput.Size);
Image<Gray, byte> imgDilatedEdges = new Image<Gray, byte>(imgInput.Size);
Image<Bgr, byte> imgOutput;

// 1. Edge Detection
CvInvoke.Canny(imgInput, imgEdges, 25, 80);

// 2. Dilation
CvInvoke.Dilate(
    imgEdges,
    imgDilatedEdges,
    CvInvoke.GetStructuringElement(
        ElementShape.Rectangle,
        new Size(3, 3),
        new Point(-1, -1)),
    new Point(-1, -1),
    5,
    BorderType.Default,
    new MCvScalar(0));

// 3. Contours Detection
VectorOfVectorOfPoint inputContours = new VectorOfVectorOfPoint();
Mat hierarchy = new Mat();
CvInvoke.FindContours(
    imgDilatedEdges,
    inputContours,
    hierarchy,
    RetrType.External,
    ChainApproxMethod.ChainApproxSimple);
VectorOfPoint primaryContour = (from contour in inputContours.ToList()
                                orderby contour.GetArea() descending
                                select contour).FirstOrDefault();

// 4. Corner Point Extraction
RotatedRect bounding = CvInvoke.MinAreaRect(primaryContour);
PointF topLeft = (from point in bounding.GetVertices()
                  orderby Math.Sqrt(Math.Pow(point.X, 2) + Math.Pow(point.Y, 2))
                  select point).FirstOrDefault();
PointF topRight = (from point in bounding.GetVertices()
                  orderby Math.Sqrt(Math.Pow(imgInput.Width - point.X, 2) + Math.Pow(point.Y, 2))
                  select point).FirstOrDefault();
PointF botLeft = (from point in bounding.GetVertices()
                  orderby Math.Sqrt(Math.Pow(point.X, 2) + Math.Pow(imgInput.Height - point.Y, 2))
                  select point).FirstOrDefault();
PointF botRight = (from point in bounding.GetVertices()
                   orderby Math.Sqrt(Math.Pow(imgInput.Width - point.X, 2) + Math.Pow(imgInput.Height - point.Y, 2))
                   select point).FirstOrDefault();
double boundingWidth = Math.Sqrt(Math.Pow(topRight.X - topLeft.X, 2) + Math.Pow(topRight.Y - topLeft.Y, 2));
double boundingHeight = Math.Sqrt(Math.Pow(botLeft.X - topLeft.X, 2) + Math.Pow(botLeft.Y - topLeft.Y, 2));
bool isLandscape = boundingWidth > boundingHeight;

// 5. Define warp crieria as triangles              
PointF[] srcTriangle = new PointF[3];
PointF[] dstTriangle = new PointF[3];
Rectangle ROI;
if (isLandscape)
{
    srcTriangle[0] = botLeft;
    srcTriangle[1] = topLeft;
    srcTriangle[2] = topRight;
    dstTriangle[0] = new PointF(0, (float)boundingHeight);
    dstTriangle[1] = new PointF(0, 0);
    dstTriangle[2] = new PointF((float)boundingWidth, 0);
    ROI = new Rectangle(0, 0, (int)boundingWidth, (int)boundingHeight);
}
else
{
    srcTriangle[0] = topLeft;
    srcTriangle[1] = topRight;
    srcTriangle[2] = botRight;
    dstTriangle[0] = new PointF(0, (float)boundingWidth);
    dstTriangle[1] = new PointF(0, 0);
    dstTriangle[2] = new PointF((float)boundingHeight, 0);
    ROI = new Rectangle(0, 0, (int)boundingHeight, (int)boundingWidth);
}
Mat warpMat = new Mat(2, 3, DepthType.Cv32F, 1);
warpMat = CvInvoke.GetAffineTransform(srcTriangle, dstTriangle);

// 6. Apply the warp and crop
CvInvoke.WarpAffine(imgInput, imgInput, warpMat, imgInput.Size);
imgOutput = imgInput.Copy(ROI);
imgOutput.Save("Output1.bmp");

使用了两种扩展方法:

static List<VectorOfPoint> ToList(this VectorOfVectorOfPoint vectorOfVectorOfPoint)
{
    List<VectorOfPoint> result = new List<VectorOfPoint>();
    for (int contour = 0; contour < vectorOfVectorOfPoint.Size; contour++)
    {
        result.Add(vectorOfVectorOfPoint[contour]);
    }
    return result;
}

static double GetArea(this VectorOfPoint contour)
{
    RotatedRect bounding = CvInvoke.MinAreaRect(contour);
    return bounding.Size.Width * bounding.Size.Height;
}

输出

元示例

【讨论】:

  • 我会在发布代码时小心。很抱歉给您带来不便。
  • 没关系。只是如果你清楚你想要做什么,你会得到更好的建议,更快。如果难以理解问题,大多数人不会费心提供帮助。希望我的回答对你有用。
  • 它工作正常。非常感谢您的帮助。你救了我。在您回答之前,我正在处理以下链接。 learnopencv.com/…让我知道我是否在正确的轨道上?
  • 在您回答之前,我正在处理此问题的以下链接。 learnopencv.com/… 请让我知道我是否在正确的轨道上?我认为 Opencv 似乎相当困难。
  • 你能告诉我你是怎么想出来的吗?我每天都用头撞墙。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-04
  • 1970-01-01
相关资源
最近更新 更多