一、图片大小
二、强制解压缩
三、灰度图片
四、图片调色
五、设置马赛克
一、图片大小
位图:由一个个像素点组成的图像
图片像素点个数:就是图片宽高乘积
一个像素点的大小: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
值,否则就是一个纯色图片,一片空白:
这个就是所有像素点RGB
值取相同的结果。我们需要将每个像素点的RGB
值区分开,图片轮廓就有了。如下图:
下面有几种转换为灰度的方法:
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、创建好需要的
icon
的image
对象,在滑动前能够完成所有的解码操作,因此能够解决卡顿问题 - 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;
});
});
网友评论