iOS图片处理

一、图片大小
二、强制解压缩
三、灰度图片
四、图片调色
五、设置马赛克

hibo.jpg

一、图片大小

位图:由一个个像素点组成的图像
图片像素点个数:就是图片宽高乘积
一个像素点的大小:4个字节(存放RGBA值,每一分量占1个字节)
图片大小:像素点个数乘以4个字节,即size = w * h * 4

下面打印一下看看图片的大小是否和计算的一致:

//图片大小
-(void)picSize{
    _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.width)];
    [self.view addSubview:_imageView];
    UIImage *image = [UIImage imageNamed:@"girl.jpg"];
    _imageView.image = image;
    //获取原始的二进制数
    CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
    NSLog(@"%lu",sizeof(uint8_t));
    NSLog(@"%lu",sizeof(uint32_t));
    NSLog(@"%ld",[(__bridge NSData *)rawData length]);
    NSLog(@"%f",image.size.height*image.size.width*4);
}

打印结果:

2019-07-10 21:48:25.401816+0800 图片处理[972:33257] 1
2019-07-10 21:48:25.402106+0800 图片处理[972:33257] 4
2019-07-10 21:48:25.402533+0800 图片处理[972:33257] 2465280
2019-07-10 21:48:25.402734+0800 图片处理[972:33257] 2465280.000000

通过length得到的图片大小和w * h * 4得到的一致。

二、强制解压缩

1、创建绘制图片的上下文

CGBitmapContextCreate(void * __nullable data,
    size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,
    CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)

参数:

data:所需要的内存空间 传nil会自动分配
width/height:当前画布的大小
bitsPerComponent:每个颜色分量的大小 RGBA每一个分量占1个字节
bytesPerRow:每一行使用的字节数4*width
bitmapInfo:RGBA绘制的顺序

2、根据数据源在上下文(画板)绘制图片

CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);

重新绘制可以压缩图片。下面设置一下压缩尺寸进行对比:

//图片处理-强制解压缩操作-把元数据绘制到当前的上下文-压缩图片
-(UIImage*)imageDetail:(UIImage *)image{
    //获取当前图片数据源
    CGImageRef imageRef = image.CGImage;
    //设置大小改变压缩图片
    NSUInteger width = CGImageGetWidth(imageRef)/3;
    NSUInteger height = CGImageGetHeight(imageRef)/3;
    //创建颜色空间
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
    /*
     创建绘制当前图片的上下文
     CGBitmapContextCreate(void * __nullable data,
     size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,
     CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)
     data:所需要的内存空间 传nil会自动分配
     width/height:当前画布的大小
     bitsPerComponent:每个颜色分量的大小 RGBA 每一个分量占1个字节
     bytesPerRow:每一行使用的字节数 4*width
     bitmapInfo:RGBA绘制的顺序
     */
    CGContextRef contextRef =
    CGBitmapContextCreate(nil,
                          width,
                          height,
                          8,
                          4*width,
                          colorSpace,
                          kCGImageAlphaNoneSkipLast);
    //根据数据源在上下文(画板)绘制图片
    CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);
    
    imageRef = CGBitmapContextCreateImage(contextRef);
    CGContextRelease(contextRef);
    return [UIImage imageWithCGImage:imageRef scale:image.scale orientation:UIImageOrientationUp];
}

运行对比图片清晰度:

compare.jpg

三、灰度图片

图片是由一个个像素点构成,一个像素点表示一种色彩,而色彩是由红绿蓝(RGB)三原色组成。那么将每个像素的分量值取相同值,颜色就变成了黑色、白色或灰色,而由这些点组成的图片就叫做灰度图片也称之为黑白照片。
彩色图片转灰度,只需要将图片的每个像素点的RGB值取相同值即可,但也要区别与其他像素点的RGB值,否则就是一个纯色图片,一片空白:

gray1.jpg

这个就是所有像素点RGB值取相同的结果。我们需要将每个像素点的RGB值区分开,图片轮廓就有了。如下图:

gray2.png

下面有几种转换为灰度的方法:

1、浮点算法:R = G = B = 0.3R + 0.59G + 0.11*B
2、平均值法:R = G = B = (R+G+B)/3
3、任取一个分量色:R = G = B = R或G或B

代码如下:

//灰度图片
-(UIImage *)grayImage:(UIImage *)image{
    CGImageRef imageRef = image.CGImage;
    //1、获取图片宽高
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetWidth(imageRef);
    //2、创建颜色空间
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    //3、根据像素点个数创建一个所需要的空间
    UInt32 *imagePiexl = (UInt32 *)calloc(width*height, sizeof(UInt32));
    CGContextRef contextRef = CGBitmapContextCreate(imagePiexl, width, height, 8, 4*width, colorSpaceRef, kCGImageAlphaNoneSkipLast);
    //4、根据图片数据源绘制上下文
    CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), image.CGImage);
    //5、将彩色图片像素点重新设置颜色
    //取平均值 R=G=B=(R+G+B)/3
    for (int y=0; y<height; y++) {
        for (int x=0; x<width; x++) {
            //计算平均值重新存储像素点-直接操作像素点
            uint8_t *rgbPiexl = (uint8_t *)&imagePiexl[y*width+x];
            //rgbPiexl[0],rgbPiexl[1],rgbPiexl[2];
            //(rgbPiexl[0]+rgbPiexl[1]+rgbPiexl[2])/3;
            uint32_t gray = rgbPiexl[0]*0.3+rgbPiexl[1]*0.59+rgbPiexl[2]*0.11;
            rgbPiexl[0] = gray;
            rgbPiexl[1] = gray;
            rgbPiexl[2] = gray;
        }
    }
    //根据上下文绘制
    CGImageRef finalRef = CGBitmapContextCreateImage(contextRef);
    //释放用过的内存
    CGContextRelease(contextRef);
    CGColorSpaceRelease(colorSpaceRef);
    free(imagePiexl);
    return [UIImage imageWithCGImage:finalRef scale:image.scale orientation:UIImageOrientationUp];
}

分别使用以上提到的灰度处理方法显示图片,效果如下:

show.png

四、图片调色

通过对以上方法的了解,我们还可以不设置灰度值,通过给RGB每个分量加入不同系数来对图片调色。
代码如下:

//调色
-(void)setRGB{
    _imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, WIDTH, WIDTH)];
    [self.view addSubview:_imageView];
    UIImage *image = [UIImage imageNamed:@"head.jpg"];
    _imageView.image = [self setRGBImage:image R:1.0 g:1.0 b:1.0];
    
    NSArray *arr = @[@"R",@"G",@"B"];
    for (int i=0; i<3; i++) {
        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(50, WIDTH+30+50*i, WIDTH, 40)];
        label.text = arr[i];
        [label sizeToFit];
        [self.view addSubview:label];
        UISlider *slider  = [[UISlider alloc] initWithFrame:CGRectMake(100, WIDTH+20+50*i, WIDTH-200, 40)];
        slider.tag = I;
        slider.value = 1.0;
        [slider addTarget:self action:@selector(event:) forControlEvents:UIControlEventValueChanged];
        [self.view addSubview:slider];
        [self event:slider];
    }
    
}
-(void)event:(UISlider *)slider{
    static float r,g,b,a;
    if (slider.tag==0) {
        r = slider.value;
    }else if(slider.tag==1){
        g = slider.value;
    }else if(slider.tag==2){
        b = slider.value;
    }
    UIImage *image = [UIImage imageNamed:@"head.jpg"];
    NSLog(@"%f",a);
    _imageView.image = [self setRGBImage:image R:r g:g b:b];
}
-(UIImage *)setRGBImage:(UIImage *)image R:(CGFloat)rk g:(CGFloat)gk b:(CGFloat)bk{
    CGImageRef imageRef = image.CGImage;
    //1、获取图片宽高
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetWidth(imageRef);
    //2、创建颜色空间
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    //3、根据像素点个数创建一个所需要的空间
    UInt32 *imagePiexl = (UInt32 *)calloc(width*height, sizeof(UInt32));
    CGContextRef contextRef = CGBitmapContextCreate(imagePiexl, width, height, 8, 4*width, colorSpaceRef, kCGImageAlphaNoneSkipLast);
    //4、根据图片数据源绘制上下文
    CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);
    //5、将彩色图片像素点重新设置颜色
    //取平均值 R=G=B=(R+G+B)/3
    for (int y=0; y<height; y++) {
        for (int x=0; x<width; x++) {
            //操作像素点
            uint8_t *rgbPiexl = (uint8_t *)&imagePiexl[y*width+x];
            //该色值下不做处理
            if (rgbPiexl[0]>245&&rgbPiexl[1]>245&&rgbPiexl[2]>245) {
                
            }else{
                rgbPiexl[0] = rgbPiexl[0]*rk;
                rgbPiexl[1] = rgbPiexl[1]*gk;
                rgbPiexl[2] = rgbPiexl[2]*bk;
            }
            
        }
    }
    //根据上下文绘制
    CGImageRef finalRef = CGBitmapContextCreateImage(contextRef);
    //释放用过的内存
    CGContextRelease(contextRef);
    CGColorSpaceRelease(colorSpaceRef);
    free(imagePiexl);
    return [UIImage imageWithCGImage:finalRef scale:image.scale orientation:UIImageOrientationUp];
}

滑动滑块观察颜色变化。如下:

rgb.jpg

五、设置马赛克

马赛克就是让图片看上去模糊不清。将特定区域的像素点设置为同一种颜色,整体就会变得模糊,区域块越大越模糊,越小越接近于原始像素。
同样使用强制解压缩操作,操作像素点,马赛克部分实际操作:

1、设置区域大小;
2、在该区域获取一个像素点(第一个)作为整个区域的取色;
3、将取色设置到区域中;
4、取下一个区域同上去色设置区域。

C库函数 - memcpy()

memcpy(void *__dst, const void *__src, size_t __n);

第一个参数:指向用于存储复制内容的目标数组;
第二个参数:指向要复制的数据源;
第三个参数:要复制的字节数。

代码实现:

//马赛克
-(UIImage*)mosaicWithImage:(UIImage *)image{
    CGImageRef imageRef = image.CGImage;
    //1、获取图片宽高
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetWidth(imageRef);
    //2、创建颜色空间
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    //3、根据像素点个数创建一个所需要的空间
    UInt32 *imagePiexl = (UInt32 *)calloc(width*height, sizeof(UInt32));
    CGContextRef contextRef = CGBitmapContextCreate(imagePiexl, width, height, 8, 4*width, colorSpaceRef, kCGImageAlphaNoneSkipLast);
    //4、根据图片数据源绘制上下文
    CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), imageRef);
    //5、获取像素数组
    UInt8 *bitmapPixels = CGBitmapContextGetData(contextRef);
    UInt8 *pixels[4] = {0};
    NSUInteger currentPixels = 0;//当前的像素点
    NSUInteger preCurrentPiexls = 0;//
    NSUInteger mosaicSize = 20;//马赛克尺寸
    for (NSUInteger i = 0;  i < height - 1; i++) {
        for (NSUInteger j = 0 ; j < width - 1; j++) {
            currentPixels = i * width + j;
            if (i % mosaicSize == 0) {
                if (j % mosaicSize == 0) {
                    memcpy(pixels, bitmapPixels + 4 * currentPixels, 4);
                }else{
                    memcpy(bitmapPixels + 4 * currentPixels, pixels, 4);
                }
            }else{
                preCurrentPiexls = (i - 1) * width + j;
                memcpy(bitmapPixels + 4 * currentPixels, bitmapPixels + 4 * preCurrentPiexls, 4);
            }
        }
    }
    //根据上下文创建图片数据源
    CGImageRef finalRef = CGBitmapContextCreateImage(contextRef);
    //释放用过的内存
    CGContextRelease(contextRef);
    CGColorSpaceRelease(colorSpaceRef);
    free(imagePiexl);
    return [UIImage imageWithCGImage:finalRef scale:image.scale orientation:UIImageOrientationUp];
}

显示图片:

mosaic.jpg

这里将区域尺寸定死了,我们还可以设置一个滑动条来动态设置区域大小。如下图:

masaic.gif

五、应用遇到的问题

问题1:

个人中心,tableview加载列表,列表加载icon引起的卡顿。

cell.leftImageView.image = [UIImage imageNamed:icon];

imageName:bundle中读取图片,从bundle顶层文件夹开始查找,为耗时操作,找到后,进行解码操作并加入缓存,下次使用直接从缓存取即可,但解决不了第一次加载卡顿的问题。

解决方法:

  • 1️⃣、创建好需要的iconimage对象,在滑动前能够完成所有的解码操作,因此能够解决卡顿问题
  • 2️⃣、异步加载image对象,解码后返回主线程显示

异步加载如下:

 dispatch_async(dispatch_get_global_queue(0, 0), ^{
        UIImage *image = [UIImage imageNamed:icon];
        dispatch_async(dispatch_get_main_queue(), ^{
            cell.leftImageView.image = image;
        });
    });
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值