• 卡片机时代

很重要的一点是,相机本身是没有方向概念的,它不理解拍摄的内容,只会以相机自己的坐标系去保存数据,下图展示了相机对“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. Sentinel限流、降级配置详解

    安装Sentinel 下载sentinel-dashboard-1.8.2.jar 安装有jdk环境,8080端口未被占用 在jar包所在目录打开cmd,输入命令启动:java -jar sentin ...

  2. 4种Golang并发操作中常见的死锁情形

    摘要:什么是死锁,在Go的协程里面死锁通常就是永久阻塞了,你拿着我的东西,要我先给你然后再给我,我拿着你的东西又让你先给我,不然就不给你.我俩都这么想,这事就解决不了了. 本文分享自华为云社区< ...

  3. Jmeter的初体验--安装

    准备工作 安装JMeter前需要安装配置好Java 一.安装 1.直接在官网下载安装即可,下载地址:http://jmeter.apache.org/download_jmeter.cgi,(Wind ...

  4. 论如何在服务器上部署一个自己的web前端项目

    就在前两天,有新人通过邮箱问到笔者,如何部署自己的web前端项目?笔者在此详细介绍. 一.购买云服务器 配置用户名密码.安全组 二.下载Xshell于Xftp工具 用于登录服务器和文件上传 三.在li ...

  5. 基于源码编译的lnmp架构实现论坛的搭建及memcache的应用

    系统环境: RHEL6 x86-64 selinux and iptables disabled LNMP代表的就是:Linux系统下Nginx+MySQL+PHP这种网站服务器架构 Linux是一类 ...

  6. docker&flask快速构建服务接口(二)

    系列其他内容 docker快速创建轻量级的可移植的容器✓ docker&flask快速构建服务接口✓ docker&uwsgi高性能WSGI服务器生产部署必备 docker&g ...

  7. js-监听网络状态

    <script> // 监听网络状态 window.addEventListener("online", function(){ alert("网络连接了&q ...

  8. apache php RabbitMQ配置方式

    确定自己的php版本号和位数,去pecl.php.net下载版本相应的rabbitmq扩展包, 以php5版本为例,在http://pecl.php.net/package/amqp里面选择php5对 ...

  9. JDK源码阅读:Object类阅读笔记

    Object 1. @HotSpotIntrinsicCandidate @HotSpotIntrinsicCandidate public final native Class<?> g ...

  10. Django学习day12随堂笔记

    每日测验 """ 1.什么是cookie和session,你能描述一下它们的由来和工作机制吗(切勿糊弄,敷衍了事) 2.django中如何操作cookie和session ...