Question

J'ai un morceau de code qui met en place une session de capture de la caméra pour traiter les images en utilisant OpenCV, puis définissez la propriété d'image d'un UIImageView avec un UIImage généré à partir du cadre. Lorsque l'application démarre, l'image de la vue de l'image est nul et aucune image apparaître jusqu'à ce que je pousse un autre contrôleur de vue sur la pile puis pop off. Ensuite, l'image reste la même jusqu'à ce que je le fais à nouveau. déclarations NSLog montrent que le rappel est appelé à peu près le taux de trame correct. Toutes les idées pourquoi il ne se présente pas? Je réduit le framerate tout le chemin à 2 images par seconde. Est-il pas le traitement assez rapide?

Voici le code:

- (void)setupCaptureSession {
    NSError *error = nil;

    // Create the session
    AVCaptureSession *session = [[AVCaptureSession alloc] init];

    // Configure the session to produce lower resolution video frames, if your 
    // processing algorithm can cope. We'll specify medium quality for the
    // chosen device.
    session.sessionPreset = AVCaptureSessionPresetLow;

    // Find a suitable AVCaptureDevice
    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    // Create a device input with the device and add it to the session.
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device 
                                                                        error:&error];
    if (!input) {
        // Handling the error appropriately.
    }
    [session addInput:input];

    // Create a VideoDataOutput and add it to the session
    AVCaptureVideoDataOutput *output = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
    output.alwaysDiscardsLateVideoFrames = YES;
    [session addOutput:output];

    // Configure your output.
    dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
    [output setSampleBufferDelegate:self queue:queue];
    dispatch_release(queue);

    // Specify the pixel format
    output.videoSettings = 
    [NSDictionary dictionaryWithObject:
     [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] 
                                forKey:(id)kCVPixelBufferPixelFormatTypeKey];


    // If you wish to cap the frame rate to a known value, such as 15 fps, set 
    // minFrameDuration.
    output.minFrameDuration = CMTimeMake(1, 1);

    // Start the session running to start the flow of data
    [session startRunning];

    // Assign session to an ivar.
    [self setSession:session];
}

// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    // Lock the base address of the pixel buffer
    CVPixelBufferLockBaseAddress(imageBuffer,0);

    // Get the number of bytes per row for the pixel buffer
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    // Get the pixel buffer width and height
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 

    // Create a device-dependent RGB color space
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 
    if (!colorSpace) 
     {
        NSLog(@"CGColorSpaceCreateDeviceRGB failure");
        return nil;
     }

    // Get the base address of the pixel buffer
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    // Get the data size for contiguous planes of the pixel buffer.
    size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer); 

    // Create a Quartz direct-access data provider that uses data we supply
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, baseAddress, bufferSize, 
                                                              NULL);
    // Create a bitmap image from data supplied by our data provider
    CGImageRef cgImage = 
    CGImageCreate(width,
                  height,
                  8,
                  32,
                  bytesPerRow,
                  colorSpace,
                  kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
                  provider,
                  NULL,
                  true,
                  kCGRenderingIntentDefault);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);

    // Create and return an image object representing the specified Quartz image
    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

    return image;
}


// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
       fromConnection:(AVCaptureConnection *)connection {
    // Create a UIImage from the sample buffer data
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
    [self.delegate cameraCaptureGotFrame:image];
}
Était-ce utile?

La solution

Cela pourrait être lié à enfilage Essayez:

[self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:) withObject:image waitUntilDone:NO];

Autres conseils

Cela ressemble à un problème de filetage. Vous ne pouvez pas mettre à jour votre point de vue dans un autre fil que dans le thread principal. Dans votre configuration, ce qui est bon, la fonction de délégué captureOutput: didOutputSampleBuffer: est appelé dans un thread secondaire. Donc, vous ne pouvez pas définir l'affichage d'image à partir de là. La réponse de l'art Gillespie est une façon de le résoudre si vous pouvez vous débarrasser de l'erreur mauvaise d'accès.

Une autre façon consiste à modifier le tampon d'échantillon dans captureOutput: didOutputSampleBuffer: et ont est montré en ajoutant un AVCaptureVideoPreviewLayer par exemple à votre session de capture. C'est certainement le moyen préféré si vous modifiez seulement une petite partie de l'image, comme surlignant quelque chose.

BTW. Votre mauvaise erreur d'accès pourrait se produire parce que vous ne conserviez l'image créée dans le fil secondaire et il sera libéré avant cameraCaptureGotFrame est appelé sur le thread principal

Mise à jour : Pour conserver correctement l'image, augmenter le nombre de référence dans captureOutput: didOutputSampleBuffer: (dans le thread secondaire) et décrémenter dans cameraCaptureGotFrame:. (dans le thread principal)

// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
        didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
        fromConnection:(AVCaptureConnection *)connection
{
    // Create a UIImage from the sample buffer data
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];

    // increment ref count
    [image retain];
    [self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:)
        withObject:image waitUntilDone:NO];

}

- (void) cameraCaptureGotFrame:(UIImage*)image
{
    // whatever this function does, e.g.:
    imageView.image = image;

    // decrement ref count
    [image release];
}

Si vous n'incrémente pas le compte de référence, l'image est libéré par le pool de libération automatique du deuxième fil avant la balise cameraCaptureGotFrame: est appelée dans le thread principal. Si vous ne décrémentez pas dans le thread principal, les images ne sont jamais libérés et vous manquez de mémoire en quelques secondes.

Vous faites un setNeedsDisplay sur le UIImageView après chaque nouvelle mise à jour de propriété d'image?

Edit:

Où et quand vous mettez à jour la propriété d'image d'arrière-plan dans votre vue de l'image?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top