Avcapturesession فقط الحصول على إطار واحد لجهاز iPhone 3GS
-
25-09-2019 - |
سؤال
لديّ قطعة من التعليمات البرمجية تقوم بإعداد جلسة التقاط من الكاميرا لمعالجة الإطارات باستخدام OpenCV ثم ضبط خاصية Image الخاصة بـ UiimageView مع uiimage تم إنشاؤها من الإطار. عند بدء تشغيل التطبيق ، تكون صورة عرض الصورة لا شيء ولا تظهر أي إطارات حتى أدفع وحدة تحكم عرض أخرى على المكدس ثم قم بتشغيلها. ثم تبقى الصورة كما هي حتى أفعل ذلك مرة أخرى. تُظهر عبارات NSLOG أن رد الاتصال يسمى بمعدل الإطار الصحيح تقريبًا. أي أفكار لماذا لا تظهر؟ لقد قمت بتقليل الإطار على طول الطريق إلى 2 إطارات في الثانية. أليس معالجة بسرعة كافية؟
هذا هو الرمز:
- (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];
}
المحلول
يمكن أن يكون هذا مرتبطًا بالخيوط - تجرب:
[self.delegate performSelectorOnMainThread:@selector(cameraCaptureGotFrame:) withObject:image waitUntilDone:NO];
نصائح أخرى
هذا يبدو وكأنه مشكلة في الخيوط. لا يمكنك تحديث وجهات نظرك في أي مؤشر ترابط آخر عن الخيط الرئيسي. في الإعداد الخاص بك ، وهو أمر جيد ، وظيفة المندوب CaptureOutput: didoutputsamplebuffer: يسمى في موضوع ثانوي. لذلك لا يمكنك تعيين عرض الصورة من هناك. إجابة Art Gillespie هي إحدى طرق حلها إذا تمكنت من التخلص من خطأ الوصول السيئ.
طريقة أخرى هي تعديل العينة المخزن المؤقت في CaptureOutput: didoutputsamplebuffer: ويظهر بإضافة أ AvcaptureVideOpReviewLayer مثيل لجلسة الالتقاط. هذه بالتأكيد الطريقة المفضلة إذا قمت فقط بتعديل جزء صغير من الصورة مثل تسليط الضوء على شيء ما.
راجع للشغل: قد ينشأ خطأ الوصول السيئ لأنك لا تحتفظ بالصورة التي تم إنشاؤها في الخيط الثانوي وبالتالي سيتم تحريرها من قبل cameracapturegotframe يتم استدعاؤه على الموضوع الرئيسي.
تحديث: للاحتفاظ بالصورة بشكل صحيح ، وزيادة عدد المرجع في CaptureOutput: didoutputsamplebuffer: (في الخيط الثانوي) وخفضه في cameracapturegotframe: (في الموضوع الرئيسي).
// 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];
}
إذا لم تقم بزيادة عدد المرجع ، يتم تحرير الصورة بواسطة تجمع الإصدار التلقائي للخيط الثاني قبل cameracapturegotframe: يسمى في الموضوع الرئيسي. إذا لم تقلله في الخيط الرئيسي ، فلن يتم تحرير الصور أبدًا وتنفد الذاكرة في غضون بضع ثوان.
هل تقوم بعمل setNeedsDisplay على UiimageView بعد كل تحديث خاصية صورة جديدة؟
تعديل:
أين ومتى تقوم بتحديث خاصية صورة الخلفية في طريقة عرض الصور الخاصة بك؟