本文对之前做过的相机模块做个小结,包括自定义相机拍照界面,照片处理及保存等,感兴趣的朋友可以做个参考
如果你要执行以下操作,你应该使用该系统API来构建自定义相机:
构建自定义的相机用户界面,将拍照或视频录制集成到应用中
为用户提供对照片和视频捕获更直接的控制,例如焦点,曝光等增强选项。
与系统相机 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 {
// 申请权限失败
}
});
}];
- 如果默认横屏,需要根据屏幕方向进行旋转
自定义相机配置信息
需要创建以下属性
//捕获设备,通常是前置摄像头,后置摄像头,麦克风(音频输入)
@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];
- 创建视频输入源 并添加到会话
self.input = [[AVCaptureDeviceInput alloc]initWithDevice:self.device error:nil];
if ([self.session canAddInput:self.input]) {
[self.session addInput:self.input];
}
- 创建视频输出源 并添加到会话
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