寻找图像或笛卡尔平面中的负空间

时间:2018-02-19 07:36:27

标签: c# algorithm math image-processing

  

请注意 - 这本质上是 Math 问题。但是,我还标记了 C#   因为这是我正在使用的语言

摘要

我正在寻找可以在图像中找到负空间(或空格)的算法(或其名称)。最接近我发现Dijkstra's algorithm(看似很接近),但它实际上是实际问题的一个子集。也就是说,遍历笛卡尔平面遍历每个未填充的坐标(或在我的情况下为黑色)以找到掩码。以下示例

Dijkstra算法示例

enter image description here

背景

我需要整理成千上万张有人工制品的图片。通过清理我的具体意思是:

  1. 使用边缘检测查找图像中对象的边缘
  2. 遮蔽 负空间,这样我就可以将图像背景转换为纯白色
  3. 裁剪图像至最佳尺寸。
  4. 目前我正在使用 Canny Edge Detection 来查找图片中最重要的部分。我可以很好地裁剪图像(如下所示),还可以找到出现问题的所有图像。但是,我无法找到最佳算法(或其名称)以找到负空间。

    原始图片的示例

    正如您所看到的,图像看起来非常干净,但不是

    enter image description here

    突出问题的示例

    图像背景中有很多人工制品,需要将其删除

    enter image description here

    Canny边缘检测示例

    这可以很好地清理图像

    enter image description here

    问题

    Dijkstra's algorithms前提是它寻找所有可能的路径,它基本上解决了旅行销售人的问题

    问题是;对于称重和距离测量,该算法实际上做的比我需要做的要多得多,并且当它具有最短路径时(我需要它来完成图像)它停止。

    伪代码

     1  function Dijkstra(Graph, source):
     2
     3      create vertex set Q
     4
     5      for each vertex v in Graph:             // Initialization
     6          dist[v] ← INFINITY                  // Unknown distance from source to v
     7          prev[v] ← UNDEFINED                 // Previous node in optimal path from source
     8          add v to Q                          // All nodes initially in Q (unvisited nodes)
     9
    10      dist[source] ← 0                        // Distance from source to source
    11      
    12      while Q is not empty:
    13          u ← vertex in Q with min dist[u]    // Node with the least distance
    14                                                      // will be selected first
    15          remove u from Q 
    16          
    17          for each neighbor v of u:           // where v is still in Q.
    18              alt ← dist[u] + length(u, v)
    19              if alt < dist[v]:               // A shorter path to v has been found
    20                  dist[v] ← alt 
    21                  prev[v] ← u 
    22
    23      return dist[], prev[]
    

    有人可以建议使用算法或修改伪代码 Dijkstra的算法来实现这个目标吗?

1 个答案:

答案 0 :(得分:0)

问题的答案只是 Flood-fill算法

然而,为了解决从图像中清除细微伪影的整个问题,总解决方案如下:

  1. 使用具有适当阈值的Canny边缘检测来获取图像中对象的轮廓

  2. 使用高斯模糊来模拟Canny结果足以使洪水泛滥不会出血

  3. 使用填充填充创建蒙版并将其应用回原始图像

  4. 针对年轻球员的一些陷阱。

    • PixelFormats,您需要确保所有内容都采用相同的格式
    • 不使用扫描线或锁定像素直接编辑位图
    • 在可能的情况下并行算法,在这种情况下,洪水填充和模糊,其中良好的候选人

    更新

    即使是更快的方法也只是使用带有颜色阈值值的 Parallel FloodFill

    颜色阈值

    public static bool IsSimilarColor(this Color source, Color target, int threshold)
    {
       int r = source.R - target.R, g = source.G - target.G, b = source.B - target.B;
    
       return (r * r + g * g + b * b) <= threshold * threshold;
    }
    

    并行FloodFill

    public static Bitmap ToWhiteCorrection(this Bitmap source, Color sourceColor, Color targetColor, Color maskColor, int threshold, Size tableSize, int cpu = 0)
    {
       using (var dbMask = new DirectBitmap(source))
       {
          using (var dbDest = new DirectBitmap(source))
          {
             var options = new ParallelOptions
                {
                   MaxDegreeOfParallelism = cpu <= 0 ? Environment.ProcessorCount : cpu
                };
    
             // Divide the image up
             var rects = dbMask.Bounds.GetSubRects(tableSize);
    
             Parallel.ForEach(rects, options, rect => ProcessWhiteCorrection(dbMask, dbDest, rect, sourceColor, targetColor, maskColor, threshold));
    
             return dbDest.CloneBitmap();
          }
       }
    }
    
      private static void ProcessWhiteCorrection(this DirectBitmap dbMask, DirectBitmap dbDest, Rectangle rect, Color sourceColor, Color targetColor, Color maskColor, int threshold)
      {
         var pixels = new Stack<Point>();
    
         AddStartLocations(dbMask, rect, pixels, sourceColor, threshold);
    
         while (pixels.Count > 0)
         {
            var point = pixels.Pop();
    
            if (!rect.Contains(point))
            {
               continue;
            }
    
            if (!dbMask[point]
                  .IsSimilarColor(sourceColor, threshold))
            {
               continue;
            }
    
            dbMask[point] = maskColor;
            dbDest[point] = targetColor;
    
            pixels.Push(new Point(point.X - 1, point.Y));
            pixels.Push(new Point(point.X + 1, point.Y));
            pixels.Push(new Point(point.X, point.Y - 1));
            pixels.Push(new Point(point.X, point.Y + 1));
         }
      }
    

    <强>工人

    private static void ProcessWhiteCorrection(this DirectBitmap dbMask, DirectBitmap dbDest, Rectangle rect, Color sourceColor, Color targetColor, Color maskColor, int threshold)
    {
       var pixels = new Stack<Point>();
    
       // this basically looks at a 5 by 5 rectangle in all 4 corners of the current rect
       // and looks to see if we are all the source color
       // basically it just picks good places to start the fill
       AddStartLocations(dbMask, rect, pixels, sourceColor, threshold);
    
       while (pixels.Count > 0)
       {
          var point = pixels.Pop();
    
          if (!rect.Contains(point))
          {
             continue;
          }
    
          if (!dbMask[point].IsSimilarColor(sourceColor, threshold))
          {
             continue;
          }
    
          dbMask[point] = maskColor;
          dbDest[point] = targetColor;
    
          pixels.Push(new Point(point.X - 1, point.Y));
          pixels.Push(new Point(point.X + 1, point.Y));
          pixels.Push(new Point(point.X, point.Y - 1));
          pixels.Push(new Point(point.X, point.Y + 1));
       }
    }
    

    直接位图

    public class DirectBitmap : IDisposable
    {
       public DirectBitmap(int width, int height, PixelFormat pixelFormat = PixelFormat.Format32bppPArgb)
       {
          Width = width;
          Height = height;
          Bounds = new Rectangle(0, 0, Width, Height);
          Bits = new int[width * height];
          BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
          Bitmap = new Bitmap(width, height, width * 4, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject());
    
          using (var g = Graphics.FromImage(Bitmap))
          {
             g.Clear(Color.White);
          }
       }
    
       public DirectBitmap(Bitmap source)
       {
          Width = source.Width;
          Height = source.Height;
          Bounds = new Rectangle(0, 0, Width, Height);
          Bits = new int[source.Width * source.Height];
          BitsHandle = GCHandle.Alloc(Bits, GCHandleType.Pinned);
          Stride = (int)GetStride(PixelFormat, Width);
    
          Bitmap = new Bitmap(source.Width, source.Height, Stride, PixelFormat.Format32bppPArgb, BitsHandle.AddrOfPinnedObject());
    
          using (var g = Graphics.FromImage(Bitmap))
          {
             g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height));
          }
       }
    
       ...