iOS 簡單圖片濾鏡效果

目前好多APP都具備濾鏡美顏效果功能,作為iOS開發者,多少也要懂一點圖片的處理效果,本文章只是簡單介紹基本的濾鏡效果:

一、圖片儲存原理

要對圖片進行修改,那首先就要了解他的儲存原理,在iOS開發中接觸最多的主要的圖片格式:jpg、png、webp,不管是什么格式的圖片,可以看成是一個個像素點構成,這一個個點就是RGBA(jpg格式沒有A通道)。當我們儲存圖片時,系統都會有對應的一個緩沖池(buffer)進行存儲,buffer裝載的信息大致可以分為兩個部分:
一個是Info,這個是圖片的描述信息,
另外一個就是Data,這里面就是圖片數據信息,但是這不是最原始的RGB數據,而是經過壓縮編碼后的數據,我們要修改的也是這個數據。
簡單畫個圖理解下:


buffer

二、濾鏡效果

實現濾鏡效果的步驟:

1、將UIImage轉化為Data數據
2、Data數據進行灰度、色彩、美白等處理
3、將處理后的Data數據載轉化回UIImage

根據以上三步,做如下操作:

1、將UIImage轉化為Data數據

這里需要介紹一個方法,參數說明詳見

//Bitmap上下文(位圖結構)
CGBitmapContextCreate(<#void * _Nullable data#>, <#size_t width#>, <#size_t height#>, <#size_t bitsPerComponent#>, <#size_t bytesPerRow#>, <#CGColorSpaceRef  _Nullable space#>, <#uint32_t bitmapInfo#>)
//image ---> data
- (unsigned char*)p_imageToData:(UIImage *)image {
    //使用的是框架是CoreGraphics,所以要把image ——> CGImage
    CGImageRef imageRef = [image CGImage];
    CGSize imageSize = image.size;
    //顏色空間
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    //分配bit級空間大小,一個像素點分為 R G B A 為4byte,像素點個數 = 寬*高
    void *data = malloc(4*imageSize.width*imageSize.height);
    //Bitmap上下文
    //kCGImageAlphaPremultipliedLast 當前顏色的排列順序
    //kCGBitmapByteOrder32Big 位數  4*8
    CGContextRef context = CGBitmapContextCreate(data, imageSize.width, imageSize.height, 8, 4*imageSize.width, colorSpaceRef, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
    //渲染
    //參數1:bitmap上下文
    //參數2:需要渲染的空間大小
    //參數3:原始圖片
    CGContextDrawImage(context, CGRectMake(0, 0, imageSize.width, imageSize.height), imageRef);
    //釋放
    CGColorSpaceRelease(colorSpaceRef);
    CGContextRelease(context);
    return (unsigned char*)data;
}
2、Data數據進行灰度、色彩、美白等處理

a、灰度處理


灰度顏色

上面幾組顏色,他們的RGB值就是顯示的對應的值,可見當RGB值都相等時,表現出來的都是灰色,只是程度不同而已。灰度處理也是這個原理,將像素點的RGB設置成相等的,這里提供一個灰度處理公式:

Gray = 0.299*Red +0.587*Green +0.114*Blue

- (unsigned char*)p_grayDataWithData:(unsigned char*)imageData imageSize:(CGSize)imageSize {
    unsigned char* data = malloc(4*sizeof(unsigned char)*imageSize.width*imageSize.height);
    //初始化data 內存
    //參數1:內存地址
    //參數2:填充的值
    //參數3:需要填充的內存空間大小
    memset(data, 0, 4*imageSize.width*imageSize.height);
    for (int h = 0; h < imageSize.height; h++) {
        for (int w = 0; w < imageSize.width; w++) {
            //當前像素點的位置
            unsigned int index = h*imageSize.width+w;
            //取出原始的RGBA
            //imageData+index*4: imageData地址加上偏移量(每個像素4byte)
            unsigned char red = *(imageData+index*4);
            unsigned char green = *(imageData+index*4+1);
            unsigned char blue = *(imageData+index*4+2);
            //灰度處理
            unsigned int newRGB = red*0.299+green*0.587+blue*0.114;
            //可能算出來的值大于255
            newRGB = newRGB > 255? 255:newRGB;
            memset(data+index*4, newRGB, 1);
            memset(data+index*4+1, newRGB, 1);
            memset(data+index*4+2, newRGB, 1);
        }
    }
    return data;
}

上面代碼提到的有關內存的,畫了個簡單的圖幫助理解:


b、簡單美白處理
本文采用映射表的方法處理美白效果,0-255之間有256個點,按每組32個,分8組,節點為55,110,155,185,225,240,250,255,然后再將段按照32等分分割,畫個圖說明下:


- (unsigned char*)p_skinWhiteWithData:(unsigned char*)imageData imageSize:(CGSize)imageSize {
    void *data = malloc(4*imageSize.width*imageSize.height);
    memset(data, 0, imageSize.width*imageSize.height);
    NSArray *array = @[@"55",@"110",@"155",@"185",@"225",@"240",@"250",@"255"];
    NSMutableArray *colorArray = [[NSMutableArray alloc] init];
    int last = 0;//記錄前一次的值,一開始為 0
    for (int i = 0; i < 8; i++) {
        int num = [array[i] intValue];
        float step = (num - last)/32.0;//步長,每次增加多少
        for (int j = 0; j < 32; j++) {
            float newNum = last+step*j;//上一次的值加上步長*j 得到對應位置的值
            NSString *newNumStr = [NSString stringWithFormat:@"%lf",newNum];
            [colorArray addObject:newNumStr];
        }
        last = num;
    }
    for (int h = 0; h < imageSize.height; h++) {
        for (int w = 0; w < imageSize.width; w++) {
            unsigned int index = h*imageSize.width+w;
            //取出原始的RGBA
            unsigned int red = *(imageData+index*4);
            unsigned int green = *(imageData+index*4+1);
            unsigned int blue = *(imageData+index*4+2);
            //美白處理
            unsigned int newRed = [colorArray[red] floatValue];
            unsigned int newGreen = [colorArray[green] floatValue];
            unsigned int newBlue = [colorArray[blue] floatValue];
            memset(data+index*4, newRed, 1);
            memset(data+index*4+1, newGreen, 1);
            memset(data+index*4+2, newBlue, 1);
        }
    }
    return data;
    
}

c、彩色底版處理
同樣給出一個處理彩色底版的算法:

newValue = 255 - oldValue

- (unsigned char*)p_colorDataWithData:(unsigned char*)imageData imageSize:(CGSize)imageSize {
    unsigned char* data = malloc(4*sizeof(unsigned char)*imageSize.width*imageSize.height);
    //初始化data 內存, 1:內存地址  2:填充的值  3:需要填充的內存空間大小
    memset(data, 0, 4*imageSize.width*imageSize.height);
    for (int h = 0; h < imageSize.height; h++) {
        for (int w = 0; w < imageSize.width; w++) {
            unsigned int index = h*imageSize.width+w;
            //取出原始的RGBA
            unsigned char red = *(imageData+index*4);
            unsigned char green = *(imageData+index*4+1);
            unsigned char blue = *(imageData+index*4+2);
            //彩色處理
            unsigned int newRed = 255-red;
            unsigned int newGreen = 255-green;
            unsigned int newBlue = 255-blue;
            memset(data+index*4, newRed, 1);
            memset(data+index*4+1, newGreen, 1);
            memset(data+index*4+2, newBlue, 1);
        }
    }
    return data;
}
3、將處理后的Data數據載轉化回UIImage
- (UIImage *)p_dataToImage:(unsigned char*)imageData imageSize:(CGSize)imageSize {
    //原始數據   4*imageSize.width*imageSize.height 數據空間大小
    CGDataProviderRef dataProRef = CGDataProviderCreateWithData(NULL, imageData, 4*imageSize.width*imageSize.height, NULL);
    
    CGImageRef imageRef = CGImageCreate(imageSize.width, imageSize.height, 8, 32, 4*imageSize.width, CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrderDefault, dataProRef, NULL, NO, kCGRenderingIntentDefault);
    UIImage *imageNew = [UIImage imageWithCGImage:imageRef];
    //釋放
    CFRelease(imageRef);
    CGDataProviderRelease(dataProRef);
    return imageNew;
}

四、結果

直接上效果圖:


效果圖

五、總結

以上是簡單的處理效果,如果想要多樣的處理效果就是計算新的RGB值的算法優化問題了,你們也可以隨便改,會出現不同的效果呦。最后送上本Demo的鏈接地址:https://github.com/xyzcwb/ImageDemo

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容