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

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

内容简介:在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实现实时美颜滤镜并获取原图

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


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

查看所有标签

猜你喜欢:

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

Programming From The Ground Up

Programming From The Ground Up

Jonathan Bartlett / Bartlett Publishing / 2004-07-31 / USD 34.95

Programming from the Ground Up is an introduction to programming using assembly language on the Linux platform for x86 machines. It is a great book for novices who are just learning to program as wel......一起来看看 《Programming From The Ground Up》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器