内容简介:在iOS里面进行实时美颜,我使用的是具体使用方法如下:
实时美颜
在iOS里面进行实时美颜,我使用的是 GPUImageBeautifyFilter
, 具体原理可以看 作者的文章 。
具体使用方法如下:
1.在项目的 Podfile
里面引入 GPUImage
target 'TestBeauty' do pod 'GPUImage' end
2.将 GPUImageBeautyFilter.h
和 GPUImageBeautyFilter.m
添加到项目中
3.编写相关代码
在 ViewController.m
里面实现如下代码:
#import "ViewController.h" #import <GPUImage/GPUImage.h> #import "GPUImageBeautifyFilter.h" @interface ViewController () @property (nonatomic, strong) GPUImageVideoCamera *videoCamera; @property (nonatomic, strong) GPUImageView *filterView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // 设置使用前置摄像头进行美颜 self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPreset640x480 cameraPosition:AVCaptureDevicePositionFront]; self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait; self.videoCamera.horizontallyMirrorFrontFacingCamera = YES; // 镜像 self.filterView = [[GPUImageView alloc] initWithFrame:self.view.frame]; self.filterView.backgroundColor = [UIColor redColor]; self.filterView.center = self.view.center; [self.view addSubview:self.filterView]; [self.videoCamera addTarget:self.filterView]; [self.videoCamera startCameraCapture]; }
运行项目之后,就可以成功实时美颜。
4.获取美颜之前的原图
如果想要获取美颜之前的原图,可以实现 GPUImageVideoCameraDelegate
协议里面的方法
需要进行如下修改:
-
@interface ViewController () <GPUImageVideoCameraDelegate>
-
在
ViewController.m
的viewDidLoad方法里面,让self.videoCamera.delegate = self;
-
实现协议里面的
- (void)willOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
方法。将CMSampleBufferRef
->CVPixelBufferRef
->CGImage
->UIImage
经过如下转换,获取到原图
通过下方这个方法,可以把 CMSampleBufferRef
转成 UIImage
, stackoverflow 上有人已经给出了答案,代码如下
#define clamp(a) (a>255?255:(a<0?0:a)) - (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer { CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer,0); size_t width = CVPixelBufferGetWidth(imageBuffer); size_t height = CVPixelBufferGetHeight(imageBuffer); uint8_t *yBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); size_t yPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0); uint8_t *cbCrBuffer = CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 1); size_t cbCrPitch = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 1); int bytesPerPixel = 4; uint8_t *rgbBuffer = malloc(width * height * bytesPerPixel); for(int y = 0; y < height; y++) { uint8_t *rgbBufferLine = &rgbBuffer[y * width * bytesPerPixel]; uint8_t *yBufferLine = &yBuffer[y * yPitch]; uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch]; for(int x = 0; x < width; x++) { int16_t y = yBufferLine[x]; int16_t cb = cbCrBufferLine[x & ~1] - 128; int16_t cr = cbCrBufferLine[x | 1] - 128; uint8_t *rgbOutput = &rgbBufferLine[x*bytesPerPixel]; int16_t r = (int16_t)roundf( y + cr * 1.4 ); int16_t g = (int16_t)roundf( y + cb * -0.343 + cr * -0.711 ); int16_t b = (int16_t)roundf( y + cb * 1.765); rgbOutput[0] = 0xff; rgbOutput[1] = clamp(b); rgbOutput[2] = clamp(g); rgbOutput[3] = clamp(r); } } CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(rgbBuffer, width, height, 8, width * bytesPerPixel, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); CGImageRef quartzImage = CGBitmapContextCreateImage(context); UIImage *image = [UIImage imageWithCGImage:quartzImage]; CGContextRelease(context); CGColorSpaceRelease(colorSpace); CGImageRelease(quartzImage); free(rgbBuffer); CVPixelBufferUnlockBaseAddress(imageBuffer, 0); return image; }
经过以上几步,可以成功拿到未美颜的原图。但是需要注意图片的方向问题:图片的 imageOrientation
的值是0(即 UIImageOrientationUp
),但是把图片发送到服务器打开一看,发现逆时针旋转了90度,而且镜像了。而且图片的宽和高的值对换了,比如说正常图片是 宽480x高640
,但是拿到的未美颜的图片是 宽640X高480
。
在镜头里看到的美颜后的人脸图片如下图所示:
在协议方法 willOutputSampleBuffer
里面用上面这个 imageFromSampleBuffer
方法取出的未美颜的原图所如下所示:
因为未美颜的原图的 imageOrientation
的值是0,不能直接使用我写的 另一篇笔记:调用相机拍照图片旋转了90度 里的方法一调整图片方向,但是调整的原理是一样的。
所以,在把未美颜的原图发送到服务器之前,需要将图片进行如下变换:
- (UIImage *)fixNotBeautyImageOrientation:(UIImage *)originImage { CGAffineTransform transform = CGAffineTransformIdentity; transform = CGAffineTransformTranslate(transform, originImage.size.height, 0); transform = CGAffineTransformRotate(transform, M_PI_2); transform = CGAffineTransformTranslate(transform, originImage.size.width, 0); transform = CGAffineTransformScale(transform, -1, 1); CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.width, image.size.height, CGImageGetBitsPerComponent(image.CGImage), 0, CGImageGetColorSpace(image.CGImage), CGImageGetBitmapInfo(image.CGImage)); CGContextConcatCTM(ctx, transform); CGContextDrawImage(ctx, CGRectMake(0,0,image.size.width,image.size.height), image.CGImage); CGImageRef cgimg = CGBitmapContextCreateImage(ctx); UIImage *img = [UIImage imageWithCGImage:cgimg]; CGContextRelease(ctx); CGImageRelease(cgimg); return img; }
下面用图片来详细说一下是如何进行变换,最后得到正确的图片的:
- 步骤一、获取初始的CTM
黑色区域表示用于生成图片的 图形上下文
。初始的时候,坐标原点在左下角,X轴向右,Y轴向上。
有一个需要注意的地方: UIGraphicsGetCurrentContext()获得的图形上下文坐标原点是在左上角的。而使用Quartz方法如 CGBitmapContextCreate
获得的图形上下文的坐标原点是在左下角的。
- 步骤二、沿着X正方向移动
因为未美颜的图片的widht是640,height是480。此处沿着X正方向移动了480。
- 步骤三、绕原点,从X轴向Y轴旋转90度
由于在 步骤一
里面提到,有两种不同的坐标系, 旋转的时候,如果弧度是正的,表示从X轴正方向向Y轴正方向旋转,如果弧度是负的,表示从X轴正方向向Y轴正方向旋转。 这样就不用考虑到底是 逆时针还是顺时针旋转的问题
了。
- 步骤四、沿着X正方向移动
因为未美颜的图片的widht是640,height是480。此处沿着X正方向移动了640。
- 步骤五、X轴沿着Y轴翻转180度
- 步骤六、变换完成之后,以此为基础画图。
上方代码里面 CGBitmapContextCreate
的宽是480长高640,与图片大小一样。画出来大小就是图片大小。假如说 CGBitmapContextCreate
设置的宽是500高是700,比图片本身大一点。那么在第五步的基础上,画出来的图片如下图所示,上方和右方会有黑边:
到此为止,在把未美颜的原图经过变换之后,生成新的方向正确的图片,就可以发送给服务器了。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 微信原图暴露的 Exif,是什么?怎么用?为什么压缩会丢失?
- iOS给一张照片美颜
- 美颜重磅技术之 GPUImage 源码分析
- 直播SDK加入GPU自定义美颜
- 美颜相机中的设计模式——装饰者模式
- 开发者回应 iPhone XS 美颜:没内置滤镜、算法可改进
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Head Rush Ajax
Brett McLaughlin、Eric Freeman、Elisabeth Freeman / O'Reilly Media, Inc. / 2006-03-01 / USD 34.99
Ajax, or Asynchronous JavaScript and XML, is a term describing the latest rage in web development. Ajax is used to create interactive web applications with XML-based web services, and using JavaScript......一起来看看 《Head Rush Ajax》 这本书的介绍吧!