• 卡片机时代

很重要的一点是,相机本身是没有方向概念的,它不理解拍摄的内容,只会以相机自己的坐标系去保存数据,下图展示了相机对“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自定义拍照框拍照&裁剪(一)的更多相关文章

  1. iOS Swift WisdomScanKit二维码扫码SDK,自定义全屏拍照SDK,系统相册图片浏览,编辑SDK

    iOS Swift WisdomScanKit 是一款强大的集二维码扫码,自定义全屏拍照,系统相册图片编辑多选和系统相册图片浏览功能于一身的 Framework SDK [1]前言:    今天给大家 ...

  2. 如何用uniapp+vue开发自定义相机插件——拍照+录像功能

    调用手机的相机功能并实现拍照和录像是很多APP与插件都必不可少的一个功能,今天智密科技就来分享一下如何基于uniapp + vue实现自定义相机界面,并且实现: 1: 自定义拍照 2: 自定义录像 3 ...

  3. Android Camera开发系列(下)——自定义Camera实现拍照查看图片等功能

    Android Camera开发系列(下)--自定义Camera实现拍照查看图片等功能 Android Camera开发系列(上)--Camera的基本调用与实现拍照功能以及获取拍照图片加载大图片 上 ...

  4. ios手机竖屏拍照图片旋转90°问题解决方法

    手机拍照会给图片添加一个Orientaion信息(即拍照方向),如下: 用ios手机拍照,系统会给图片加上一个方向的属性, ios相机默认的拍照方向是后摄Home键在右为正,前摄Home键在左为正. ...

  5. iOS开发UI篇 -- UISearchBar 属性、方法详解及应用(自定义搜索框样式)

    很多APP都会涉及到搜索框,苹果也为我们提供了默认的搜索框UISearchBar.但实际项目中我们通常需要更改系统默认搜索框的样式.为了实现这一目标,我们需要先搞懂 UISearchBar 的属性及方 ...

  6. iOS自定义的UISwitch按钮

    UISwitch开关控件 开关代替了点选框.开关是到目前为止用起来最简单的控件,不过仍然可以作一定程度的定制化. 一.创建 UISwitch* mySwitch = [[ UISwitchalloc] ...

  7. vue3系列:vue3.0自定义弹框组件V3Popup|vue3.x手机端弹框组件

    基于Vue3.0开发的轻量级手机端弹框组件V3Popup. 之前有分享一个vue2.x移动端弹框组件,今天给大家带来的是Vue3实现自定义弹框组件. V3Popup 基于vue3.x实现的移动端弹出框 ...

  8. 【iOS自定义键盘及键盘切换】详解

    [iOS自定义键盘]详解 实现效果展示: 一.实现的协议方法代码 #import <UIKit/UIKit.h> //创建自定义键盘协议 @protocol XFG_KeyBoardDel ...

  9. 在ASP.NET中引用自定义提示框

    在html网页中自定义提示框 正文: 在一般的B/S架构中项目,与用户的交互信息是非常重要的.在一般的情况下,设计人员都在把用户信息呈现在html中,用div和span去弹出相关信息.对于一般的情况而 ...

随机推荐

  1. RabbitMq四种模式介绍和授权

    rabbitmqctl change_password admin admin123 修改admin密码 界面管理和授权操作 1新增用户 rabbitmqctl add_user admin amin ...

  2. Linux核心知识

    电脑:辅助人脑的工具 现在的人们几乎无时无刻都会碰电脑!不管是桌上型电脑(桌机).笔记型电脑(笔电).平板电脑.智慧型手机等等,这些东西都算是电脑.虽然接触的这么多,但是,你了解电脑里面的元件有什么吗 ...

  3. Hopper Disassembler系列之Sublime Text 3 爆破

    https://www.52pojie.cn/thread-793069-1-1.html 当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9. 当参数 ...

  4. C# Equals方法和==有什么区别

    开发工具:VS2019 一.关于这两个比较,需要从值类型和引用类型两方面来说 (A)先说值类型 上图: 因为在对值类型进行比较时候,不管 .Equals() 方法还是 == 方法,都是对值类型变量(图 ...

  5. 史上最详细的信号使用说明(已被收藏和N次)

    Unix环境高级编程(第三版) 第10章 信号 文章目录 1. 引言 2. 信号的概念 2.1 信号操作之忽略信号 2.2 信号操作之捕捉信号 2.3 信号操作之执行系统默认操作 2.4 常见的信号 ...

  6. token过期时间

    token有两个有效期,在config/jwt.php下面 # ./config/jwt.php'ttl' => env('JWT_TTL', 600), ##600min=10h 'refre ...

  7. vue项目 'node-sass'问题

    Cannot find module 'node-sass' 解决办法: 运行命令:cnpm install node-sass@latest 即可解决,( 网络差的同学可以选择重新下载no-modu ...

  8. Selenium系列5-XPath路径表达式

    Xpath介绍 XPath 使用路径表达式在 XML 文档中进行导航 XPath 使用路径表达式来选取 XML 文档中的节点或者节点集.这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似. ...

  9. CodeForce-792C Divide by Three(数学)

    Divide by Three CodeForces - 792C 有一个正整数 n 写在黑板上.它有不超过 105 位. 你需要通过删除一些位使得他变成一个美丽的数,并且需要删除尽量少的位数.删除的 ...

  10. 洛谷P1309——瑞士轮(归并排序)

    https://www.luogu.org/problem/show?pid=1309#sub 题目背景 在双人对决的竞技性比赛,如乒乓球.羽毛球.国际象棋中,最常见的赛制是淘汰赛和循环赛.前者的特点 ...