iOS 操作像素点给图片打马赛克

马赛克前
屏幕快照 2017-05-02 下午7.31.23.png
马赛克后
屏幕快照 2017-05-02 下午7.32.06.png

代码演示

写一个继承自NSObject的类,创建类方法+ (UIImage*)imageProcess:(UIImage*)image;

第一步:确定图片的宽高
/*
     两种方案
     1 image.size.width
     2 GImageGetWidth(<#CGImageRef  _Nullable image#>)

     */
    CGImageRef imageRef = image.CGImage;
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    
第二步:创建颜色空间
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    
第三步:创建图片上下文(解析图片信息,绘制图片)

开辟内存空间,这块空间用于处理马赛克图片
参数1:数据源
参数2:图片宽
参数3:图片高
参数4:表示每一个像素点,每一个分量大小
在我们图像学中,像素点:ARGB组成 每一个表示一个分量(例如,A,R,G,B)
在我们计算机图像学中每一个分量的大小是8个字节
参数5:每一行大小(其实图片是由像素数组组成的)
如何计算每一行的大小,所占用的内存
首先计算每一个像素点大小(我们取最大值): ARGB是4个分量 = 每个分量8个字节 * 4
参数6:颜色空间
参数7:是否需要透明度

CGContextRef contextRef = CGBitmapContextCreate(nil, width, height, 8, width*4, colorSpaceRef, kCGImageAlphaPremultipliedLast);
    
第四步:根据图片上下文绘制图片
CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);
第五步:获取图片的像素数组
unsigned char * bitMapData = CGBitmapContextGetData(contextRef);
第六步:核心算法 图片打码,加入马赛克

马赛克:将图片模糊,(马赛克算法可以是可逆,也可以是不可逆,取决于打码的算法)
对图片进行采样

 我们今天处理的原理:让一个像素点替换为和它相同的矩形区域(正方形,圆形都可以)
 矩形区域包含了N个像素点
//选择马赛克区域
    //矩形区域:认为是打码的级别,马赛克点的大小(失真的强度)
    //这里将级别写死了level
    //level 马赛克点的大小
    NSUInteger currentIndex , preCurrentIndex, level = 3;
    //像素点默认是4个通道,默认值是0
    unsigned char * pixels[4] = {0};
    for (NSUInteger i = 0; i < height - 1; i++) {
        for (NSUInteger j = 0; j < width - 1; j++) {
            //循环便利每一个像素点,然后筛选,打码
            //获取当前像素点坐标-》指针位移方式处理像素点-》修改
            //指针位移
            currentIndex = (i * width) + j ;
            //计算矩形的区域
            /*
             分析下面筛选算法(组成马赛克点,矩形算法)
             假设:level = 3 (3*3的矩形)
             
             
             宽的规律:========
             第一步:i=0 j=0 level=3  i%level = 0  j%level=0
             第一次运行循环:
             memcpy(pixels, bitMapData+4*currentIndex, 4) 给我们的像素点赋值
             在这里我们以字节作为单位来获取,一个像素=4个字节,bitMapData+4*currentIndex一个像素点的读取,每次读区4个字节(开始的位置)
             第一次运行结果:获取第一个像素点的值
             
             第二步循环结果:
             i=0 j=1 i%level=0 j%level=1
             memcpy(bitMapData+4*currentIndex, pixels, 4);将第一个像素点的值拷贝复制替换给第二个像素点(指针位移方式计算)
             循环结果:第一个像素点值 赋值给 第二个像素点
             
             
             第三次运行循环:第一行第三列
             i=0 j=2 level=3  
             i%level=0 j%level=2
             循环结果:第三次像素点的值 = 第一次像素点的值
             
             
             
             高的规律:
             i=1 j=1 
             
             
             preCurrentIndex = (i - 1) * width + j;
             memcpy(bitMapData+4*currentIndex, bitMapData+4*preCurrentIndex, 4);
             
             第二行第一个像素点的值 = 第一行i 一个像素点的值
             
             */
            
            //通过这个算法,截取了一个3*3的一个矩形
            
            if (i % level==0) {
                if (j % level==0) {
                    //拷贝区域 c语言拷贝数据的函数
                    /*
                     参数1:拷贝目标(像素点)
                     参数2:源文件
                     参数3:要截取的长度(字节计算)
                     */
                    memcpy(pixels, bitMapData+4*currentIndex, 4);
                    
                }else{
                    //将上一个像素点的值赋值给第二个(指针位移的方式计算原理)
                    memcpy(bitMapData+4*currentIndex, pixels, 4);
                }
                
            }else{
                /*
                 例如:i=1  j=0
                 preCurrentIndex = (i - 1) * width + j;
                 */
                preCurrentIndex = (i - 1) * width + j;
                memcpy(bitMapData+4*currentIndex, bitMapData+4*preCurrentIndex, 4);
            }
            
            
        }
    }

第七步:获取图片数据集合
NSUInteger size = width * height * 4;
    CGDataProviderRef providerRef = CGDataProviderCreateWithData(NULL, bitMapData, size, NULL);
第八部:创建马赛克图片

参数1:宽
参数2:高
参数3:表示每一个像素点,每一个分量的大小
参数4:每一个像素点的大小
参数5:每一行内存大小
参数6:颜色空间
参数7:位图信息
参数8:数据源(数据集合)
参数9:数据解码器
参数10:是否抗锯齿
参数11:渲染器

CGImageRef mossicImageRef = CGImageCreate(width,
                                              height,
                                              8,
                                              4*8,
                                              width*4,
                                              colorSpaceRef,kCGImageAlphaPremultipliedLast,
                                              providerRef,
                                              NULL,
                                              NO,
                                              kCGRenderingIntentDefault);
    

第九步:创建输出马赛克图片(填充颜色)
CGContextRef outContextRef = CGBitmapContextCreate(nil,
                                                       width,
                                                       height,
                                                       8,
                                                       width*4,
                                                       colorSpaceRef,
                                                       kCGImageAlphaPremultipliedLast);
    //绘制图片
    CGContextDrawImage(outContextRef, CGRectMake(0, 0, width, height),mossicImageRef);
    
    //创建图片
    CGImageRef resultImageRef = CGBitmapContextCreateImage(outContextRef);
    UIImage *outImage = [UIImage imageWithCGImage:resultImageRef];
    
第十步:释放内存
//释放内存
    CGImageRelease(resultImageRef);
    CGImageRelease(mossicImageRef);
    CGColorSpaceRelease(colorSpaceRef);
    CGDataProviderRelease(providerRef);
    CGContextRelease(outContextRef);
    

完整代码

#import "ImageUntils.h"

@implementation ImageUntils

+ (UIImage*)imageProcess:(UIImage*)image{
    //第一步:确定图片的宽高
    /*
     两种方案
     1 image.size.width
     2 GImageGetWidth(<#CGImageRef  _Nullable image#>)

     */
    CGImageRef imageRef = image.CGImage;
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    
    //第二步:创建颜色空间
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    
    //第三部:创建图片上下文(解析图片信息,绘制图片)
    /*
     开辟内存空间,这块空间用于处理马赛克图片
     参数1:数据源
     参数2:图片宽
     参数3:图片高
     参数4:表示每一个像素点,每一个分量大小
            在我们图像学中,像素点:ARGB组成 每一个表示一个分量(例如,A,R,G,B)
            在我们计算机图像学中每一个分量的大小是8个字节
     参数5:每一行大小(其实图片是由像素数组组成的)
            如何计算每一行的大小,所占用的内存
            首先计算每一个像素点大小(我们取最大值): ARGB是4个分量 = 每个分量8个字节 * 4
     参数6:颜色空间
     参数7:是否需要透明度
     
     */
   CGContextRef contextRef = CGBitmapContextCreate(nil, width, height, 8, width*4, colorSpaceRef, kCGImageAlphaPremultipliedLast);
    
    //第四步:根据图片上下文绘制图片
    CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);
    //第五步:获取图片的像素数组
    unsigned char * bitMapData = CGBitmapContextGetData(contextRef);
    
    //第六步:图片打码,加入马赛克
    /*
     核心算法
     马赛克:将图片模糊,(马赛克算法可以是可逆,也可以是不可逆,取决于打码的算法)
     对图片进行采样
     
     我们今天处理的原理:让一个像素点替换为和它相同的矩形区域(正方形,圆形都可以)
     矩形区域包含了N个像素点
     
     */
    
    //选择马赛克区域
    //矩形区域:认为是打码的级别,马赛克点的大小(失真的强度)
    //这里将级别写死了level
    //level 马赛克点的大小
    NSUInteger currentIndex , preCurrentIndex, level = 3;
    //像素点默认是4个通道,默认值是0
    unsigned char * pixels[4] = {0};
    for (NSUInteger i = 0; i < height - 1; i++) {
        for (NSUInteger j = 0; j < width - 1; j++) {
            //循环便利每一个像素点,然后筛选,打码
            //获取当前像素点坐标-》指针位移方式处理像素点-》修改
            //指针位移
            currentIndex = (i * width) + j ;
            //计算矩形的区域
            /*
             分析下面筛选算法(组成马赛克点,矩形算法)
             假设:level = 3 (3*3的矩形)
             
             
             宽的规律:========
             第一步:i=0 j=0 level=3  i%level = 0  j%level=0
             第一次运行循环:
             memcpy(pixels, bitMapData+4*currentIndex, 4) 给我们的像素点赋值
             在这里我们以字节作为单位来获取,一个像素=4个字节,bitMapData+4*currentIndex一个像素点的读取,每次读区4个字节(开始的位置)
             第一次运行结果:获取第一个像素点的值
             
             第二步循环结果:
             i=0 j=1 i%level=0 j%level=1
             memcpy(bitMapData+4*currentIndex, pixels, 4);将第一个像素点的值拷贝复制替换给第二个像素点(指针位移方式计算)
             循环结果:第一个像素点值 赋值给 第二个像素点
             
             
             第三次运行循环:第一行第三列
             i=0 j=2 level=3  
             i%level=0 j%level=2
             循环结果:第三次像素点的值 = 第一次像素点的值
             
             
             
             高的规律:
             i=1 j=1 
             
             
             preCurrentIndex = (i - 1) * width + j;
             memcpy(bitMapData+4*currentIndex, bitMapData+4*preCurrentIndex, 4);
             
             第二行第一个像素点的值 = 第一行i 一个像素点的值
             
             */
            
            //通过这个算法,截取了一个3*3的一个矩形
            
            if (i % level==0) {
                if (j % level==0) {
                    //拷贝区域 c语言拷贝数据的函数
                    /*
                     参数1:拷贝目标(像素点)
                     参数2:源文件
                     参数3:要截取的长度(字节计算)
                     */
                    memcpy(pixels, bitMapData+4*currentIndex, 4);
                    
                }else{
                    //将上一个像素点的值赋值给第二个(指针位移的方式计算原理)
                    memcpy(bitMapData+4*currentIndex, pixels, 4);
                }
                
            }else{
                /*
                 例如:i=1  j=0
                 preCurrentIndex = (i - 1) * width + j;
                 */
                preCurrentIndex = (i - 1) * width + j;
                memcpy(bitMapData+4*currentIndex, bitMapData+4*preCurrentIndex, 4);
            }
            
            
        }
    }
    
    //第七步:获取图片数据集合
    NSUInteger size = width * height * 4;
    CGDataProviderRef providerRef = CGDataProviderCreateWithData(NULL, bitMapData, size, NULL);
    //第八部:创建马赛克图片
    /*
     参数1:宽
     参数2:高
     参数3:表示每一个像素点,每一个分量的大小
     参数4:每一个像素点的大小
     参数5:每一行内存大小
     参数6:颜色空间
     参数7:位图信息
     参数8:数据源(数据集合)
     参数9:数据解码器
     参数10:是否抗锯齿
     参数11:渲染器
     */
    CGImageRef mossicImageRef = CGImageCreate(width,
                                              height,
                                              8,
                                              4*8,
                                              width*4,
                                              colorSpaceRef,kCGImageAlphaPremultipliedLast,
                                              providerRef,
                                              NULL,
                                              NO,
                                              kCGRenderingIntentDefault);
    
    //第九步:创建输出马赛克图片(填充颜色)
    CGContextRef outContextRef = CGBitmapContextCreate(nil,
                                                       width,
                                                       height,
                                                       8,
                                                       width*4,
                                                       colorSpaceRef,
                                                       kCGImageAlphaPremultipliedLast);
    //绘制图片
    CGContextDrawImage(outContextRef, CGRectMake(0, 0, width, height),mossicImageRef);
    
    //创建图片
    CGImageRef resultImageRef = CGBitmapContextCreateImage(outContextRef);
    UIImage *outImage = [UIImage imageWithCGImage:resultImageRef];
    
    
    
    //释放内存
    CGImageRelease(resultImageRef);
    CGImageRelease(mossicImageRef);
    CGColorSpaceRelease(colorSpaceRef);
    CGDataProviderRelease(providerRef);
    CGContextRelease(outContextRef);
    

    
    return outImage;
}
@end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容