en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

4.06.2012

影像與資料陣列的轉換(上)

  

影像與資料陣列的轉換,是在對圖片做影像處理時會使用到的技巧,目的是將圖片轉換至人類可以理解的 RGBA 的資料結構陣列上,接著分別對每個像素點做影像處理,最後將處理的結果的轉換成影像並顯示,下面所要介紹的方法為通用方法,除了 iOS,您也可以使用在 MAC OS X Cocoa Application 上。


資料結構的宣告
首先要先了解影像處理所要使用的色域,在示範中主要是針對 32 位元的深度的影像,也就是包含 RGBA 資訊的影像(紅色、綠色、藍色和透明度,每個資訊共 8 位元, 0-255 的表示值),並使用 typedef struct 來定義其結構(rgba順序要正確)。
typedef struct RGBAData {
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;
} RGBAData;


將影像轉入資料陣列中
接下來要利用先前所製作的資料結構將影像轉存進來,方法是利用此結構製作一個與影像相同大小的一維陣列,並利用 CGContextRef 來做影像與資料結構的轉換。
//將圖片轉換成 CGImageRef 格式並取得大小
CGImageRef imageRef = [myImageView.image CGImage];
int width = CGImageGetWidth(imageRef);
int height = CGImageGetHeight(imageRef);

//資料結構的參數和色域
NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * width;
NSUInteger bitsPerComponent = 8;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

//宣告一個與圖片大小相同的資料結構一維陣列
RGBAData *sourceData = malloc(height * width * bytesPerPixel);

//將圖片資訊寫入資料結構陣列中
CGContextRef context = CGBitmapContextCreate((unsigned char *)sourceData,
                                             width,
                                             height,
                                             bitsPerComponent,
                                             bytesPerRow,
                                             colorSpace,
                                             kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);

現在我們已經成功將影像內的資訊轉存到 sourceData 中,而 sourceData 的形態為 RGBAData 結構的一維陣列。


對資料陣列做影像處理
將影像轉換成人類可以理解的資料結構之後,就可以對此結構做影像處理,下列程式碼是對影像做鏡射的簡單示範(將 sourceData 中的資訊做反序排列)。
//製作一個新的資料結構陣列來存放處理的結果
RGBAData *resultData = malloc(height * width * bytesPerPixel);

//影像做鏡射
for (int y=0; y!=height; y++)
    for (int x=0; x!=width; x++) {

        //取得陣列中像素的索引值
        int pixelIndex = (width * y) + x ;

        //製作鏡射
        int length = width * height;
        resultData[pixelIndex].r = sourceData[length-1 - pixelIndex].r;
        resultData[pixelIndex].g = sourceData[length-1 - pixelIndex].g;
        resultData[pixelIndex].b = sourceData[length-1 - pixelIndex].b;
        resultData[pixelIndex].a = 255;
}

程式碼到此,我們已經成功將鏡射後的影像資訊放在 resultData 中,接下來就是將此結構內的資訊在轉換成電腦能解讀圖片資訊。


將資料陣列中的資訊轉成圖片
同樣透過 CGContextRef 來做資料結構與影像之間的轉換,在此必須注意,由於轉換是對等的,所以在製作 CGContextRef 時,參數大小和色域模型都必須和之前的 CGContextRef 保持一致。
//先轉成CGContextRef
context = CGBitmapContextCreate((unsigned char *)resultData,
                                width,
                                height,
                                bitsPerComponent,
                                bytesPerRow,
                                colorSpace,
                                kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

//再由CGContextRef轉成CGImageRef
CGImageRef cgImage=CGBitmapContextCreateImage(context);

//放入UIImageView
[myImageView setImage:[UIImage imageWithCGImage:cgImage]];


ps:補充一下大致的轉換過程,以便釐清每個步驟的程式碼。
UIImage -> CGImageRef -> CGContextRef -> RGBA Pixel Data -> Image Process -> New RGBA Pixel Data -> CGContextRef -> CGImageRef -> UIImage













沒有留言:

張貼留言