iOS图片处理

作者: yahibo | 来源:发表于2019-07-11 13:57 被阅读0次

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

    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.png

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

    gray2.jpg

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

    1、浮点算法:R = G = B = 0.3*R + 0.59*G + 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.jpg

    四、图片调色

    通过对以上方法的了解,我们还可以不设置灰度值,通过给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对象,解码后返回主线程显示
    • 3、资源可能在bundle中,读取耗时,切换至Assets.xcassets中即可

    异步加载如下:

     dispatch_async(dispatch_get_global_queue(0, 0), ^{
            UIImage *image = [UIImage imageNamed:icon];
            dispatch_async(dispatch_get_main_queue(), ^{
                cell.leftImageView.image = image;
            });
        });
    

    相关文章

      网友评论

        本文标题:iOS图片处理

        本文链接:https://www.haomeiwen.com/subject/qqyrkctx.html