iOS实现实时美颜滤镜并获取原图

栏目: IOS · 发布时间: 5年前

内容简介:在iOS里面进行实时美颜,我使用的是具体使用方法如下:

实时美颜

在iOS里面进行实时美颜,我使用的是 GPUImageBeautifyFilter , 具体原理可以看 作者的文章

具体使用方法如下:

1.在项目的 Podfile 里面引入 GPUImage

target 'TestBeauty' do
    pod 'GPUImage'
end

2.将 GPUImageBeautyFilter.hGPUImageBeautyFilter.m 添加到项目中

iOS实现实时美颜滤镜并获取原图

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 转成 UIImagestackoverflow 上有人已经给出了答案,代码如下

#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

在镜头里看到的美颜后的人脸图片如下图所示:

iOS实现实时美颜滤镜并获取原图

在协议方法 willOutputSampleBuffer 里面用上面这个 imageFromSampleBuffer 方法取出的未美颜的原图所如下所示:

iOS实现实时美颜滤镜并获取原图

因为未美颜的原图的 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;
}

下面用图片来详细说一下是如何进行变换,最后得到正确的图片的:

  1. 步骤一、获取初始的CTM

黑色区域表示用于生成图片的 图形上下文 。初始的时候,坐标原点在左下角,X轴向右,Y轴向上。

有一个需要注意的地方: UIGraphicsGetCurrentContext()获得的图形上下文坐标原点是在左上角的。而使用Quartz方法如 CGBitmapContextCreate 获得的图形上下文的坐标原点是在左下角的。

iOS实现实时美颜滤镜并获取原图

  1. 步骤二、沿着X正方向移动

因为未美颜的图片的widht是640,height是480。此处沿着X正方向移动了480。

iOS实现实时美颜滤镜并获取原图

  1. 步骤三、绕原点,从X轴向Y轴旋转90度

由于在 步骤一 里面提到,有两种不同的坐标系, 旋转的时候,如果弧度是正的,表示从X轴正方向向Y轴正方向旋转,如果弧度是负的,表示从X轴正方向向Y轴正方向旋转。 这样就不用考虑到底是 逆时针还是顺时针旋转的问题 了。

iOS实现实时美颜滤镜并获取原图

iOS实现实时美颜滤镜并获取原图

  1. 步骤四、沿着X正方向移动

因为未美颜的图片的widht是640,height是480。此处沿着X正方向移动了640。

iOS实现实时美颜滤镜并获取原图

  1. 步骤五、X轴沿着Y轴翻转180度

iOS实现实时美颜滤镜并获取原图

  1. 步骤六、变换完成之后,以此为基础画图。

上方代码里面 CGBitmapContextCreate 的宽是480长高640,与图片大小一样。画出来大小就是图片大小。假如说 CGBitmapContextCreate 设置的宽是500高是700,比图片本身大一点。那么在第五步的基础上,画出来的图片如下图所示,上方和右方会有黑边:

iOS实现实时美颜滤镜并获取原图

到此为止,在把未美颜的原图经过变换之后,生成新的方向正确的图片,就可以发送给服务器了。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux程序设计

Linux程序设计

马修 / 陈健 / 人民邮电出版社 / 2007-7 / 89.00元

《Linux 程序设计(第3版)》讲述在Linux系统及其他UNIX风格的操作系统上进行的程序开发,主要内容包括标准Linux C语言函数库和由不同的Linux或UNIX标准指定的各种工具的使用方法,大多数标准Linux开发工具的使用方法,通过DBM和MySQL数据库系统对Linux中的数据进行存储,为X视窗系统建立图形化用户界面等。《Linux 程序设计(第3版)》通过先介绍程序设计理论,再以适......一起来看看 《Linux程序设计》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

SHA 加密
SHA 加密

SHA 加密工具