vImage學(xué)習(xí)筆記(一)——概述
一、關(guān)于圖像格式 Image Formats
圖像格式(Image Formats)規(guī)定了像素數(shù)據(jù)如何在內(nèi)存中存儲莉测。圖像文件格式(比如JPG、PNG唧喉、GIF等)用來在程序中轉(zhuǎn)換圖像數(shù)據(jù)捣卤,并將數(shù)據(jù)存儲在硬盤上忍抽。諸如Image I/O等框架可以從硬盤加載各種格式的圖像文件,并且在內(nèi)存中使用董朝。在內(nèi)存中鸠项,圖像是通過二維數(shù)組來存儲像素數(shù)據(jù)的,圖像中的每個像素都對應(yīng)數(shù)組中的一個元素子姜。
圖像格式(Image Formats)包含兩種類型:二維平面型(planar)和交叉型(interleaved)祟绊。二維平面型圖像把不同通道的數(shù)據(jù)存儲在不同的緩沖區(qū)內(nèi),一個典型的平面型圖像通常包括red闲询、green久免、blue和alpha四個通道浅辙。交叉型圖像把不同通道的數(shù)據(jù)輪換著存儲:ARGBARGBARGB……
圖像數(shù)據(jù)可以是整型(integer)和浮點型(float)扭弧。在vImage中,通過一個8位(bit)的無符號整型數(shù)值表示色飽和度等級记舆。數(shù)值范圍0255鸽捻,255表示最大色飽和度,0表示無色飽和度泽腮。浮點型數(shù)值通常從0.01.0表示色飽和度御蒲。
以下是核心操作使用到的圖像格式:
- Planar8 單通道(顏色或alpha)圖像。每個像素都是一個8bit的無符號整數(shù)诊赊,數(shù)據(jù)類型是Pixel_8
- PlanarF 單通道(顏色)圖像厚满。每個像素都是一個32bit的浮點數(shù),數(shù)據(jù)類型是Pixel_F
- ARGB8888 圖像包含四個交叉通道碧磅,alpha碘箍、red、green鲸郊、blue丰榴,順序固定。每個像素都是一個32位的數(shù)組(包含四個8位無符號整數(shù))秆撮。數(shù)據(jù)類型是Pixel_8888
- ARGBFFFF 圖像包含四個交叉通道四濒,alpha、red职辨、green盗蟆、blue,順序固定舒裤。每個像素都是一個包含四個浮點數(shù)的數(shù)組姆涩。數(shù)據(jù)類型是Pixel_FFFF
- RGBA8888 圖像包含四個交叉通道,red惭每、green骨饿、blue亏栈、alpha,順序固定宏赘。每個像素都是一個32位的數(shù)組(包含四個8位無符號整數(shù))绒北。數(shù)據(jù)類型是Pixel_8888
- RGBAFFFF 圖像包含四個交叉通道,red察署、green闷游、blue、alpha贴汪,順序固定脐往。每個像素都是一個包含四個浮點數(shù)的數(shù)組。數(shù)據(jù)類型是Pixel_FFFF
可以將其他格式的圖像轉(zhuǎn)換為vImage的圖像格式扳埂。例如业簿,可以通過vImageConvert_16SToF函數(shù)將一個16位像素的圖像轉(zhuǎn)換為vImage支持的32位像素。下面這些函數(shù)可以幫助你在vImage圖像格式之間進(jìn)行轉(zhuǎn)換阳懂,也可以把vImage不支持的圖像格式轉(zhuǎn)換為vImage格式梅尤。
- vImageConvert_16SToF 將一個16位符號整型planar圖像格式(或 vImage_Buffer.width=4的interleaved-multiply)緩沖區(qū)( vImage_Buffer )轉(zhuǎn)換為浮點型數(shù)值緩沖區(qū)
- vImageConvert_16UToF 將一個16位無符號整型planar圖像格式(或 vImage_Buffer.width=4的interleaved-multiply)緩沖區(qū)( vImage_Buffer )轉(zhuǎn)換為浮點型數(shù)值緩沖區(qū)
- ImageConvert_FTo16S 將一個浮點型planar圖像格式(或 vImage_Buffer.width=4的interleaved-multiply)緩沖區(qū)( vImage_Buffer )轉(zhuǎn)換為16位符號整型緩沖區(qū)
- vImageConvert_FTo16U 將一個浮點型planar圖像格式(或 vImage_Buffer.width=4的interleaved-multiply)緩沖區(qū)( vImage_Buffer )轉(zhuǎn)換為16位無符號整型緩沖區(qū)
- vImageConvert_16UtoPlanar8 將一個16位無符號整型planar圖像格式(或 vImage_Buffer.width=4的interleaved-multiply)緩沖區(qū)( vImage_Buffer )轉(zhuǎn)換為8位整型緩沖區(qū)
- vImageConvert_Planar8to16U 將一個8位整型planar圖像格式(或 vImage_Buffer.width=4的interleaved-multiply)緩沖區(qū)( vImage_Buffer )轉(zhuǎn)換為16位無符號整型緩沖區(qū)
- vImageConvert_ARGB1555toPlanar8 將16位/像素圖像(alpha通道1bit,red/green/blue通道5bit)轉(zhuǎn)換為Planar8格式岩调。
- vImageConvert_ARGB1555toARGB8888 將16位/像素圖像(alpha通道1bit巷燥,red/green/blue通道5bit)轉(zhuǎn)換為ARGB8888格式。
- vImageConvert_Planar8toARGB1555 將Planar8格式圖像轉(zhuǎn)換為包含1bit alpha号枕,5bit red缰揪,5bit green,5bit blue的16位/像素圖像葱淳。
- vImageConvert_ARGB8888toARGB1555 將ARGB8888格式圖像轉(zhuǎn)換為包含1bit alpha钝腺,5bit red,5bit green蛙紫,5bit blue的16位/像素圖像拍屑。
- vImageConvert_RGB565toPlanar8 將5bit red,6bit green坑傅,5bit blue的16位/像素圖像轉(zhuǎn)換為Planar8
- vImageConvert_RGB565toARGB8888 將5bit red僵驰,6bit green,5bit blue的16位/像素圖像轉(zhuǎn)換為ARGB8888
- vImageConvert_Planar8toRGB565 將Planar8圖像轉(zhuǎn)換為5-6-6圖像
- vImageConvert_ARGB8888toRGB565 將ARGB8888圖像轉(zhuǎn)換為5-6-5圖像
- vImageConvert_Planar16FtoPlanarF 將16位浮點型planar圖像轉(zhuǎn)換為32位浮點型
二唁毒、vImage練習(xí)
Loading Image Data
要將vImage集成到你的應(yīng)用中蒜茴,首先要把raw圖像數(shù)據(jù)加載到內(nèi)存〗鳎可以使用Image I/O框架把任何主流的圖像文件(JPG粉私、PNG、GIF等)加載到C類型緩沖池中(void *arrays)近零。
以下是從本地文件提取raw圖像數(shù)據(jù)的例子:
NSURL* url = [NSURL fileURLWithPath:filename];
//Create the image source with the options left null for now
//Keep in mind since we created it, we're responsible for getting rid of it
CGImageSourceRef image_source = CGImageSourceCreateWithURL( (CFURLRef)url, NULL);
if(image_source == NULL)
{
//Something went wrong
fprintf(stderr, "VImage error: Couldn't create image source from URL\n");
return false;
}
//Now that we got the source, let's create an image from the first image in the CGImageSource
CGImageRef image = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
//We created our image, and that's all we needed the source for, so let's release it
CFRelease(image_source);
if(image == NULL)
{
//something went wrong
fprintf(stderr, "VImage error: Couldn't create image source from URL\n");
return false;
}
把圖像加載到內(nèi)存之后诺核,就可以通過vImage函數(shù)進(jìn)行各種處理了抄肖。請密切注意函數(shù)下劃線之后的字符,那代表了像素數(shù)據(jù)的格式窖杀。vImage函數(shù)既可以在源緩沖區(qū)中直接處理漓摩,也可以在提供的目標(biāo)緩沖區(qū)中處理。
由于vImage只負(fù)責(zé)處理圖像入客,你還需要想辦法把圖像顯示出來管毙。根據(jù)你的應(yīng)用開發(fā)環(huán)境(Carbon or Cocoa),你需要找到一個方法(例如Quartz)去顯示合成后的像素數(shù)據(jù)桌硫,或?qū)D像數(shù)據(jù)存儲到磁盤(Image I/O)夭咬。
使用二維圖像格式
大多數(shù)vImage函數(shù)是從四種圖像格式開始的。二維圖像每次編碼一個通道(先存儲所有的紅色通道數(shù)據(jù)铆隘,然后是綠色卓舵,藍(lán)色,alpha)咖驮,而交叉圖像在內(nèi)存是混合存儲所有通道的边器。
注意:有時候你可能并不需要處理所有的通道训枢。比如托修,你知道你要處理的圖像中是不需要alpha通道的,或可能你的圖像是灰度圖恒界,因此你只需要一個通道睦刃。這種情況下,使用二維圖像格式可以讓你把需要的通道隔離出來十酣。
拼貼(Tiles)技術(shù)
在圖像應(yīng)用中涩拙,通常要使用Tiles將一個圖像分割成幾個小圖。之所以稱為tiling技術(shù)耸采,是因為這看起來很像是把多個地板瓷磚拼接成一大塊兒的過程兴泥,圖像中多個小的單位拼貼在一起,形成一個大的圖像虾宇。這個技術(shù)的優(yōu)勢在于搓彻,把數(shù)據(jù)分散成N個小的單位后,可以分散填充到高速緩存中嘱朽,這使CPU的處理速度加快了許多旭贬。
通常來說,當(dāng)被處理的數(shù)據(jù)(包括輸入數(shù)據(jù)和輸出數(shù)據(jù))放在處理器的數(shù)據(jù)緩存中時搪泳,vImage具有更好的性能稀轨。訪問處理器緩存中的數(shù)據(jù)比訪問內(nèi)存數(shù)據(jù)要快很多。然而CPU緩存是很快岸军,但是它在空間上是有限制的奋刽。不同的CPU緩存大小不一樣瓦侮,但總的來說,在Intel處理器中保存少于2MB的tile圖像佣谐,還有在PowerPC處理器中保存少于512KB的tile還是很有優(yōu)勢的脏榆。
-
以下是Tiles技術(shù)的一些技巧:
- 有些CPU緩存一次只能保存很少量的數(shù)據(jù)(通常是512KB或更少)
- 128KB - 512KB的tile數(shù)據(jù)吞吐量最優(yōu)
- 很多vImage函數(shù)內(nèi)部可以使用tile技術(shù)(還有多線程)。如果你想自己控制tiling台谍,可以在調(diào)用函數(shù)時在flags參數(shù)中添加kvImageDoNotTile標(biāo)記须喂,這樣可以避免函數(shù)在內(nèi)部使用tiling或多線程技術(shù)。
- 對于平方形tile趁蕊,128KB-256KB的tile大小具有最好的吞吐率坞生。
- 不同函數(shù)的最優(yōu)tile大小也是不同的。具有少量字節(jié)計算的函數(shù)(大多數(shù)是轉(zhuǎn)換函數(shù))在file size 小于16KB時是最快的掷伙,典型的vImage函數(shù)在256KB tile size下是最快的是己。
- 有些CPU緩存一次只能保存很少量的數(shù)據(jù)(通常是512KB或更少)
數(shù)據(jù)緩沖排列
當(dāng)分配圖像的浮點型數(shù)據(jù)時,保持4個字節(jié)的排列是很重要的任柜。這說明你分配的字節(jié)數(shù)應(yīng)該是4的整數(shù)倍卒废。
以下是數(shù)據(jù)排列和緩沖區(qū)大小的一些技巧:
- 盡管vImage默認(rèn)可以有更少的數(shù)據(jù)排列,但為了性能最優(yōu)化宙地,所有數(shù)據(jù)都應(yīng)該是16字節(jié)排列摔认,且rowbytes應(yīng)該是16的整數(shù)倍。
- 浮點型數(shù)據(jù)必須至少是4字節(jié)宅粥,否則某些函數(shù)可能會報錯参袱。
- 函數(shù)的rowBytes參數(shù)不能傳入2的冪次方。
緩沖區(qū)(buffer)重用機(jī)制
很多函數(shù)在執(zhí)行任務(wù)時秽梅,會使用臨時緩沖區(qū)來存儲中間值抹蚀。在一開始只需創(chuàng)建一次buffer,就可以在多個函數(shù)中使用企垦,這樣可以節(jié)省時間环壤。
如果你沒有提供buffer,這些函數(shù)會自行創(chuàng)建buffer(當(dāng)然钞诡,使用完后釋放)郑现。
如果你只需要調(diào)用幾次該函數(shù),并且不在意短暫的阻塞臭增,那么讓函數(shù)自行創(chuàng)建buffer是個明智的選擇懂酱。
每個使用到臨時buffer的函數(shù)都有一個src和desc參數(shù)(數(shù)據(jù)類型都是vImage_Buffer)。函數(shù)只使用了這些參數(shù)的height和width域誊抛;忽略data和rowBytes域列牺。
可能的話淹冰,應(yīng)用也應(yīng)該嘗試重用vImage_Buffer數(shù)據(jù)的data域指向的圖像緩沖區(qū)脖苏。這樣可以節(jié)省時間,否則還要重新分配并且把原來的buffer抹平。
在實際應(yīng)用中很钓,要盡可能的避免使用堆和其他可能引起阻塞的操作(比如內(nèi)存分配)旁赊。
適當(dāng)?shù)氖褂镁€程
vImage是線程安全的并且可重入蒸殿。如果你分割了你的圖像比默,你可以使用多線程處理不同的tiles。如果你使用不同的處理器處理不同的tiles驼修,你應(yīng)選擇那些水平方向上不相鄰的tiles殿遂。否則tile的邊緣可能會共享到cache,這有可能導(dǎo)致兩個處理器之間耗時的干擾乙各。
在vImage函數(shù)工作時墨礁,vImage的輸出buffer狀態(tài)是未知的。有可能buffer中的像素數(shù)據(jù)既不是起始數(shù)據(jù)也不是結(jié)束數(shù)據(jù)耳峦,而是計算過程中的一個中間值恩静。
在OS X 10.4之后,一些vImage函數(shù)在內(nèi)部使用了多線程技術(shù)蹲坷。他們自己做了參數(shù)檢查和多線程來提高性能驶乾。vImage維持了它延遲分配線程緩沖池的風(fēng)格去做這件事。這些線程一旦被創(chuàng)建就不會被銷毀循签,他們會被重用级乐。被調(diào)用的線程要等待上一個線程完成自己的任務(wù),在這之前會被阻塞懦底。使用內(nèi)部實現(xiàn)了多線程技術(shù)的函數(shù)是安全的唇牧。
線程安全的函數(shù)通過鎖來保持?jǐn)?shù)據(jù)一致性罕扎。如果你不希望函數(shù)使用鎖聚唐,你需要設(shè)置kvImageDoNotTile標(biāo)志位來阻止vImage使用多線程和tiling技術(shù)。如果你設(shè)置了該標(biāo)志位腔召,你的應(yīng)用需要自行處理數(shù)據(jù)tiling和多線程杆查。
把2D核分為1D核
如果你使用卷積方法對圖像添加濾鏡,你可以通過將2D核分裂為兩個1D核來提高性能臀蛛,這樣可以使用兩次卷積(一個維度可以使用一次)亲桦。
當(dāng)然,你可以將2D核傳給vImageConvolve函數(shù)浊仆。vImage使用2D核來完成9層8加的操作來計算每個像素結(jié)果客峭。為了更好的性能,對每個1D卷積濾鏡調(diào)用一次vImageConvolve函數(shù)抡柿。核分裂后舔琅,vImage通過每個卷積對每個像素執(zhí)行3層2加的操作,加起來是6層4加洲劣。我們注意到將核分裂成兩個后备蚓,算法復(fù)雜度在乘法上節(jié)省了1/3课蔬,在加法上節(jié)省了1/2。對于M×N的內(nèi)核郊尝,處理消耗從M*N驟減到M+N二跋。一個5×5的核分裂后的處理速度加快2.5倍,一個11×11的核分裂后可能加快超過5倍流昏。
注意這個技術(shù)在常規(guī)情況下是比較慢的扎即。一般在圖像特別大,或圖像無法存入內(nèi)存時使用這個技術(shù)况凉。
有幾種情況下铺遂,分裂特別大的濾鏡是執(zhí)行卷積操作的唯一方案。一個總計超過224的濾鏡茎刚,vImage使用8位的卷積操作下襟锐,運行起來會有內(nèi)存溢出的風(fēng)險。這個時候膛锭,可以采用分裂濾鏡來避免溢出粮坞,你甚至可以在放大濾鏡之前,通過分裂技術(shù)向濾鏡里添加更多的固定精度的點初狰。這種技術(shù)的中間值精度損失程度未知莫杈。