0

現在、 AVCaptureAVCaptureVideoPreviewLayerなどから必要なものを取得するのに大きな問題が発生しています。

私は現在、この写真に示すように、カメラの小さなプレビューをビューの中央に配置するアプリを作成しています ( Iphone デバイスで使用できますが、iPad でも動作する場合はより良いでしょう)。

私の見解

そのために、カメラの比率を維持したいので、次の構成を使用しました。

rgbaImage = nil;

NSArray *possibleDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *device = [possibleDevices firstObject];
if (!device) return;

AVCaptureSession *session = [[AVCaptureSession alloc] init];
self.captureSession = session;
self.captureDevice = device;

NSError *error = nil;
AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];
if( !input )
{
    [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"NoCameraAuthorizationTitle", nil)
                                message:NSLocalizedString(@"NoCameraAuthorizationMsg", nil)
                               delegate:self
                      cancelButtonTitle:NSLocalizedString(@"OK", nil)
                      otherButtonTitles:nil] show];
    return;
}

[session beginConfiguration];
session.sessionPreset = AVCaptureSessionPresetPhoto;
[session addInput:input];

AVCaptureVideoDataOutput *dataOutput = [[AVCaptureVideoDataOutput alloc] init];
[dataOutput setAlwaysDiscardsLateVideoFrames:YES];
[dataOutput setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32BGRA)}];

[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
[session addOutput:dataOutput];

self.stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
[session addOutput:self.stillImageOutput];

connection = [dataOutput.connections firstObject];
[self setupCameraOrientation];

NSError *errorLock;
if ([device lockForConfiguration:&errorLock])
{
//        Frame rate
    device.activeVideoMinFrameDuration = CMTimeMake((int64_t)1, (int32_t)FPS);
    device.activeVideoMaxFrameDuration = CMTimeMake((int64_t)1, (int32_t)FPS);

    AVCaptureFocusMode focusMode = AVCaptureFocusModeContinuousAutoFocus;
    AVCaptureExposureMode exposureMode = AVCaptureExposureModeContinuousAutoExposure;

    CGPoint point = CGPointMake(0.5, 0.5);
    if ([device isAutoFocusRangeRestrictionSupported])
    {
        device.autoFocusRangeRestriction = AVCaptureAutoFocusRangeRestrictionNear;
    }
    if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:focusMode])
    {
        [device setFocusPointOfInterest:point];
        [device setFocusMode:focusMode];
    }
    if ([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:exposureMode])
    {
        [device setExposurePointOfInterest:point];
        [device setExposureMode:exposureMode];
    }
    if ([device isLowLightBoostSupported])
    {
        device.automaticallyEnablesLowLightBoostWhenAvailable = YES;
    }
    [device unlockForConfiguration];
}

if (device.isFlashAvailable)
{
    [device lockForConfiguration:nil];
    [device setFlashMode:AVCaptureFlashModeOff];
    [device unlockForConfiguration];

    if ([device isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus])
    {
        [device lockForConfiguration:nil];
        [device setFocusMode:AVCaptureFocusModeContinuousAutoFocus];
        [device unlockForConfiguration];
    }
}

previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:session];
previewLayer.frame = self.bounds;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.layer insertSublayer:previewLayer atIndex:0];

[session commitConfiguration];

ご覧のとおり、AVLayerVideoGravityResizeAspectFillプロパティを使用して、適切な比率を確保しています。

私は多くのことを試しましたが、実際には成功しなかったため、ここから問題が始まります。 私の目標は、ユーザーがプレビューレイヤーで見ることができるものと同等の画像を取得することです。ビデオ フレームは、プレビューで見ることができる画像よりも大きな画像を提供することを知っています。

私は3つの方法を試しました:

1)パーソナル コンピューティングの使用: ビデオ フレーム サイズと画面サイズ、レイヤー サイズと位置の両方を知っているので、比率を計算し、それを使用してビデオ フレーム内の同等の位置を計算しようとしました。ビデオ フレーム (sampleBuffer) はピクセル単位ですが、mainScreen 境界から取得する位置はアップル メジャーであり、ビデオ フレーム サイズが実際のデバイスの全画面サイズ。

--> これにより、実際に私の IPAD で非常に良い結果が得られました。高さと幅の両方は良好ですが、(x,y) の原点が元の位置から少しずれています... (詳細: 実際に 72 ピクセルを削除すると、良い出力が得られる位置)

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)avConnection
{
if (self.forceStop) return;
if (_isStopped || _isCapturing || !CMSampleBufferIsValid(sampleBuffer)) return;

CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
__block CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer];

CGRect rect = image.extent;

    CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width/* * [UIScreen mainScreen].scale*/;
CGFloat screenHeight = screenRect.size.height/* * [UIScreen mainScreen].scale*/;
NSLog(@"%f, %f ---",screenWidth, screenHeight);

float myRatio = ( rect.size.height / screenHeight );
float myRatioW = ( rect.size.width / screenWidth );
NSLog(@"Ratio w :%f h:%f ---",myRatioW, myRatio);


CGPoint p = [captureViewControler.view convertPoint:previewLayer.frame.origin toView:nil];
NSLog(@"-Av-> %f, %f --> %f, %f", p.x, p.y, self.bounds.size.height, self.bounds.size.width);
rect.origin = CGPointMake(p.x * myRatioW, p.y  * myRatio);

NSLog(@"%f, %f ----> %f %f", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
NSLog(@"%f",  previewLayer.frame.size.height * (  rect.size.height / screenHeight  ));
rect.size = CGSizeMake(rect.size.width, previewLayer.frame.size.height * myRatio);

image = [image imageByCroppingToRect:rect];
its = [ImageUtils cropImageToRect:uiImage(sampleBuffer) toRect:rect];
NSLog(@"--------------------------------------------");
[captureViewControler sendToPreview:its];
}

2) StillImage キャプチャの使用: この方法は、私が IPAD を使用している限り、実際に機能していました。しかし、実際の問題は、これらのクロップフレームを使用して画像ライブラリをフィードしていることと、メソッドcaptureStillImageAsynchronouslyFromConnectionが画像のシステムサウンドを呼び出していることです(別のサウンドを呼び出して回避するなどの「解決策」についてよく読んでいますが、機能していません)実際にはiPhone 6で発生するフリーズを解決していないため、この方法は不適切なようです。

AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connect in self.stillImageOutput.connections)
{
    for (AVCaptureInputPort *port in [connect inputPorts])
    {
        if ([[port mediaType] isEqual:AVMediaTypeVideo] )
        {
            videoConnection = connect;
            break;
        }
    }
    if (videoConnection) { break; }
}

[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection
                                                   completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error)
 {
     if (error)
     {
         NSLog(@"Take picture failed");
     }
     else
     {
         NSData *jpegData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
         UIImage *takenImage = [UIImage imageWithData:jpegData];

         CGRect outputRect = [previewLayer metadataOutputRectOfInterestForRect:previewLayer.bounds];
         NSLog(@"image cropped : %@", NSStringFromCGRect(outputRect));
         CGImageRef takenCGImage = takenImage.CGImage;
         size_t width = CGImageGetWidth(takenCGImage);
         size_t height = CGImageGetHeight(takenCGImage);
         NSLog(@"Size cropped : w: %zu h: %zu", width, height);
         CGRect cropRect = CGRectMake(outputRect.origin.x * width, outputRect.origin.y * height, outputRect.size.width * width, outputRect.size.height * height);
         NSLog(@"final cropped : %@", NSStringFromCGRect(cropRect));

         CGImageRef cropCGImage = CGImageCreateWithImageInRect(takenCGImage, cropRect);
         takenImage = [UIImage imageWithCGImage:cropCGImage scale:1 orientation:takenImage.imageOrientation];
         CGImageRelease(cropCGImage);

         its = [ImageUtils rotateUIImage:takenImage];
         image = [[CIImage alloc] initWithImage:its];
}

3)比率でmetadataOuputを使用する:これは実際にはまったく機能していませんが、stillImageプロセスでこれを使用すると最も役立つと思いました(metadataOutputRectOfInterestForRectの結果を使用して割合を取得し、それを比率と組み合わせます) . これを使用して、写真間の比率の違いを追加して、正しい出力を得たいと思いました。

CGRect rect = image.extent;
CGSize size = CGSizeMake(1936.0, 2592.0);

float rh = (size.height / rect.size.height);
float rw = (size.width / rect.size.width);

CGRect outputRect = [previewLayer metadataOutputRectOfInterestForRect:previewLayer.bounds];
NSLog(@"avant cropped : %@", NSStringFromCGRect(outputRect));
outputRect.origin.x = MIN(1.0, outputRect.origin.x * rw);
outputRect.origin.y = MIN(1.0, outputRect.origin.y * rh);
outputRect.size.width = MIN(1.0, outputRect.size.width * rw);
outputRect.size.height = MIN(1.0, outputRect.size.height * rh);
NSLog(@"final cropped : %@", NSStringFromCGRect(outputRect));

UIImage *takenImage = [[UIImage alloc] initWithCIImage:image];
NSLog(@"takenImage : %@", NSStringFromCGSize(takenImage.size));

CGImageRef takenCGImage = [[CIContext contextWithOptions:nil] createCGImage:image fromRect:[image extent]];
size_t width = CGImageGetWidth(takenCGImage);
size_t height = CGImageGetHeight(takenCGImage);
NSLog(@"Size cropped : w: %zu h: %zu", width, height);
CGRect cropRect = CGRectMake(outputRect.origin.x * width, outputRect.origin.y * height, outputRect.size.width * width, outputRect.size.height * height);
CGImageRef cropCGImage = CGImageCreateWithImageInRect(takenCGImage, cropRect);
its = [UIImage imageWithCGImage:cropCGImage scale:1 orientation:takenImage.imageOrientation];

誰かがこれで私を助けてくれることを願っています。どうもありがとう。

4

1 に答える 1

0

私は最終的にこのコードを使用して解決策を見つけました。私のエラーは、画像間の比率を使用しようとしたことであり、metadataOutputRectOfInterestForRectが新しい他の画像に対して変更する必要のないパーセンテージ値を返すことを考慮していませんでした。

-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)avConnection
{    
    CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
    __block CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer];

    CGRect outputRect = [previewLayer metadataOutputRectOfInterestForRect:previewLayer.bounds];
    outputRect.origin.y = outputRect.origin.x;
    outputRect.origin.x = 0;
    outputRect.size.height = outputRect.size.width;
    outputRect.size.width = 1;

    UIImage *takenImage = [[UIImage alloc] initWithCIImage:image];
    CGImageRef takenCGImage = [cicontext createCGImage:image fromRect:[image extent]];
    size_t width = CGImageGetWidth(takenCGImage);
    size_t height = CGImageGetHeight(takenCGImage);
    CGRect cropRect = CGRectMake(outputRect.origin.x * width, outputRect.origin.y * height, outputRect.size.width * width, outputRect.size.height * height);
    CGImageRef cropCGImage = CGImageCreateWithImageInRect(takenCGImage, cropRect);
    UIImage *its = [UIImage imageWithCGImage:cropCGImage scale:1 orientation:takenImage.imageOrientation];
}
于 2015-11-16T08:53:19.617 に答える