Wie bekomme ich Pixeldaten von einem UIImage (Cocoa Touch) oder CGImage (Core Graphics)?

200

Ich habe ein UIImage (Cocoa Touch). Ich freue mich, ein CGImage oder etwas anderes zu erhalten, das verfügbar sein soll. Ich möchte diese Funktion schreiben:

- (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;
}
Olie
quelle

Antworten:

249

Zu Ihrer Information, ich habe Keremks Antwort mit meiner ursprünglichen Gliederung kombiniert, die Tippfehler bereinigt, sie verallgemeinert, um eine Reihe von Farben zurückzugeben, und das Ganze zum Kompilieren gebracht. Hier ist das Ergebnis:

+ (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;
}
Olie
quelle
4
Vergessen Sie nicht, die Farben zu entfernen. Auch diese Multiplikationen mit 1 bewirken nichts.
Peter Hosey
2
Auch wenn es richtig ist. Wenn count eine große Zahl ist (wie die Länge einer Bildzeile oder die gesamte Bildgröße!), Ist die Leistung dieser Methode nicht gut, da zu viele UIColor-Objekte sehr schwer sind. Im wirklichen Leben, wenn ich ein Pixel brauche, ist UIColor in Ordnung (ein NSArray von UIColors ist zu viel für mich). Und wenn Sie eine Reihe von Farben benötigen, werde ich UIColors nicht verwenden, da ich wahrscheinlich OpenGL usw. Verwenden werde. Meiner Meinung nach hat diese Methode keine reale Anwendung, ist aber für Bildungszwecke sehr gut.
Nacho4D
4
Viel zu umständlich Malloc einen großen Raum nur um ein Pixel zu lesen.
Ragnarius
46
BENUTZEN SIE CALLOC STATT MALLOC !!!! Ich habe diesen Code verwendet, um zu testen, ob bestimmte Pixel transparent sind, und ich habe falsche Informationen zurückbekommen, bei denen Pixel transparent sein sollten, weil der Speicher nicht zuerst gelöscht wurde. Die Verwendung von Calloc (Breite * Höhe, 4) anstelle von Malloc hat den Trick getan. Der Rest des Codes ist großartig, DANKE!
DonnaLea
5
Das ist toll. Vielen Dank. Hinweis zur Verwendung: x und y sind die Koordinaten innerhalb des Bildes, von denen RGBAs abgerufen werden sollen, und 'count' ist die Anzahl der Pixel, die von diesem Punkt abgerufen werden sollen, von links nach rechts und dann zeilenweise. Beispiel Verwendung: So erhalten Sie RGBAs für ein gesamtes Bild: getRGBAsFromImage:image atX:0 andY:0 count:image.size.width*image.size.height So erhalten Sie RGBAs für die letzte Zeile eines Bildes: getRGBAsFromImage:image atX:0 andY:image.size.height-1 count:image.size.width
Harris
49

Eine Möglichkeit besteht darin, das Bild in einen Bitmap-Kontext zu zeichnen, der von einem bestimmten Puffer für einen bestimmten Farbraum (in diesem Fall RGB) unterstützt wird: (Beachten Sie, dass dadurch die Bilddaten in diesen Puffer kopiert werden möchten es zwischenspeichern, anstatt diesen Vorgang jedes Mal auszuführen, wenn Sie Pixelwerte abrufen müssen)

Siehe unten als Beispiel:

// 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];
keremk
quelle
Ich habe etwas Ähnliches in meiner App gemacht. Um ein beliebiges Pixel zu extrahieren, zeichnen Sie einfach in eine 1x1-Bitmap mit einem bekannten Bitmap-Format und passen den Ursprung des CGBitmapContext entsprechend an.
Mark Bessey
5
Dadurch geht Speicher verloren. Release rawData: free (rawData);
Adam Waite
5
CGContextDrawImageerfordert drei Argumente und oben werden nur zwei angegeben ... Ich denke, es sollte seinCGContextDrawImage(context, CGRectMake(0, 0, width, height), image)
user1135469
25

Apples technische Fragen und Antworten QA1509 zeigt den folgenden einfachen Ansatz:

CFDataRef CopyImagePixels(CGImageRef inImage)
{
    return CGDataProviderCopyData(CGImageGetDataProvider(inImage));
}

Verwenden Sie CFDataGetBytePtrdiese Option , um zu den tatsächlichen Bytes zu gelangen (und verschiedene CGImageGet*Methoden, um zu verstehen, wie sie interpretiert werden).

Yakovlev
quelle
10
Dies ist kein guter Ansatz, da das Pixelformat pro Bild stark variiert. Es gibt verschiedene Dinge, die sich ändern können, darunter 1. die Ausrichtung des Bildes 2. das Format der Alpha-Komponente und 3. die Bytereihenfolge von RGB. Ich persönlich habe einige Zeit damit verbracht, Apples Dokumente zu entschlüsseln, aber ich bin mir nicht sicher, ob es sich lohnt. Wenn Sie es schnell erledigen möchten, wählen Sie einfach die Lösung von keremk.
Tyler
1
Diese Methode gibt mir immer BGR-Format, auch wenn ich das Bild speichere (aus meiner App im Farbraum RGBA)
Soumyajit
1
@Soumyaljit Hiermit werden die Daten in dem Format kopiert, in dem die CGImageRef-Daten ursprünglich gespeichert wurden. In den meisten Fällen wird dies BGRA sein, könnte aber auch YUV sein.
Cameron Lowell Palmer
18

Ich konnte nicht glauben, dass es hier keine einzige richtige Antwort gibt. Es müssen keine Zeiger zugewiesen werden, und die nicht multiplizierten Werte müssen noch normalisiert werden. Um auf den Punkt zu kommen, hier ist die richtige Version für Swift 4. UIImageNur zur Verwendung .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)
        }
    }
}

Der Grund, warum Sie das Bild zuerst in einen Puffer zeichnen / konvertieren müssen, ist, dass Bilder verschiedene Formate haben können. Dieser Schritt ist erforderlich, um es in ein konsistentes Format zu konvertieren, das Sie lesen können.

Erik Aigner
quelle
Wie kann ich diese Erweiterung für mein Bild verwenden? let imageObj = UIImage (benannt: "greencolorImg.png")
Sagar Sukode
let imageObj = UIImage(named:"greencolorImg.png"); imageObj.cgImage?.colors(at: [pt1, pt2])
Erik Aigner
Denken Sie daran pt1und pt2sind CGPointVariablen.
AnBisw
rettete meinen Tag :)
Dari
1
@ErikAigner funktioniert dies, wenn ich die Farbe jedes Pixels eines Bildes erhalten möchte? Was sind die Leistungskosten?
Ameet Dhas
9

Hier ist ein SO- Thread, in dem @Matt nur das gewünschte Pixel in einen 1x1-Kontext rendert, indem das Bild so verschoben wird, dass das gewünschte Pixel mit dem einen Pixel im Kontext übereinstimmt.

Wirbel
quelle
9
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);
Nidal Fakhouri
quelle
Ich habe dies gepostet und es hat bei mir funktioniert, aber wenn ich vom Puffer zurück zu einem UIImage gehe, kann ich noch keine Ideen finden. Irgendwelche Ideen?
Nidal Fakhouri
8

UIImage ist ein Wrapper. Die Bytes sind CGImage oder CIImage

Laut der Apple-Referenz zu UIImage ist das Objekt unveränderlich und Sie haben keinen Zugriff auf die Hintergrundbytes. Während es wahr ist, dass Sie auf die CGImage-Daten zugreifen können, wenn Sie die UIImagemit a CGImage(explizit oder implizit) gefüllt haben , wird sie zurückgegeben, NULLwenn die UIImagevon a unterstützt wird CIImageund umgekehrt.

Bildobjekte bieten keinen direkten Zugriff auf die zugrunde liegenden Bilddaten. Sie können die Bilddaten jedoch in anderen Formaten zur Verwendung in Ihrer App abrufen. Insbesondere können Sie die Eigenschaften cgImage und ciImage verwenden, um Versionen des Bildes abzurufen, die mit Core Graphics bzw. Core Image kompatibel sind. Sie können auch die Funktionen UIImagePNGRepresentation ( :) und UIImageJPEGRepresentation ( : _ :) verwenden, um ein NSData-Objekt zu generieren, das die Bilddaten entweder im PNG- oder JPEG-Format enthält.

Allgemeine Tricks, um dieses Problem zu umgehen

Wie angegeben sind Ihre Optionen

  • UIImagePNGRepresentation oder JPEG
  • Stellen Sie fest, ob das Bild CGImage- oder CIImage-Sicherungsdaten enthält, und rufen Sie diese dort ab

Keiner dieser Tricks ist besonders gut, wenn Sie eine Ausgabe wünschen, die keine ARGB-, PNG- oder JPEG-Daten enthält und die Daten nicht bereits von CIImage unterstützt werden.

Meine Empfehlung, versuchen Sie es mit CIImage

Während der Entwicklung Ihres Projekts ist es möglicherweise sinnvoller, UIImage vollständig zu vermeiden und etwas anderes auszuwählen. UIImage wird als Obj-C-Image-Wrapper häufig von CGImage bis zu dem Punkt unterstützt, an dem wir dies für selbstverständlich halten. CIImage ist in der Regel ein besseres Wrapper-Format, da Sie einen CIContext verwenden können , um das gewünschte Format zu erhalten, ohne wissen zu müssen, wie es erstellt wurde. In Ihrem Fall wäre das Abrufen der Bitmap eine Frage des Aufrufs

- render: toBitmap: rowBytes: bounds: format: colorSpace:

Als zusätzlichen Bonus können Sie schöne Manipulationen am Bild vornehmen, indem Sie Filter auf das Bild verketten. Dies löst viele Probleme, bei denen das Bild auf dem Kopf steht oder gedreht / skaliert werden muss usw.

Cameron Lowell Palmer
quelle
6

Aufbauend auf der Antwort von Olie und Algal finden Sie hier eine aktualisierte Antwort für 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
}

Swillsea
quelle
3

Swift 5 Version

Die hier gegebenen Antworten sind entweder veraltet oder falsch, da sie Folgendes nicht berücksichtigen:

  1. Die Pixelgröße des Bildes kann von der Punktgröße abweichen, die von image.size.width/ zurückgegeben wirdimage.size.height .
  2. Es kann verschiedene Layouts geben, die von Pixelkomponenten im Bild verwendet werden, wie z. B. BGRA, ABGR, ARGB usw., oder es kann überhaupt keine Alpha-Komponente wie BGR und RGB geben. Beispielsweise,UIView.drawHierarchy(in:afterScreenUpdates:) Verfahren BGRA-Bilder erzeugen.
  3. Farbkomponenten können für alle Pixel im Bild mit dem Alpha vormultipliziert werden und müssen durch Alpha geteilt werden, um die ursprüngliche Farbe wiederherzustellen.
  4. Für die von verwendete Speicheroptimierung CGImagekann die Größe einer Pixelzeile in Bytes größer sein als die bloße Multiplikation der Pixelbreite mit 4.

Der folgende Code soll eine universelle Swift 5-Lösung bereitstellen, um das UIColorPixel für all diese Sonderfälle zu ermitteln. Der Code ist auf Benutzerfreundlichkeit und Klarheit optimiert, nicht auf Leistung.

public extension UIImage {

    var pixelWidth: Int {
        return cgImage?.width ?? 0
    }

    var pixelHeight: Int {
        return cgImage?.height ?? 0
    }

    func pixelColor(x: Int, y: Int) -> UIColor {
        assert(
            0..<pixelWidth ~= x && 0..<pixelHeight ~= y,
            "Pixel coordinates are out of bounds")

        guard
            let cgImage = cgImage,
            let data = cgImage.dataProvider?.data,
            let dataPtr = CFDataGetBytePtr(data),
            let colorSpaceModel = cgImage.colorSpace?.model,
            let componentLayout = cgImage.bitmapInfo.componentLayout
        else {
            assertionFailure("Could not get a pixel of an image")
            return .clear
        }

        assert(
            colorSpaceModel == .rgb,
            "The only supported color space model is RGB")
        assert(
            cgImage.bitsPerPixel == 32 || cgImage.bitsPerPixel == 24,
            "A pixel is expected to be either 4 or 3 bytes in size")

        let bytesPerRow = cgImage.bytesPerRow
        let bytesPerPixel = cgImage.bitsPerPixel/8
        let pixelOffset = y*bytesPerRow + x*bytesPerPixel

        if componentLayout.count == 4 {
            let components = (
                dataPtr[pixelOffset + 0],
                dataPtr[pixelOffset + 1],
                dataPtr[pixelOffset + 2],
                dataPtr[pixelOffset + 3]
            )

            var alpha: UInt8 = 0
            var red: UInt8 = 0
            var green: UInt8 = 0
            var blue: UInt8 = 0

            switch componentLayout {
            case .bgra:
                alpha = components.3
                red = components.2
                green = components.1
                blue = components.0
            case .abgr:
                alpha = components.0
                red = components.3
                green = components.2
                blue = components.1
            case .argb:
                alpha = components.0
                red = components.1
                green = components.2
                blue = components.3
            case .rgba:
                alpha = components.3
                red = components.0
                green = components.1
                blue = components.2
            default:
                return .clear
            }

            // If chroma components are premultiplied by alpha and the alpha is `0`,
            // keep the chroma components to their current values.
            if cgImage.bitmapInfo.chromaIsPremultipliedByAlpha && alpha != 0 {
                let invUnitAlpha = 255/CGFloat(alpha)
                red = UInt8((CGFloat(red)*invUnitAlpha).rounded())
                green = UInt8((CGFloat(green)*invUnitAlpha).rounded())
                blue = UInt8((CGFloat(blue)*invUnitAlpha).rounded())
            }

            return .init(red: red, green: green, blue: blue, alpha: alpha)

        } else if componentLayout.count == 3 {
            let components = (
                dataPtr[pixelOffset + 0],
                dataPtr[pixelOffset + 1],
                dataPtr[pixelOffset + 2]
            )

            var red: UInt8 = 0
            var green: UInt8 = 0
            var blue: UInt8 = 0

            switch componentLayout {
            case .bgr:
                red = components.2
                green = components.1
                blue = components.0
            case .rgb:
                red = components.0
                green = components.1
                blue = components.2
            default:
                return .clear
            }

            return .init(red: red, green: green, blue: blue, alpha: UInt8(255))

        } else {
            assertionFailure("Unsupported number of pixel components")
            return .clear
        }
    }

}

public extension UIColor {

    convenience init(red: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) {
        self.init(
            red: CGFloat(red)/255,
            green: CGFloat(green)/255,
            blue: CGFloat(blue)/255,
            alpha: CGFloat(alpha)/255)
    }

}

public extension CGBitmapInfo {

    enum ComponentLayout {

        case bgra
        case abgr
        case argb
        case rgba
        case bgr
        case rgb

        var count: Int {
            switch self {
            case .bgr, .rgb: return 3
            default: return 4
            }
        }

    }

    var componentLayout: ComponentLayout? {
        guard let alphaInfo = CGImageAlphaInfo(rawValue: rawValue & Self.alphaInfoMask.rawValue) else { return nil }
        let isLittleEndian = contains(.byteOrder32Little)

        if alphaInfo == .none {
            return isLittleEndian ? .bgr : .rgb
        }
        let alphaIsFirst = alphaInfo == .premultipliedFirst || alphaInfo == .first || alphaInfo == .noneSkipFirst

        if isLittleEndian {
            return alphaIsFirst ? .bgra : .abgr
        } else {
            return alphaIsFirst ? .argb : .rgba
        }
    }

    var chromaIsPremultipliedByAlpha: Bool {
        let alphaInfo = CGImageAlphaInfo(rawValue: rawValue & Self.alphaInfoMask.rawValue)
        return alphaInfo == .premultipliedFirst || alphaInfo == .premultipliedLast
    }

}
Desmond Hume
quelle
Was ist mit Orientierungsänderungen? Müssen Sie UIImage.Orientation nicht auch überprüfen?
Endavid
Außerdem componentLayoutfehlt Ihnen der Fall, wenn es nur Alpha gibt .alphaOnly.
Endavid
@endavid "Nur Alpha" wird nicht unterstützt (da dies ein sehr seltener Fall ist). Die Bildausrichtung fällt nicht in den Geltungsbereich dieses Codes, aber es gibt viele Ressourcen zum Normalisieren der Ausrichtung von a. Dies UIImageist das, was Sie tun würden, bevor Sie mit dem Lesen der Pixelfarben beginnen.
Desmond Hume
2

Basierend auf verschiedenen Antworten, aber hauptsächlich darauf , funktioniert dies für das, was ich brauche:

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

Als Ergebnis dieser Zeilen haben Sie ein Pixel im AARRGGBB-Format, wobei Alpha in der 4-Byte-Ganzzahl ohne Vorzeichen immer auf FF gesetzt ist pixel1.

cprcrack
quelle
1

Verwenden Sie das zugrunde liegende CGImage und seinen dataProvider, um auf die RGB-Rohwerte eines UIImage in Swift 5 zuzugreifen:

import UIKit

let image = UIImage(named: "example.png")!

guard let cgImage = image.cgImage,
    let data = cgImage.dataProvider?.data,
    let bytes = CFDataGetBytePtr(data) else {
    fatalError("Couldn't access image data")
}
assert(cgImage.colorSpace?.model == .rgb)

let bytesPerPixel = cgImage.bitsPerPixel / cgImage.bitsPerComponent
for y in 0 ..< cgImage.height {
    for x in 0 ..< cgImage.width {
        let offset = (y * cgImage.bytesPerRow) + (x * bytesPerPixel)
        let components = (r: bytes[offset], g: bytes[offset + 1], b: bytes[offset + 2])
        print("[x:\(x), y:\(y)] \(components)")
    }
    print("---")
}

https://www.ralfebert.de/ios/examples/image-processing/uiimage-raw-pixels/

Ralf Ebert
quelle