如何自动切换UIImage?

我有一个包含形状的UIImage; 其余的是透明的。 我想通过裁剪尽可能多的透明部分来获得另一个UIImage,仍保留所有非透明像素 – 类似于GIMP中的autocropfunction。 我该怎么做呢?

这种方法可能比您希望的更具侵入性,但它可以完成工作。 我正在做的是为UIImage创建一个位图上下文,获取指向原始图像数据的指针,然后通过它筛选寻找不透明的像素。 我的方法返回一个CGRect,我用它来创建一个新的UIImage。

- (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 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); 

我从这篇非常有用的文章中抓取了一些代码。 希望能帮助到你!

Swift版本:

 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(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 } 

从@ Danny182的答案改进后,我还添加了空白(任何比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(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 } } 

Swift 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(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 } } 

斯威夫特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; } 

}