本文對(duì)之前做過(guò)的相機(jī)模塊做個(gè)小結(jié),包括自定義相機(jī)拍照界面,照片處理及保存等,感興趣的朋友可以做個(gè)參考
如果你要執(zhí)行以下操作,你應(yīng)該使用該系統(tǒng)API來(lái)構(gòu)建自定義相機(jī):
構(gòu)建自定義的相機(jī)用戶界面,將拍照或視頻錄制集成到應(yīng)用中
為用戶提供對(duì)照片和視頻捕獲更直接的控制,例如焦點(diǎn),曝光等增強(qiáng)選項(xiàng)。
與系統(tǒng)相機(jī) UI 產(chǎn)生不同的結(jié)果,例如 RAW 格式的照片,深度圖或需要自定義視頻元數(shù)據(jù)
從采集設(shè)備 (Capture device) 實(shí)時(shí)獲取視頻像素或音頻數(shù)據(jù)。
準(zhǔn)備工作
1.添加plist配置相機(jī)權(quán)限
2.判斷有無(wú)權(quán)限
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
如果未申請(qǐng)過(guò)權(quán)限,則進(jìn)行權(quán)限獲取
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
dispatch_sync(dispatch_get_main_queue(), ^{
if (granted) {
// 申請(qǐng)權(quán)限成功
} else {
// 申請(qǐng)權(quán)限失敗
}
});
}];
- 如果默認(rèn)橫屏,需要根據(jù)屏幕方向進(jìn)行旋轉(zhuǎn)
自定義相機(jī)配置信息
需要?jiǎng)?chuàng)建以下屬性
//捕獲設(shè)備,通常是前置攝像頭,后置攝像頭,麥克風(fēng)(音頻輸入)
@property(nonatomic) AVCaptureDevice *device;
//session會(huì)話 由它把輸入輸出結(jié)合在一起,并開(kāi)始啟動(dòng)捕獲設(shè)備(攝像頭)
@property(nonatomic) AVCaptureSession *session;
//AVCaptureDeviceInput 代表輸入設(shè)備,使用AVCaptureDevice 來(lái)初始化
@property(nonatomic) AVCaptureDeviceInput *input;
//照片輸出流
@property (nonatomic) AVCapturePhotoOutput *imageOutPut;
//圖像預(yù)覽層,實(shí)時(shí)顯示捕獲的圖像
@property(nonatomic) AVCaptureVideoPreviewLayer *previewLayer;
- 初始化session會(huì)話,用來(lái)結(jié)合輸入輸出
self.session = [[AVCaptureSession alloc] init];
if ([self.session canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
[self.session setSessionPreset:AVCaptureSessionPreset1280x720]; //拿到的圖像的大小可以自行設(shè)定
}
- 獲取視頻輸入設(shè)備(攝像頭)
self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
- 創(chuàng)建視頻輸入源 并添加到會(huì)話
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
if ([self.session canAddInput:self.input]) {
[self.session addInput:self.input];
}
- 創(chuàng)建視頻輸出源 并添加到會(huì)話
self.imageOutPut = [[AVCapturePhotoOutput alloc]init];
if ([self.session canAddOutput:self.imageOutPut]) {
[self.session addOutput:self.imageOutPut];
}
AVCaptureConnection *imageConnection = [self.imageOutPut connectionWithMediaType:AVMediaTypeVideo];
// 設(shè)置 imageConnection 控制相機(jī)拍攝圖片的角度方向
if (imageConnection.supportsVideoOrientation) {
imageConnection.videoOrientation = AVCaptureVideoOrientationLandscapeRight;
}
- 初始化預(yù)覽層,session會(huì)話負(fù)責(zé)驅(qū)動(dòng)input輸入源進(jìn)行信息的采集,layer預(yù)覽層負(fù)責(zé)把采集到的圖像進(jìn)行渲染顯示
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];
- 開(kāi)始采集界面
[self.session startRunning];
拍照屬性設(shè)置 (可選項(xiàng))
需要先加鎖在進(jìn)行設(shè)置
if ([self.device lockForConfiguration:nil]) { // 修改設(shè)備的屬性,先加鎖
……設(shè)置內(nèi)容……
//解鎖
[self.device unlockForConfiguration];
}
- 聚焦
// 添加聚焦手勢(shì)
- (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 函數(shù)后面Point取值范圍是取景框左上角(0,0)到取景框右下角(1,1)之間,有時(shí)按這個(gè)來(lái)但位置不對(duì),按實(shí)際適配
CGPoint focusPoint = CGPointMake( point.x /size.width , point.y/size.height );
if ([self.device lockForConfiguration:nil]) {
[self.session beginConfiguration];
/*****必須先設(shè)定聚焦位置,在設(shè)定聚焦方式******/
//聚焦點(diǎn)的位置
if ([self.device isFocusPointOfInterestSupported]) {
[self.device setFocusPointOfInterest:focusPoint];
}
// 聚焦模式
if ([self.device isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
[self.device setFocusMode:AVCaptureFocusModeAutoFocus];
}else{
NSLog(@"聚焦模式修改失敗");
}
//曝光點(diǎn)的位置
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];
}
}
- 白平衡
//自動(dòng)白平衡
if ([self.device isWhiteBalanceModeSupported:AVCaptureWhiteBalanceModeAutoWhiteBalance]) {
[self.device setWhiteBalanceMode:AVCaptureWhiteBalanceModeAutoWhiteBalance];
}
- 切換攝像頭
//獲取攝像頭的數(shù)量
NSUInteger cameraCount = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo] count];
//攝像頭小于等于1的時(shí)候直接返回
if (cameraCount <= 1) return;
AVCaptureDevice *newCamera = nil;
AVCaptureDeviceInput *newInput = nil;
//獲取當(dāng)前相機(jī)的方向(前還是后)
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];
//先移除原來(lái)的input
[self.session removeInput:self.input];
if ([self.session canAddInput:newInput]) {
[self.session addInput:newInput];
self.input = newInput;
} else {
//如果不能加現(xiàn)在的input,就加原來(lái)的input
[self.session addInput:self.input];
}
[self.session commitConfiguration];
}
- 閃光燈配置
if ([[self.imageOutPut supportedFlashModes] containsObject:@(AVCaptureFlashModeOn)]) {
self.imageOutPut.photoSettingsForSceneMonitoring.flashMode = AVCaptureFlashModeOn;
}
- 手電筒配置
//獲取當(dāng)前相機(jī)的方向(前還是后) 前置攝像頭不允許開(kāi)啟手電筒
AVCaptureDevicePosition position = [[self.input device] position];
if (position == AVCaptureDevicePositionFront) {
NSLog(@"前置攝像時(shí)無(wú)法開(kāi)啟手電筒");
return;
}
AVCaptureDevice *device = self.device;
if ([device hasTorch]) { // 判斷是否有閃光燈
// 請(qǐng)求獨(dú)占訪問(wèn)硬件設(shè)備
[device lockForConfiguration:nil];
if (sender.selected == NO) {
sender.selected = YES;
[device setTorchMode:AVCaptureTorchModeOn]; // 手電筒開(kāi)
} else {
sender.selected = NO;
[device setTorchMode:AVCaptureTorchModeOff]; // 手電筒關(guān)
}
// 請(qǐng)求解除獨(dú)占訪問(wèn)硬件設(shè)備
[device unlockForConfiguration];
}
拍照獲取圖片
- 拍照操作
AVCaptureConnection * videoConnection = [self.imageOutPut connectionWithMediaType:AVMediaTypeVideo];
if (videoConnection == nil) {
return;
}
AVCapturePhotoSettings *set = [AVCapturePhotoSettings photoSettings];
[self.imageOutPut capturePhotoWithSettings:set delegate:self];
- 通過(guò)代理方法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];
}
}
獲取到圖片需要進(jìn)行處理,前置攝像頭需處理左右成像問(wèn)題
例:
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);
- 保存到相冊(cè)
//存儲(chǔ)
[[PHPhotoLibrary sharedPhotoLibrary]performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromImage:fixImage];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
if (error) {
NSLog(@"%@",@"保存失敗");
} else {
NSLog(@"%@",@"保存成功");
}
}];
其他
可以給圖片加水印,定位信息,拍攝時(shí)間信息等 給圖像添加信息需要使用文件存儲(chǔ),UIImage無(wú)法存儲(chǔ)資料
github項(xiàng)目地址: https://github.com/percival888/CustomCameraDemo
參考:
https://cloud.tencent.com/developer/article/1532831
https://blog.csdn.net/u010029439/article/details/113201454