背景

前段时间的一个周末,一个女生让我帮她换一下他的证件照背景,我又没带电脑。我又不好意思拒接,怎么办呢?应用商店下载一个证件照换背景的APP,瞬间换完,我正准备保存时,跳出来一个支付框,如果你要保存,支付2元钱,出于面子,我只好掏了2块钱,保存了。于是我就想,这种技术活,还给别人付钱,自己来撸吧.我是一个专职Android开发,那么就用Android来撸吧.

先来了解一下Android里原生API对图片操作,一般有两种方式,

  1. 一种是利用好Canvas绘制图片,
  2. 一种是利用Bitmap的原生API,获取像素进行操作

这两种操作我都写了对应的文章,可以快速查看

  • Android:让你的“女神”逆袭,代码撸彩妆(画妆)
  • Android:代码撸彩妆 2(大眼,瘦脸,大长腿)
image image

今天的主题是在Android里使用OpenCv来操作图片,并实现两个不同的效果,换证件照背景和污点修复.

代码已经托管在Github上,和上两篇文章代码地址一样,分支with-photo-changecolor

Github ,如果你喜欢,欢迎star 谢谢

Android OpenCv 快速入门

环境搭建

原生的API对图片的操作有限,并且一些颜色空间转化麻烦,效率低,那我们使用一个专业的图片操作库来操作图片,会变得容易些.

OpenCv有很多语言版本,当然底层是c/c++,他支持Android/IOS,Windows,Mac等,我们直接选择Android版本. 那么来搭建一下环境,有两部

  • 下载OpenCv SDK 地址,将SDK 打包成aar,集成到项目中,快速获取aar,可以直接到我打好的包里获取 Github中获取. 打aar包很简单,用Android Studio打开下载好的SDK,然后到其目录下,执行./gradlew assembleRelease 或者用侧边的辅助工具
    image
  • 集成到你要使用OpenCv的项目,如下
image

图像灰度测试

集成完成后,进行OpenCV SDK接入成功测试

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了,是不是很简单.


image

换证件照背景 (从蓝色到红色)

换证件照算法,直接使用了一个c++ 版本算法的,翻译为Android的. c++文章地址

主要步骤:

  1. 把RGB图像转换到HSV空间
  2. 取背景的一小块20*20,计算蓝色背景的平均色调和饱和度
  3. 设置阈值,取出蓝色背景替换为红色背景
  4. 把HSV图像转换会RGB空间
  5. 滤波器去除边缘效应

Android 代码如下:

 private void startDetail() {        Mat image = new Mat();        Utils.bitmapToMat(bitmap, image);        Mat hsvImg = new Mat();        Imgproc.cvtColor(image, hsvImg, Imgproc.COLOR_BGR2HSV);        List 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

image

效果

image

污点修复

修复原理

先来说一下污点修复的算法,一篇论文提到的 《An ImageInpainting Technique Based On the Fast Marching Method》

image

可以简单理解为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.     *     * Note:     * 
    *
  • * An example using the inpainting technique can be found at * opencv_source_code/samples/cpp/inpaint.cpp *
  • *
  • * (Python) An example using the inpainting technique can be found at * opencv_source_code/samples/python/inpaint.py *
  • *
*/ 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);    }

效果

image

图片来源:https://www.cnblogs.com/hellowooorld/p/7048614.html

总结

本篇文章,主要介绍了OpenCV怎么快速使用,并结合了两个实际的例子,来进一步说明借助OpenCV里的API,可以实现很多不错的效果.

文中图片来源网络,若又侵权,请联系作者,立刻删除!

本篇文章的两个例子代码地址:github ,如果你喜欢迎star,后续关于图片的操作,都会在此库里更新.

推荐阅读

Android:让你的“女神”逆袭,代码撸彩妆(画妆)
Flutter PIP(画中画)效果的实现
Android 绘制原理浅析【干货】

更多相关文章

  1. sms AT指令返回错误代码: CMS errors & CME errors 的区别!
  2. Android(安卓)中UI与耗时操作的分离
  3. 【整理】Android中使用XML自定义组件各种状态下的背景图片
  4. ListView页眉页脚效果VS android背景渐变
  5. 【Android进阶学习】设置透明效果的三种方法
  6. android数据库操作之直接读取db文件
  7. Android模拟器常用操作
  8. Android——自定义TabLayout背景,实现类似浏览器中的选项卡效果,修
  9. Android(安卓)几种常用关于屏幕操作的方法(获取屏幕大小,全屏,显示

随机推荐

  1. Android横竖屏切换小结
  2. android中的search dialog
  3. 跟我学Android NDK开发(一)
  4. android progressbar style定义解析
  5. android 设置支持pad等大屏幕、大分辨率
  6. Android开发——控件基础(三)EditText组件
  7. Android开发你需要具备的基本功
  8. 空闲时间玩玩Andriod,你知道如何配置环境
  9. AndroidStudio安装教程(Windows环境下)
  10. android文字阴影效果设置