内容简介:在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 美颜:没内置滤镜、算法可改进
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
产品经理面试宝典
[美] Gayle Laakmann McDowell、[美]Jackie Bavaro / 吴海星、陈少芸 / 人民邮电出版社 / 2015-3 / 59.00元
本书针对IT 行业产品经理,以面试为主线,首先介绍产品经理职责以及谷歌、微软等知名企业中产品经理的作用和要求;然后采访了几位知名企业的产品经理,介绍成为产品经理的基本素质;之后从简历准备、各公司面试要点到具体面试问题进行详细分析,这部分是本书的重点内容。读者对象包括IT 行业产品经理以及对如何做好产品有兴趣的人士。一起来看看 《产品经理面试宝典》 这本书的介绍吧!