Question

Hé là, je suis en train d'accéder aux données brutes de l'appareil photo de l'iPhone en utilisant AVCaptureSession. Je suis le guide fourni par Apple (lien ).

Les données brutes du SampleBuffer est au format YUV (Ai-je raison ici sur le format de l'image vidéo brute ??), comment obtenir directement les données pour le composant Y sur les données brutes stockées dans la SampleBuffer.

Était-ce utile?

La solution

Lors de la configuration du AVCaptureVideoDataOutput qui renvoie les cadres de Camera Raw, vous pouvez définir le format des trames en utilisant le code comme suit:

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

Dans ce cas, un format de pixel BGRA est spécifié (j'ai utilisé ce pour faire correspondre un format de couleur pour une texture OpenGL ES). Chaque pixel dans ce format a un octet pour le bleu, le vert, le rouge et alpha, dans cet ordre. Voulez-vous profiter ce fait, il est facile de tirer des composantes de couleur, mais vous ne sacrifiez un peu les performances en avoir besoin de faire la conversion de la caméra YUV natif colorspace.

D'autres sont pris en charge kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange colorimétriques et kCVPixelFormatType_420YpCbCr8BiPlanarFullRange sur les appareils plus récents et kCVPixelFormatType_422YpCbCr8 sur l'iPhone 3G. Le suffixe VideoRange ou FullRange indique simplement si les octets sont renvoyés entre 16-235 Y et 16 - 240 à UV ou en entier 0 -. 255 pour chaque composante

Je crois que le colorspace par défaut utilisé par une instance AVCaptureVideoDataOutput est le YUV 4: 2: 0 colorspace plan (sauf sur l'iPhone 3G, où il est YUV 4: 2: 2 entrelacée). Cela signifie qu'il y a deux plans de données d'image contenues dans la trame vidéo, avec le plan Y venir en premier. Pour chaque pixel de l'image résultante, il y a un octet pour la valeur Y à ce pixel.

vous obtiendrez à ces données Y premières en mettant en œuvre quelque chose comme ceci dans votre rappel délégué:

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

On peut alors déterminer l'emplacement dans les données de trame pour chaque X, Y de coordonnées sur l'image et tirer sur l'octet qui correspond à la composante Y à cette coordonnée.

échantillon de FindMyiCone d'Apple de WWDC 2010 (accessible avec les vidéos) montre comment pour traiter des données brutes BGRA de chaque trame. J'ai aussi créé un exemple d'application, que vous pouvez télécharger le code , qui effectue suivi d'objet basé sur la couleur en utilisant la vidéo en direct de la caméra de l'iPhone. Les deux montrent comment traiter les données brutes des pixels, mais aucun de ces travaux dans le modèle colorimétrique YUV.

Autres conseils

En plus de la réponse de Brad, et votre propre code, vous voulez considérer les points suivants:

Étant donné que votre image a deux plans distincts, la fonction CVPixelBufferGetBaseAddress ne reviendra pas l'adresse de base du plan mais l'adresse de base d'une structure de données supplémentaires. Il est probablement dû à la mise en œuvre actuelle que vous obtenez une adresse assez proche du premier plan afin que vous puissiez voir l'image. Mais c'est la raison pour laquelle il est déplacé et a des ordures en haut à gauche. La manière correcte pour recevoir le premier plan est le suivant:

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

Une ligne de l'image peut être plus longue que la largeur de l'image (en raison de l'arrondissement). Voilà pourquoi il y a des fonctions distinctes pour obtenir la largeur et le nombre d'octets par ligne. Vous ne disposez pas de ce problème pour le moment. Mais cela pourrait changer avec la prochaine version d'iOS. Donc, votre code devrait être:

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

S'il vous plaît noter que votre code lamentablement échouer sur un iPhone 3G.

Si vous avez seulement besoin du canal de luminance, je vous recommande de ne pas utiliser le format BGRA, comme il est livré avec une surcharge de conversion. Apple suggérons d'utiliser BGRA si vous faites des choses de rendu, mais vous n'avez pas besoin pour extraire les informations de luminance. Comme Brad déjà mentionné, le format le plus efficace est le format natif YUV caméra.

Cependant, extraire les octets depuis le tampon d'échantillon est un peu difficile, surtout en ce qui concerne l'iPhone 3G avec elle est entrelacée le format YUV 422. Donc, voici mon code, qui fonctionne très bien avec l'iPhone 3G, 3GS, iPod Touch 4 et 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

Ceci est tout simplement le point culminant de tout le monde est d'autre travail, au-dessus et sur d'autres sujets, converti en rapide 3 pour toute personne qui trouve utile.

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)
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top