检测图像中的正方形

问题描述

在一所学校,我们正在准备扫描的艺术品,并希望将其自动裁剪为正确的尺寸。孩子们(尝试)在矩形内绘制:

enter image description here

我想检测内部矩形边框,所以我在Accord.net中应用了一些过滤器:

main()

这将产生以下反转图像,BlobCounter将其用作输入:

enter image description here

但是blobcounter的结果并不是非常准确,紫色线表示BC已检测到的内容

enter image description here

在Accord.net中,var newImage = new Bitmap(@"C:\Temp\temp.jpg"); var g = Graphics.FromImage(newImage); var pen = new Pen(Color.Purple,10); var grayScaleFilter = new Grayscale(1,0); var image = grayScaleFilter.Apply(newImage); image.Save(@"C:\temp\grey.jpg"); var skewChecker = new DocumentSkewChecker(); var angle = skewChecker.GetSkewAngle(image); var rotationFilter = new RotateBilinear(-angle); rotationFilter.FillColor = Color.White; var rotatedImage = rotationFilter.Apply(image); rotatedImage.Save(@"C:\Temp\rotated.jpg"); var thresholdFilter = new IterativeThreshold(10,128); thresholdFilter.ApplyInPlace(rotatedImage); rotatedImage.Save(@"C:\temp\threshold.jpg"); var invertFilter = new Invert(); invertFilter.ApplyInPlace(rotatedImage); rotatedImage.Save(@"C:\temp\inverted.jpg"); var bc = new BlobCounter { BackgroundThreshold = Color.Black,FilterBlobs = true,MinWidth = 1000,MinHeight = 1000 }; bc.ProcessImage(rotatedImage); foreach (var rect in bc.GetobjectsRectangles()) { g.DrawRectangle(pen,rect); } newImage.Save(@"C:\Temp\test.jpg"); 会有更好的替代方法吗?还是其他C#库更适合这种计算机视觉?

解决方法

这是一个简单的解决方案,当我无聊的午休时。

基本上,它只是从外部到内部扫描所有尺寸的颜色阈值(黑色),然后得到最突出的结果。

给予

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe bool IsValid(int* scan0Ptr,int x,int y,int stride,double thresh)
{
   var c = *(scan0Ptr + x + y * stride);
   var r = ((c >> 16) & 255);
   var g = ((c >> 8) & 255);
   var b = ((c >> 0) & 255);

   // compare it against the threshold
   return r * r + g * g + b * b < thresh;
}

private static int GetBest(IEnumerable<int> array)
   => array.Where(x => x != 0)
      .GroupBy(i => i)
      .OrderByDescending(grp => grp.Count())
      .Select(grp => grp.Key)
      .First();

示例

private static unsafe Rectangle ConvertImage(string path,Color source,double threshold)
{

   var thresh = threshold * threshold;

   using var bmp = new Bitmap(path);

   // lock the array for direct access
   var bitmapData = bmp.LockBits(new Rectangle(0,bmp.Width,bmp.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppPArgb);
   int left,top,bottom,right;

   try
   {
      // get the pointer
      var scan0Ptr = (int*)bitmapData.Scan0;    
      // get the stride
      var stride = bitmapData.Stride / 4;

      var array = new int[bmp.Height];

      for (var y = 0; y < bmp.Height; y++)
      for (var x = 0; x < bmp.Width; x++)
         if (IsValid(scan0Ptr,x,y,stride,thresh))
         {
            array[y] = x;
            break;
         }

      left = GetBest(array);

      array = new int[bmp.Height];

      for (var y = 0; y < bmp.Height; y++)
      for (var x = bmp.Width-1; x > 0; x--)
         if (IsValid(scan0Ptr,thresh))
         {
            array[y] = x;
            break;
         }

      right = GetBest(array);

      array = new int[bmp.Width];

      for (var x = 0; x < bmp.Width; x++)
      for (var y = 0; y < bmp.Height; y++)
         if (IsValid(scan0Ptr,thresh))
         {
            array[x] = y;
            break;
         }

      top = GetBest(array);

      array = new int[bmp.Width];

      for (var x = 0; x < bmp.Width; x++)
      for (var y = bmp.Height-1; y > 0; y--)
         if (IsValid(scan0Ptr,thresh))
         {
            array[x] = y;
            break;
         }

      bottom = GetBest(array);


   }
   finally
   {
      // unlock the bitmap
      bmp.UnlockBits(bitmapData);
   }

   return new Rectangle(left,right-left,bottom-top);

}

用法

var fileName = @"D:\7548p.jpg";

var rect = ConvertImage(fileName,Color.Black,50);

using var src = new Bitmap(fileName);
using var target = new Bitmap(rect.Width,rect.Height);
using var g = Graphics.FromImage(target);

g.DrawImage(src,new Rectangle(0,target.Width,target.Height),rect,GraphicsUnit.Pixel); 

target.Save(@"D:\Test.Bmp");

输出

enter image description here

注意:

  • 这并不意味着防弹或最佳解决方案。只是一个简单的快速操作。
  • 有很多方法可以做到这一点,即使是机器学习方法也可能更好,更强大。
  • 这里有很多代码重复,基本上我只是复制,粘贴和调整了每一面
  • 我刚刚选择了一个似乎可行的任意阈值。玩
  • 获取最常见的一方可能不是最佳方法,也许您想对结果进行分类。
  • 您可能会理智地限制一面需要扫描的数量。