实战精通OpenCV第一章--基于Android的图片卡通化及肤色改变(三)

第一章 基于Android的图片卡通化及肤色改变

一、基于Visual Studio的图片卡通化

二、基于Visual Studio的肤色改变

三、Android代码移植

最近由于工作比较忙,很抱歉没有抽出时间来更新这个系列的博客。在这里感谢一下关注该博客的同学。其实写这个系列的初衷,主要还是想记录读书的过程,同时给自己一些动力坚持下去,没有期望关注度。但是大家的关注将给我更大的动力坚持下去。以后也会尽量抽时间更新后面的章节。

书归正传,今天继续完成第一章的最后一部分的内容:Android代码的移植

关于本书第一章所涉及到的知识点,其实在前两部分已经介绍的差不多了,本节的重点在于介绍在app集成opencv的方法和基本调用流程。没有什么理论知识,大部分是程序化的流程。另外,这部分要求对android开发有一些基础。如果你对android开发没有兴趣,或是只想了解opencv或本书涉及到的图像处理知识,这一节可以直接忽略。

老规矩,先上效果图。这张照片是对着显示器拍的,纹理比较明显,局部效果可能不是很好。有兴的同学可以将demo下载下来,自拍玩一玩。demo的地址会在文章最后给出。

处理前处理后

涉及到的知识点:

1、Android studio工程集成opencv sdk的相关配置

2、图像处理的移植

1、opencv集成配置

首先到官网下载Opencv Android版sdk:https://opencv.org/opencv-3-4-1/。下载完成后得到压缩文件opencv-3.4.1-android-sdk.zip,解压之。

新建AndroidStudio项目,通过File -> New -> Import Module导入模块。模块路径选择如下图,选到java这一层即可。

将native->libs文件夹下的内容,拷贝到libs文件夹下。

然后做如下配置:

  • 修改module的sdkversion与主app一致

  • 删除模块清单文件中的version声明

  • 主app中添加依赖项和native库搜索路径

做完上述配置之后,尝试编译你的app。编译成功证明基本环境已经配置完毕。

2、opencv的使用和代码移植

opencv对应的java接口基本与c++接口保持一致。将第二章图像处理部分的源码复制过来,改为java语法格式,基本就能使用了。在项目中需要注意的有两点。

  • 必须手动初始化Opencv库,调用如下方法进行初始化
OpenCVLoader.initAsync
  • opencv底层均由native c++方法实现,因此必须加载so,否则会error
System.loadLibrary("opencv_java3");

关于图像处理过程的代码,正如上面所说,改成java语法格式就能够使用了,直接上改过之后的源码。

private void alignMat(Mat src) {
        Size size = src.size();
        Mat gray = new Mat(src.size(), CV_8UC3);
        Mat yuv = new Mat(size, CV_8UC3);
        Mat faceOutline = Mat.zeros(size, CV_8UC3);
        Mat srcClone = src.clone();
        Scalar color = new Scalar(255, 255, 0);
        Imgproc.cvtColor(src, gray, COLOR_BGR2GRAY);
        Imgproc.cvtColor(src, yuv, COLOR_BGR2YCrCb); // 色彩空间转换

        //画笑脸开始
        double sw = size.width;
        double sh = size.height;
        int thickness = 4;
        double faceH = sh / 2 * 70 / 100;
        double faceW = faceH * 72 / 100;
        Imgproc.ellipse(faceOutline, new Point(sw / 2, sh / 2), new Size(faceW, faceH), 0.0, 0.0, 360.0, color, thickness, LINE_4 ,0);
        double eyeW = faceW * 23 / 100;
        double eyeH = faceH * 11 / 100;
        double eyeX = faceW * 48 / 100;
        double eyeY = faceH * 13 / 100;
        Size eyeSize = new Size(eyeW, eyeH);
        int eyeA = 15;
        int eyeYshift = 11;
        Imgproc.ellipse(faceOutline, new Point(sw / 2 - eyeX, sh / 2 - eyeY), eyeSize, 0, 180 + eyeA, 360 - eyeA, color, thickness, LINE_AA, 0);
        Imgproc.ellipse(faceOutline, new Point(sw / 2 - eyeX, sh / 2 - eyeY - eyeYshift), eyeSize, 0, 0 + eyeA, 180 - eyeA, color, thickness, LINE_AA, 0);
        Imgproc.ellipse(faceOutline, new Point(sw / 2 + eyeX, sh / 2 - eyeY), eyeSize, 0, 180 + eyeA, 360 - eyeA, color, thickness, LINE_AA, 0);
        Imgproc.ellipse(faceOutline, new Point(sw / 2 + eyeX, sh / 2 - eyeY - eyeYshift), eyeSize, 0, 0 + eyeA, 180 - eyeA, color, thickness, LINE_AA, 0);
        double mouthY = faceH * 48 / 100;
        double mouthW = faceW * 45 / 100;
        double mouthH = faceH * 6 / 100;
        Imgproc.ellipse(faceOutline, new Point(sw / 2, sh / 2 + mouthY), new Size(mouthW, mouthH), 0, 0, 180, color, thickness, LINE_AA, 0);
        int fontFace = FONT_HERSHEY_COMPLEX;
        float fontScale = 1.0f;
        int fontThickness = 2;
        String szMsg = "Put your face here";
        Imgproc.putText(faceOutline, szMsg, new Point(sw * 23 / 100, sh * 10 / 100), fontFace, fontScale, color, fontThickness, LINE_AA, false);
        //画笑脸结束

        //中值+拉普拉斯+闭运算进行轮廓提取
        Mat mask, maskPlusBorder;
        maskPlusBorder = Mat.zeros((int)sh + 2, (int)sw + 2, CV_8UC1);
        mask = maskPlusBorder.submat(new Rect(1, 1, (int)sw, (int)sh));
        Imgproc.medianBlur(gray, gray, 7);
        Mat edges = gray.clone();
        Imgproc.Laplacian(gray, edges, CV_8U);
        Imgproc.resize(edges, mask, size);
	    int EDGES_THRESHOLD = 80;
        Imgproc.threshold(mask, mask, EDGES_THRESHOLD, 255, THRESH_BINARY);
        Imgproc.dilate(mask, mask, new Mat());
        Imgproc.erode(mask, mask, new Mat());

        //种子像素点
        int NUM_SKIN_POINTS = 6;
        Point[] skinPts = new Point[NUM_SKIN_POINTS];
        skinPts[0] = new Point(sw / 2, sh / 2 - sh / 6);
        skinPts[1] = new Point(sw / 2 - sw / 11, sh / 2 - sh / 6);
        skinPts[2] = new Point(sw / 2 + sw / 11, sh / 2 - sh / 6);
        skinPts[3] = new Point(sw / 2, sh / 2 - sh / 16);
        skinPts[4] = new Point(sw / 2 - sw / 9, sh / 2 - sh / 16);
        skinPts[5] = new Point(sw / 2 + sw / 9, sh / 2 - sh / 16);

        //上下限
	    int LOWER_Y = 60;
	    int UPPER_Y = 80;
	    int LOWER_Cr = 25;
	    int UPPER_Cr = 15;
	    int LOWER_Cb = 20;
	    int UPPER_Cb = 15;

        Scalar lowerDiff = new Scalar(LOWER_Y, LOWER_Cr, LOWER_Cb);
        Scalar upperDiff = new Scalar(UPPER_Y, UPPER_Cr, UPPER_Cb);

        //漫水填充
	    int CONNECTED_COMPONENTS = 4;
	    int flags = CONNECTED_COMPONENTS | FLOODFILL_FIXED_RANGE | FLOODFILL_MASK_ONLY;
        Mat edgeMask = mask.clone();
        for (int i = 0; i < NUM_SKIN_POINTS; i++) {
            Imgproc.floodFill(yuv, maskPlusBorder, skinPts[i], new Scalar(255, 255, 255), null, lowerDiff, upperDiff, flags);
        }

        //最后叠加矩阵
        Imgproc.threshold(mask, mask, 0, 255, THRESH_BINARY);
        Core.subtract(mask, edgeMask, mask);
        int Red = 0;
        int Green = 70;
        int Blue = 0;
        Core.add(src, new Scalar(Red, Green, Blue), src, mask);
    }

本章结语

本章介绍了Mastering Opencv一书第一章所涉及到的知识点,并实现了一个简易的demo。

关于图像处理的流程还有很多能够提升的地方。比如,现在的流程中,漫水填充的六个seed点完全是经验值,而且比例是写死的。这就要求拍照时,必须距离合适,而且是正面照才能得到不错的效果,稍远或者稍近都会导致效果都不好。解决这个问题的方法当然是首先进行图像中的人脸检测,然后根据人脸大小,按比例选取漫水填充的seed。关于人脸检测算法,在后面的章节会有所提及,但这并不是本章的重点。作为本书的第一章,其目的主要还是要告诉读者,opencv其实可以实现许多有意思的功能,并介绍一些图像处理方面的思想。随着对后面章节的学习,对opencv的认识,也将会逐步深入。

最后,贴上本Android工程的源码,仅供借鉴 https://github.com/mymottoissh/Mastering-Opencv-Chapter1

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值