2014-02-21 2 views
11

AV Foundation을 사용하는 사진 앱이 있습니다. 화면의 위쪽 절반을 차지하는 AVCaptureVideoPreviewLayer를 사용하여 미리보기 레이어를 설정했습니다. 따라서 사용자가 사진을 찍으려고 할 때 볼 수있는 것은 화면 상단에 보이는 것입니다.캡쳐 된 이미지를 정확히 자른 모습 AVCaptureVideoPreviewLayer

위대한 작품이지만 사용자가 실제로 사진을 찍고 사진을 레이어의 콘텐츠로 설정하려고하면 이미지가 왜곡됩니다. 나는 연구를했고 내가 이미지를자를 필요가 있음을 깨달았다.

내가하고 싶은 일은 전체 캡처 된 이미지를 잘라내어 사용자가 원래 화면 상단에서 볼 수 있었던 것뿐입니다.

나는 이것을 수행 할 수 있었지만 수동 CGRect 값을 입력하여이 작업을 수행하고 있으며 여전히 완벽하지는 않습니다. 이것을하기위한 더 쉬운 방법이 있어야합니다.

이미지를 자르기에 대한 지난 2 일 동안 스택 오버플로에 대한 모든 게시물을 문자 그대로 전달했지만 아무 것도 작동하지 않았습니다.

캡쳐 된 이미지를 프로그램 방식으로 잘라내어 최종 이미지가 미리보기 레이어에서 원래 본 이미지와 정확하게 일치해야합니다. 여기

내 viewDidLoad에 구현 한 것입니다 :

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    AVCaptureSession *session =[[AVCaptureSession alloc]init]; 
    [session setSessionPreset:AVCaptureSessionPresetPhoto]; 

    AVCaptureDevice *inputDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; 

    NSError *error = [[NSError alloc]init]; 
    AVCaptureDeviceInput *deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:&error]; 

    if([session canAddInput:deviceInput]) 
     [session addInput:deviceInput]; 

    CALayer *rootLayer = [[self view]layer]; 
    [rootLayer setMasksToBounds:YES]; 

    _previewLayer = [[AVCaptureVideoPreviewLayer alloc]initWithSession:session]; 
    [_previewLayer setFrame:CGRectMake(0, 0, rootLayer.bounds.size.width, rootLayer.bounds.size.height/2)]; 
    [_previewLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill]; 

    [rootLayer insertSublayer:_previewLayer atIndex:0]; 

    _stillImageOutput = [[AVCaptureStillImageOutput alloc] init]; 
    [session addOutput:_stillImageOutput]; 

    [session startRunning]; 
    } 

그리고 여기에 사용자가 사진을 캡처 버튼을 누를 때 실행되는 코드입니다 :

-(IBAction)stillImageCapture { 
    AVCaptureConnection *videoConnection = nil; 
    for (AVCaptureConnection *connection in _stillImageOutput.connections){ 
     for (AVCaptureInputPort *port in [connection inputPorts]){ 
      if ([[port mediaType] isEqual:AVMediaTypeVideo]){ 
       videoConnection = connection; 
       break; 
      } 
     } 
     if (videoConnection) { 
      break; 
     } 
    } 

    NSLog(@"about to request a capture from: %@", _stillImageOutput); 

    [_stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) { 
     if(imageDataSampleBuffer) { 
      NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer]; 

      UIImage *image = [[UIImage alloc]initWithData:imageData]; 
      CALayer *subLayer = [CALayer layer]; 
      subLayer.frame = _previewLayer.frame; 
      image = [self rotate:image andOrientation:image.imageOrientation]; 

      //Below is the crop that is sort of working for me, but as you can see I am manually entering in values and just guessing and it still does not look perfect. 
      CGRect cropRect = CGRectMake(0, 650, 3000, 2000); 
      CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], cropRect); 

      subLayer.contents = (id)[UIImage imageWithCGImage:imageRef].CGImage; 
      subLayer.frame = _previewLayer.frame; 

      [_previewLayer addSublayer:subLayer]; 
     } 
    }]; 
} 

답변

18

AVCaptureVideoPreviewLayer

에서보세요을
-(CGRect)metadataOutputRectOfInterestForRect:(CGRect)layerRect 

이 방법을 사용하면 레이어의 보이는 CGRect 실제 카메라 출력으로

주의 사항 : 실제 카메라는 "윗면이 위로"장착되어 있지 않지만 시계 방향으로 90도 회전되었습니다. (그래서 당신이 당신의 아이폰을 잡으면 - 홈 버튼 오른쪽, 카메라가 실제로 위로 윗면이다).

위의 방법을 사용하면 CGRect를 변환해야 이미지를 화면에 정확히자를 수 있습니다.

예 :

CGRect visibleLayerFrame = THE ACTUAL VISIBLE AREA IN THE LAYER FRAME 
CGRect metaRect = [self.previewView.layer metadataOutputRectOfInterestForRect:visibleLayerFrame]; 


CGSize originalSize = [originalImage size]; 

if (UIInterfaceOrientationIsPortrait(_snapInterfaceOrientation)) { 
    // For portrait images, swap the size of the image, because 
    // here the output image is actually rotated relative to what you see on screen. 

    CGFloat temp = originalSize.width; 
    originalSize.width = originalSize.height; 
    originalSize.height = temp; 
} 


// metaRect is fractional, that's why we multiply here 

CGRect cropRect; 

cropRect.origin.x = metaRect.origin.x * originalSize.width; 
cropRect.origin.y = metaRect.origin.y * originalSize.height; 
cropRect.size.width = metaRect.size.width * originalSize.width; 
cropRect.size.height = metaRect.size.height * originalSize.height; 

cropRect = CGRectIntegral(cropRect); 

이 조금 혼란 스러울 수 있지만, 무엇을 만들어 내게 정말 이해하는 것은 이것이 수

장치 "홈 버튼을 오른쪽으로"보류 -> 당신은이 나타납니다 X - 축은 실제로 iPhone의 "높이"를 따라 위치하며, y 축은 iPhone의 "너비"를 따라 위치합니다. 그렇기 때문에 인물 사진의 경우 크기를 바꾸어야합니다.)

+0

가 user3117509 @이 대답은 포인트를받을 자격이, 그것을 받아주세요! – Daniel

+1

및 @Cabus 이미지를 회전하지 않습니다. 나는 다음과 같은 코드로 이것을했다. 'UIImage * croppedImage = [UIImage imageWithCGImage : imageRef]; UIGraphicsBeginImageContext (croppedImage.size); [[UIImage imageWithCGImage : [croppedImage CGImage] 스케일 : 1.0 방향 : UIImageOrientationRight] drawInRect : CGRectMake (0,0, croppedImage.size.height, croppedImage.size.width)]; UIImage * rotatedCroppedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();' – Daniel

6

@Cabus는 해결할 수있는 해결책을 가지고 있으며 자신의 대답에 대해지지해야합니다.그러나, 나는 다음과 스위프트 내 자신의 버전을했다 :

// The image returned in initialImageData will be larger than what 
// is shown in the AVCaptureVideoPreviewLayer, so we need to crop it. 
let image : UIImage = UIImage(data: initialImageData)! 

let originalSize : CGSize 
let visibleLayerFrame = self.previewView!.bounds // THE ACTUAL VISIBLE AREA IN THE LAYER FRAME 

// Calculate the fractional size that is shown in the preview 
let metaRect : CGRect = (self.videoPreviewLayer?.metadataOutputRectOfInterestForRect(visibleLayerFrame))! 
if (image.imageOrientation == UIImageOrientation.Left || image.imageOrientation == UIImageOrientation.Right) { 
    // For these images (which are portrait), swap the size of the 
    // image, because here the output image is actually rotated 
    // relative to what you see on screen. 
    originalSize = CGSize(width: image.size.height, height: image.size.width) 
} 
else { 
    originalSize = image.size 
} 

// metaRect is fractional, that's why we multiply here. 
let cropRect : CGRect = CGRectIntegral(
     CGRect(x: metaRect.origin.x * originalSize.width, 
       y: metaRect.origin.y * originalSize.height, 
       width: metaRect.size.width * originalSize.width, 
       height: metaRect.size.height * originalSize.height)) 

let finalImage : UIImage = 
    UIImage(CGImage: CGImageCreateWithImageInRect(image.CGImage, cropRect)!, 
     scale:1, 
     orientation: image.imageOrientation) 
0

여기 스위프트 3에 @Erik 알렌의 대답 :

let originalSize: CGSize 
let visibleLayerFrame = self?.photoView.bounds 

// Calculate the fractional size that is shown in the preview 
let metaRect = (self?.videoPreviewLayer?.metadataOutputRectOfInterest(for: visibleLayerFrame ?? CGRect.zero)) ?? CGRect.zero 

if (image.imageOrientation == UIImageOrientation.left || image.imageOrientation == UIImageOrientation.right) { 
    // For these images (which are portrait), swap the size of the 
    // image, because here the output image is actually rotated 
    // relative to what you see on screen. 
    originalSize = CGSize(width: image.size.height, height: image.size.width) 
} else { 
    originalSize = image.size 
} 

let cropRect: CGRect = CGRect(x: metaRect.origin.x * originalSize.width, y: metaRect.origin.y * originalSize.height, width: metaRect.size.width * originalSize.width, height: metaRect.size.height * originalSize.height).integral 

if let finalCgImage = image.cgImage?.cropping(to: cropRect) { 
    let finalImage = UIImage(cgImage: finalCgImage, scale: 1.0, orientation: image.imageOrientation) 

    // User your image... 
} 
관련 문제