700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 实战精通OpenCV第一章--基于Android的图片卡通化及肤色改变(三)

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

时间:2019-06-04 00:12:03

相关推荐

实战精通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:/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工程的源码,仅供借鉴/mymottoissh/Mastering-Opencv-Chapter1。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。