2013-05-15 10 views
5

Qual è la strategia per prendere una UIImmagine con uno sfondo trasparente e determinare il rettangolo più piccolo da ritagliare in modo che vengano lasciati solo i dati dell'immagine visibile (insieme allo sfondo extra trasparente se l'immagine i dati non sono rettangolari ovviamente)?Rileva rettangolo di ritaglio per UIImage con sfondo trasparente

Ho trovato molte informazioni sul ritaglio di UIImage su un CGRect, su molti controller di vista ritaglio che richiedono l'intervento dell'utente e su numerose librerie open source con classi e categorie di elaborazione dell'immagine (inclusi MGImageUtilities e NYXImagesKit), ma non ancora risolve il mio particolare problema

La mia app attuale ha come target iOS 5.0, quindi la compatibilità con quella sarebbe ottimale.

EDIT: A proposito, spero che ci sia un modo migliore della forza bruta che osserva ogni pixel nello scenario peggiore dei dati di immagine in righe e colonne che cercano i limiti del bordo.

risposta

9

Hai avuto la possibilità di vedere https://gist.github.com/spinogrizz/3549921?

sembra che sia esattamente quello di cui hai bisogno.

solo quindi non è perso, un copia incolla & da quella pagina:

- (UIImage *) imageByTrimmingTransparentPixels { 
    int rows = self.size.height; 
    int cols = self.size.width; 
    int bytesPerRow = cols*sizeof(uint8_t); 

    if (rows < 2 || cols < 2) { 
     return self; 
    } 

    //allocate array to hold alpha channel 
    uint8_t *bitmapData = calloc(rows*cols, sizeof(uint8_t)); 

    //create alpha-only bitmap context 
    CGContextRef contextRef = CGBitmapContextCreate(bitmapData, cols, rows, 8, bytesPerRow, NULL, kCGImageAlphaOnly); 

    //draw our image on that context 
    CGImageRef cgImage = self.CGImage; 
    CGRect rect = CGRectMake(0, 0, cols, rows); 
    CGContextDrawImage(contextRef, rect, cgImage); 

    //summ all non-transparent pixels in every row and every column 
    uint16_t *rowSum = calloc(rows, sizeof(uint16_t)); 
    uint16_t *colSum = calloc(cols, sizeof(uint16_t)); 

    //enumerate through all pixels 
    for (int row = 0; row < rows; row++) { 
     for (int col = 0; col < cols; col++) 
     { 
      if (bitmapData[row*bytesPerRow + col]) { //found non-transparent pixel 
       rowSum[row]++; 
       colSum[col]++; 
      } 
     } 
    } 

    //initialize crop insets and enumerate cols/rows arrays until we find non-empty columns or row 
    UIEdgeInsets crop = UIEdgeInsetsMake(0, 0, 0, 0); 

    for (int i = 0; i<rows; i++) {  //top 
     if (rowSum[i] > 0) { 
      crop.top = i; break; 
     } 
    } 

    for (int i = rows; i >= 0; i--) {  //bottom 
     if (rowSum[i] > 0) { 
      crop.bottom = MAX(0, rows-i-1); break; 
     } 
    } 

    for (int i = 0; i<cols; i++) {  //left 
     if (colSum[i] > 0) { 
      crop.left = i; break; 
     } 
    } 

    for (int i = cols; i >= 0; i--) {  //right 
     if (colSum[i] > 0) { 
      crop.right = MAX(0, cols-i-1); break; 
     } 
    } 

    free(bitmapData); 
    free(colSum); 
    free(rowSum); 

    if (crop.top == 0 && crop.bottom == 0 && crop.left == 0 && crop.right == 0) { 
     //no cropping needed 
     return self; 
    } 
    else { 
     //calculate new crop bounds 
     rect.origin.x += crop.left; 
     rect.origin.y += crop.top; 
     rect.size.width -= crop.left + crop.right; 
     rect.size.height -= crop.top + crop.bottom; 

     //crop it 
     CGImageRef newImage = CGImageCreateWithImageInRect(cgImage, rect); 

     //convert back to UIImage 
     return [UIImage imageWithCGImage:newImage]; 
    } 
} 
+0

E 'un po' brutto, ma funziona abbastanza bene. Grazie. –

+0

Ha funzionato per me (Y) – Xeieshan

+1

Non funziona per me - ma spero che ci sia un'altra versione che galleggia su ... – Benjohn

-5

Fare riferimento CoreImage diapositive sul WWDC. La tua risposta è diretta.

0

Qui è in Swift 4

extension UIImage { 

func cropAlpha() -> UIImage { 

    let cgImage = self.cgImage!; 

    let width = cgImage.width 
    let height = cgImage.height 

    let colorSpace = CGColorSpaceCreateDeviceRGB() 
    let bytesPerPixel:Int = 4 
    let bytesPerRow = bytesPerPixel * width 
    let bitsPerComponent = 8 
    let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue 

    guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo), 
     let ptr = context.data?.assumingMemoryBound(to: UInt8.self) else { 
      return self 
    } 

    context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: width, height: height)) 

    var minX = width 
    var minY = height 
    var maxX: Int = 0 
    var maxY: Int = 0 

    for x in 1 ..< width { 
     for y in 1 ..< height { 

      let i = bytesPerRow * Int(y) + bytesPerPixel * Int(x) 
      let a = CGFloat(ptr[i + 3])/255.0 

      if(a>0) { 
       if (x < minX) { minX = x }; 
       if (x > maxX) { maxX = x }; 
       if (y < minY) { minY = y}; 
       if (y > maxY) { maxY = y}; 
      } 
     } 
    } 

    let rect = CGRect(x: CGFloat(minX),y: CGFloat(minY), width: CGFloat(maxX-minX), height: CGFloat(maxY-minY)) 
    let imageScale:CGFloat = self.scale 
    let croppedImage = self.cgImage!.cropping(to: rect)! 
    let ret = UIImage(cgImage: croppedImage, scale: imageScale, orientation: self.imageOrientation) 

    return ret; 
} 

}