Frage

Hallo, ich versuche den Zugriff auf Rohdaten von iPhone-Kamera AVCaptureSession verwenden. Ich folge der Anleitung von Apple ( Link hier ).

Die Rohdaten aus dem anderen sample sind in YUV-Format (Am korrigiere ich hier über das rohen Video-Frame-Format ??), wie direkt die Daten für die Y-Komponente in dem aus gespeicherten anderes sample die Rohdaten zu erhalten.

War es hilfreich?

Lösung

Wenn die AVCaptureVideoDataOutput Einrichten, dass die Renditen des RAW-Kamerarahmen, das Format der Frames unter Verwendung von Code wie die folgenden festlegen:

[videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];

In diesem Fall wird ein BGRA Pixelformat angegeben wird (I verwendet, um dieses ein Farbformat für eine OpenGL ES Textur für den Abgleich). Jedes Pixel in diesem Format hat ein Byte für blau, grün, rot und alpha, in dieser Reihenfolge. gehen mit diesem macht es einfach Farbkomponenten zu ziehen, aber Sie tun etwas Leistung opfern durch, um die Umwandlung von der Kamera-native YUV-Farbraum zu machen.

Weitere unterstützte Farbräume sind kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange und kCVPixelFormatType_420YpCbCr8BiPlanarFullRange auf neueren Geräten und kCVPixelFormatType_422YpCbCr8 auf dem iPhone 3G. Das VideoRange oder FullRange Suffix einfach anzeigt, ob der Bytes zwischen 16 zurückgeführt werden - 235 für Y und 16 bis 240 für UV-oder Voll 0 -. 255 für jede Komponente

Ich glaube, dass der Standard-Farbraum durch eine AVCaptureVideoDataOutput Instanz verwendet wird, ist das YUV 4: 2: 0 planaren Farbraum (außer auf dem iPhone 3G, wo es YUV 4: 2: 2 verschachtelt). Dies bedeutet, dass es zwei Ebenen von Bilddaten, die innerhalb des Video-Frame, wobei der Y-Ebene kommen zuerst. Für jedes Pixel in Ihrem resultierenden Bild gibt es einen Byte für den Y-Wert an diesem Pixel.

Sie würden an diesen rohen Y-Daten erhalten, indem so etwas in Ihrem Delegaten Rückruf Implementierung:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);

    unsigned char *rawPixelBase = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);

    // Do something with the raw pixels here

    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
}

Sie könnten dann für jeden X die Position in den Rahmendaten herauszufinden, Y auf dem Bildkoordinaten und das Byte aus derjenigen entspricht, bei der Koordinate der Y-Komponente ziehen.

Apples FindMyiCone Probe von WWDC 2010 (erreichbar mit den Videos) zeigt, wie raw BGRA Daten von jedem Rahmen zu verarbeiten. Ich habe auch eine Beispielanwendung erstellt, die Sie den Code für herunterladen können hier , dass führt farbbasierte Objektverfolgung , um das Live-Video von der Kamera iPhone. Beide zeigen, wie Rohpixeldaten zu verarbeiten, aber keiner von ihnen arbeiten im YUV-Farbraum.

Andere Tipps

Neben Brad Antwort und Ihren eigenen Code, möchten Sie folgendes beachten:

Da Ihr Bild hat zwei getrennte Ebene, die Funktion CVPixelBufferGetBaseAddress wird die Basisadresse der Ebene nicht zurück, sondern die Basisadresse einer zusätzlichen Datenstruktur. Es ist wahrscheinlich aufgrund der aktuellen Implementierung, dass Sie eine Adresse nahe genug an der ersten Ebene erhalten, so dass Sie das Bild sehen können. Aber es ist der Grund, warum es verschoben ist und hat Müll auf links oben. Der richtige Weg, um die erste Ebene zu empfangen ist:

unsigned char *rowBase = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);

Eine Zeile in dem Bild sein könnte länger als die Breite des Bildes (durch Rundungen). Deshalb gibt es getrennte Funktionen für das Erhalten der Breite und die Anzahl der Bytes pro Zeile. Sie haben dieses Problem nicht im Moment. Aber das könnte mit der nächsten Version von iOS ändern. So Ihr Code sein sollte:

int bufferHeight = CVPixelBufferGetHeight(pixelBuffer);
int bufferWidth = CVPixelBufferGetWidth(pixelBuffer);
int bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
int size = bufferHeight * bytesPerRow ;

unsigned char *pixel = (unsigned char*)malloc(size);

unsigned char *rowBase = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
memcpy (pixel, rowBase, size);

Bitte beachten Sie auch, dass Ihr Code kläglich auf einem iPhone 3G wird scheitern.

Wenn Sie nur den Luminanzkanal benötigen, empfehle ich gegen BGRA Format, wie es bei einem Umsatz Overhead kommt. Apple vorschlagen, mit BGRA wenn Sie Rendering Sachen tun, aber Sie müssen es nicht für die Helligkeitsinformation zu extrahieren. Als Brad bereits erwähnt, ist das effizienteste Format ist das Kamera-native YUV-Format.

Doch aus dem Probenpuffer, das richtige Bytes zu extrahieren ist ein bisschen schwierig, vor allem der iPhone 3G in Bezug auf mit ihm YUV 422 Format verschachtelt ist. So, hier ist mein Code, der gut mit dem iPhone 3G funktioniert, 3GS, iPod Touch 4 und iPhone 4S.

#pragma mark -
#pragma mark AVCaptureVideoDataOutputSampleBufferDelegate Methods
#if !(TARGET_IPHONE_SIMULATOR)
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;
{
    // get image buffer reference
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    // extract needed informations from image buffer
    CVPixelBufferLockBaseAddress(imageBuffer, 0);
    size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer);
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    CGSize resolution = CGSizeMake(CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer));

    // variables for grayscaleBuffer 
    void *grayscaleBuffer = 0;
    size_t grayscaleBufferSize = 0;

    // the pixelFormat differs between iPhone 3G and later models
    OSType pixelFormat = CVPixelBufferGetPixelFormatType(imageBuffer);

    if (pixelFormat == '2vuy') { // iPhone 3G
        // kCVPixelFormatType_422YpCbCr8     = '2vuy',    
        /* Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1 */

        // copy every second byte (luminance bytes form Y-channel) to new buffer
        grayscaleBufferSize = bufferSize/2;
        grayscaleBuffer = malloc(grayscaleBufferSize);
        if (grayscaleBuffer == NULL) {
            NSLog(@"ERROR in %@:%@:%d: couldn't allocate memory for grayscaleBuffer!", NSStringFromClass([self class]), NSStringFromSelector(_cmd), __LINE__);
            return nil; }
        memset(grayscaleBuffer, 0, grayscaleBufferSize);
        void *sourceMemPos = baseAddress + 1;
        void *destinationMemPos = grayscaleBuffer;
        void *destinationEnd = grayscaleBuffer + grayscaleBufferSize;
        while (destinationMemPos <= destinationEnd) {
            memcpy(destinationMemPos, sourceMemPos, 1);
            destinationMemPos += 1;
            sourceMemPos += 2;
        }       
    }

    if (pixelFormat == '420v' || pixelFormat == '420f') {
        // kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange = '420v', 
        // kCVPixelFormatType_420YpCbCr8BiPlanarFullRange  = '420f',
        // Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]).  
        // Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]).
        // baseAddress points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
        // i.e.: Y-channel in this format is in the first third of the buffer!
        int bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
        baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer,0);
        grayscaleBufferSize = resolution.height * bytesPerRow ;
        grayscaleBuffer = malloc(grayscaleBufferSize);
        if (grayscaleBuffer == NULL) {
            NSLog(@"ERROR in %@:%@:%d: couldn't allocate memory for grayscaleBuffer!", NSStringFromClass([self class]), NSStringFromSelector(_cmd), __LINE__);
            return nil; }
        memset(grayscaleBuffer, 0, grayscaleBufferSize);
        memcpy (grayscaleBuffer, baseAddress, grayscaleBufferSize); 
    }

    // do whatever you want with the grayscale buffer
    ...

    // clean-up
    free(grayscaleBuffer);
}
#endif

Das ist einfach der Höhepunkt jeder ist sonst harte Arbeit, über und auf anderen Themen, zu schnellen 3 für jedermann umgewandelt, dass findet es nützlich.

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
    if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
        CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)

        let pixelFormatType = CVPixelBufferGetPixelFormatType(pixelBuffer)
        if pixelFormatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
           || pixelFormatType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange {

            let bufferHeight = CVPixelBufferGetHeight(pixelBuffer)
            let bufferWidth = CVPixelBufferGetWidth(pixelBuffer)

            let lumaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
            let size = bufferHeight * lumaBytesPerRow
            let lumaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)
            let lumaByteBuffer = unsafeBitCast(lumaBaseAddress, to:UnsafeMutablePointer<UInt8>.self)

            let releaseDataCallback: CGDataProviderReleaseDataCallback = { (info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, size: Int) -> () in
                // https://developer.apple.com/reference/coregraphics/cgdataproviderreleasedatacallback
                // N.B. 'CGDataProviderRelease' is unavailable: Core Foundation objects are automatically memory managed
                return
            }

            if let dataProvider = CGDataProvider(dataInfo: nil, data: lumaByteBuffer, size: size, releaseData: releaseDataCallback) {
                let colorSpace = CGColorSpaceCreateDeviceGray()
                let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)

                let cgImage = CGImage(width: bufferWidth, height: bufferHeight, bitsPerComponent: 8, bitsPerPixel: 8, bytesPerRow: lumaBytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo, provider: dataProvider, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent)

                let greyscaleImage = UIImage(cgImage: cgImage!)
                // do what you want with the greyscale image.
            }
        }

        CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top