Я пытаюсь отобразить UIImage, в режиме реального времени прибывающий из камеры, и кажется, что мой UIImageView не отображает изображение правильно. Это - метод который a AVCaptureVideoDataOutputSampleBufferDelegate
должен реализовать
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
// Create a UIImage from the sample buffer data
UIImage *theImage = [self imageFromSampleBuffer:sampleBuffer];
// NSLog(@"Got an image! %f %f", theImage.size.width, theImage.size.height);
// NSLog(@"The image view is %@", imageView);
// UIImage *theImage = [[UIImage alloc] initWithData:[NSData
// dataWithContentsOfURL:[NSURL
// URLWithString:@"http://farm4.static.flickr.com/3092/2915896504_a88b69c9de.jpg"]]];
[self.session stopRunning];
[imageView setImage: theImage];
}
Избавиться от легких проблем:
setImage:theImage
к imageView изображение загружается правильно (и второй вызов к отчетам NSLog ненулевой объект).imageFromSampleBuffer:
прекрасен, начиная с отчетов NSLog размер, чтобы быть 360x480, который является размером, который я ожидал.Код, который я использую, является недавно отправленным AVFoundation
отрывок от Apple, доступной здесь.
В частности, это - код, который я использую, который настраивает AVCaptureSession
объект и друзья (которых я понимаю очень мало), и создают объект UIImage из Базовых Видео буферов (это imageFromSampleBuffer
метод).
Наконец, я могу заставить приложение отказывать, если я пытаюсь отправить drawInRect:
к простому UIView разделяют на подклассы с UIImage
возвращенный imageFromSamplerBuffer
, в то время как это не отказывает, если я использую UIImage
от URL как выше. Вот отслеживание стека от отладчика в катастрофическом отказе (я получаю сигнал EXC_BAD_ACCESS):
#0 0x34a977ee in decode_swap ()
#1 0x34a8f80e in decode_data ()
#2 0x34a8f674 in img_decode_read ()
#3 0x34a8a76e in img_interpolate_read ()
#4 0x34a63b46 in img_data_lock ()
#5 0x34a62302 in CGSImageDataLock ()
#6 0x351ab812 in ripc_AcquireImage ()
#7 0x351a8f28 in ripc_DrawImage ()
#8 0x34a620f6 in CGContextDelegateDrawImage ()
#9 0x34a61fb4 in CGContextDrawImage ()
#10 0x321fd0d0 in -[UIImage drawInRect:blendMode:alpha:] ()
#11 0x321fcc38 in -[UIImage drawInRect:] ()
Править: Вот еще некоторая информация о UIImage том, чтобы быть возвращенным тем битом кода.
Используя метод, описанный здесь, я могу добраться до пикселей и распечатать их, и они смотрят хорошо на первый взгляд (каждое значение в альфа-канале 255, например). Однако существует что-то немного прочь с буферными размерами. Изображение, которое я получаю из Flickr, с которой URL 375x500, и [pixelData length]
дает мне 750000 = 375*500*4
, который является математическим ожиданием. Однако пиксельные данные изображения, возвращенного из imageFromSampleBuffer:
имеет размер 691208 = 360*480*4 + 8
, таким образом, существует 8 дополнительных байтов в пиксельных данных. CVPixelBufferGetDataSize
самостоятельно возвращает это off-8 значение. Я думал на мгновение, что это могло произойти из-за выделения буферов в выровненных положениях в памяти, но 691200 является кратным 256, так, чтобы не объяснял это также. Это несоответствие размера является единственной разницей, которую я могу сказать между двумя UIImages, и это могло доставлять неприятности. Однако, нет никакой причины, выделяющей дополнительную память для буфера, должен вызвать нарушение EXC_BAD_ACCESS.
Большое спасибо за любую справку, и сообщают мне, нужно ли Вам больше информации.
У меня была та же проблема ... но я нашел этот старый пост, и его метод создания CGImageRef работает!
http://forum.unity3d.com/viewtopic.php?p=300819
Вот рабочий пример:
app has a member UIImage theImage;
// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
//... just an example of how to get an image out of this ...
CGImageRef cgImage = [self imageFromSampleBuffer:sampleBuffer];
theImage.image = [UIImage imageWithCGImage: cgImage ];
CGImageRelease( cgImage );
}
- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer // Create a CGImageRef from sample buffer data
{
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CVPixelBufferLockBaseAddress(imageBuffer,0); // Lock the image buffer
uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); // Get information of the image
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
CGImageRef newImage = CGBitmapContextCreateImage(newContext);
CGContextRelease(newContext);
CGColorSpaceRelease(colorSpace);
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
/* CVBufferRelease(imageBuffer); */ // do not call this!
return newImage;
}
Бен Лулиер написал хорошую статью о том, как это сделать.
Я использую его пример приложения в качестве отправной точки, и оно у меня работает. Наряду с заменой функции imageFromSamplerBuffer
чем-то, что создает CGImageRef с CGBitmapContextCreate, он использует основную очередь отправки (через dispatch_get_main_queue ()
) при настройке делегата выходного буфера выборки. Это не лучшее решение, потому что ему нужна последовательная очередь, и, насколько я понимаю, основная очередь не является последовательной очередью. Так что, хотя у вас нет гарантии, что кадры будут в правильном порядке, это пока работает для меня :)