2012-04-17 16 views
16

Tworzę aplikację, w której chciałbym uchwycić obraz z przedniej kamery, bez pokazywania jakiegokolwiek ekranu przechwytywania. Chcę zrobić zdjęcie całkowicie w kodzie bez interakcji użytkownika. Jak mam to zrobić dla kamery z widokiem z przodu?iOS: Przechwyć obraz z kamery skierowanej do przodu

+2

Masz na myśli po cichu przechwycić obraz bez wiedzy użytkownika anyt o tym porozmawiać? – rid

+2

Tak, wiem, że to brzmi źle, ale jest całkowicie nieszkodliwe. Aplikacja sprawi, że będą przyciągać zabawną twarz i chcę ją uchwycić, aby mogli zobaczyć, jak wyglądają głupio. – mtmurdock

+1

Twoja implementacja takiej funkcji może być nieszkodliwa, ale mogę myśleć o wielu innych przypadkach, w których byłaby ona inna niż (prawdopodobnie dlatego nie jest to możliwe). – inkedmn

Odpowiedz

3

Prawdopodobnie trzeba użyć AVFoundation, aby przechwycić strumień wideo/obraz bez jego wyświetlania. W przeciwieństwie do UIImagePickerController, nie działa "out-of-the-box". Spójrz na przykład na Apple AVCam, aby zacząć.

41

Jak zrobić zdjęcie za pomocą Przednia kamera AVFoundation:

Development Ostrzeżenia:

  • sprawdzić ustawienia aplikacji i orientację obrazu starannie
  • AVFoundation i związane z nią ramy są paskudne molochów i bardzo trudne do zrozumienia/wdrożenia. Zrobiłem mój kod jak chude jak to możliwe, ale proszę sprawdzić to doskonały poradnik dla lepszego wyjaśnienia (strona internetowa nie jest dostępna dłużej, łącza poprzez archive.org): http://www.benjaminloulier.com/posts/ios4-and-direct-access-to-the-camera

ViewController.h

// Frameworks 
#import <CoreVideo/CoreVideo.h> 
#import <CoreMedia/CoreMedia.h> 
#import <AVFoundation/AVFoundation.h> 
#import <UIKit/UIKit.h> 

@interface CameraViewController : UIViewController <AVCaptureVideoDataOutputSampleBufferDelegate> 

// Camera 
@property (weak, nonatomic) IBOutlet UIImageView* cameraImageView; 
@property (strong, nonatomic) AVCaptureDevice* device; 
@property (strong, nonatomic) AVCaptureSession* captureSession; 
@property (strong, nonatomic) AVCaptureVideoPreviewLayer* previewLayer; 
@property (strong, nonatomic) UIImage* cameraImage; 

@end 

ViewController.m

#import "CameraViewController.h" 

@implementation CameraViewController 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    [self setupCamera]; 
    [self setupTimer]; 
} 

- (void)setupCamera 
{  
    NSArray* devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo]; 
    for(AVCaptureDevice *device in devices) 
    { 
     if([device position] == AVCaptureDevicePositionFront) 
      self.device = device; 
    } 

    AVCaptureDeviceInput* input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil]; 
    AVCaptureVideoDataOutput* output = [[AVCaptureVideoDataOutput alloc] init]; 
    output.alwaysDiscardsLateVideoFrames = YES; 

    dispatch_queue_t queue; 
    queue = dispatch_queue_create("cameraQueue", NULL); 
    [output setSampleBufferDelegate:self queue:queue]; 

    NSString* key = (NSString *) kCVPixelBufferPixelFormatTypeKey; 
    NSNumber* value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; 
    NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; 
    [output setVideoSettings:videoSettings]; 

    self.captureSession = [[AVCaptureSession alloc] init]; 
    [self.captureSession addInput:input]; 
    [self.captureSession addOutput:output]; 
    [self.captureSession setSessionPreset:AVCaptureSessionPresetPhoto]; 

    self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession]; 
    self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; 

    // CHECK FOR YOUR APP 
    self.previewLayer.frame = CGRectMake(0, 0, self.view.frame.size.height, self.view.frame.size.width); 
    self.previewLayer.orientation = AVCaptureVideoOrientationLandscapeRight; 
    // CHECK FOR YOUR APP 

    [self.view.layer insertSublayer:self.previewLayer atIndex:0]; // Comment-out to hide preview layer 

    [self.captureSession startRunning]; 
} 

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection 
{ 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    CVPixelBufferLockBaseAddress(imageBuffer,0); 
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(imageBuffer); 
    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); 

    self.cameraImage = [UIImage imageWithCGImage:newImage scale:1.0f orientation:UIImageOrientationDownMirrored]; 

    CGImageRelease(newImage); 

    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 
} 

- (void)setupTimer 
{ 
    NSTimer* cameraTimer = [NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(snapshot) userInfo:nil repeats:YES]; 
} 

- (void)snapshot 
{ 
    NSLog(@"SNAPSHOT"); 
    self.cameraImageView.image = self.cameraImage; // Comment-out to hide snapshot 
} 

@end 

Podłącz się do UIViewController z UIImageView dla migawka i będzie działać! Migawki są wykonywane programowo w odstępach 2,0 sekundy bez wprowadzania danych przez użytkownika. Skomentuj wybrane linie, aby usunąć warstwę podglądu i informacje o obrazie.

Wszelkie dodatkowe pytania/komentarze, proszę dać mi znać!

+1

Bardzo ładne! Polecam, aby ta odpowiedź była akceptowana nad moją (zakładając, że działa). – Tim

+0

Czy to będzie przyjazny dla Apple App Store? – mtmurdock

+1

Nie jestem pewien, po raz pierwszy zastanowiłem się nad taką aplikacją. Sądzę, że musiałbyś zagłębić się w drobny druk i naprawdę sprawić, że użytkownik/Apple dowie się, że nie jest wykorzystywany do złowieszczych celów (jak wspomniano w innych postach tutaj). Twoja aplikacja brzmi zabawnie i nieszkodliwie, więc może będzie dobrze! –

0

Istnieje metoda o nazwie takePicture w dokumentach dla klasy UIImagePickerController. Jest napisane:

Użyj tej metody w połączeniu z niestandardowym widokiem nakładki, aby zainicjować programowe przechwytywanie nieruchomego obrazu. Umożliwia to wykonanie więcej niż jednego obrazu bez opuszczania interfejsu, ale wymaga ukrycia domyślnych elementów sterujących selektora obrazu.

2

konwertowane powyższy kod z objc do Swift 3, jeśli ktoś nadal szuka rozwiązania w 2017

import UIKit 
import AVFoundation 

class CameraViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate { 

@IBOutlet weak var cameraImageView: UIImageView! 

var device: AVCaptureDevice? 
var captureSession: AVCaptureSession? 
var previewLayer: AVCaptureVideoPreviewLayer? 
var cameraImage: UIImage? 

override func viewDidLoad() { 
    super.viewDidLoad() 

    setupCamera() 
    setupTimer() 
} 

func setupCamera() { 
    let discoverySession = AVCaptureDeviceDiscoverySession(deviceTypes: [.builtInWideAngleCamera], 
                  mediaType: AVMediaTypeVideo, 
                  position: .front) 
    device = discoverySession?.devices[0] 

    let input: AVCaptureDeviceInput 
    do { 
     input = try AVCaptureDeviceInput(device: device) 
    } catch { 
     return 
    } 

    let output = AVCaptureVideoDataOutput() 
    output.alwaysDiscardsLateVideoFrames = true 

    let queue = DispatchQueue(label: "cameraQueue") 
    output.setSampleBufferDelegate(self, queue: queue) 
    output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable: kCVPixelFormatType_32BGRA] 

    captureSession = AVCaptureSession() 
    captureSession?.addInput(input) 
    captureSession?.addOutput(output) 
    captureSession?.sessionPreset = AVCaptureSessionPresetPhoto 

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) 
    previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill 

    previewLayer?.frame = CGRect(x: 0.0, y: 0.0, width: view.frame.width, height: view.frame.height) 

    view.layer.insertSublayer(previewLayer!, at: 0) 

    captureSession?.startRunning() 
} 

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) { 
    let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 
    CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: .allZeros)) 
    let baseAddress = UnsafeMutableRawPointer(CVPixelBufferGetBaseAddress(imageBuffer!)) 
    let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!) 
    let width = CVPixelBufferGetWidth(imageBuffer!) 
    let height = CVPixelBufferGetHeight(imageBuffer!) 

    let colorSpace = CGColorSpaceCreateDeviceRGB() 
    let newContext = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: 
     CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) 

    let newImage = newContext!.makeImage() 
    cameraImage = UIImage(cgImage: newImage!) 

    CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: .allZeros)) 
} 

func setupTimer() { 
    _ = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(snapshot), userInfo: nil, repeats: true) 
} 

func snapshot() { 
    print("SNAPSHOT") 
    cameraImageView.image = cameraImage 
} 
} 

Również znalazłem krótszy rozwiązanie dla uzyskania obrazu z CMSampleBuffer:

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) { 
    let myPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 
    let myCIimage = CIImage(cvPixelBuffer: myPixelBuffer!) 
    let videoImage = UIImage(ciImage: myCIimage) 
    cameraImage = videoImage 
} 
+2

Dzięki, to było całkiem przydatne jako punkt wyjścia. –

+0

Nie ma problemu, cieszę się, że było użyteczne, nie wiem, czy nadal działa z Swift 4 bez pojawiania się ostrzeżeń. –

+0

Nie tylko ostrzeżenia, niektóre rzeczy muszą zostać zmienione, ale poprawka - najczęściej to robi. –

0

Konwersja powyżej kodu Swift 4

import UIKit 
import AVFoundation 

class CameraViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate { 

@IBOutlet weak var cameraImageView: UIImageView! 

var device: AVCaptureDevice? 
var captureSession: AVCaptureSession? 
var previewLayer: AVCaptureVideoPreviewLayer? 
var cameraImage: UIImage? 

override func viewDidLoad() { 
    super.viewDidLoad() 

    setupCamera() 
    setupTimer() 
} 

func setupCamera() { 
    let discoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], 
                  mediaType: AVMediaType.video, 
                  position: .front) 
    device = discoverySession.devices[0] 

    let input: AVCaptureDeviceInput 
    do { 
     input = try AVCaptureDeviceInput(device: device!) 
    } catch { 
     return 
    } 

    let output = AVCaptureVideoDataOutput() 
    output.alwaysDiscardsLateVideoFrames = true 

    let queue = DispatchQueue(label: "cameraQueue") 
    output.setSampleBufferDelegate(self, queue: queue) 
    output.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable as! String: kCVPixelFormatType_32BGRA] 

    captureSession = AVCaptureSession() 
    captureSession?.addInput(input) 
    captureSession?.addOutput(output) 
    captureSession?.sessionPreset = AVCaptureSession.Preset.photo 

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession!) 
    previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill 
    previewLayer?.frame = CGRect(x: 0.0, y: 0.0, width: view.frame.width, height: view.frame.height) 

    view.layer.insertSublayer(previewLayer!, at: 0) 

     captureSession?.startRunning() 
    } 

    func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) { 
     let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 
    CVPixelBufferLockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0)) 
     let baseAddress = UnsafeMutableRawPointer(CVPixelBufferGetBaseAddress(imageBuffer!)) 
     let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!) 
     let width = CVPixelBufferGetWidth(imageBuffer!) 
     let height = CVPixelBufferGetHeight(imageBuffer!) 

     let colorSpace = CGColorSpaceCreateDeviceRGB() 
     let newContext = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: 
     CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue) 

     let newImage = newContext!.makeImage() 
     cameraImage = UIImage(cgImage: newImage!) 

     CVPixelBufferUnlockBaseAddress(imageBuffer!, CVPixelBufferLockFlags(rawValue: 0)) 
    } 

    func setupTimer() { 
     _ = Timer.scheduledTimer(timeInterval: 2.0, target: self, selector: #selector(snapshot), userInfo: nil, repeats: true) 
    } 

    @objc func snapshot() { 
     print("SNAPSHOT") 
     cameraImageView.image = cameraImage 
    } 
} 
Powiązane problemy