2008-10-29 100 views
21

我有一个我们从客户收到的产品图像块。每个产品图片都是一些东西的图片,并且是以白色背景拍摄的。我想裁剪图像的所有周围部分,但只留下中间的产品。这可能吗?从图像中删除周围的空白

举个例子:http://www.5dnet.de/media/catalog/product/d/r/dress_shoes_5.jpg][1]

我不希望所有白色像素去掉,但是我想要的图像裁剪,使像素的最顶层的行包含一个非白色像素,左大多数垂直像素行包含一个非白色像素,最底部水平行像素包含一个非白色像素等。

C#或VB.net中的代码将不胜感激。

回答

6

我已经编写了自己的代码来完成这个任务 - 基础知识不会太困难。

基本上,您需要扫描像素行/列以检查非白色像素,并隔离产品图像的边界,然后创建一个仅包含该区域的新位图。

请注意,尽管Bitmap.GetPixel()方法有效,但速度相对较慢。如果处理时间很重要,则需要使用Bitmap.LockBits()来锁定内存中的位图,然后使用unsafe { }块中的一些简单指针直接访问像素。

This article CodeProject给出了一些您可能会觉得有用的细节。

+0

这是不是花太多时间为1000×1000一图像处理?请指教。 – techno 2016-02-20 10:45:56

+0

取决于你的“太多时间”的定义 - 这取决于你的上下文。我建议使用`Bitmap.GetPixel()`编写代码,然后对结果进行基准测试以查看。另请注意,智能算法比个别像素读取的微优化更重要。 – Bevan 2016-02-24 02:10:13

5

这当然有可能。在伪代码中:

topmost = 0 
for row from 0 to numRows: 
    if allWhiteRow(row): 
     topmost = row 
    else: 
     # found first non-white row from top 
     break 

botmost = 0 
for row from numRows-1 to 0: 
    if allWhiteRow(row): 
     botmost = row 
    else: 
     # found first non-white row from bottom 
     break 

对于左和右类似。

allWhiteRow的代码将涉及查看该行中的像素,并确保它们都是关闭至255,255,255。

16

这是我(相当长的)解决方案:

public Bitmap Crop(Bitmap bmp) 
{ 
    int w = bmp.Width, h = bmp.Height; 

    Func<int, bool> allWhiteRow = row => 
    { 
    for (int i = 0; i < w; ++i) 
     if (bmp.GetPixel(i, row).R != 255) 
     return false; 
    return true; 
    }; 

    Func<int, bool> allWhiteColumn = col => 
    { 
    for (int i = 0; i < h; ++i) 
     if (bmp.GetPixel(col, i).R != 255) 
     return false; 
    return true; 
    }; 

    int topmost = 0; 
    for (int row = 0; row < h; ++row) 
    { 
    if (allWhiteRow(row)) 
     topmost = row; 
    else break; 
    } 

    int bottommost = 0; 
    for (int row = h - 1; row >= 0; --row) 
    { 
    if (allWhiteRow(row)) 
     bottommost = row; 
    else break; 
    } 

    int leftmost = 0, rightmost = 0; 
    for (int col = 0; col < w; ++col) 
    { 
    if (allWhiteColumn(col)) 
     leftmost = col; 
    else 
     break; 
    } 

    for (int col = w-1; col >= 0; --col) 
    { 
    if (allWhiteColumn(col)) 
     rightmost = col; 
    else 
     break; 
    } 

    int croppedWidth = rightmost - leftmost; 
    int croppedHeight = bottommost - topmost; 
    try 
    { 
    Bitmap target = new Bitmap(croppedWidth, croppedHeight); 
    using (Graphics g = Graphics.FromImage(target)) 
    { 
     g.DrawImage(bmp, 
     new RectangleF(0, 0, croppedWidth, croppedHeight), 
     new RectangleF(leftmost, topmost, croppedWidth, croppedHeight), 
     GraphicsUnit.Pixel); 
    } 
    return target; 
    } 
    catch (Exception ex) 
    { 
    throw new Exception(
     string.Format("Values are topmost={0} btm={1} left={2} right={3}", topmost, bottommost, leftmost, rightmost), 
     ex); 
    } 
} 
+1

完美的作品,除非croppedWidth或croppedHeight为零,在这种情况下,我分别将它们设置为bmp.Width或bmp.Height,它的作用就像一个魅力:) – 2011-12-25 14:23:59

+0

这是否适合每种图像?像PNG JPEG或GIF? – MonsterMMORPG 2016-06-29 11:50:55

29

我发现我不得不调整德米特里的答案,以确保它的工作原理与图片,唐”实际上需要裁剪(水平,垂直或两者)...

public static Bitmap Crop(Bitmap bmp) 
    { 
     int w = bmp.Width; 
     int h = bmp.Height; 

     Func<int, bool> allWhiteRow = row => 
     { 
      for (int i = 0; i < w; ++i) 
       if (bmp.GetPixel(i, row).R != 255) 
        return false; 
      return true; 
     }; 

     Func<int, bool> allWhiteColumn = col => 
     { 
      for (int i = 0; i < h; ++i) 
       if (bmp.GetPixel(col, i).R != 255) 
        return false; 
      return true; 
     }; 

     int topmost = 0; 
     for (int row = 0; row < h; ++row) 
     { 
      if (allWhiteRow(row)) 
       topmost = row; 
      else break; 
     } 

     int bottommost = 0; 
     for (int row = h - 1; row >= 0; --row) 
     { 
      if (allWhiteRow(row)) 
       bottommost = row; 
      else break; 
     } 

     int leftmost = 0, rightmost = 0; 
     for (int col = 0; col < w; ++col) 
     { 
      if (allWhiteColumn(col)) 
       leftmost = col; 
      else 
       break; 
     } 

     for (int col = w - 1; col >= 0; --col) 
     { 
      if (allWhiteColumn(col)) 
       rightmost = col; 
      else 
       break; 
     } 

     if (rightmost == 0) rightmost = w; // As reached left 
     if (bottommost == 0) bottommost = h; // As reached top. 

     int croppedWidth = rightmost - leftmost; 
     int croppedHeight = bottommost - topmost; 

     if (croppedWidth == 0) // No border on left or right 
     { 
      leftmost = 0; 
      croppedWidth = w; 
     } 

     if (croppedHeight == 0) // No border on top or bottom 
     { 
      topmost = 0; 
      croppedHeight = h; 
     } 

     try 
     { 
      var target = new Bitmap(croppedWidth, croppedHeight); 
      using (Graphics g = Graphics.FromImage(target)) 
      { 
       g.DrawImage(bmp, 
        new RectangleF(0, 0, croppedWidth, croppedHeight), 
        new RectangleF(leftmost, topmost, croppedWidth, croppedHeight), 
        GraphicsUnit.Pixel); 
      } 
      return target; 
     } 
     catch (Exception ex) 
     { 
      throw new Exception(
       string.Format("Values are topmost={0} btm={1} left={2} right={3} croppedWidth={4} croppedHeight={5}", topmost, bottommost, leftmost, rightmost, croppedWidth, croppedHeight), 
       ex); 
     } 
    } 
7

我需要一个解决方案,处理大图像(GetPixel是slo w),所以我写了下面的扩展方法。这似乎在我有限的测试中运行良好。缺点是必须在项目中检查“允许不安全代码”。

public static Image AutoCrop(this Bitmap bmp) 
{ 
    if (Image.GetPixelFormatSize(bmp.PixelFormat) != 32) 
     throw new InvalidOperationException("Autocrop currently only supports 32 bits per pixel images."); 

    // Initialize variables 
    var cropColor = Color.White; 

    var bottom = 0; 
    var left = bmp.Width; // Set the left crop point to the width so that the logic below will set the left value to the first non crop color pixel it comes across. 
    var right = 0; 
    var top = bmp.Height; // Set the top crop point to the height so that the logic below will set the top value to the first non crop color pixel it comes across. 

    var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); 

    unsafe 
    { 
     var dataPtr = (byte*)bmpData.Scan0; 

     for (var y = 0; y < bmp.Height; y++) 
     { 
      for (var x = 0; x < bmp.Width; x++) 
      { 
       var rgbPtr = dataPtr + (x * 4); 

       var b = rgbPtr[0]; 
       var g = rgbPtr[1]; 
       var r = rgbPtr[2]; 
       var a = rgbPtr[3]; 

       // If any of the pixel RGBA values don't match and the crop color is not transparent, or if the crop color is transparent and the pixel A value is not transparent 
       if ((cropColor.A > 0 && (b != cropColor.B || g != cropColor.G || r != cropColor.R || a != cropColor.A)) || (cropColor.A == 0 && a != 0)) 
       { 
        if (x < left) 
         left = x; 

        if (x >= right) 
         right = x + 1; 

        if (y < top) 
         top = y; 

        if (y >= bottom) 
         bottom = y + 1; 
       } 
      } 

      dataPtr += bmpData.Stride; 
     } 
    } 

    bmp.UnlockBits(bmpData); 

    if (left < right && top < bottom) 
     return bmp.Clone(new Rectangle(left, top, right - left, bottom - top), bmp.PixelFormat); 

    return null; // Entire image should be cropped, so just return null 
} 
1

固定在顶部剩余1px的空白和左

public Bitmap Crop(Bitmap bitmap) 
    { 
     int w = bitmap.Width; 
     int h = bitmap.Height; 

     Func<int, bool> IsAllWhiteRow = row => 
     { 
      for (int i = 0; i < w; i++) 
      { 
       if (bitmap.GetPixel(i, row).R != 255) 
       { 
        return false; 
       } 
      } 
      return true; 
     }; 

     Func<int, bool> IsAllWhiteColumn = col => 
     { 
      for (int i = 0; i < h; i++) 
      { 
       if (bitmap.GetPixel(col, i).R != 255) 
       { 
        return false; 
       } 
      } 
      return true; 
     }; 

     int leftMost = 0; 
     for (int col = 0; col < w; col++) 
     { 
      if (IsAllWhiteColumn(col)) leftMost = col + 1; 
      else break; 
     } 

     int rightMost = w - 1; 
     for (int col = rightMost; col > 0; col--) 
     { 
      if (IsAllWhiteColumn(col)) rightMost = col - 1; 
      else break; 
     } 

     int topMost = 0; 
     for (int row = 0; row < h; row++) 
     { 
      if (IsAllWhiteRow(row)) topMost = row + 1; 
      else break; 
     } 

     int bottomMost = h - 1; 
     for (int row = bottomMost; row > 0; row--) 
     { 
      if (IsAllWhiteRow(row)) bottomMost = row - 1; 
      else break; 
     } 

     if (rightMost == 0 && bottomMost == 0 && leftMost == w && topMost == h) 
     { 
      return bitmap; 
     } 

     int croppedWidth = rightMost - leftMost + 1; 
     int croppedHeight = bottomMost - topMost + 1; 

     try 
     { 
      Bitmap target = new Bitmap(croppedWidth, croppedHeight); 
      using (Graphics g = Graphics.FromImage(target)) 
      { 
       g.DrawImage(bitmap, 
        new RectangleF(0, 0, croppedWidth, croppedHeight), 
        new RectangleF(leftMost, topMost, croppedWidth, croppedHeight), 
        GraphicsUnit.Pixel); 
      } 
      return target; 
     } 
     catch (Exception ex) 
     { 
      throw new Exception(string.Format("Values are top={0} bottom={1} left={2} right={3}", topMost, bottomMost, leftMost, rightMost), ex); 
     } 
    }