功能并不难,之所以被难住是因为把问题想复杂了,记录一下。
自定义的相机拍照使用AVCaptureSession,获取指定区域图片使用图片裁切功能,重点在于不能直接使用AVCaptureSession获取到的图片,需要对图片进行放大处理然后再裁切。
自定义相机
@property (nonatomic, strong) AVCaptureSession *captureSession; // 会话
@property (nonatomic, strong) AVCaptureDevice * captureDevice; // 输入设备
@property (nonatomic, strong) AVCaptureDeviceInput * captureDeviceInput; // 输入源
@property (nonatomic, strong) AVCapturePhotoOutput *photoOutput; // 图像输出
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer; // 预览画面
@property (nonatomic, strong) AVCapturePhotoSettings *photoSettings; // 图像设置
@property (nonatomic, assign) AVCaptureDevicePosition position;
#pragma mark - 创建会话
-(AVCaptureSession *)captureSession{
if (!_captureSession) {
_captureSession = [[AVCaptureSession alloc] init];
_captureSession.sessionPreset = AVCaptureSessionPresetPhoto; // 画质
}
return _captureSession;
}
#pragma mark - 创建输入设备
-(AVCaptureDevice *)captureDevice{
if (!_captureDevice) {
// 设置默认前置摄像头
_captureDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
}
return _captureDevice;
}
#pragma mark - 创建输入源
-(AVCaptureDeviceInput *)captureDeviceInput{
if (!_captureDeviceInput) {
_captureDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.captureDevice error:nil];
}
return _captureDeviceInput;
}
#pragma mark - 创建图像输出
-(AVCapturePhotoOutput *)photoOutput{
if (!_photoOutput) {
_photoOutput = [[AVCapturePhotoOutput alloc] init];
}
return _photoOutput;
}
-(AVCaptureVideoPreviewLayer *)previewLayer{
if (!_previewLayer) {
_previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];
_previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
}
return _previewLayer;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self.layer insertSublayer:self.previewLayer atIndex:0];
// 5. 连接输入与会话
if ([self.captureSession canAddInput:self.captureDeviceInput]) {
[self.captureSession addInput:self.captureDeviceInput];
}
// 6. 连接输出与会话
if ([self.captureSession canAddOutput:self.photoOutput]) {
[self.captureSession addOutput:self.photoOutput];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive) name:UIApplicationWillEnterForegroundNotification object:nil];
}
return self;
}
#pragma mark - 开始运行
-(void)startRunning {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self.captureSession startRunning];
});
}
#pragma mark - 停止运行
-(void)stopRunning{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self.captureSession stopRunning];
});
}
#pragma mark - 拍照
-(void)shutterCamera {
self.photoSettings = [AVCapturePhotoSettings photoSettingsWithFormat:@{AVVideoCodecKey:AVVideoCodecTypeJPEG}];
[self.photoSettings setFlashMode:self.mode];
if (!self.previewLayer || !self.photoOutput || !self.photoSettings) {
return;
}
if (self.captureSession.isRunning == YES) {
[self.photoOutput capturePhotoWithSettings:self.photoSettings delegate:self];
}
}
加上相框和按钮的效果:
?拍照回调代理方法获取图片:
#pragma mark - 拍照回调代理
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo error:(NSError *)error{
if (!error) {
NSData *imageData = [photo fileDataRepresentation];
UIImage *image = [UIImage imageWithData:imageData];
//保存原始图片
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);
CGFloat width = self.previewLayer.frame.size.width;
CGFloat height = self.previewLayer.frame.size.height;
CGSize size = CGSizeMake(width, height);
//使用的SDWebImage提供的图片缩放处理方法
UIImage *scaledImage = [image sd_resizedImageWithSize:size scaleMode:(SDImageScaleModeAspectFill)];
//保存放大后的图片
UIImageWriteToSavedPhotosAlbum(scaledImage, nil, nil, nil);
//使用的SDWebImage提供的图片裁切处理方法
UIImage *targetImage = [scaledImage sd_croppedImageWithRect:CGRectMake(self.clipArea.origin.x, self.clipArea.origin.y, self.clipArea.size.width, self.clipArea.size.height)];
//图片翻转
UIImage *newImage = [UIImage imageWithCGImage:targetImage.CGImage scale:1.0f orientation:(UIImageOrientationLeft)];
//保存裁切后图片
UIImageWriteToSavedPhotosAlbum(newImage, nil, nil, nil);
if (image) {
[self saveImageWithImage:newImage];
}
}
}
需要注意的是,获取到的原始图片内容尺寸要比拍照屏幕尺寸大而且是按照宽度自适应的,而我们需要的虚线框中的内容是从全屏宽高的图片中裁切出来的:
?
?左边是获取到的原始图片,右边是我们通过摄像头看到的内容,右边是左边图片等比放大到高度与屏幕高度一样的效果,所以要对原始图片进行放大处理,然后再根据虚线框的位置frame裁切要想的内容。
至于为什么AVCaptureSession获取到的全屏图片内容要比我们看到的内容要多,还得继续查找原因。
|