2011-06-29 156 views
9

我有一個相當大的幾乎全屏的圖像,我將在iPad上顯示。圖像大約80%透明。我需要在客戶端上確定不透明像素的邊界框,然後裁剪到該邊界框。裁剪UIImage到alpha

在計算器上掃描其他的問題在這裏和閱讀一些CoreGraphics中的文檔,我想我能做到這一點:

CGBitmapContextCreate(...) // Use this to render the image to a byte array 

.. 
    - iterate through this byte array to find the bounding box 
.. 

CGImageCreateWithImageInRect(image, boundingRect); 

這只是似乎非常低效和笨重。有沒有什麼聰明的我可以用CGImage的面具或使用設備的圖形加速來做到這一點?

+0

你在設備上試過這個嗎?我敢打賭它會比你想象的要快。 –

+0

這是真的 - 一旦我真正坐下來實施它,處理時間比我想象的要快得多! – MikeQ

回答

0

有沒有聰明的騙子讓設備做的工作,但有一些方法可以加快任務,或最大限度地減少對用戶界面的影響。

首先,考慮是否需要加速這項任務。通過這個字節數組的簡單迭代可能足夠快。如果應用程序每次運行只計算一次,或者對用戶的選擇作出反應(選擇之間至少需要幾秒鐘),則可能無需投資優化此任務。

如果在圖像可用後一段時間內不需要邊界框,則可以在單獨的線程中啓動該迭代。這樣計算不會阻塞主界面線程。 Grand Central Dispatch可能更容易使用單獨的線程執行此任務。

如果任務必須加速,也許這是視頻圖像的實時處理,那麼並行處理數據可能會有所幫助。 Accelerate框架可能有助於設置數據的SIMD計算。或者,爲了真正獲得這次迭代的性能,使用NEON SIMD操作的ARM彙編語言代碼可以通過大量的開發工作獲得很好的結果。

最後的選擇是調查一個更好的算法。在檢測圖像中的特徵方面有大量工作。邊緣檢測算法可能比通過字節數組的簡單迭代更快。也許Apple會在未來爲Core Graphics增加邊緣檢測功能,這可以應用於這種情況。蘋果公司實施的圖像處理能力可能並不完全符合這種情況,但蘋果的實施應該優化使用iPad的SIMD或GPU功能,從而提高整體性能。

8

我創建了執行此,如果任何一個需要它的uImage類別...

+ (UIImage *)cropTransparencyFromImage:(UIImage *)img { 

    CGImageRef inImage = img.CGImage;   
    CFDataRef m_DataRef; 
    m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage)); 
    UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef); 

    int width = img.size.width; 
    int height = img.size.height; 

    CGPoint top,left,right,bottom; 

    BOOL breakOut = NO; 
    for (int x = 0;breakOut==NO && x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       left = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 
     } 
    } 

    breakOut = NO; 
    for (int y = 0;breakOut==NO && y < height; y++) { 

     for (int x = 0; x < width; x++) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       top = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int y = height-1;breakOut==NO && y >= 0; y--) { 

     for (int x = width-1; x >= 0; x--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       bottom = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int x = width-1;breakOut==NO && x >= 0; x--) { 

     for (int y = height-1; y >= 0; y--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       right = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 


    CGRect cropRect = CGRectMake(left.x, top.y, right.x - left.x, bottom.y - top.y); 

    UIGraphicsBeginImageContextWithOptions(cropRect.size, 
              NO, 
              0.); 
    [img drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y) 
      blendMode:kCGBlendModeCopy 
       alpha:1.]; 
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    return croppedImage; 
} 
+1

感謝這個腳本。我在我的項目中使用它,但我注意到內存泄漏問題。我想我們需要調用「CFRelease(m_DataRef);」之前「return croppedImage;」 –

+0

感謝您的有用答覆user404。你是否知道在你獲得了對他們進行變換的點之後有沒有辦法? –

+0

只是在別人正在觀看的情況下重複我的評論:我剛剛在我的程序中實現了這一點 - 這正是我需要的!但我得到和第一個if(m_PixelBuf [loc + 3]!= 0){行EXC_BAD_ACCESS {行。任何想法爲什麼?謝謝 – Smikey

13

感謝user404709製作所有的辛勤工作。 下面的代碼可以處理視網膜圖像並釋放CFDataRef。

- (UIImage *)trimmedImage { 

    CGImageRef inImage = self.CGImage; 
    CFDataRef m_DataRef; 
    m_DataRef = CGDataProviderCopyData(CGImageGetDataProvider(inImage)); 

    UInt8 * m_PixelBuf = (UInt8 *) CFDataGetBytePtr(m_DataRef); 

    size_t width = CGImageGetWidth(inImage); 
    size_t height = CGImageGetHeight(inImage); 

    CGPoint top,left,right,bottom; 

    BOOL breakOut = NO; 
    for (int x = 0;breakOut==NO && x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       left = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 
     } 
    } 

    breakOut = NO; 
    for (int y = 0;breakOut==NO && y < height; y++) { 

     for (int x = 0; x < width; x++) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       top = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int y = height-1;breakOut==NO && y >= 0; y--) { 

     for (int x = width-1; x >= 0; x--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       bottom = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 

    breakOut = NO; 
    for (int x = width-1;breakOut==NO && x >= 0; x--) { 

     for (int y = height-1; y >= 0; y--) { 

      int loc = x + (y * width); 
      loc *= 4; 
      if (m_PixelBuf[loc + 3] != 0) { 
       right = CGPointMake(x, y); 
       breakOut = YES; 
       break; 
      } 

     } 
    } 


    CGFloat scale = self.scale; 

    CGRect cropRect = CGRectMake(left.x/scale, top.y/scale, (right.x - left.x)/scale, (bottom.y - top.y)/scale); 
    UIGraphicsBeginImageContextWithOptions(cropRect.size, 
              NO, 
              scale); 
    [self drawAtPoint:CGPointMake(-cropRect.origin.x, -cropRect.origin.y) 
      blendMode:kCGBlendModeCopy 
       alpha:1.]; 
    UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
    CFRelease(m_DataRef); 
    return croppedImage; 
} 
+0

我剛剛在我的程序中實現了這一點 - 這正是我需要的!但是,如果(m_PixelBuf [loc + 3]!= 0),我會在第一個 上得到EXC_BAD_ACCESS。任何想法爲什麼?謝謝。 – Smikey

+0

@Smikey,正試圖在從相機捕獲的圖像上使用它?如果是這樣,根據user1702121的[http://stackoverflow.com/a/12614015/1541893],「從捕獲的示例緩衝區創建的上下文的''CGBitmapContextGetData()'不會返回它的位圖」。他提供了一個解決方案。 – Dan