背景
前段時間的一個週末,一個女生讓我幫她換一下他的證件照背景,我又沒帶電腦。我又很差意思拒接,怎麼辦呢?應用商店下載一個證件照換背景的APP,瞬間換完,我正準備保存時,跳出來一個支付框,若是你要保存,支付2元錢,出於面子,我只好掏了2塊錢,保存了。因而我就想,這種技術活,還給別人付錢,本身來擼吧.我是一個專職Android開發,那麼就用Android來擼吧.html
先來了解一下Android裏原生API對圖片操做,通常有兩種方式,java
- 一種是利用好Canvas繪製圖片,
- 一種是利用Bitmap的原生API,獲取像素進行操做
這兩種操做我都寫了對應的文章,能夠快速查看python
今天的主題是在Android裏使用OpenCv來操做圖片,並實現兩個不一樣的效果,換證件照背景和污點修復.c++
代碼已經託管在Github上,和上兩篇文章代碼地址同樣,分支with-photo-changecolorgit
Github ,若是你喜歡,歡迎star 謝謝github
Android OpenCv 快速入門
環境搭建
原生的API對圖片的操做有限,而且一些顏色空間轉化麻煩,效率低,那咱們使用一個專業的圖片操做庫來操做圖片,會變得容易些.算法
OpenCv有不少語言版本,固然底層是c/c++,他支持Android/IOS,Windows,Mac等,咱們直接選擇Android版本. 那麼來搭建一下環境,有兩步網絡
- 下載OpenCv SDK 地址,將SDK 打包成aar,集成到項目中,快速獲取aar,能夠直接到我打好的包裏獲取 Github中獲取. 打aar包很簡單,用Android Studio打開下載好的SDK,而後到其目錄下,執行./gradlew assembleRelease 或者用側邊的輔助工具
- 集成到你要使用OpenCv的項目,以下
圖像灰度測試
集成完成後,進行OpenCV SDK接入成功測試app
private void initLoaderOpenCV() { boolean success = OpenCVLoader.initDebug(); if (!success) { Log.d(TAG, "初始化失敗"); } } public void gray(View view) { Mat src = new Mat(); Mat dst = new Mat(); Utils.bitmapToMat(bitmap, src); Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGRA2GRAY); Bitmap resultBitmap = getResultBitmap(); Utils.matToBitmap(dst, resultBitmap); src.release(); dst.release(); showCompare(resultBitmap); }
若是接入沒問題,就能夠愉快的使用OpenCV了,是否是很簡單. ide
換證件照背景 (從藍色到紅色)
換證件照算法,直接使用了一個c++ 版本算法的,翻譯爲Android的. c++文章地址
主要步驟:
- 把RGB圖像轉換到HSV空間
- 取背景的一小塊20*20,計算藍色背景的平均色調和飽和度
- 設置閾值,取出藍色背景替換爲紅色背景
- 把HSV圖像轉換會RGB空間
- 濾波器去除邊緣效應
Android 代碼以下:
private void startDetail() { Mat image = new Mat(); Utils.bitmapToMat(bitmap, image); Mat hsvImg = new Mat(); Imgproc.cvtColor(image, hsvImg, Imgproc.COLOR_BGR2HSV); List<mat> list = new ArrayList<>(); Core.split(hsvImg, list); Mat roiH = list.get(0).submat(new Rect(0, 0, 20, 20)); Mat roiS = list.get(1).submat(new Rect(0, 0, 20, 20)); Log.i(TAG,"start sum bg"); int SumH = 0; int SumS = 0; byte[] h = new byte[1]; byte[] s = new byte[1]; //取一塊藍色背景,計算出它的平均色調和平均飽和度 for (int i = 0; i < 20; i++) { for (int j = 0; j < 20; j++) { roiH.get(j, i, h); roiS.get(j, i, s); SumH = h[0] + SumH; SumS = s[0] + SumS; } } int avgH, avgS;//藍底的平均色調和平均飽和度 avgH = SumH / 400; avgS = SumS / 400; Log.i(TAG,"depth="+list.get(0).depth()); Log.i(TAG,"start sum detail all photo"); //遍歷整個圖像 int nl = hsvImg.height(); int nc = hsvImg.width(); // byte[] changeColor = new byte[]{127}; byte[] hArray = new byte[nl * nc]; byte[] sArray = new byte[nl * nc]; byte[] vArray = new byte[nl * nc]; list.get(0).get(0,0,hArray); list.get(1).get(0,0,sArray); // list.get(2).get(0,0,vArray); int row,index; for (int j = 0; j < nl; j++) { row = j * nc; for (int i = 0; i < nc; i++) { index = row + i; if(hArray[index] <= (avgH + 20) && hArray[index] >= (avgH - 20) && sArray[index] <= (avgS + 150) && sArray[index] >= (avgS -150) ){ hArray[index] = 127; // sArray[index] = 0; // vArray[index] = (byte) 255; } } } list.get(0).put(0,0,hArray); list.get(1).put(0,0,sArray); // list.get(2).put(0,0,vArray); Log.i(TAG,"merge photo"); Core.merge(list,hsvImg); Imgproc.cvtColor(hsvImg,image, Imgproc.COLOR_HSV2BGR); Bitmap resultBitmap = getResultBitmap(); Utils.matToBitmap(image,resultBitmap); Message obtain = Message.obtain(); obtain.obj = resultBitmap; handler.sendMessage(obtain); }
Mat 爲OpenCV中圖像的保存,很相似Android裏的Bitmap,他和Bitmap轉化須要藉助OpenCv的Utils進行,OpenCV的核心API能夠查看官網,此處主要使用了Imgproc
效果
污點修復
修復原理
先來講一下污點修復的算法,一篇論文提到的 《An ImageInpainting Technique Based On the Fast Marching Method》
能夠簡單理解爲p點爲待修復區域,ε爲修復半徑,把ε的值區域的值計算出來,用於修復P點,直到修復整個Ω區域.
詳細能夠查看論文:論文地址
實際修復
OpenCV 裏面已經實現了此算法,具體方法以下:
//OpenCV Photo.java /** * Restores the selected region in an image using the region neighborhood. * * @param src Input 8-bit, 16-bit unsigned or 32-bit float 1-channel or 8-bit 3-channel image. * @param inpaintMask Inpainting mask, 8-bit 1-channel image. Non-zero pixels indicate the area that * needs to be inpainted. * @param dst Output image with the same size and type as src . * @param inpaintRadius Radius of a circular neighborhood of each point inpainted that is considered * by the algorithm. * @param flags Inpainting method that could be cv::INPAINT_NS or cv::INPAINT_TELEA * * The function reconstructs the selected image area from the pixel near the area boundary. The * function may be used to remove dust and scratches from a scanned photo, or to remove undesirable * objects from still images or video. See <http://en.wikipedia.org/wiki/Inpainting> for more details. * * <b>Note:</b> * <ul> * <li> * An example using the inpainting technique can be found at * opencv_source_code/samples/cpp/inpaint.cpp * </li> * <li> * (Python) An example using the inpainting technique can be found at * opencv_source_code/samples/python/inpaint.py * </li> * </ul> */ public static void inpaint(Mat src, Mat inpaintMask, Mat dst, double inpaintRadius, int flags) { inpaint_0(src.nativeObj, inpaintMask.nativeObj, dst.nativeObj, inpaintRadius, flags); }
其中上面提到的原理算法爲,INPAINT_TELEA.
來一張實際的圖操做修復一下,以下:
private void startInpaint() { bitmap = BitmapUtils.getBitmapByAssetsNameRGB(this,"test.png"); Mat desc = new Mat(bitmap.getHeight(),bitmap.getWidth(),CvType.CV_8UC3); //轉化爲mat對象 Utils.bitmapToMat(bitmap, desc,true); //轉化爲3通道圖像 Mat src = new Mat(); Imgproc.cvtColor(desc,src,Imgproc.COLOR_RGBA2RGB); //灰度圖像 Mat srcGray = new Mat(); Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_RGB2GRAY); //中值濾波去燥 Imgproc.medianBlur(srcGray,srcGray,3); //獲取污點的二值化圖像 Mat srcThresh = new Mat(); Imgproc.threshold(srcGray,srcThresh,242,255,Imgproc.THRESH_BINARY); Log.i("test","srcThresh channels:"+srcThresh.channels() + ",type:"+ CvType.typeToString(CvType.depth(srcThresh.type()))); Log.i("test","src channels:"+src.channels() + ",type:"+ CvType.typeToString(CvType.depth(src.type()))); // Bitmap resultBitmap = getResultBitmap(); // Utils.matToBitmap(srcThresh, resultBitmap); //修復圖像 Mat inpaintResult = new Mat(); Photo.inpaint(src,srcThresh,inpaintResult,3,Photo.INPAINT_TELEA); //把結果轉化爲bitmap 用於顯示 Bitmap resultBitmap = getResultBitmap(); Utils.matToBitmap(inpaintResult, resultBitmap); Message obtain = Message.obtain(); obtain.obj = resultBitmap; handler.sendMessage(obtain); }
效果
圖片來源:https://www.cnblogs.com/hellowooorld/p/7048614.html
總結
本篇文章,主要介紹了OpenCV怎麼快速使用,並結合了兩個實際的例子,來進一步說明藉助OpenCV裏的API,能夠實現不少不錯的效果.
文中圖片來源網絡,若又侵權,請聯繫做者,馬上刪除!
本篇文章的兩個例子代碼地址:github ,若是你喜歡迎star,後續關於圖片的操做,都會在此庫裏更新.
推薦閱讀
Android:讓你的「女神」逆襲,代碼擼彩妝(畫妝)
Flutter PIP(畫中畫)效果的實現
Android 繪製原理淺析【乾貨】
</mat>