2012-01-30 17 views
14

मेरे पास एक यूआईएममेज है जिसमें एक आकार होता है; बाकी पारदर्शी है। मैं जितना संभव हो उतना पारदर्शी भाग फसल करके एक और यूआईएममेज प्राप्त करना चाहता हूं, फिर भी सभी गैर-पारदर्शी पिक्सल को बनाए रखना - जीआईएमपी में ऑटोक्रॉप फ़ंक्शन के समान। मैं ऐसा कैसे कर पाऊंगा?मैं UIImage को कैसे ऑटोक्रॉप कर सकता हूं?

उत्तर

23

यह दृष्टिकोण आप जो उम्मीद कर रहे थे उससे थोड़ा अधिक आक्रामक हो सकता है, लेकिन यह काम पूरा हो जाता है। मैं जो कर रहा हूं वह यूआईएममेज के लिए बिटमैप संदर्भ बना रहा है, कच्चे छवि डेटा में पॉइंटर प्राप्त कर रहा है, फिर इसके माध्यम से गैर-पारदर्शी पिक्सल की तलाश में जा रहा है। मेरी विधि एक सीजीआरईटीटी देता है जिसे मैं एक नया यूआईएममेज बनाने के लिए उपयोग करता हूं।

- (CGRect)cropRectForImage:(UIImage *)image { 

CGImageRef cgImage = image.CGImage; 
CGContextRef context = [self createARGBBitmapContextFromImage:cgImage]; 
if (context == NULL) return CGRectZero; 

size_t width = CGImageGetWidth(cgImage); 
size_t height = CGImageGetHeight(cgImage); 
CGRect rect = CGRectMake(0, 0, width, height); 

CGContextDrawImage(context, rect, cgImage); 

unsigned char *data = CGBitmapContextGetData(context); 
CGContextRelease(context); 

//Filter through data and look for non-transparent pixels. 
int lowX = width; 
int lowY = height; 
int highX = 0; 
int highY = 0; 
if (data != NULL) { 
    for (int y=0; y<height; y++) { 
     for (int x=0; x<width; x++) { 
      int pixelIndex = (width * y + x) * 4 /* 4 for A, R, G, B */; 
      if (data[pixelIndex] != 0) { //Alpha value is not zero; pixel is not transparent. 
       if (x < lowX) lowX = x; 
       if (x > highX) highX = x; 
       if (y < lowY) lowY = y; 
       if (y > highY) highY = y; 
      } 
     } 
    } 
    free(data); 
} else { 
    return CGRectZero; 
} 

return CGRectMake(lowX, lowY, highX-lowX, highY-lowY); 
} 

बिटमैप संदर्भ बनाने के लिए विधि:

- (CGContextRef)createARGBBitmapContextFromImage:(CGImageRef)inImage { 

CGContextRef context = NULL; 
CGColorSpaceRef colorSpace; 
void *bitmapData; 
int bitmapByteCount; 
int bitmapBytesPerRow; 

// Get image width, height. We'll use the entire image. 
size_t width = CGImageGetWidth(inImage); 
size_t height = CGImageGetHeight(inImage); 

// Declare the number of bytes per row. Each pixel in the bitmap in this 
// example is represented by 4 bytes; 8 bits each of red, green, blue, and 
// alpha. 
bitmapBytesPerRow = (width * 4); 
bitmapByteCount = (bitmapBytesPerRow * height); 

// Use the generic RGB color space. 
colorSpace = CGColorSpaceCreateDeviceRGB(); 
if (colorSpace == NULL) return NULL; 

// Allocate memory for image data. This is the destination in memory 
// where any drawing to the bitmap context will be rendered. 
bitmapData = malloc(bitmapByteCount); 
if (bitmapData == NULL) 
{ 
    CGColorSpaceRelease(colorSpace); 
    return NULL; 
} 

// Create the bitmap context. We want pre-multiplied ARGB, 8-bits 
// per component. Regardless of what the source image format is 
// (CMYK, Grayscale, and so on) it will be converted over to the format 
// specified here by CGBitmapContextCreate. 
context = CGBitmapContextCreate (bitmapData, 
           width, 
           height, 
           8,  // bits per component 
           bitmapBytesPerRow, 
           colorSpace, 
           kCGImageAlphaPremultipliedFirst); 
if (context == NULL) free (bitmapData); 

// Make sure and release colorspace before returning 
CGColorSpaceRelease(colorSpace); 

return context; 
} 

और अंत में, लौटे CGRect से अपने नए फसली UIImage मिलती है:

CGRect newRect = [self cropRectForImage:oldImage]; 
CGImageRef imageRef = CGImageCreateWithImageInRect(oldImage.CGImage, newRect); 
UIImage *newImage = [UIImage imageWithCGImage:imageRef]; 
CGImageRelease(imageRef); 

मैं से कि कोड का एक सा पकड़ा this very useful article। आशा करता हूँ की ये काम करेगा!

+0

धन्यवाद। महान काम कर रहा है ... :) – Thampuran

9

स्विफ्ट संस्करण:

extension UIImage { 
func cropRect() -> CGRect { 
    let cgImage = self.CGImage! 
    let context = createARGBBitmapContextFromImage(cgImage) 
    if context == nil { 
     return CGRectZero 
    } 

    let height = CGFloat(CGImageGetHeight(cgImage)) 
    let width = CGFloat(CGImageGetWidth(cgImage)) 

    let rect = CGRectMake(0, 0, width, height) 
    CGContextDrawImage(context, rect, cgImage) 

    let data = UnsafePointer<CUnsignedChar>(CGBitmapContextGetData(context)) 

    if data == nil { 
     return CGRectZero 
    } 

    var lowX = width 
    var lowY = height 
    var highX: CGFloat = 0 
    var highY: CGFloat = 0 

    //Filter through data and look for non-transparent pixels. 
    for (var y: CGFloat = 0 ; y < height ; y++) { 
     for (var x: CGFloat = 0; x < width ; x++) { 
      let pixelIndex = (width * y + x) * 4 /* 4 for A, R, G, B */ 

      if data[Int(pixelIndex)] != 0 { //Alpha value is not zero pixel is not transparent. 
       if (x < lowX) { 
        lowX = x 
       } 
       if (x > highX) { 
        highX = x 
       } 
       if (y < lowY) { 
        lowY = y 
       } 
       if (y > highY) { 
        highY = y 
       } 
      } 
     } 
    } 


    return CGRectMake(lowX, lowY, highX-lowX, highY-lowY) 
} 
} 

बिटमैप संदर्भ बनाने के लिए विधि:

func createARGBBitmapContextFromImage(inImage: CGImageRef) -> CGContextRef? { 

    let width = CGImageGetWidth(inImage) 
    let height = CGImageGetHeight(inImage) 

    let bitmapBytesPerRow = width * 4 
    let bitmapByteCount = bitmapBytesPerRow * height 

    let colorSpace = CGColorSpaceCreateDeviceRGB() 
    if colorSpace == nil { 
     return nil 
    } 

    let bitmapData = malloc(bitmapByteCount) 
    if bitmapData == nil { 
     return nil 
    } 

    let context = CGBitmapContextCreate (bitmapData, 
     width, 
     height, 
     8,  // bits per component 
     bitmapBytesPerRow, 
     colorSpace, 
     CGImageAlphaInfo.PremultipliedFirst.rawValue) 

    return context 
} 

और अंत में, लौटे CGRect से अपने नए फसली UIImage मिलती है:

let image = // UIImage Source 
let newRect = image.cropRect() 
if let imageRef = CGImageCreateWithImageInRect(image.CGImage!, newRect) { 
    let newImage = UIImage(CGImage: imageRef) 
    // Use this new Image 
} 
+0

आपका समाधान मेरी जिंदगी बचाओ। बहुत धन्यवाद!!! –

0

स्विफ्ट 3 (यह एक UIImage विस्तार नहीं है, मैं किसी अन्य वर्ग में यह आवश्यक) यह किसी के लिए समय बचाने के लिए हो सकता है:

class EditImage { 

static func cropRect(_ image: UIImage) -> CGRect { 
    let cgImage = image.cgImage 
    let context = createARGBBitmapContextFromImage(inImage: cgImage!) 
    if context == nil { 
     return CGRect.zero 
    } 

    let height = CGFloat(cgImage!.height) 
    let width = CGFloat(cgImage!.width) 

    let rect = CGRect(x: 0, y: 0, width: width, height: height) 
    context?.draw(cgImage!, in: rect) 

    //let data = UnsafePointer<CUnsignedChar>(CGBitmapContextGetData(context)) 
    let data = context?.data?.assumingMemoryBound(to: UInt8.self) 

    if data == nil { 
     return CGRect.zero 
    } 

    var lowX = width 
    var lowY = height 
    var highX: CGFloat = 0 
    var highY: CGFloat = 0 

    let heightInt = Int(height) 
    let widthInt = Int(width) 
    //Filter through data and look for non-transparent pixels. 
    for y in (0 ..< heightInt) { 
     let y = CGFloat(y) 
     for x in (0 ..< widthInt) { 
      let x = CGFloat(x) 
      let pixelIndex = (width * y + x) * 4 /* 4 for A, R, G, B */ 

      if data?[Int(pixelIndex)] != 0 { //Alpha value is not zero pixel is not transparent. 
       if (x < lowX) { 
        lowX = x 
       } 
       if (x > highX) { 
        highX = x 
       } 
       if (y < lowY) { 
        lowY = y 
       } 
       if (y > highY) { 
        highY = y 
       } 
      } 
     } 
    } 


    return CGRect(x: lowX, y: lowY, width: highX - lowY, height: highY - lowY) 
} 

static func createARGBBitmapContextFromImage(inImage: CGImage) -> CGContext? { 

    let width = inImage.width 
    let height = inImage.height 

    let bitmapBytesPerRow = width * 4 
    let bitmapByteCount = bitmapBytesPerRow * height 

    let colorSpace = CGColorSpaceCreateDeviceRGB() 
    if colorSpace == nil { 
     return nil 
    } 

    let bitmapData = malloc(bitmapByteCount) 
    if bitmapData == nil { 
     return nil 
    } 

    let context = CGContext (data: bitmapData, 
     width: width, 
     height: height, 
     bitsPerComponent: 8,  // bits per component 
     bytesPerRow: bitmapBytesPerRow, 
     space: colorSpace, 
    bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) 

    return context 
} 
} 
+1

कोड @ डैनी 182 के लिए धन्यवाद, हालांकि रिटर्न लाइन (हाईएक्स - लोवाय) पर एक टाइपो था -> उच्च होना चाहिए - लोअरएक्स –

+0

शायद यही कारण है कि यह पूरी तरह से काम नहीं कर रहा था। मैं इसे आज़माउंगा। टीवाई – Danny182

+0

CGColorSpaceCreateDeviceRGB() एक वैकल्पिक वापस नहीं करता है, शून्य जांच यह जरूरी नहीं है। –

4

@ Danny182 के जवाब से बेहतर, मैं यह भी कहा white-space (किसी भी पिक्सल उज्जवल से 0xe0e0e0) के लिए ट्रिमिंग मेरी अपनी जरूरत

उपयोग:

let newimage = UIImage(named: "XXX")!.trim() 

import UIKit 

extension UIImage { 

    func trim() -> UIImage { 
     let newRect = self.cropRect 
     if let imageRef = self.cgImage!.cropping(to: newRect) { 
      return UIImage(cgImage: imageRef) 
     } 
     return self 
    } 

    var cropRect: CGRect { 
     let cgImage = self.cgImage 
     let context = createARGBBitmapContextFromImage(inImage: cgImage!) 
     if context == nil { 
      return CGRect.zero 
     } 

     let height = CGFloat(cgImage!.height) 
     let width = CGFloat(cgImage!.width) 

     let rect = CGRect(x: 0, y: 0, width: width, height: height) 
     context?.draw(cgImage!, in: rect) 

     //let data = UnsafePointer<CUnsignedChar>(CGBitmapContextGetData(context)) 
     guard let data = context?.data?.assumingMemoryBound(to: UInt8.self) else { 
      return CGRect.zero 
     } 

     var lowX = width 
     var lowY = height 
     var highX: CGFloat = 0 
     var highY: CGFloat = 0 

     let heightInt = Int(height) 
     let widthInt = Int(width) 
     //Filter through data and look for non-transparent pixels. 
     for y in (0 ..< heightInt) { 
      let y = CGFloat(y) 
      for x in (0 ..< widthInt) { 
       let x = CGFloat(x) 
       let pixelIndex = (width * y + x) * 4 /* 4 for A, R, G, B */ 

       if data[Int(pixelIndex)] == 0 { continue } // crop transparent 

       if data[Int(pixelIndex+1)] > 0xE0 && data[Int(pixelIndex+2)] > 0xE0 && data[Int(pixelIndex+3)] > 0xE0 { continue } // crop white 

       if (x < lowX) { 
        lowX = x 
       } 
       if (x > highX) { 
        highX = x 
       } 
       if (y < lowY) { 
        lowY = y 
       } 
       if (y > highY) { 
        highY = y 
       } 

      } 
     } 

     return CGRect(x: lowX, y: lowY, width: highX - lowX, height: highY - lowY) 
    } 

    func createARGBBitmapContextFromImage(inImage: CGImage) -> CGContext? { 

     let width = inImage.width 
     let height = inImage.height 

     let bitmapBytesPerRow = width * 4 
     let bitmapByteCount = bitmapBytesPerRow * height 

     let colorSpace = CGColorSpaceCreateDeviceRGB() 

     let bitmapData = malloc(bitmapByteCount) 
     if bitmapData == nil { 
      return nil 
     } 

     let context = CGContext (data: bitmapData, 
           width: width, 
           height: height, 
           bitsPerComponent: 8,  // bits per component 
      bytesPerRow: bitmapBytesPerRow, 
      space: colorSpace, 
      bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue) 

     return context 
    } 
} 
+2

पूरी तरह से काम करता है! एक सुझाव स्विफ्ट नामकरण सम्मेलनों को लागू करना होगा और जब आप एक नई वस्तु वापस कर लेंगे तो 'ट्रिम() 'फ़ंक्शन' ट्रिम किए गए() 'को कॉल करें। यदि आप 'शून्य' वापस कर देते हैं और मौजूदा UIImage इंस्टेंस को संशोधित करते हैं तो नामकरण सही होगा। – blackjacx

+0

@Daddycat टैन यिन देखें, अच्छा समाधान - यहां मेमोरी प्रबंधन के बारे में कैसे? मॉलोक की देखभाल की जाती है? – shadowf

0

स्विफ्ट 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; 
} 

}