Ho una UIImage che contiene una forma; il resto è trasparente. Mi piacerebbe ottenere un altro UIImage ritagliando il maggior numero possibile di parti trasparenti, mantenendo comunque tutti i pixel non trasparenti - simile alla funzione di ritaglio automatico di GIMP. Come potrei fare questo?Come si autocorre un UIImage?
risposta
Questo approccio potrebbe essere un po 'più invasivo di quello che speravi, ma ha portato a termine il lavoro. Quello che sto facendo è creare un contesto bitmap per UIImage, ottenendo un puntatore ai dati di immagine grezzi, quindi passando da esso alla ricerca di pixel non trasparenti. Il mio metodo restituisce un CGRect che uso per creare un nuovo 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<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);
}
Il metodo per creare il contesto Bitmap:
- (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;
}
E, infine, ottenere il vostro nuovo UIImage ritagliata dalla CGRect restituita:
CGRect newRect = [self cropRectForImage:oldImage];
CGImageRef imageRef = CGImageCreateWithImageInRect(oldImage.CGImage, newRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
ho preso un po 'di quel codice da this very useful article. Spero che sia d'aiuto!
Swift Versione:
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)
}
}
Il metodo per creare il contesto Bitmap:
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
}
E, infine, ottenere il vostro nuovo UIImage ritagliata dalla CGRect restituita:
let image = // UIImage Source
let newRect = image.cropRect()
if let imageRef = CGImageCreateWithImageInRect(image.CGImage!, newRect) {
let newImage = UIImage(CGImage: imageRef)
// Use this new Image
}
La tua soluzione mi ha salvato la vita. Grazie molto!!! –
Swift 3 (non è un'estensione UIImage, avevo bisogno di un'altra classe) si può risparmiare tempo per qualcuno:
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
}
}
grazie per il codice @ Danny182, tuttavia c'era un errore di battitura nella riga di ritorno (highX - lowY) -> dovrebbe essere highX - lowX –
forse è per questo che non funzionava perfettamente. Ci proverò. TY – Danny182
CGColorSpaceCreateDeviceRGB() non restituisce un optional, il controllo nullo non è necessario. –
migliorata da @ risposta di Danny182, ho anche aggiunto white-space (i pixel più luminoso 0xe0e0e0) trimming per il mio stesso bisogno
utilizzati:
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
}
}
Funziona perfettamente! Un suggerimento sarebbe quello di applicare le convenzioni di denominazione Swift e chiamare la funzione 'trim()' 'trimmed()' dal momento che si restituisce un nuovo oggetto. La denominazione sarebbe corretta se si restituisce 'void' e si modifica l'istanza UIImage esistente. – blackjacx
@Daddycat Tan Yin See, bella soluzione: che ne pensi della gestione della memoria qui? ci si cura di malloc? – shadowf
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;
}
}
- 1. Come copiare un UIImage?
- 2. Come si salva un UIImage in un file?
- 3. Come si converte un UIImage in NSData o CFDataRef?
- 4. Come creare un UIImage nero?
- 5. Come posso convertire FBProfilePictureView in un UIImage?
- 6. Come creare un UIImage con UIBezierPath
- 7. come convertire un CVImageBufferRef in UIImage
- 8. Come scalare un UIImage senza lisciare nulla?
- 9. Cattura UIView come UIImage
- 10. Come serializzare UIImage su JSON?
- 11. È possibile animare un UIImage?
- 12. UIImage metadati
- 13. Come ottenere l'URL da UIImage
- 14. come inserire UIImage in UITextView
- 15. crea un UIImage da due UIImageView
- 16. Qual è la differenza tra UIImage (named :) e UIImage (imageLiteralResourceName :)?
- 17. Come ottenere UIImage da EAGLView?
- 18. iPhone SDK - Come disegnare un UIImage su un altro UIImmagine?
- 19. Come posso memorizzare un UIImage in un NSDictionary?
- 20. UIImage from CGImageRef
- 21. ios8 UIImage imageNamed: crash
- 22. Come creare un UIImage dal contesto grafico corrente?
- 23. Get UIBezierpath da mascherare un UIImage
- 24. Animate un UIBezierPath che ritaglia una UIImage
- 25. Componi UIActivityTypeMessage con UIImage
- 26. Come visualizzare una UIImage in Swift
- 27. CGImage (o UIImage) da un CALayer
- 28. Converti UIImage in NSData e converti in UIImage in Swift?
- 29. Regola UIImage in UIImageView
- 30. Aggiungi UIImage a CCSprite?
Grazie. Ottimo lavoro ... :) – Thampuran