本文對之前做過的相機模塊做個小結,包括自定義相機拍照界面,照片處理及保存等,感興趣的朋友可以做個參考
如果你要執(zhí)行以下操作,你應該使用該系統(tǒng)API來構建自定義相機:
構建自定義的相機用戶界面,將拍照或視頻錄制集成到應用中
為用戶提供對照片和視頻捕獲更直接的控制,例如焦點,曝光等增強選項。
與系統(tǒng)相機 UI 產生不同的結果,例如 RAW 格式的照片,深度圖或需要自定義視頻元數據
從采集設備 (Capture device) 實時獲取視頻像素或音頻數據。
準備工作
1.添加plist配置相機權限
2.判斷有無權限
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
如果未申請過權限,則進行權限獲取
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_sync(dispatch_get_main_queue(), ^{
if (granted) {
// 申請權限成功
} else {
// 申請權限失敗
}
});
}];
- 如果默認橫屏,需要根據屏幕方向進行旋轉
自定義相機配置信息
需要創(chuàng)建以下屬性
//捕獲設備,通常是前置攝像頭,后置攝像頭,麥克風(音頻輸入)
@property(nonatomic) AVCaptureDevice *device;
//session會話 由它把輸入輸出結合在一起,并開始啟動捕獲設備(攝像頭)
@property(nonatomic) AVCaptureSession *session;
//AVCaptureDeviceInput 代表輸入設備,使用AVCaptureDevice 來初始化
@property(nonatomic) AVCaptureDeviceInput *input;
//照片輸出流
@property (nonatomic) AVCapturePhotoOutput *imageOutPut;
//圖像預覽層,實時顯示捕獲的圖像
@property(nonatomic) AVCaptureVideoPreviewLayer *previewLayer;
- 初始化session會話,用來結合輸入輸出
self.session = [[AVCaptureSession alloc] init];
if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
[self.session setSessionPreset:AVCaptureSessionPreset1280x720]; //拿到的圖像的大小可以自行設定
}
- 獲取視頻輸入設備(攝像頭)
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- 創(chuàng)建視頻輸入源 并添加到會話
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
if ([self.session canAddInput:self.input]) {
[self.session addInput:self.input];
}
- 創(chuàng)建視頻輸出源 并添加到會話
self.imageOutPut = [[AVCapturePhotoOutput alloc]init];
if ([self.session canAddOutput:self.imageOutPut]) {
[self.session addOutput:self.imageOutPut];
}
AVCaptureConnection *imageConnection = [self.imageOutPut connectionWithMediaType:AVMediaTypeVideo];
// 設置 imageConnection 控制相機拍攝圖片的角度方向
if (imageConnection.supportsVideoOrientation) {
imageConnection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
}
- 初始化預覽層,session會話負責驅動input輸入源進行信息的采集,layer預覽層負責把采集到的圖像進行渲染顯示
self.previewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.session];
self.previewLayer.frame = CGRectMake(0, 0, width,height);
self.previewLayer.connection.videoOrientation = AVCaptureVideoOrientationLandscapeRight; // 圖層展示拍攝角度方向
self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer addSublayer:self.previewLayer];
- 開始采集界面
[self.session startRunning];
拍照屬性設置 (可選項)
需要先加鎖在進行設置
if ([self.device lockForConfiguration:nil]) { // 修改設備的屬性,先加鎖
……設置內容……
//解鎖
[self.device unlockForConfiguration];
}
- 聚焦
// 添加聚焦手勢
- (void)addTap {
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(focusGesture:)];
[self.view addGestureRecognizer:tap];
}
- (void)focusGesture:(UITapGestureRecognizer*)gesture{
CGPoint point = [gesture locationInView:gesture.view];
CGSize size = self.view.bounds.size;
// focusPoint 函數后面Point取值范圍是取景框左上角(0,0)到取景框右下角(1,1)之間,有時按這個來但位置不對,按實際適配
CGPoint focusPoint = CGPointMake( point.x /size.width , point.y/size.height );
if ([self.device lockForConfiguration:nil]) {
[self.session beginConfiguration];
/*****必須先設定聚焦位置,在設定聚焦方式******/
//聚焦點的位置
if ([self.device isFocusPointOfInterestSupported]) {
[self.device setFocusPointOfInterest:focusPoint];
}
// 聚焦模式
if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
[self.device setFocusMode:AVCaptureFocusModeAutoFocus];
}else{
NSLog(@"聚焦模式修改失敗");
}
//曝光點的位置
if ([self.device isExposurePointOfInterestSupported]) {
[self.device setExposurePointOfInterest:focusPoint];
}
//曝光模式
if ([self.device isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
[self.device setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
} else {
NSLog(@"曝光模式修改失敗");
}
[self.device unlockForConfiguration];
[self.session commitConfiguration];
}
}
- 白平衡
//自動白平衡
if ([self.device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
[self.device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
}
- 切換攝像頭
//獲取攝像頭的數量
NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
//攝像頭小于等于1的時候直接返回
if (cameraCount <= 1) return;
AVCaptureDevice *newCamera = nil;
AVCaptureDeviceInput *newInput = nil;
//獲取當前相機的方向(前還是后)
AVCaptureDevicePosition position = [[self.input device] position];
if (position == AVCaptureDevicePositionFront) {
//獲取后置攝像頭
newCamera = [self cameraWithPosition:AVCaptureDevicePositionBack];
}else{
//獲取前置攝像頭
newCamera = [self cameraWithPosition:AVCaptureDevicePositionFront];
}
[self.previewLayer addAnimation:animation forKey:nil];
//輸入流
newInput = [AVCaptureDeviceInput deviceInputWithDevice:newCamera error:nil];
if (newInput != nil) {
[self.session beginConfiguration];
//先移除原來的input
[self.session removeInput:self.input];
if ([self.session canAddInput:newInput]) {
[self.session addInput:newInput];
self.input = newInput;
} else {
//如果不能加現在的input,就加原來的input
[self.session addInput:self.input];
}
[self.session commitConfiguration];
}
- 閃光燈配置
if ([[self.imageOutPut supportedFlashModes] containsObject:@(AVCaptureFlashModeOn)]) {
self.imageOutPut.photoSettingsForSceneMonitoring.flashMode = AVCaptureFlashModeOn;
}
- 手電筒配置
//獲取當前相機的方向(前還是后) 前置攝像頭不允許開啟手電筒
AVCaptureDevicePosition position = [[self.input device] position];
if (position == AVCaptureDevicePositionFront) {
NSLog(@"前置攝像時無法開啟手電筒");
return;
}
AVCaptureDevice *device = self.device;
if ([device hasTorch]) { // 判斷是否有閃光燈
// 請求獨占訪問硬件設備
[device lockForConfiguration:nil];
if (sender.selected == NO) {
sender.selected = YES;
[device setTorchMode:AVCaptureTorchModeOn]; // 手電筒開
} else {
sender.selected = NO;
[device setTorchMode:AVCaptureTorchModeOff]; // 手電筒關
}
// 請求解除獨占訪問硬件設備
[device unlockForConfiguration];
}
拍照獲取圖片
- 拍照操作
AVCaptureConnection * videoConnection = [self.imageOutPut connectionWithMediaType:AVMediaTypeVideo];
if (videoConnection == nil) {
return;
}
AVCapturePhotoSettings *set = [AVCapturePhotoSettings photoSettings];
[self.imageOutPut capturePhotoWithSettings:set delegate:self];
- 通過代理方法captureOutput:didFinishProcessingPhoto:error:獲取到圖片
-(void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error {
if (!error) {
NSData *imageData = [photo fileDataRepresentation];
UIImage *image = [UIImage imageWithData:imageData];
//處理圖片
[self handleOriginalImage:image];
}
}
獲取到圖片需要進行處理,前置攝像頭需處理左右成像問題
例:
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, aImage.size.height, aImage.size.width);
transform = CGAffineTransformRotate(transform, M_PI);
transform = CGAffineTransformTranslate(transform, aImage.size.height,0);
transform = CGAffineTransformScale(transform, -1, 1);
CGContextRef ctx =CGBitmapContextCreate(NULL, aImage.size.height, aImage.size.width,CGImageGetBitsPerComponent(aImage.CGImage),0,CGImageGetColorSpace(aImage.CGImage),CGImageGetBitmapInfo(aImage.CGImage));
CGContextConcatCTM(ctx, transform);
CGContextDrawImage(ctx,CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
CGImageRef cgimg =CGBitmapContextCreateImage(ctx);
img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
- 保存到相冊
//存儲
[[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromImage:fixImage];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (error) {
NSLog(@"%@",@"保存失敗");
} else {
NSLog(@"%@",@"保存成功");
}
}];
其他
可以給圖片加水印,定位信息,拍攝時間信息等 給圖像添加信息需要使用文件存儲,UIImage無法存儲資料
github項目地址: https://github.com/percival888/CustomCameraDemo
參考:
https://cloud.tencent.com/developer/article/1532831
https://blog.csdn.net/u010029439/article/details/113201454