iOS --- 通过CPU实现的简单滤镜效果

iOS中使用CPU实现滤镜效果的原理很简单, 即将图片转换成像素数据, 然后对每一个像素进行相应的滤镜效果计算, 然后重新得到过滤后的图片.

CPU滤镜效果代码如下:

头文件

//  CPUImageFilterUtil.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

//LOMO
static const float colormatrix_lomo[] = {
  1.7f,  0.1f, 0.1f, 0, -73.1f,
  0,  1.7f, 0.1f, 0, -73.1f,
  0,  0.1f, 1.6f, 0, -73.1f,
  0,  0, 0, 1.0f, 0 };

//黑白
static const float colormatrix_heibai[] = {
  0.8f,  1.6f, 0.2f, 0, -163.9f,
  0.8f,  1.6f, 0.2f, 0, -163.9f,
  0.8f,  1.6f, 0.2f, 0, -163.9f,
  0,  0, 0, 1.0f, 0 };
//复古
static const float colormatrix_huajiu[] = {
  0.2f,0.5f, 0.1f, 0, 40.8f,
  0.2f, 0.5f, 0.1f, 0, 40.8f,
  0.2f,0.5f, 0.1f, 0, 40.8f,
  0, 0, 0, 1, 0 };

//哥特
static const float colormatrix_gete[] = {
  1.9f,-0.3f, -0.2f, 0,-87.0f,
  -0.2f, 1.7f, -0.1f, 0, -87.0f,
  -0.1f,-0.6f, 2.0f, 0, -87.0f,
  0, 0, 0, 1.0f, 0 };

//锐化
static const float colormatrix_ruise[] = {
  4.8f,-1.0f, -0.1f, 0,-388.4f,
  -0.5f,4.4f, -0.1f, 0,-388.4f,
  -0.5f,-1.0f, 5.2f, 0,-388.4f,
  0, 0, 0, 1.0f, 0 };

//淡雅
static const float colormatrix_danya[] = {
  0.6f,0.3f, 0.1f, 0,73.3f,
  0.2f,0.7f, 0.1f, 0,73.3f,
  0.2f,0.3f, 0.4f, 0,73.3f,
  0, 0, 0, 1.0f, 0 };

//酒红
static const float colormatrix_jiuhong[] = {
  1.2f,0.0f, 0.0f, 0.0f,0.0f,
  0.0f,0.9f, 0.0f, 0.0f,0.0f,
  0.0f,0.0f, 0.8f, 0.0f,0.0f,
  0, 0, 0, 1.0f, 0 };

//清宁
static const float colormatrix_qingning[] = {
  0.9f, 0, 0, 0, 0,
  0, 1.1f,0, 0, 0,
  0, 0, 0.9f, 0, 0,
  0, 0, 0, 1.0f, 0 };

//浪漫
static const float colormatrix_langman[] = {
  0.9f, 0, 0, 0, 63.0f,
  0, 0.9f,0, 0, 63.0f,
  0, 0, 0.9f, 0, 63.0f,
  0, 0, 0, 1.0f, 0 };

//光晕
static const float colormatrix_guangyun[] = {
  0.9f, 0, 0,  0, 64.9f,
  0, 0.9f,0,  0, 64.9f,
  0, 0, 0.9f,  0, 64.9f,
  0, 0, 0, 1.0f, 0 };

//蓝调
static const float colormatrix_landiao[] = {
  2.1f, -1.4f, 0.6f, 0.0f, -31.0f,
  -0.3f, 2.0f, -0.3f, 0.0f, -31.0f,
  -1.1f, -0.2f, 2.6f, 0.0f, -31.0f,
  0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};

//梦幻
static const float colormatrix_menghuan[] = {
  0.8f, 0.3f, 0.1f, 0.0f, 46.5f,
  0.1f, 0.9f, 0.0f, 0.0f, 46.5f,
  0.1f, 0.3f, 0.7f, 0.0f, 46.5f,
  0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};

//夜色
static const float colormatrix_yese[] = {
  1.0f, 0.0f, 0.0f, 0.0f, -66.6f,
  0.0f, 1.1f, 0.0f, 0.0f, -66.6f,
  0.0f, 0.0f, 1.0f, 0.0f, -66.6f,
  0.0f, 0.0f, 0.0f, 1.0f, 0.0f
};

static const float colormatrixs[][20]={
  {
    0.8f,  1.6f, 0.2f, 0, -163.9f,
    0.8f,  1.6f, 0.2f, 0, -163.9f,
    0.8f,  1.6f, 0.2f, 0, -163.9f,
    0,  0, 0, 1.0f, 0
  },
  {
    0.2f,0.5f, 0.1f, 0, 40.8f,
    0.2f, 0.5f, 0.1f, 0, 40.8f,
    0.2f,0.5f, 0.1f, 0, 40.8f,
    0, 0, 0, 1, 0
  },
  {
    1.9f,-0.3f, -0.2f, 0,-87.0f,
    -0.2f, 1.7f, -0.1f, 0, -87.0f,
    -0.1f,-0.6f, 2.0f, 0, -87.0f,
    0, 0, 0, 1.0f, 0
  },
  {
    4.8f,-1.0f, -0.1f, 0,-388.4f,
    -0.5f,4.4f, -0.1f, 0,-388.4f,
    -0.5f,-1.0f, 5.2f, 0,-388.4f,
    0, 0, 0, 1.0f, 0
  },
  {
    0.6f,0.3f, 0.1f, 0,73.3f,
    0.2f,0.7f, 0.1f, 0,73.3f,
    0.2f,0.3f, 0.4f, 0,73.3f,
    0, 0, 0, 1.0f, 0
  },
  {
    1.2f,0.0f, 0.0f, 0.0f,0.0f,
    0.0f,0.9f, 0.0f, 0.0f,0.0f,
    0.0f,0.0f, 0.8f, 0.0f,0.0f,
    0, 0, 0, 1.0f, 0
  },
  {
    0.9f, 0, 0, 0, 0,
    0, 1.1f,0, 0, 0,
    0, 0, 0.9f, 0, 0,
    0, 0, 0, 1.0f, 0
  },
  {
    0.9f, 0, 0, 0, 63.0f,
    0, 0.9f,0, 0, 63.0f,
    0, 0, 0.9f, 0, 63.0f,
    0, 0, 0, 1.0f, 0
  },
  {
    0.9f, 0, 0,  0, 64.9f,
    0, 0.9f,0,  0, 64.9f,
    0, 0, 0.9f,  0, 64.9f,
    0, 0, 0, 1.0f, 0
  },
  {
    2.1f, -1.4f, 0.6f, 0.0f, -31.0f,
    -0.3f, 2.0f, -0.3f, 0.0f, -31.0f,
    -1.1f, -0.2f, 2.6f, 0.0f, -31.0f,
    0.0f, 0.0f, 0.0f, 1.0f, 0.0f
  },
  {
    0.8f, 0.3f, 0.1f, 0.0f, 46.5f,
    0.1f, 0.9f, 0.0f, 0.0f, 46.5f,
    0.1f, 0.3f, 0.7f, 0.0f, 46.5f,
    0.0f, 0.0f, 0.0f, 1.0f, 0.0f
  },
  {
    1.0f, 0.0f, 0.0f, 0.0f, -66.6f,
    0.0f, 1.1f, 0.0f, 0.0f, -66.6f,
    0.0f, 0.0f, 1.0f, 0.0f, -66.6f,
    0.0f, 0.0f, 0.0f, 1.0f, 0.0f
  }
};

@interface CPUImageFilterUtil : NSObject

+ (UIImage *)imageWithImage:(UIImage *)inImage withColorMatrix:(const float*)f;

@end

实现文件

//  CPUImageFilterUtil.m

#import "CPUImageFilterUtil.h"

@implementation CPUImageFilterUtil

// 返回一个使用RGBA通道的位图上下文
static CGContextRef CreateRGBABitmapContext (CGImageRef inImage)
{
  CGContextRef context = NULL;
  CGColorSpaceRef colorSpace;

  //内存空间的指针,该内存空间的大小等于图像使用RGB通道所占用的字节数。
  void *bitmapData; 

  int bitmapByteCount;
  int bitmapBytesPerRow;

  //获取横向的像素点的个数
  size_t pixelsWide = CGImageGetWidth(inImage);
  size_t pixelsHigh = CGImageGetHeight(inImage); //纵向

  //每一行的像素点占用的字节数,每个像素点的ARGB四个通道各占8个bit(0-255)的空间
  bitmapBytesPerRow = (int)(pixelsWide * 4);
  //计算整张图占用的字节数
  bitmapByteCount = (int)(bitmapBytesPerRow * pixelsHigh);

  //创建依赖于设备的RGB通道
  colorSpace = CGColorSpaceCreateDeviceRGB();

  //分配足够容纳图片字节数的内存空间
  bitmapData = malloc(bitmapByteCount); 

  //创建CoreGraphic的图形上下文,该上下文描述了bitmaData指向的内存空间需要绘制的图像的一些绘制参数
  context = CGBitmapContextCreate (bitmapData, pixelsWide, pixelsHigh, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);

  //Core Foundation中通过含有Create、Alloc的方法名字创建的指针,需要使用CFRelease()函数释放
  CGColorSpaceRelease( colorSpace );

  return context;
}

// 返回一个指针,该指针指向一个数组,数组中的每四个元素都是图像上的一个像素点的RGBA的数值(0-255),用无符号的char是因为它正好的取值范围就是0-255
static unsigned char *RequestImagePixelData(UIImage *inImage)
{
  CGImageRef img = [inImage CGImage];
  CGSize size = [inImage size];

  //使用上面的函数创建上下文
  CGContextRef cgctx = CreateRGBABitmapContext(img); 

  CGRect rect = {{0,0},{size.width, size.height}};

  //将目标图像绘制到指定的上下文,实际为上下文内的bitmapData。
  CGContextDrawImage(cgctx, rect, img);
  unsigned char *data = CGBitmapContextGetData (cgctx);

  //释放上面的函数创建的上下文
  CGContextRelease(cgctx);
  return data;
}

static void changeRGBA(int *red,int *green,int *blue,int *alpha, const float* f)//修改RGB的值
{
  int redV = *red;
  int greenV = *green;
  int blueV = *blue;
  int alphaV = *alpha;

  *red = f[0] * redV + f[1] * greenV + f[2] * blueV + f[3] * alphaV + f[4];
  *green = f[0+5] * redV + f[1+5] * greenV + f[2+5] * blueV + f[3+5] * alphaV + f[4+5];
  *blue = f[0+5*2] * redV + f[1+5*2] * greenV + f[2+5*2] * blueV + f[3+5*2] * alphaV + f[4+5*2];
  *alpha = f[0+5*3] * redV + f[1+5*3] * greenV + f[2+5*3] * blueV + f[3+5*3] * alphaV + f[4+5*3];

  if (*red > 255)
  {
    *red = 255;
  }
  if(*red < 0)
  {
    *red = 0;
  }
  if (*green > 255)
  {
    *green = 255;
  }
  if (*green < 0)
  {
    *green = 0;
  }
  if (*blue > 255)
  {
    *blue = 255;
  }
  if (*blue < 0)
  {
    *blue = 0;
  }
  if (*alpha > 255)
  {
    *alpha = 255;
  }
  if (*alpha < 0)
  {
    *alpha = 0;
  }
}

+ (UIImage*)imageWithImage:(UIImage*)inImage withColorMatrix:(const float*) f
{
  unsigned char *imgPixel = RequestImagePixelData(inImage);
  CGImageRef inImageRef = [inImage CGImage];
  GLuint w = (GLuint)CGImageGetWidth(inImageRef);
  GLuint h = (GLuint)CGImageGetHeight(inImageRef);

  int wOff = 0;
  int pixOff = 0;

  //双层循环按照长宽的像素个数迭代每个像素点
  for(GLuint y = 0;y< h;y++)
  {
    pixOff = wOff;

    for (GLuint x = 0; x<w; x++)
    {
      int red = (unsigned char)imgPixel[pixOff];
      int green = (unsigned char)imgPixel[pixOff+1];
      int blue = (unsigned char)imgPixel[pixOff+2];
      int alpha = (unsigned char)imgPixel[pixOff+3];
      changeRGBA(&red, &green, &blue, &alpha, f);

      //回写数据
      imgPixel[pixOff] = red;
      imgPixel[pixOff+1] = green;
      imgPixel[pixOff+2] = blue;
      imgPixel[pixOff+3] = alpha;

      //将数组的索引指向下四个元素
      pixOff += 4;
    }

    wOff += w * 4;
  }

  NSInteger dataLength = w * h * 4;

  //下面的代码创建要输出的图像的相关参数.
  //其中(CGDataProviderReleaseDataCallback)&freeData要做的内存释放非常关键, 采用回调函数的方式进行释放(因data不能在此时释放, 否则就得不到处理后的图片).
  //推断:iOS在绘制UIImage的时候使用的内存信息只是作了一个简单的引用指向,所以我们立即释放data的话就会造成数据错误。
  //如果仔细看CGDataProviderCreateWithData方法的注释,正确的做法应该是实现自己的释放方法,然后将该方法作为CGDataProviderCreateWithData的最后一个参数进行传入,那么CGDataProviderRef释放的时候就会对该CGDataProviderReleaseDataCallback进行回调,在里面我们可以安全释放我们的图像数据。
  CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, imgPixel, dataLength, (CGDataProviderReleaseDataCallback)&freeData);

  int bitsPerComponent = 8;
  int bitsPerPixel = 32;
  int bytesPerRow = 4 * w;
  CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
  CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
  CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;

  //创建要输出的图像
  CGImageRef imageRef = CGImageCreate(w, h, bitsPerComponent, bitsPerPixel, bytesPerRow,colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);

  UIImage *myImage = [UIImage imageWithCGImage:imageRef];

  CFRelease(imageRef);
  CGColorSpaceRelease(colorSpaceRef);
  CGDataProviderRelease(provider);
  return myImage;
}

void freeData(void *info, const void *data, size_t size) {
  free((unsigned char *)data);
}

@end

使用方法

UIImage *originImage = [UIImage imageNamed:@"testImage"];
const float *colorMatrix = colormatrix_lomo;
UIImage *filteredImage = [CPUImageFilterUtil imageWithImage:originImage withColorMatrix:colorMatrix];

效果如下:

效果1 效果2

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-12-25 10:45:52

iOS --- 通过CPU实现的简单滤镜效果的相关文章

iOS --- 使用GPUImage实现的简单滤镜效果

GPUImage 是一个基于 GPU 图像和视频处理的开源 iOS 框架.由于使用 GPU 来处理图像和视频,所以速度非常快. 除了速度上的优势,GPUImage 还提供了很多很棒的图像处理滤镜,但有时候这些基本功能仍然无法满足实际开发中的需求,GPUImage 还支持自定义滤镜. 简单滤镜 GPUImageSepiaFilter *filter = [[GPUImageSepiaFilter alloc] init]; _filteredImage = [filter imageByFilte

iOS开发多线程篇—多线程简单介绍

iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过“活动监视器”可以查看Mac系统中所开启的进程 2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行 3.线程

iOS开发拓展篇-XMPP简单介绍

iOS开发拓展篇-XMPP简单介绍 一.即时通讯简单介绍 1.简单说明 即时通讯技术(IM)支持用户在线实时交谈.如果要发送一条信息,用户需要打开一个小窗口,以便让用户及其朋友在其中输入信息并让交谈双方都看到交谈的内容 有许多的IM系统,如AOL IM.Yahoo IM. MSN以及QQ,它们最大的区别在于各自通讯协议的实现,所以即时通讯技术的核心在于它的传输协议 协议用来说明信息在网络上如何传输,如果有了统一的传输协议,那么应当可以实现各个IM之间的直接通讯,为了创建即时通讯的统一标准,目前已

iOS:Swift界面实例1, 简单界面

Apple推出了基于Objective-C的新语言Swift. 通过实例, 我们可以很好的感受这门新语言 注意事项: 在XCode6_Beta中, 如果有中文, IDE的自动补全功能就会失效, 所以开始调试的时候可以先用英文, 后面再用中文替代. 1. 新建iOS -> Single View Application. 2. 修改AppDelegate.swift文件 1 // 2 // AppDelegate.swift 3 // UIByCode_Swift_1_HelloWorld 4 /

iOS开发UI篇—Quartz2D简单使用(三)

iOS开发UI篇-Quartz2D简单使用(三) 一.通过slider控制圆的缩放 1.实现过程 新建一个项目,新建一个继承自UIview的类,并和storyboard中自定义的view进行关联. 界面搭建,如图: 代码示例: YYViewController.m文件 1 // 2 // YYViewController.m 3 // 04-对圆进行缩放 4 // 5 // Created by apple on 14-6-11. 6 // Copyright (c) 2014年 itcase.

iOS开发UI篇-UIWindow简单介绍

iOS开发UI篇—UIWindow简单介绍 一.简单介绍 UIWindow是一种特殊的UIView,通常在一个app中只会有一个UIWindow iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的view,最后将控制器的view添加到UIWindow上,于是控制器的view就显示在屏幕上了 一个iOS程序之所以能显示到屏幕上,完全是因为它有UIWindow.也就说,没有UIWindow,就看不见任何UI界面 补充:UIWindow是创建的第一个视图控件(创建的第一个

文顶顶 iOS开发UI篇—iOS开发中三种简单的动画设置

iOS开发UI篇—iOS开发中三种简单的动画设置 [在ios开发中,动画是廉价的] 一.首尾式动画 代码示例: // beginAnimations表示此后的代码要“参与到”动画中 [UIView beginAnimations:nil context:nil]; //设置动画时长 [UIView setAnimationDuration:2.0]; self.headImageView.bounds = rect; // commitAnimations,将beginAnimation之后的所

iOS开发数据库篇—FMDB简单介绍

iOS开发数据库篇—FMDB简单介绍 一.简单说明 1.什么是FMDB FMDB是iOS平台的SQLite数据库框架 FMDB以OC的方式封装了SQLite的C语言API 2.FMDB的优点 使用起来更加面向对象,省去了很多麻烦.冗余的C语言代码 对比苹果自带的Core Data框架,更加轻量级和灵活 提供了多线程安全的数据库操作方法,有效地防止数据混乱 3.FMDB的github地址 https://github.com/ccgus/fmdb 二.核心类 FMDB有三个主要的类 (1)FMDa

ios开发UI基础—Kvc简单介绍

一.KVC简单介绍 KVC key valued coding 键值编码 KVC通过键值间接编码 补充: 与KVC相对的时KVO,即key valued observer 键值观察.监听某一个模型的属性,只要模型属性的值一变化就通知你. 二.使用KVC 1.KVC基本使用介绍 (1)代码示例: 新建一个命令行项目,用以演示KVC的用法 完成之后为项目添加一个Person类. 为Person类添加两个属性(name和age),注意这两个属性的类型. 1 #import <Foundation/Fo