總結一下最近看到的關於圖像處理的知識
當前流行的圖片處理方式有
首先講解一下32位RGBA模式
UIImage * image = [UIImage imageNamed:@"back.png"];
// 1.
/*
把UIImage對象轉換爲需要的核心圖形庫調用的CGImage對象。同時,得到圖形的寬度和高度。
*/
CGImageRef inputCGImage = [image CGImage];
NSUInteger width = CGImageGetWidth(inputCGImage);
NSUInteger height = CGImageGetHeight(inputCGImage);
// 2.
/*
由於使用的是32位RGB顏色空間模式,你需要定義一些參數bytesPerPixel(每個像素大小),bitsPerComponent(每個顏色通道大小),計算出bytesPerRow(每行多大)。數組存儲像素的值
*/
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
UInt32 * pixels;
pixels = (UInt32 *) calloc(height * width, sizeof(UInt32));
// 3.
/*
創建一個RGB模式的顏色空間CGColorSpace和一個容器CGBitmapContext,將像素指針參數傳遞到容器中緩存進行存儲。
*/
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixels, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
// 4.
/*
把緩存中的圖形繪製在顯示器上,像素的填充格式有創建context的時候進行指定的
*/
CGContextDrawImage(context, CGRectMake(0, 0, width, height), inputCGImage);
// 5. Cleanup
/*
清除colorSpace和context
*/
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
NOTE:當你繪製圖像的時候,設備的GPU會進行解碼並將它顯示在屏幕。爲了訪問本地數據,你需要一份像素的複製,就像剛纔做的那樣
此時此刻,pixels存儲着圖像的所有像素信息。下面的幾行代碼會對pixels進行遍歷,並打印:
// 1.
/*
定義一些簡單處理32位像素的宏。爲了得到紅色通道的值,你需要得到前8位,以此類推
*/
#define Mask8(x) ( (x) & 0xFF )
#define R(x) ( Mask8(x) )
#define G(x) ( Mask8(x >> 8 ) )
#define B(x) ( Mask8(x >> 16) )
NSLog(@"Brightness of image:");
// 2.
/*
定義一個指向第一個像素的指針,並使用2個for循環來遍歷像素
*/
UInt32 * currentPixel = pixels;
for (NSUInteger j = 0; j < height; j++) {
for (NSUInteger i = 0; i < width; i++) {
// 3.
/*
得到當前像素的值賦值給currentPixel並把它的亮度值打印出來
*/
UInt32 color = *currentPixel;
printf("%3.0f ",(R(color)+G(color)+B(color))/3.0);
// 4.
/*
增加currentPixel的值,使它指向下一個像素。如果你對指針的運算比較生疏,記住這個:currentPixel是一個指向UInt32的變量,當你把它加1後,他就會向前移動4字節(32位),然後指向下一個像素值
提示:還有一種非正統的方法就是把currentPiexl聲明爲一個指向8字節的類型的指針,比如char。這種方法,你每增加1,你將會移動圖形的下一個顏色通道。與它進行位移運算,你會得到顏色通道的8位數值。
*/
currentPixel++;
}
printf("\n");
}
原圖修改
根據前面所介紹的方法,將圖片的inputImage繪製出來,並且返回在桌面上
#pragma mark - Private
#define Mask8(x) ( (x) & 0xFF )
#define R(x) ( Mask8(x) )
#define G(x) ( Mask8(x >> 8 ) )
#define B(x) ( Mask8(x >> 16) )
#define A(x) ( Mask8(x >> 24) )
#define RGBAMake(r, g, b, a) ( Mask8(r) | Mask8(g) << 8 | Mask8(b) << 16 | Mask8(a) << 24 )
- (UIImage *)processUsingPixels:(UIImage*)inputImage {
// 1. Get the raw pixels of the image
UInt32 * inputPixels;
CGImageRef inputCGImage = [inputImage CGImage];
NSUInteger inputWidth = CGImageGetWidth(inputCGImage);
NSUInteger inputHeight = CGImageGetHeight(inputCGImage);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSUInteger bytesPerPixel = 4;
NSUInteger bitsPerComponent = 8;
NSUInteger inputBytesPerRow = bytesPerPixel * inputWidth;
inputPixels = (UInt32 *)calloc(inputHeight * inputWidth, sizeof(UInt32));
CGContextRef context = CGBitmapContextCreate(inputPixels, inputWidth, inputHeight,
bitsPerComponent, inputBytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, inputWidth, inputHeight), inputCGImage);
UIImage * ghostImage = [UIImage imageNamed:@"ghost"];
CGImageRef ghostCGImage = [ghostImage CGImage];
//把ghost圖片寬度縮小25%,並把它的圓點設定在ghostOrigin上(在inputImage的位置)
CGFloat ghostImageAspectRatio = ghostImage.size.width / ghostImage.size.height;
NSInteger targetGhostWidth = inputWidth * 0.25;
CGSize ghostSize = CGSizeMake(targetGhostWidth,targetGhostWidth / ghostImageAspectRatio);
CGPoint ghostOrigin = CGPointMake(inputWidth * 0.5,inputHeight * 0.2);
//創建ghost圖片的緩存圖
NSUInteger ghostBytesPerRow = bytesPerPixel * ghostSize.width;
UInt32 * ghostPixels = (UInt32 *)calloc(ghostSize.width * ghostSize.height, sizeof(UInt32));
CGContextRef ghostContext = CGBitmapContextCreate(ghostPixels,ghostSize.width, ghostSize.height,bitsPerComponent, ghostBytesPerRow, colorSpace,
kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(ghostContext, CGRectMake(0, 0, ghostSize.width, ghostSize.height),ghostCGImage);
/*現在已經到了把幽靈圖像合併到你的照片中的最佳時間了。
合併:像前面提到的,每一個顏色都有一個透明通道來標識透明度。並且,你每創建一張圖像,每一個像素都會有一個顏色值。
所以,如果遇到有透明度和半透明的顏色值該如何處理呢?
答案是,對透明度進行混合。在最頂層的顏色會使用一個公式與它後面的顏色進行混合。公式如下:
NewColor = TopColor * TopColor.Alpha + BottomColor * (1 - TopColor.Alpha) ;
這是一個標準的線性差值方程。
·當頂層透明度爲1時,新的顏色值等於頂層顏色值。
·當頂層透明度爲0時,新的顏色值於底層顏色值。
·最後,當頂層的透明度值是0到1之前的時候,新的顏色值會混合借於頂層和底層顏色值之間。
還可以用 premultiplied alpha的方法。
當處理成千上萬像素的時候,他的性能會得以發揮。
*/
//通過對幽靈圖像像素數的循環和offsetPixelCountForInput獲得輸入的圖像。記住,雖然你使用的是2維數據存儲圖像,但在內存他它實際上是一維的。
NSUInteger offsetPixelCountForInput = ghostOrigin.y * inputWidth + ghostOrigin.x;
for (NSUInteger j = 0; j < ghostSize.height; j++) {
for (NSUInteger i = 0; i < ghostSize.width; i++) {
UInt32 * inputPixel = inputPixels + j * inputWidth + i + offsetPixelCountForInput;
UInt32 inputColor = *inputPixel;
UInt32 * ghostPixel = ghostPixels + j * (int)ghostSize.width + i;
UInt32 ghostColor = *ghostPixel;
// Do some processing here
}
}
return inputImage;
}
// Blend the ghost with 50% alpha (重點)
CGFloat ghostAlpha = 0.5f * (A(ghostColor) / 255.0);
UInt32 newR = R(inputColor) * (1 - ghostAlpha) + R(ghostColor) * ghostAlpha;
UInt32 newG = G(inputColor) * (1 - ghostAlpha) + G(ghostColor) * ghostAlpha;
UInt32 newB = B(inputColor) * (1 - ghostAlpha) + B(ghostColor) * ghostAlpha;
// Clamp, not really useful here :p
newR = MAX(0,MIN(255, newR));
newG = MAX(0,MIN(255, newG));
newB = MAX(0,MIN(255, newB));
*inputPixel = RGBAMake(newR, newG, newB, A(inputColor));
最後一句,將返回值替換
// Create a new UIImage
CGImageRef newCGImage = CGBitmapContextCreateImage(context);
UIImage * processedImage = [UIImage imageWithCGImage:newCGImage];
return processedImage;
// Convert the image to black and white
for (NSUInteger j = 0; j < inputHeight; j++) {
for (NSUInteger i = 0; i < inputWidth; i++) {
UInt32 * currentPixel = inputPixels + (j * inputWidth) + i;
UInt32 color = *currentPixel;
// Average of RGB = greyscale
UInt32 averageColor = (R(color) + G(color) + B(color)) / 3.0;
*currentPixel = RGBAMake(averageColor, averageColor, averageColor, A(color));
}
}
最後的一步就是清除內存。ARC不能代替你對CGImageRefs和CGContexts進行管理。添加如下代碼到返回語句之前。
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);
CGContextRelease(ghostContext);
free(inputPixels);
free(ghostPixels);
raywenderlich.com/wp-content/uploads/2014/03/SpookCam-Starter.zip