2011-01-27 16 views
11

32bpp ARGB modunda bir System.Drawing.Bitmap varsayalım. Bu büyük bir bitmap, ancak ortada bir yerlerde nispeten küçük bir görüntü ile çoğunlukla tamamen şeffaf pikseller.Bir bitmap'i otomatik olarak minimum boyuta mı ayarlıyorsunuz?

"Gerçek" resmin kenarlıklarını algılamak için hızlı bir algoritma nedir, bu yüzden tüm saydam pikselleri etrafından kırpabilirim?

Alternatif olarak, bunun için kullanabileceğim Net bir işlev var mı?

+2

Kesinti düzgün mü? öyleyse, L-> R ve T-> B piksellerini okumak çok çabuk çalışırdı. –

+0

Kare kare ise, daha da fazla zaman kazandırabilir ve ikili aramaları 4 kenardan merkezden uzaklaştırabilirsiniz (en azından piksel sorgularını kesme) –

+0

Küçük, gömülü görüntüde ayrıca saydam pikseller bulunabilir mi? –

cevap

23

Temel fikir, görüntünün her bir pikseli için görüntünün üst, sol, sağ ve alt sınırlarını bulmaktır. Bunu verimli bir şekilde yapmak için, oldukça yavaş olan GetPixel yöntemini kullanmayın. Bunun yerine LockBits kullanın. İşte

ben ile geldi uygulama görebilirsiniz: ... Muhtemelen optimize edilebilir

static Bitmap TrimBitmap(Bitmap source) 
{ 
    Rectangle srcRect = default(Rectangle); 
    BitmapData data = null; 
    try 
    { 
     data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 
     byte[] buffer = new byte[data.Height * data.Stride]; 
     Marshal.Copy(data.Scan0, buffer, 0, buffer.Length); 
     int xMin = int.MaxValue; 
     int xMax = 0; 
     int yMin = int.MaxValue; 
     int yMax = 0; 
     for (int y = 0; y < data.Height; y++) 
     { 
      for (int x = 0; x < data.Width; x++) 
      { 
       byte alpha = buffer[y * data.Stride + 4 * x + 3]; 
       if (alpha != 0) 
       { 
        if (x < xMin) xMin = x; 
        if (x > xMax) xMax = x; 
        if (y < yMin) yMin = y; 
        if (y > yMax) yMax = y; 
       } 
      } 
     } 
     if (xMax < xMin || yMax < yMin) 
     { 
      // Image is empty... 
      return null; 
     } 
     srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax); 
    } 
    finally 
    { 
     if (data != null) 
      source.UnlockBits(data); 
    } 

    Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height); 
    Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height); 
    using (Graphics graphics = Graphics.FromImage(dest)) 
    { 
     graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel); 
    } 
    return dest; 
} 

, ama GDI + uzmanı değilim, bu yüzden ben daha fazla araştırma yapmadan gelenin en iyisi


DÜZENLEME:

  1. tarama righ soldan: aslında resmin bazı bölümlerini tarayarak vermeyerek, bunu optimize etmek için basit bir yol var saydam olmayan bir piksel bulana kadar; saydam olmayan bir piksel bulana kadar yukarıya doğru tara (x, y) (xMin, yMin)
  2. (x> = xMin için); Saydam olmayan bir piksel bulana kadar sağa sola tarama yapın (yalnızca y> = yMin için); xMax
  3. 'u şeffaf olmayan bir piksel bulana kadar aşağıdan yukarıya tarayın (yalnızca xMin için < = x < = xMax); Şeffaf olmayan kısmı elbette küçükse beri

    static Bitmap TrimBitmap(Bitmap source) 
    { 
        Rectangle srcRect = default(Rectangle); 
        BitmapData data = null; 
        try 
        { 
         data = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); 
         byte[] buffer = new byte[data.Height * data.Stride]; 
         Marshal.Copy(data.Scan0, buffer, 0, buffer.Length); 
    
         int xMin = int.MaxValue, 
          xMax = int.MinValue, 
          yMin = int.MaxValue, 
          yMax = int.MinValue; 
    
         bool foundPixel = false; 
    
         // Find xMin 
         for (int x = 0; x < data.Width; x++) 
         { 
          bool stop = false; 
          for (int y = 0; y < data.Height; y++) 
          { 
           byte alpha = buffer[y * data.Stride + 4 * x + 3]; 
           if (alpha != 0) 
           { 
            xMin = x; 
            stop = true; 
            foundPixel = true; 
            break; 
           } 
          } 
          if (stop) 
           break; 
         } 
    
         // Image is empty... 
         if (!foundPixel) 
          return null; 
    
         // Find yMin 
         for (int y = 0; y < data.Height; y++) 
         { 
          bool stop = false; 
          for (int x = xMin; x < data.Width; x++) 
          { 
           byte alpha = buffer[y * data.Stride + 4 * x + 3]; 
           if (alpha != 0) 
           { 
            yMin = y; 
            stop = true; 
            break; 
           } 
          } 
          if (stop) 
           break; 
         } 
    
         // Find xMax 
         for (int x = data.Width - 1; x >= xMin; x--) 
         { 
          bool stop = false; 
          for (int y = yMin; y < data.Height; y++) 
          { 
           byte alpha = buffer[y * data.Stride + 4 * x + 3]; 
           if (alpha != 0) 
           { 
            xMax = x; 
            stop = true; 
            break; 
           } 
          } 
          if (stop) 
           break; 
         } 
    
         // Find yMax 
         for (int y = data.Height - 1; y >= yMin; y--) 
         { 
          bool stop = false; 
          for (int x = xMin; x <= xMax; x++) 
          { 
           byte alpha = buffer[y * data.Stride + 4 * x + 3]; 
           if (alpha != 0) 
           { 
            yMax = y; 
            stop = true; 
            break; 
           } 
          } 
          if (stop) 
           break; 
         } 
    
         srcRect = Rectangle.FromLTRB(xMin, yMin, xMax, yMax); 
        } 
        finally 
        { 
         if (data != null) 
          source.UnlockBits(data); 
        } 
    
        Bitmap dest = new Bitmap(srcRect.Width, srcRect.Height); 
        Rectangle destRect = new Rectangle(0, 0, srcRect.Width, srcRect.Height); 
        using (Graphics graphics = Graphics.FromImage(dest)) 
        { 
         graphics.DrawImage(source, destRect, srcRect, GraphicsUnit.Pixel); 
        } 
        return dest; 
    } 
    

    önemli kazanım olmayacak: yMax


EDIT2 içine mağaza y: Burada yukarıdaki yaklaşımın bir uygulama var hala piksellerin çoğunu tarayacak. Ama büyükse, sadece şeffaf olmayan kısımdaki dikdörtgenler taranacaktır.

+0

En pratik yaklaşım gibi görünüyor. Bundan daha iyisini görmüyorum. LockBits yöntemi ile de güzel ipucu. +1 –

+2

BTW, Grafik: 'return source.Clone (srcRect, source.PixelFormat);' –

+2

Mükemmel çözüm kullanmadan görüntüyü kırpmanın daha basit bir yolunun olduğunu fark ettim, ancak resimlerimi buldum Bir pikselin çok fazla kırpılmış olması. Mantıksal olarak sizinki doğru görünüyor, ancak aramayı ** Rectangle.FromLTRB ** - ** srcRect = Rectangle.FromLTRB (xMin, yMin, xMax + 1, yMax + 1) ** olarak değiştirdim ve şimdi mükemmel çalışıyor. –

1

bir bölme & fethetmek yaklaşım önermek istiyoruz:

  1. orta görüntü split (ördikey)
  2. çek eğer öyleyse, kutu)
  3. bölünmüş sol yarısını sınırlayan için min/max hatırlamak (kesme hattı üzerinde şeffaf olmayan piksel varsa tekrar dikey
  4. kesme hattı şeffaf olmayan pikseller içeriyorsa -> değilse güncelleme sınırlama kutusu
  5. , muhtemelen
  6. sol-sağ yarısında devam (Ben resim bilmiyorum) en soldaki yarısını atabilirsiniz bulana kadar (eğer görüntü ortada bir yerde olduğunu belirtti) görüntünün en sol kenarı
  7. 'un sağ yarısı için aynıdır
+3

5'inci noktanın yanlış olduğunu düşünüyorum: Saydam olmayan pikselleri olan birkaç ayrı alan olabilir, bu nedenle kesim çizgisinde saydam olmayan bir piksel bulunmadığı anlamına gelmez –

+0

Teşekkürler bjoernz, ama evet: ikili arama her zaman resimlerim için çalışmaz - örneğin, boşluk ile ayrılmış iki görüntü olabilir. – Blorgbeard

İlgili konular