iOS自定义拍照框拍照&裁剪(一)
卡片机时代
很重要的一点是,相机本身是没有方向概念的,它不理解拍摄的内容,只会以相机自己的坐标系去保存数据,下图展示了相机对“F”进行四个角度拍摄时返回的图片数据。


最初的卡片机时代,照片都会经由底片洗出来,那时不存在照片的方向问题,我们总可以把洗出来的照片通过简单的旋转来进行观看。比如这张照片墙中的照片,你能否说哪些照片是横着?哪些颠倒着?你甚至都无法判断每张照片相机是以何种角度拍摄的,因为每张都已经旋转至适合观看的角度。

数码时代
可是到了数码时代,不再需要底片,照片需要被存成一个图像文件。对于上面的拍摄角度,存储方式并没有变化,所有的场景仍然是以相机的坐标系来保存。于是这些照片仍像上面一样,原封不动的保存了下来。
虽然存储方式不变,和卡机机时代的实体相片不同的是,由于电子设备可不知道照片应该如何旋转,只能够以它存储于磁盘中的方向来展示。这便是为何照片传到电脑上之后,会出现横了,或者颠倒的情况。
方向传感器
为了克服这一情况,让照片可以真实的反应人们拍摄时看到的场景,现在很多相机中就加入了方向传感器,它能够记录下拍摄时相机的方向,并将这一信息保存在照片中。照片的存储方式还是没有任何改变,它仍然是以相机的坐标系来保存,只是当相机来浏览这些照片时,相机可以根据照片中的方向信息,结合此时相机的方向,对照片进行旋转,从而转到适合人们观看的角度。
iPhone上的情况
在iOS的设备中也包含了方向传感器,它但是它默认的照片方向并不是竖着拿手机时的情况,而是横向,即Home键在右侧,如下:

如此一来,如果竖着拿手机拍摄时,就相当于对手机顺时针旋转了90度,也即上面相机图片中的最后一幅

iPhone拍照方向问题处理
iPhone通过API获取到的是图片内容数据,不带方向信息,此时,我们需要手动的根据传感器方向将方向信息填充进去,并且根据方向信息对照片进行相应的旋转,得到人类视角正向的图片。代码如下:
//iOS11以上支持AVCapturePhotoOutput代理
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo
error:(NSError *)error {
if (error) {
NSLog(@"获取图片错误 --- %@",error.localizedDescription);
}
if (photo) {
UIImageOrientation orient = UIImageOrientationUp;
//后置摄像头
if (self.shootingOrientation == UIDeviceOrientationLandscapeRight){
orient = UIImageOrientationDown;
}else if(self.shootingOrientation == UIDeviceOrientationPortrait){
orient = UIImageOrientationRight;
}else if(self.shootingOrientation == UIDeviceOrientationLandscapeLeft){
orient = UIImageOrientationUp;
}else if(self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown){
orient = UIImageOrientationLeft;
}
//前置摄像头
AVCaptureDevice *currentDevice = [self.videoWriterManager.videoInput device];
AVCaptureDevicePosition currentPosition = [currentDevice position];
if (currentPosition == AVCaptureDevicePositionFront){
//前置摄像头
if (self.shootingOrientation == UIDeviceOrientationLandscapeRight){
orient = UIImageOrientationUp;
}else if(self.shootingOrientation == UIDeviceOrientationLandscapeLeft){
orient = UIImageOrientationDown;
}
}
UIImage *image = [UIImage imageWithCGImage:photo.CGImageRepresentation scale:1 orientation:orient];
//图片修正为人类视角看起来正向的图片
//[self fixOrientation:image];
}
}
//将原始图片数据调整为 人类视角正向的图片
- (UIImage *)fixOrientation:(UIImage *)image{
// No-op if the orientation is already correct
if (image.imageOrientation == UIImageOrientationUp) return image;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
CGFloat imageWidth = CGImageGetWidth(image.CGImage);
CGFloat imageHeight = CGImageGetHeight(image.CGImage);
//创建的位图宽高
CGFloat contextWidth = imageWidth < imageHeight ? imageWidth : imageHeight;
CGFloat contextHeight = imageWidth > imageHeight ? imageWidth : imageHeight;
switch (image.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:{
//创建横着显示的位图上下文,宽大于高
CGFloat temp = contextWidth;
contextWidth = contextHeight;
contextHeight = temp;
//旋转180度
transform = CGAffineTransformTranslate(transform, contextWidth, contextHeight);
transform = CGAffineTransformRotate(transform, M_PI);
}
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
//逆时针旋转90度
transform = CGAffineTransformTranslate(transform, contextWidth, 0);
transform = CGAffineTransformRotate(transform, M_PI_2);
break;
case UIImageOrientationRight:
case UIImageOrientationRightMirrored:
//顺时针90度
transform = CGAffineTransformTranslate(transform, 0, contextHeight);
transform = CGAffineTransformRotate(transform, -M_PI_2);
break;
default:
break;
}
switch (image.imageOrientation) {
case UIImageOrientationUpMirrored:
case UIImageOrientationDownMirrored:
//镜像图片Y轴翻转坐标系
transform = CGAffineTransformTranslate(transform, contextWidth, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
case UIImageOrientationLeftMirrored:
case UIImageOrientationRightMirrored:
//镜像图片Y轴翻转坐标系
transform = CGAffineTransformTranslate(transform, contextHeight, 0);
transform = CGAffineTransformScale(transform, -1, 1);
break;
default:
break;
}
//创建竖直显示的画布
CGContextRef ctx = CGBitmapContextCreate(NULL, contextWidth, contextHeight,
CGImageGetBitsPerComponent(image.CGImage), 0,
CGImageGetColorSpace(image.CGImage),
CGImageGetBitmapInfo(image.CGImage));
CGContextConcatCTM(ctx, transform);
CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
// And now we just create a new UIImage from the drawing context
CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
UIImage *img = [UIImage imageWithCGImage:cgimg];
CGContextRelease(ctx);
CGImageRelease(cgimg);
return img;
}
图片来源于(https://feihu.me/blog/2015/how-to-handle-image-orientation-on-iOS/)
参考 https://feihu.me/blog/2015/how-to-handle-image-orientation-on-iOS/
iOS自定义拍照框拍照&裁剪(一)的更多相关文章
- iOS Swift WisdomScanKit二维码扫码SDK,自定义全屏拍照SDK,系统相册图片浏览,编辑SDK
iOS Swift WisdomScanKit 是一款强大的集二维码扫码,自定义全屏拍照,系统相册图片编辑多选和系统相册图片浏览功能于一身的 Framework SDK [1]前言: 今天给大家 ...
- 如何用uniapp+vue开发自定义相机插件——拍照+录像功能
调用手机的相机功能并实现拍照和录像是很多APP与插件都必不可少的一个功能,今天智密科技就来分享一下如何基于uniapp + vue实现自定义相机界面,并且实现: 1: 自定义拍照 2: 自定义录像 3 ...
- Android Camera开发系列(下)——自定义Camera实现拍照查看图片等功能
Android Camera开发系列(下)--自定义Camera实现拍照查看图片等功能 Android Camera开发系列(上)--Camera的基本调用与实现拍照功能以及获取拍照图片加载大图片 上 ...
- ios手机竖屏拍照图片旋转90°问题解决方法
手机拍照会给图片添加一个Orientaion信息(即拍照方向),如下: 用ios手机拍照,系统会给图片加上一个方向的属性, ios相机默认的拍照方向是后摄Home键在右为正,前摄Home键在左为正. ...
- iOS开发UI篇 -- UISearchBar 属性、方法详解及应用(自定义搜索框样式)
很多APP都会涉及到搜索框,苹果也为我们提供了默认的搜索框UISearchBar.但实际项目中我们通常需要更改系统默认搜索框的样式.为了实现这一目标,我们需要先搞懂 UISearchBar 的属性及方 ...
- iOS自定义的UISwitch按钮
UISwitch开关控件 开关代替了点选框.开关是到目前为止用起来最简单的控件,不过仍然可以作一定程度的定制化. 一.创建 UISwitch* mySwitch = [[ UISwitchalloc] ...
- vue3系列:vue3.0自定义弹框组件V3Popup|vue3.x手机端弹框组件
基于Vue3.0开发的轻量级手机端弹框组件V3Popup. 之前有分享一个vue2.x移动端弹框组件,今天给大家带来的是Vue3实现自定义弹框组件. V3Popup 基于vue3.x实现的移动端弹出框 ...
- 【iOS自定义键盘及键盘切换】详解
[iOS自定义键盘]详解 实现效果展示: 一.实现的协议方法代码 #import <UIKit/UIKit.h> //创建自定义键盘协议 @protocol XFG_KeyBoardDel ...
- 在ASP.NET中引用自定义提示框
在html网页中自定义提示框 正文: 在一般的B/S架构中项目,与用户的交互信息是非常重要的.在一般的情况下,设计人员都在把用户信息呈现在html中,用div和span去弹出相关信息.对于一般的情况而 ...
随机推荐
- swiper tabs综合示例
html部分: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <m ...
- ubuntu软件工具推荐
时间:2019-04-11 记录:PangYuaner 标题:串口调试利器--Minicom配置及使用详解 地址:https://www.cnblogs.com/wonux/p/5897127.htm ...
- windows 10 + tensorflow-gpu 环境搭建
安装过程可基本按照ubuntu装法,参考https://www.cnblogs.com/xbit/p/9768238.html 其中: conda配置文件:C:\Users\Administrator ...
- Kickstart部署之NFS架构
原文转自:https://www.cnblogs.com/itzgr/p/10200615.html作者:木二 目录 一 准备 1.1 完整架构:Kickstart+DHCP+NFS+TFTP+PXE ...
- APMServ中Apache启动失败的原因
APMServ中Apache启动失败绝大多数的情况是因为APMServ得路径出错和80端口被占用,也有可能您使用的是WIN8系统,下面SJY根据不同情况告诉大家如何解决APMServ中Apache启动 ...
- 整型:int
整型:int 整型变量的定义和输出 注意://short<=int<=long<=longlong 代码示例一: #include<stdio.h> int ma ...
- 存储系统管理(二)——Linux系统的swap分区、磁盘加密、磁盘阵列
磁盘驱动器上的空间 , 用作当前未使用部分内存的溢出.这样 , 系统就能在主内存中留出空间用于储存当前正在处理的数据 , 并在系统面临主内存空间不足的风险时提供应急溢出. swap分区的建立: fdi ...
- shell循环语句while
格式1: while 条件 do 执行命令 done 格式2: while 条件;do 命令 done 例子: while [ 1 -eq 1 ];do echo "这一步需要先修改/dat ...
- MongoDB(14)- 查询 null 字段或缺少某个字段的文档
插入测试数据 db.inventory.insertMany([ { _id: 1, item: null }, { _id: 2 } ]) 后面的栗子都会用到这里的测试数据 查询匹配包含值为 nul ...
- Git 系列教程(1)- Git 简介
前言 因为工作中目前要大量使用 Git,虽然之前已经会用了,但没有系统的总结过,现在来重新总结 概念篇会直接搬网上的教程,比如:菜鸟.廖雪峰.老张.中文版Git,就不再花时间自己总结过概念了 Git ...