iOS中滤镜处理及相关内存泄漏问题的解决
最近工作之余在做一个美图秀秀的仿品 做到滤镜这块的时候 自己就参考了网上几位博主(名字忘了记,非常抱歉)的博客,但是发现跟着他们的demo做的滤镜处理,都会有很严重的内存泄漏,于是就自己按照大体的思路将代码重新整理了下,并解决了内存泄漏问题。
大体思路如下:
根据图片创建一个CoreGraphic的图形上文->根据图形上下文获取图片每个像素的RGBA的色值数组->遍历数组,按照颜色矩阵进行像素色值调整->输出绘制新的图片
具体流程如下:
首先创建一个RGBA通道位图上下文:注意在以下方法中,不要立刻释放malloc方法生成的bitmapData内存空间指针,(可能有的朋友觉得已经把内存空间地址给了位图上下文就可以立马释放掉了,但是由于位图上下文在后来的图像渲染时,仍然需要这一块内存,因此不能在此处立马释放掉内存,之前拜读的几篇博客索性就不释放内存了,因此会导致内存泄漏,处理一些高清图像时,手机内存会轻易飙升到1G以上,而导致程序挂掉)不然会导致位图上下文的内容数据不能正常存在而导致图片生成失败,在这里需要一个全局内存指针来指向它,并且在合适的时候释放内存,具体看如下代码:
创建RGBA通道位图上下文:该位图上下文主要提供了一个画板,配置了画板的绘图所占用的字节数,设备依赖的RGB通道等信息。该上下文主要用于提供所有渲染图像的像素的RGBA值数组,以便后续对像素值的遍历处理。
#pragma mark---------------------------------------->创建一个使用RGBA通道的位图上下文
static CGContextRef CreateRGBABitmapContex(CGImageRef inImage){ CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void *bitmapData;//内存空间的指针,该内存空间的大小等于图像使用RGB通道所占用的字节数。
long bitmapByteCount;
long bitmapBytePerRow; /* 获取像素的横向和纵向个数 */
size_t pixelsWith = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage); /* 每一行的像素点占用的字节数,每个像素点的RGBA四个通道各占8bit空间 */
bitmapBytePerRow = (pixelsWith * 4); /* 整张图片占用的字节数 */
bitmapByteCount = (bitmapBytePerRow * pixelsHigh); /* 创建依赖设备的RGB通道 */
colorSpace = CGColorSpaceCreateDeviceRGB(); /* 分配足够容纳图片字节数的内存空间 */
bitmapData = malloc(bitmapByteCount); /* 引用内存地址 以便在合适的地方释放内存空间 */
bitmap = bitmapData; /* 创建CoreGraphic的图形上下文 该上下文描述了bitmaData指向的内存空间需要绘制的图像的一些绘制参数 */ context = CGBitmapContextCreate(bitmapData, pixelsWith, pixelsHigh, 8, bitmapBytePerRow, colorSpace, kCGImageAlphaPremultipliedLast); /* Core Foundation中含有Create、Alloc的方法名字创建的指针,需要使用CFRelease()函数释放 */
CGColorSpaceRelease(colorSpace); /* 此处必须手动释放内存 不然会有内存暴增的现象 但如果在这里释放 真机运行时情况可能就不太好了 (注意:在模拟器上 在此释放不会有任何问题 模拟器的图形上下文和画板机制与真机不同) */
// free(bitmapData);
return context; }
返回目标图像的RBGA像素色值的数组指针:该指针指向一个数组,数组中的每四个元素都是图像上的一个像素点的RGBA的数值(0-255),用无符号的char是因为它正好的取值范围就是0-255
static unsigned char *RequestImagePixelData(UIImage * inImage){
CGImageRef img = [inImage CGImage];
CGSize size = [inImage size];
//使用上面的函数创建上下文
CGContextRef cgctx = CreateRGBABitmapContex(img);
CGRect rect = {{0,0},{size.width,size.height}};
//将目标图像绘制到指定的上下文,实际为上下文内的bitmapData。
CGContextDrawImage(cgctx, rect, img);
unsigned char *data = CGBitmapContextGetData(cgctx);
//释放上面的函数创建的上下文
CGContextRelease(cgctx);
cgctx = NULL;
return data;
}
将一个像素RGBA值数组通过一个颜色矩阵进行转换:颜色矩阵决定了图像的渲染效果,因此不同的滤镜效果可以通过设置不同的颜色矩阵进行转换。如果不懂颜色矩阵,可以参考如下的博客:http://www.cnblogs.com/yjmyzz/archive/2010/10/16/1852878.html,在这里就不过多描述了。
注意:在以下方法中,建议先取值并赋值给变量,因为每个像素点的色值,都要调用这个方法,对于一张稍大的高清图,会遍历非常多的次数,因此,里面的每一步多余的操作,都会引起积累起来的长时间处理,博主当时也踩了这个坑,导致处理一张图片时极度耗时。
static void changeRGB(int *red,int* green,int*blue,int*alpha ,const float *f){
//先取值并赋值给变量
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[5] * redV + f[6]*greenV + f[7]*blueV + f[8] * alphaV+ f[9];
*blue = f[10] * redV + f[11]*greenV + f[12]*blueV + f[11] * alphaV+ f[14];
*alpha = f[15] * redV + f[16]*greenV + f[17]*blueV + f[18] * alphaV+ f[19];
//超出边界值的都默认为边界值
if (*red<0) {
*red=0;
}
if (*red>255) {
*red = 255;
}
if (*green<0) {
*green = 0;
}
if (*green>255) {
*green = 255;
}
if (*blue<0) {
*blue = 0;
}
if (*blue>255) {
*blue = 255;
}
if (*alpha>255) {
*alpha=255;
}
if (*alpha<0) {
*alpha = 0;
}
}
以下方法就是暴露给大家的最终图片处理方法了,通过传入一张图片和一个颜色矩阵f,即可完成一张图片的滤镜渲染,并且,在生成一张图片后,最好是将该图像转换为NSData类型进行存储,然后释放掉之前全局变量内存指针,最后再将NSData数据回传给需要的方法。如果不将生成图像转化为NSData存储,而直接使用生成的UIImage对象,则在释放掉内存指针后,UIImage对象也将不存在,楼主亲测,是个大坑,读者尽量避免此类情况。
- (UIImage *)createImageWithImage:(UIImage *)inImage andColorMatrix:(const float *)f{
/* 图片位图像素值数组 */
unsigned char *imgPixel = RequestImagePixelData(inImage);
CGImageRef inImageRef = [inImage CGImage];
long w = CGImageGetWidth(inImageRef);
long h = CGImageGetHeight(inImageRef);
int wOff = 0;
int pixOff = 0;
/* 遍历修改位图像素值 */
for (long y = 0; y<h; y++) {
pixOff = wOff;
for (long 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];
changeRGB(&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;
//创建要输出的图像的相关参数
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, imgPixel, dataLength, NULL);
if (!provider) {
NSLog(@"创建输出图像相关参数失败!");
}else{
int bitsPerComponent = 8;
int bitsPerPixel = 32;
ItemCount bytesPerRow = 4 * w;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent rederingIntent = kCGRenderingIntentDefault;
//创建要输出的图像
CGImageRef imageRef = CGImageCreate(w, h,bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider,NULL, NO, rederingIntent);
if (!imageRef) {
NSLog(@"创建输出图像失败");
}else{
UIImage *my_image = [UIImage imageWithCGImage:imageRef];
CFRelease(imageRef);
CGColorSpaceRelease(colorSpaceRef);
CGDataProviderRelease(provider);
NSData *data = UIImageJPEGRepresentation(my_image, 1.0);
/* 在这里就可以释放内存了 并且在此之后my_image由于运行时和图形上下文机制已经没有图像内容了 只能使用刚刚生成的图片二进制数据进行图片回传 */
free(bitmap);
//这里的block是demo中需要的 可以不做关注
if (_imageBLOCK) {
_imageBLOCK([UIImage imageWithData:data]);
}
return [UIImage imageWithData:data];
}
}
return nil;
}
我的demo的github地址为:https://github.com/China131/JHFilterDemo.git,效果图如下:
demo的操作很简单,即动态改变颜色矩阵的值,实时生成渲染图片,您可以慢慢调试,如果发现您喜欢的渲染类型,直接点击保存图片,Xcode即可打印一个完整的颜色矩阵,您只需要将颜色矩阵保存,就拥有了独一无二的滤镜哦。

iOS中滤镜处理及相关内存泄漏问题的解决的更多相关文章
- iOS中滤镜种类及相关介绍
- 在Activity中使用Thread导致的内存泄漏
https://github.com/bboyfeiyu/android-tech-frontier/tree/master/issue-7/%E5%9C%A8Activity%E4%B8%AD%E4 ...
- 深入理解Node.js中的垃圾回收和内存泄漏的捕获
深入理解Node.js中的垃圾回收和内存泄漏的捕获 文章来自:http://wwsun.github.io/posts/understanding-nodejs-gc.html Jan 5, 2016 ...
- 5个Android开发中比较常见的内存泄漏问题及解决办法
android中一个对象已经不需要了,但是其他对象还持有他的引用,导致他不能回收,导致这个对象暂存在内存中,这样内存泄漏就出现了. 内存泄漏出现多了,会是应用占用过多的没存,当占用的内存超过了系统 ...
- 面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?
概念 内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用.即无用对象持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间浪费. 可达 ...
- Android 内存泄漏分析与解决方法
在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...
- Android开发之漫漫长途 番外篇——内存泄漏分析与解决
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- Android开发 |常见的内存泄漏问题及解决办法
在Android开发中,内存泄漏是比较常见的问题,有过一些Android编程经历的童鞋应该都遇到过,但为什么会出现内存泄漏呢?内存泄漏又有什么影响呢? 在Android程序开发中,当一个对象已经不需要 ...
- iOS开发那些事--性能优化–内存泄露问题的解决(转)
内存泄漏问题的解决 内存泄漏(Memory Leaks)是当一个对象或变量在使用完成后没有释放掉,这个对象一直占有着这块内存,直到应用停止.如果这种对象过多内存就会耗尽,其它的应用就无法运行.这个问题 ...
随机推荐
- 关于 MonoDevelop on Linux 单步调试问题的解决
在 MonoDevelop 中默认是关闭对外部程序集(.dll)的调试,可通过如下步骤来解决这个问题. 通过菜单[Edit]-[Preferences]-[Debugger]进入到调试器的设置页,把“ ...
- java中hashcode()和equals()的详解
今天下午研究了半天hashcode()和equals()方法,终于有了一点点的明白,写下来与大家分享(zhaoxudong 2008.10.23晚21.36). 1. 首先equals()和hashc ...
- Log4j 简单应用
#输出日志的包路径log4j.logger.com=DEBUG,FILE log4j.rootLogger=WARN,stdout #控制台日志 log4j.appender.stdout=org.a ...
- android 图像处理系列合集
为了便于大家对滤镜算法的学习,以后发布的图像处理滤镜系列帖子会在这里汇总,本人第一次写合集,写得不好的地方大家请见谅,手头上虽然有一些滤镜的算法,但是大多不是android版的,教程里的代码大多是我借 ...
- Mvc中域的添加和不同域之间的跳转
一.在新添加的域中中的 AreaRegistration中作如下设置: 二.在原来的Global.asax中设置: 三.不同域之间的跳转 @Url.Action("Index", ...
- 神经网络和Deep Learning
参考资料: 在线免费书籍 http://neuralnetworksanddeeplearning.com/chap1.html Chapter 1 1. perceptron 感知机 it's a ...
- #MySQL 5.7.8 支持Json类型
As of MySQL 5.7.8, MySQL supports a native JSON data type that enables efficient access to data in J ...
- POJ 2342 Anniversary party(树形dp)
Anniversary party Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 7230 Accepted: 4162 ...
- mysql 函数 GROUP_CONCAT 单元格中最长字符串和excel导出问题
GROUP_CONCAT 使用方式GROUP_CONCAT ([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符']) SELECT ...
- HDU 2204Eddy's爱好(容斥原理)
Eddy's爱好 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Sta ...