cover_image

iPhone相机拍照真实图片区域与预览视图的关系以及处理

李亚、孙洪琳 之家技术 2022年06月08日 10:00

关注“之家技术”,获取更多技术干货

图片

总篇第144篇 2022年第19篇

场景

我们在开发VIN码识别控件的时候,遇到了预览框预览到的图片和真实截取出来的图片在不同iPhone上获得的图片不一致。不同尺寸屏幕的手机上发生了不同类型的位置偏移。
从下图可见,左侧的图片为实际相机预览看到的,右侧为拍照后得到的,2张图的内容有区别。

图片

图片













原因探查

经过调研发现,iOS相机拍照时的预览内容与实际得到的真实图片不是100%一致的。当我们在相机拍摄时,自定义了输出照片的长宽像素值(比如1280×720),iPhone的取景框默认会撑满不留黑边的展示出照片,会进行"等比放大"处理,这样就会有部分图像是超出屏幕可视区的。这就好比单反相机的一个参数:查看区域和拍摄区域的视野率。

我们这里的VIN相机默认设置了1280×720的分辨率进行拍摄,经过测试,iPhone拍摄后获取到的真实图片与图像预览区视图存在一定的换算关系,如下图:

图片


手机纵向拍摄举例,这里分为2种情况:

1.情况A:当实际照片的宽高比大于屏幕拍摄预览的宽高比时,系统会将实际照片和拍摄预览区以高度对齐,然后在水平方向上居中展示,左右部分超出。

a.手机屏幕看到的图像部分为半透明蓝色区域;

b.图片左右侧半透明绿色区域示意为,实际照片超出屏幕的部分;

c.淡粉色透明区域为实际VIN码取景框与屏幕边界的间距;

2.情况B:当实际图片的宽高比小于拍摄区的宽高比时,系统会将实际图片和拍摄区以宽度对齐,然后在垂直方向上居中展示,上下部分超出。

a.手机屏幕看到的为半透明粉色区域;

b.图片上下半透明绿色区域为实际照片超出屏幕的部分;

c.VIN码取景框左右淡粉色透明区域为取景框屏幕边界的间距;


由此可以看出,当我们需要在拍照获取到的照片上,准确计算出取景框看到的图像部分进行裁切时,根据情况A或B,在计算了屏幕边界距离的基础上,需要再加上屏幕上的非可见区域部分的距离,才可以得到一个完美的所见即所得的VIN照片。

代码处理

如上图所示,由于不同手机屏幕宽高比的不同,我们的预览区的宽高比也是动态的。所以针对出现的图片宽高比和预览区宽高比不同的情况,我们要做出相应的位置补偿。


计算裁切区域在屏幕大小照片上的位置补偿


根据图片和预览区的宽高比,分别计算在A、B两种情况下水平和垂直方向上的补偿,然后重新计算裁切区在屏幕大小照片上的大小和位置信息。

- (CGRect)fixCropRect:(CGRect)cropRect realImageSize:(CGSize)realImageSize previewRect:(CGRect)previewRect {             CGRect fixedCropRect = cropRect;    // 图片宽高比    CGFloat imageRatio = realImageSize.width / realImageSize.height;    // 预览框的宽高比    CGFloat previewRatio = previewRect.size.width / previewRect.size.height; 
if (imageRatio > previewRatio) { // 图片的宽高比 > 拍摄区的宽高比 /* 当出现情况A的时候: 图片宽高比: 720/1280 = 0.5625 拍摄预览区宽高比: 390/753 = 0.517928287 图片的宽高比 > 拍摄区的宽高比时, 系统自动以【高】对齐, 实际图片比可视区宽, 横向居中【左右】超出屏幕 */ // 计算实际图片换算到屏幕的宽度: 423.5625 CGFloat imageScreenWidth = imageRatio * previewRect.size.height; // 计算出 x 需要补偿的距离, 并且补偿 x fixedCropRect.origin.x = (imageScreenWidth - cropRect.size.width)/2; } else if (imageRatio < previewRatio) { // 图片的宽高比 < 拍摄区的宽高比 /* 当出现情况B的时候: 图片宽高比: 720/1280 = 0.5625 拍摄预览区宽高比: 375/603 = 0.621890547 图片的宽高比 < 拍摄区的宽高比时, 系统自动以【宽】对齐, 实际图片比可视区高, 纵向居中【上下】超出屏幕 */ // 计算实际图片换算到屏幕的高度: 667 CGFloat imageScreenHeight = previewRect.size.width/imageRatio; // 计算出 y 需要补偿的距离 CGFloat diff_y = ( imageScreenHeight - previewRect.size.height )/2; // 对 y 进行补偿 fixedCropRect.origin.y += diff_y; } else { // 图片的宽高比 == 拍摄区的宽高比, 1:1 的情况, 不用做裁切框定位补偿 }
return fixedCropRect;}


计算裁切区域在实际图片上的大小和位置信息


根据图片和预览区的宽高比,分别计算在A、B两种情况下水平和垂直方向上的缩放补偿,然后重新计算裁切区在实际图片上的大小和位置信息。

// 计算裁切区域,算换的到之际图片大小后的位置- (CGRect)subimageCropRectOnImage:(UIImage *)orgimage cropRect:(CGRect)cropRect previewRect:(CGRect)previewRect {             CGSize imageSize = orgimage.size;    CGFloat PREVIEW_WIDTH = previewRect.size.width;    CGFloat PREVIEW_HEIGHT = previewRect.size.height;
CGRect rectOnImage;
CGFloat imageRatio = imageSize.width / imageSize.height; CGFloat previewRatio = PREVIEW_WIDTH / PREVIEW_HEIGHT;
if (imageRatio > previewRatio) { // 图片的宽高比 > 拍摄区的宽高比 /* 当出现情况A的时候: 图片宽高比: 720/1280 = 0.5625 拍摄预览区宽高比: 390/753 = 0.517928287 图片的宽高比 > 拍摄区的宽高比时, 系统自动以【高】对齐, 实际图片比可视区宽, 横向居中【左右】超出屏幕 */ // 以高为基准, 计算图片放大比例 CGFloat scale = PREVIEW_HEIGHT / imageSize.height; CGFloat x = cropRect.origin.x / scale; CGFloat y = cropRect.origin.y / scale; CGFloat w = cropRect.size.width / scale; CGFloat h = cropRect.size.height / scale; rectOnImage = CGRectMake(x, y, w, h); } else { // 图片的宽高比 <= 拍摄区的宽高比 /* 当出现情况B的时候: 图片宽高比: 720/1280 = 0.5625 拍摄预览区宽高比: 375/603 = 0.621890547 图片的宽高比 < 拍摄区的宽高比时, 系统自动以【宽】对齐, 实际图片比可视区高, 纵向居中【上下】超出屏幕 */ // 以宽为基准, 计算图片放大比例 CGFloat scale = PREVIEW_WIDTH / imageSize.width; CGFloat x = cropRect.origin.x / scale; CGFloat y = cropRect.origin.y / scale; CGFloat w = cropRect.size.width / scale; CGFloat h = cropRect.size.height / scale; rectOnImage = CGRectMake(x, y, w, h); }
return rectOnImage;}


代码整合优化


整合2部分代码,一步输出正确裁切框在图片上的位置,获得正确的裁切照片。

- (CGRect)subimageCropRectOnImage:(UIImage *)orgimage cropRect:(CGRect)cropRect previewRect:(CGRect)previewRect {              CGSize imageSize = orgimage.size;    CGFloat PREVIEW_WIDTH = previewRect.size.width;    CGFloat PREVIEW_HEIGHT = previewRect.size.height;
CGRect rectOnImage;
CGFloat imageRatio = imageSize.width / imageSize.height; CGFloat previewRatio = PREVIEW_WIDTH / PREVIEW_HEIGHT;
if (imageRatio > previewRatio) { // 图片的宽高比 > 拍摄区的宽高比 /* 当出现情况A的时候: 图片宽高比: 720/1280 = 0.5625 拍摄预览区宽高比: 390/753 = 0.517928287 图片的宽高比 > 拍摄区的宽高比时, 系统自动以【高】对齐, 实际图片比可视区宽, 横向居中【左右】超出屏幕 */ // ↓↓↓ 补偿计算 ↓↓↓ // 求实际图片换算到屏幕的宽度: 423.5625 CGFloat imageScreenWidth = imageRatio * previewRect.size.height; // 计算出 x 需要补偿的距离, 并且补偿 x cropRect.origin.x = (imageScreenWidth - cropRect.size.width)/2; // ↓↓↓ 比例放大计算 ↓↓↓ // 以高为基准, 计算图片放大比例 CGFloat scale = PREVIEW_HEIGHT / imageSize.height; CGFloat x = cropRect.origin.x / scale; CGFloat y = cropRect.origin.y / scale; CGFloat w = cropRect.size.width / scale; CGFloat h = cropRect.size.height / scale; rectOnImage = CGRectMake(x, y, w, h); } else { // 图片的宽高比 <= 拍摄区的宽高比 /* 当出现情况B的时候: 图片宽高比: 720/1280 = 0.5625 拍摄预览区宽高比: 375/603 = 0.621890547 图片的宽高比 < 拍摄区的宽高比时, 系统自动以【宽】对齐, 实际图片比可视区高, 纵向居中【上下】超出屏幕 */ // ↓↓↓ 补偿计算 ↓↓↓ // 计算实际图片换算到屏幕的高度: 667 CGFloat imageScreenHeight = previewRect.size.width/imageRatio; // 计算出 y 需要补偿的距离 CGFloat diff_y = ( imageScreenHeight - previewRect.size.height )/2; // 对 y 进行补偿 cropRect.origin.y += diff_y; // ↓↓↓ 比例放大计算 ↓↓↓ // 以宽为基准, 计算图片放大比例 CGFloat scale = PREVIEW_WIDTH / imageSize.width; CGFloat x = cropRect.origin.x / scale; CGFloat y = cropRect.origin.y / scale; CGFloat w = cropRect.size.width / scale; CGFloat h = cropRect.size.height / scale; rectOnImage = CGRectMake(x, y, w, h); }
return rectOnImage;}
// 输出最终图片- (UIImage *)ak_subImageOfImage:(UIImage *)orgimage cropRectOnImage:(CGRect)rectOnImage { UIImage *submage = nil; CGImageRef imageRotatedRef = orgimage.CGImage; CGImageRef subImageRef = CGImageCreateWithImageInRect(imageRotatedRef, rectOnImage); UIGraphicsBeginImageContext(rectOnImage.size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextDrawImage(context, rectOnImage, subImageRef); submage = [UIImage imageWithCGImage:subImageRef]; UIGraphicsEndImageContext(); CGImageRelease(subImageRef); return submage;}


作者简介

图片

二手车事业部-技术部

孙洪琳

图片

二手车事业部-技术部

李亚

加入汽车之家多年,一直从事研发工作,现负责二手车之家以及其他汽车之家二手车业务的相关研发工作。

图片

阅读更多:

▼ 关注「之家技术」,获取更多技术干货 

图片

继续滑动看下一个
之家技术
向上滑动看下一个