J'ai un UIImage (Cocoa Touch). À partir de là, je suis heureux d’obtenir une image CGImage ou tout autre élément de votre choix disponible. J'aimerais écrire cette fonction:
- (int)getRGBAFromImage:(UIImage *)image atX:(int)xx andY:(int)yy {
// [...]
// What do I want to read about to help
// me fill in this bit, here?
// [...]
int result = (red << 24) | (green << 16) | (blue << 8) | alpha;
return result;
}
Merci!
Pour info, j'ai combiné la réponse de Keremk avec mon contour d'origine, nettoyé les fautes de frappe, généralisé pour renvoyer un tableau de couleurs et obtenu le tout pour compiler. Voici le résultat:
+ (NSArray*)getRGBAsFromImage:(UIImage*)image atX:(int)x andY:(int)y count:(int)count
{
NSMutableArray *result = [NSMutableArray arrayWithCapacity:count];
// First get the image into your data buffer
CGImageRef imageRef = [image CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char));
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGContextRelease(context);
// Now your rawData contains the image data in the RGBA8888 pixel format.
NSUInteger byteIndex = (bytesPerRow * y) + x * bytesPerPixel;
for (int i = 0 ; i < count ; ++i)
{
CGFloat alpha = ((CGFloat) rawData[byteIndex + 3] ) / 255.0f;
CGFloat red = ((CGFloat) rawData[byteIndex] ) / alpha;
CGFloat green = ((CGFloat) rawData[byteIndex + 1] ) / alpha;
CGFloat blue = ((CGFloat) rawData[byteIndex + 2] ) / alpha;
byteIndex += bytesPerPixel;
UIColor *acolor = [UIColor colorWithRed:red green:green blue:blue alpha:alpha];
[result addObject:acolor];
}
free(rawData);
return result;
}
Une façon de le faire est de dessiner l’image dans un contexte bitmap sauvegardé par un tampon donné pour un espace de couleur donné (dans ce cas, il s’agit du RVB): vouloir le mettre en cache au lieu de faire cette opération à chaque fois que vous devez obtenir des valeurs en pixels)
Voir ci-dessous à titre d'exemple:
// First get the image into your data buffer
CGImageRef image = [myUIImage CGImage];
NSUInteger width = CGImageGetWidth(image);
NSUInteger height = CGImageGetHeight(image);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char *rawData = malloc(height * width * 4);
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colorSpace);
CGContextDrawImage(context, CGRectMake(0, 0, width, height));
CGContextRelease(context);
// Now your rawData contains the image data in the RGBA8888 pixel format.
int byteIndex = (bytesPerRow * yy) + xx * bytesPerPixel;
red = rawData[byteIndex];
green = rawData[byteIndex + 1];
blue = rawData[byteIndex + 2];
alpha = rawData[byteIndex + 3];
Le Questions techniques QA1509 d'Apple présente l'approche simple suivante:
CFDataRef CopyImagePixels(CGImageRef inImage)
{
return CGDataProviderCopyData(CGImageGetDataProvider(inImage));
}
Utilisez CFDataGetBytePtr
pour obtenir les octets réels (et divers CGImageGet*
méthodes pour comprendre comment les interpréter).
Impossible de croire qu'il n'y a pas une seule bonne réponse ici. Il n'est pas nécessaire d'allouer des pointeurs et les valeurs non multipliées doivent encore être normalisées. Pour aller droit au but, voici la version correcte pour Swift 4. Pour UIImage
, utilisez simplement .cgImage
.
extension CGImage {
func colors(at: [CGPoint]) -> [UIColor]? {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bytesPerPixel = 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 nil
}
context.draw(self, in: CGRect(x: 0, y: 0, width: width, height: height))
return at.map { p in
let i = bytesPerRow * Int(p.y) + bytesPerPixel * Int(p.x)
let a = CGFloat(ptr[i + 3]) / 255.0
let r = (CGFloat(ptr[i]) / a) / 255.0
let g = (CGFloat(ptr[i + 1]) / a) / 255.0
let b = (CGFloat(ptr[i + 2]) / a) / 255.0
return UIColor(red: r, green: g, blue: b, alpha: a)
}
}
}
Voici un SO thread où @Matt ne restitue que le pixel souhaité dans un contexte 1x1 en déplaçant l'image de sorte que le pixel souhaité s'aligne sur le pixel du contexte. .
NSString * path = [[NSBundle mainBundle] pathForResource:@"filename" ofType:@"jpg"];
UIImage * img = [[UIImage alloc]initWithContentsOfFile:path];
CGImageRef image = [img CGImage];
CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(image));
const unsigned char * buffer = CFDataGetBytePtr(data);
Selon le référence Apple sur UIImage l'objet est immuable et vous n'avez pas accès aux octets de sauvegarde. S'il est vrai que vous pouvez accéder aux données CGImage si vous avez renseigné le UIImage
avec un CGImage
(explicitement ou implicitement), il retournera NULL
si le UIImage
est soutenu par un CIImage
et vice-versa.
Les objets image ne fournissent pas d'accès direct à leurs données d'image sous-jacentes. Cependant, vous pouvez récupérer les données d'image dans d'autres formats pour les utiliser dans votre application. Plus précisément, vous pouvez utiliser les propriétés cgImage et ciImage pour récupérer des versions de l'image compatibles avec Core Graphics et Core Image, respectivement. Vous pouvez également utiliser les fonctions UIImagePNGRepresentation ( :) et UIImageJPEGRepresentation (: _ :) pour générer un objet NSData contenant les données d'image au format PNG ou JPEG. format.
Comme indiqué, vos options sont
Aucune de ces méthodes n'est particulièrement utile si vous souhaitez obtenir une sortie qui n'est pas au format ARGB, PNG ou JPEG et que les données ne sont pas déjà sauvegardées par CIImage.
Lors du développement de votre projet, il serait peut-être plus judicieux d’éviter UIImage et de choisir autre chose. UIImage, en tant qu'encapsuleur d'image Obj-C, est souvent pris en charge par CGImage au point où nous le prenons pour acquis. CIImage a tendance à être un meilleur format de wrapper dans la mesure où vous pouvez utiliser un CIContext pour obtenir le format souhaité sans avoir besoin de savoir comment il a été créé. Dans votre cas, obtenir le bitmap consisterait à appeler
- render: toBitmap: rowBytes: bounds: format: colorSpace:
En prime, vous pouvez commencer à effectuer de jolies manipulations sur l'image en chaînant les filtres sur l'image. Cela résout beaucoup de problèmes lorsque l'image est à l'envers ou doit être pivotée/réduite, etc.
S'appuyant sur la réponse de Olie et Algal, voici une réponse mise à jour pour Swift 3
public func getRGBAs(fromImage image: UIImage, x: Int, y: Int, count: Int) -> [UIColor] {
var result = [UIColor]()
// First get the image into your data buffer
guard let cgImage = image.cgImage else {
print("CGContext creation failed")
return []
}
let width = cgImage.width
let height = cgImage.height
let colorSpace = CGColorSpaceCreateDeviceRGB()
let rawdata = calloc(height*width*4, MemoryLayout<CUnsignedChar>.size)
let bytesPerPixel = 4
let bytesPerRow = bytesPerPixel * width
let bitsPerComponent = 8
let bitmapInfo: UInt32 = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Big.rawValue
guard let context = CGContext(data: rawdata, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
print("CGContext creation failed")
return result
}
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height))
// Now your rawData contains the image data in the RGBA8888 pixel format.
var byteIndex = bytesPerRow * y + bytesPerPixel * x
for _ in 0..<count {
let alpha = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 3, as: UInt8.self)) / 255.0
let red = CGFloat(rawdata!.load(fromByteOffset: byteIndex, as: UInt8.self)) / alpha
let green = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 1, as: UInt8.self)) / alpha
let blue = CGFloat(rawdata!.load(fromByteOffset: byteIndex + 2, as: UInt8.self)) / alpha
byteIndex += bytesPerPixel
let aColor = UIColor(red: red, green: green, blue: blue, alpha: alpha)
result.append(aColor)
}
free(rawdata)
return result
}
Basé sur différentes réponses mais principalement sur this , cela fonctionne pour ce dont j'ai besoin:
UIImage *image1 = ...; // The image from where you want a pixel data
int pixelX = ...; // The X coordinate of the pixel you want to retrieve
int pixelY = ...; // The Y coordinate of the pixel you want to retrieve
uint32_t pixel1; // Where the pixel data is to be stored
CGContextRef context1 = CGBitmapContextCreate(&pixel1, 1, 1, 8, 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst);
CGContextDrawImage(context1, CGRectMake(-pixelX, -pixelY, CGImageGetWidth(image1.CGImage), CGImageGetHeight(image1.CGImage)), image1.CGImage);
CGContextRelease(context1);
À la suite de ces lignes, vous aurez un pixel au format AARRGGBB avec alpha toujours défini sur FF dans l’entier non signé de 4 octets pixel1
.