2008-10-29 110 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); 
     } 
    }