iOS 图片处理

1.图片横向显示

功能要求需要开启手机的前置摄像头拍摄图片,设置的前置摄像头分辨率是1280 * 720,但是发现显示图片是是1280 * 720,但是图片成90旋转。要想正常显示,需要对图片进行旋转处理。但是为什么会得到横向的图片?

原因

至于为什么图片是方向是反的呢,因为定义的captureOutput是AVCaptureVideoDataOutput类型,如果是AVCaptureStillImageOutput就不存在这个问题。在头文件AVCaptureSession.hAVCaptureVideoOrientation枚举类型定义视频方位的定义。而这个视频默认的方向是AVCaptureVideoOrientationLandscapeRight,所以得到的图片是横向。


typedef NS_ENUM(NSInteger, AVCaptureVideoOrientation) {

AVCaptureVideoOrientationPortrait          = 1,

AVCaptureVideoOrientationPortraitUpsideDown = 2,

AVCaptureVideoOrientationLandscapeRight    = 3,

AVCaptureVideoOrientationLandscapeLeft      = 4,

} NS_AVAILABLE(10_7, 4_0) __TVOS_PROHIBITED;

解决方法

方法一didOutputSampleBuffer回调函数中,对照片进行旋转处理。


Pixel_8888 bgColor = {0,0,0,0};

vImage_Buffer ibuff = {srcBuff,height,width,bytesPerRow};// 原始图片

vImage_Buffer ubuff = {outBuff,height,width,bytesPerRow};// 结果图片

vImage_Error err = vImageRotate90_ARGB8888(&ibuff, &ubuff, 1, bgColor, 0);

方法二 通过设置AVCaptureConnection从根源上解决图片方向问题。


AVCaptureConnection *videoConnection1 = [self connectionWithMediaType:AVMediaTypeVideo fromConnections:[_videoCapture1 connections]];

if ([videoConnection1 isVideoOrientationSupported]){

[videoConnection1 setVideoOrientation:AVCaptureVideoOrientationPortrait];

}

AVCaptureConnection对象有一个属性叫做videoOrientation这是一个AVCaptureVideoOrientation。可以通过这个设置这个改变视频的方向。

2.图片缩放

通过摄像头得到的图片需要放在指定大小,指定位置的区域。这就要对原始图片进行缩放处理。主要有3种情况:

CSImageDrawFrameScaleToFillWidth:在新的边框填充图片,确保不变形,自定义宽度,高度自适应。

CSImageDrawFrameScaleToFillHeight:在新的边框填充图片,确保不变形,自定义高度,宽度自适应。


- (UIImage *)reSizeImage:(NSData *)imgData withSize:(CGSize)size{

// 保证高度 宽度自定义

UIImage *image = [[UIImage alloc] initWithData:imgData];

UIGraphicsBeginImageContext(size);

[image drawInRect:CGRectMake(0, 0, size.width, size.height)];

UIImage *reSizeImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return reSizeImage;

}

图片按比例压缩,客户端的处理是高度自定义,宽度按原比例自适应。但是发现按比例压缩之后的图片较原图片,感觉模糊很多,之前以为是错觉发现完全不是,就是模糊了。这个模糊让人感觉很奇怪,于是review的代码。

感觉代码处理没有什么问题,为什么图片是变模糊呢?


UIKIT_EXTERN void    UIGraphicsBeginImageContext(CGSize size);

UIKIT_EXTERN void    UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) NS_AVAILABLE_IOS(4_0);

原因在于UIGraphicsBeginImageContext中默认scale的系数是为1.0,在iPhone4以上设备就会产生模糊。为什么这个scale系数的设置会对图片产生模糊的效果呢,因为随着设备的分辨的提升,之前scale = 1.0代表当前设备的分辨率是320 * 480(就是iPhone4设备之前的分辨率)而测试的机子是iPhone6它相应的scale = 2.0 (640 * 960)所以图片在上面显示就会出现模糊。因此我们选择一个可以设置scale的函数。为了不产生模糊应该使用UIGraphicsBeginImageContextWithOptions来替代UIGraphicsBeginImageContext

CSImageDrawFrameFillFull:就是不管宽高比例多少,都会对图片进行拉伸,不确保不变形,不确保不压缩,使图片能够充满整个目标区域。

前面两种情况其实就是按某一比例缩放图片,但是只能保证满足宽高的某一个条件,不能够填满全部,但是图片不会变形。第三种,确保图片能够填满整个区域,但是为了适应区域的大小,图片就有可能会被裁剪或者部分被填充。

3.图片剪裁

这里的图片剪裁,主要有矩形区域裁剪和指定路线的裁剪。

矩形区域 :矩形区域的裁剪很简单,给出所需要裁剪的区域坐标就可以了。


- (UIImage*)redrawImage:(UIImage*)image size:(CGSize)size
{
UIGraphicsBeginImageContextWithOptions(size, NO, [[UIScreen mainScreen] scale]);
[image drawInRect:ccr(0, 0, size.width, size.height)];
UIImage* ret = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return ret;
}

指定线路: 指定线路的区域裁剪比矩形裁剪会复杂一点,有点像PS中用磁性套索进行圈图,通过指定一系列的关键点来控制扣出的图片区域,这个需要一个path来绘制这样的线路。将所有的点添加到path中,然后通过CGPathGetBoundingBox获取剪裁区域的边框。其它跟矩形图片剪裁类似。

4.图片格式转换

在定义一个AVCaptureVideoDataOutput的时候是可以设置它的一个输出图片格式,通过属性变量videoSettings设置kCVPixelBufferPixelFormatTypeKey,这个key-value是必须要设置的。其中value的值是非常的多, 这里kCVPixelBufferPixelFormatTypeKey是指解码后图片的格式。怎样选择这个值,因为看文档还有很多其它的选项提供可以选择,但是并非全部都支持。这个值的选择跟机器硬件解码有很大的关系,选择更有效的格式,处理速度更快。现在基本推荐使用的格式都是kCVPixelFormatType_32BGRA,只能说这个值对当前硬件解码是最优最高效的。图片的格式转化大部分用到OpenCV,OpenCV提供了一些接口函数,比如将图片转化成Mat结构(UIImageToMa())

UIImage - > Mat


static void UIImageToMat(const UIImage* image, cv::Mat& m)

{

CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);

CGFloat cols = image.size.width;

CGFloat rows = image.size.height;

m.create(rows, cols, CV_8UC4); // 8 bits per component, 4 channels

CGContextRef contextRef = CGBitmapContextCreate(m.data, m.cols, m.rows, 8,

m.step[0], colorSpace, kCGImageAlphaNoneSkipLast | kCGBitmapByteOrderDefault);

CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);

CGContextRelease(contextRef);

}

Mat - > UIImage


- (UIImage*)getImageFromCVMat:(cv::Mat)cvMat {

NSData* data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];

CGColorSpaceRef colorSpace;

if (cvMat.elemSize() == 1) {

colorSpace = CGColorSpaceCreateDeviceGray();

} else {

colorSpace = CGColorSpaceCreateDeviceRGB();

}

CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

// Creating CGImage from cv::Mat

CGImageRef imageRef = CGImageCreate(

cvMat.cols,  // width

cvMat.rows,  // height

8,  // bits per component

8 * cvMat.elemSize(),  // bits per pixel

cvMat.step[0],  // bytesPerRow

colorSpace,  // colorspace

kCGImageAlphaNone | kCGBitmapByteOrderDefault,  // bitmap info

provider,  // CGDataProviderRef

NULL,  // decode

false,  // should interpolate

kCGRenderingIntentDefault  // intent

);

UIImage* finalImage = [UIImage imageWithCGImage:imageRef];

CGImageRelease(imageRef);

CGDataProviderRelease(provider);

CGColorSpaceRelease(colorSpace);

return finalImage;

}

这是图片格式转化,还有转换图片的色彩空间cv::cvtColor(image, image, CV_BGRA2RGB)文档还有很多种选择输出,找到你需要的即可。



2016.8.11更新

用iPhone前置摄像头拍摄出来的图片跟摄像头看上去是镜像的,找了几种方法,但发现好像不是每个方法都适用,发现最简单的一种方法解决我的问题。


/*

typedef NS_ENUM(NSInteger, UIImageOrientation) {

UIImageOrientationUp,            // default orientation

UIImageOrientationDown,          // 180 deg rotation

UIImageOrientationLeft,          // 90 deg CCW

UIImageOrientationRight,        // 90 deg CW

UIImageOrientationUpMirrored,    // as above but image mirrored along other axis. horizontal flip

UIImageOrientationDownMirrored,  // horizontal flip

UIImageOrientationLeftMirrored,  // vertical flip

UIImageOrientationRightMirrored, // vertical flip

};

*/

UIImage *iamge = [[UIImage alloc] initWithData:imgData];

UIImage *flippedImage = [UIImage imageWithCGImage:imagData.CGImage scale:1.0 orientation:UIImageOrientationUpMirrored];

还有一种更简便的方法,设置AVCaptureConnection参数videoMirrored这个参数。

    [self.captureOutput connectionWithMediaType:AVMediaTypeVideo].videoMirrored = NO;

你可能感兴趣的:(iOS 图片处理)