######【前文提要】
因工作一直沒有接觸過濾鏡領域,所以在閒暇之餘閱讀了下官方文件,本文嘗試實現通用美圖軟體背景虛化景深效果,純屬娛樂,大神無視勿噴。
大致為突顯女主上半身形象,並以上半身為中心漸變模糊擴散的效果。
######一、為圖片新增高斯模糊濾鏡
既然需要執行濾鏡操作,那肯定離不開Core Image這一強大的框架了,感興趣的童鞋可以點選進入檢視文件。本篇文章中主要使用其幾種常用的濾鏡。對於模糊效果,系統提供了很多樣式,但畢竟不是設計,無法通過肉眼區別它們之間的區別,因此這裡簡單的選取了高斯模糊效果。
首先我們來建立高斯模糊濾鏡,對於 CIFilter就不做過多的介紹了。將具體濾鏡名稱傳入即可建立對應濾鏡樣式。這裡需要注意的我們傳入的圖片資訊並非我們常用的UIImage,因為UIImage是不可變的,只能通過已存在的圖片建立它,而濾鏡需要對原始圖片進行修改,因此這裡我們需要將UIImage轉換為CIImage型別做處理。
//高斯模糊濾鏡
CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
UIImage *image = [UIImage imageNamed:@"IMG_0857.JPG"];
//將UIImage轉換為CIImage型別
CIImage *ciImage = [[CIImage alloc]initWithImage:image];
//設定輸入的圖片資訊
[filter setValue:ciImage forKey:kCIInputImageKey];
//設定模糊程度
[filter setValue:@8 forKey:kCIInputRadiusKey];//預設為10複製程式碼
執行如上操作生成的效果如下,也即是文章頂部效果圖中的模糊效果:
######二、確定顯示區域
常用美圖軟體的童鞋們會發現背景虛化效果是存在兩種顯示調節形式的,一種為兩個同心圓確定區域,一種為平行矩形確定區域。以美圖秀秀為例。
######那如何通過程式碼實現如上效果呢?
其實同心圓與平行矩形在程式碼中對應分別為CIRadialGradient和CILinearGradient濾鏡。我們可稱其為徑向漸變濾鏡。
這裡拿同心圓的徑向漸變濾鏡為例通過程式碼生成一個內圓半徑為300,外圓半徑為500的同心圓。需要注意的是設定圓點或半徑並不是以iOS裝置的螢幕解析度為參照的,而是由原影象資訊來決定的。例如原圖片畫素為(750,1334),若想將同心圓的圓點定位在圖片中心,需要將inputCenter設定為(375,667),同理對於同心圓中內圓與外圓的半徑設定也是一樣。並且還需要座標系問題,在這裡(0,0)點屬於螢幕左下角而非左上角。也就說他使用的是現實中的直角座標系。
//徑向漸變濾鏡(同心圓)
CIFilter *radialFilter = [CIFilter filterWithName:@"CIRadialGradient"];
//圓點
[radialFilter setValue:[CIVector vectorWithX:image.size.width / 2 Y:image.size.height / 2] forKey:@"inputCenter"];
//內圓半徑
[radialFilter setValue:@300 forKey:@"inputRadius0"];
//外圓半徑
[radialFilter setValue:@500 forKey:@"inputRadius1"];複製程式碼
上述程式碼可生成如下圖效果:
根據效果圖我們可以看出,內圓部分是完全透明的,內圓與外圓之間部分呈現漸變模糊效果。
######三、背景虛化效果合成
通過上兩步的操作我們生成了高斯模糊後的圖片並且確定了所需顯示的區域,接下來我們要通過CIBlendWithMask濾鏡來進行效果合成。對於CIBlendWithMask濾鏡,字面意思為遮蓋物混合濾鏡,文件解釋過於草率,我們根據文件給予的效果圖來分析:
如圖所示, CIBlendWithMask濾鏡可將左側的三張圖片合成為右側的輸出圖片。具體的執行流程是,左一圖做為左二圖的填充,後續在與左三進行混合。(這張圖建議大家仔細觀察下)
######那如何通過這種混合方式實現自己的需求呢?
我們可以將左三圖分別替換為原圖、同心圓、高斯模糊濾鏡生成的效果圖。這樣即可實現我們想要的背景虛化的效果了。也可理解為我們將需要高亮顯示的位置在原圖上扣出來再與整體的模糊效果進行混合,程式碼如下:
//濾鏡混合
CIFilter *maskFilter = [CIFilter filterWithName:@"CIBlendWithMask"];
//原圖
[maskFilter setValue:ciImage forKey:kCIInputImageKey];
//高斯模糊處理後的圖片
[maskFilter setValue:filter.outputImage forKey:kCIInputBackgroundImageKey];
//遮蓋圖片,這裡為徑向漸變所生成的同心圓或同軸矩形
[maskFilter setValue:radialFilter.outputImage forKey:kCIInputMaskImageKey];複製程式碼
最後通過CIContext生成執行濾鏡操作後的圖片,給imageView賦值。
⚠️注意:若需要測試高斯模糊等效果,均需要呼叫下方程式碼,將對應的輸出影象更改下即可,比如想測試高斯模糊效果,將下方程式碼maskFilter.outputImage 更改為filter.outputImage即可。
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef endImageRef = [context createCGImage:maskFilter.outputImage fromRect:ciImage.extent];
imageView.image = [UIImage imageWithCGImage:endImageRef];複製程式碼
因濾鏡操作CPU渲染會比GPU要慢,所以效果呈現會慢一些,換做真機就沒問題啦。另外仍可以繼續優化,將當前的context替換為OpenGL的context,程式碼如下:
CIContext *context = [CIContext contextWithEAGLContext:[[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]];複製程式碼
使用它的優點是渲染的影象儲存在GPU上,並不需要拷貝回CPU記憶體。並且如果需要實時濾鏡處理的話,這種建立形式更好。
最後不要忘記記憶體洩漏問題,對於非OC物件要手動進行記憶體釋放,前一篇文章關於記憶體洩漏,還有哪些是你不知道的?也有描述過。因此需要新增記憶體釋放程式碼
CGImageRelease(endImageRef);複製程式碼
本文暫時寫到這裡,demo已上傳Github,喜歡的可以點個贊關注我,比心(。・ω・。)ノ♡
github.com/LSure/SureB…
最後放一張模擬器中的效果圖
另附感言:P圖軟體公司的開發真不容易啊~