转载请注明出处:https://blog.csdn.net/mymottoissh/article/details/86709457

本系列博客是基于《Mastering OpenCV with Practical ComputerVision Projects》

附本书电子版的下载地址:https://homepages.thm.de/~hg11237/Start/OpenCV/Mastering%20OpenCV%20with%20Practical%20Computer%20Vision%20Projects%20%5BeBook%5D.pdf

书归正传,开始正文。

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

本章将分三篇进行阐述:

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

二、基于Visual Studio的肤色改变

三、Android代码移植

本篇是第一篇

本篇涉及到的主要知识点包括:

  • 实现图片卡通化的基本思路
  • 双边滤波
  • 边缘检测之拉普拉斯算子、Sobel算子及Scharr算子

处理流程:

上效果图

第一幅为原图,第二幅为卡通化后的图片,第三幅为黑(鬼)化的图片。

为了能从原图获得一张卡通化的图片,首先确定思路。

  1. 去噪声。
  2. 通过边缘检测,获取图像的轮廓作为掩膜。
  3. 平坦化图片中低频部分,使其看起来更加卡通化。
  4. 将2、3得到的幅图片叠加得到最终图片。

第一步去噪。选择是中值滤波滤除噪点。这个不多说了。

然后是边缘检测。关于如何选用滤波器,引用一段书中的原话:

There are many different edge detection flters, such as Sobel, Scharr, Laplacian filters, or Canny-edge detector. We will use a Laplacian edge flter since it produces edges that look most similar to hand sketches compared to Sobel or Scharr, and that are quite consistent compared to a Canny-edge detector, which produces very clean line drawings but is affected more by random noise in the camera frames and the line drawings therefore often change drastically between frames.

简单地说选择了拉普拉斯算子。原因是,相比于其他滤波器,拉普拉斯得到的结果看起来更像素描的感觉。关于涉及到的理论知识在最后统一进行阐述。总之第二步,高斯滤波器进行边缘提取,生成一副素描图片。为了使素描更加清晰,需要对其进行二值化处理。

接下来是将平坦区域进一步柔化。这里选用双边滤波,在柔化平坦区域的同时,还能保证边缘不被模糊掉。双边的缺点是速度慢。为了加速滤波过程,这里采用了一个小技巧:首先缩小图片,然后进行双边滤波,最后复原图片。效果与直接对原图片进行滤波差不多,速度却快上几倍。

最后叠加图片。

上代码:

void cartoonifyImage(Mat& src, Mat& dst)
{Mat gray;cvtColor(src, gray, CV_BGR2GRAY);const int MEDIAN_BLUR_FILTER_SIZE = 7;medianBlur(gray, gray, MEDIAN_BLUR_FILTER_SIZE); // 中值Mat edges;const int LAPLACIAN_FILTER_SIZE = 5;Laplacian(gray, edges, CV_8U, LAPLACIAN_FILTER_SIZE); // 拉普拉斯Mat mask;const int EDGES_THRESHOLD = 80;threshold(edges, mask, EDGES_THRESHOLD, 255, THRESH_BINARY_INV); //二值化// 图片缩小Size size = src.size();Size smallSize;smallSize.width = size.width / 2;smallSize.height = size.height / 2;Mat smallImg = Mat(smallSize, CV_8UC3);resize(src, smallImg, smallSize, 0, 0, INTER_LINEAR);// 双边滤波Mat tmp = Mat(smallSize, CV_8UC3);int repetitions = 7;for (int i = 0; i < repetitions; i++){int ksize = 9;double sigmaColor = 9;double sigmaSpace = 7;bilateralFilter(smallImg, tmp, ksize, sigmaColor, sigmaSpace);bilateralFilter(tmp, smallImg, ksize, sigmaColor, sigmaSpace);}// 图像复原Mat bigImg;resize(smallImg, bigImg, size, 0, 0, INTER_LINEAR);dst.setTo(0);bigImg.copyTo(dst, mask);
}

有了图像卡通化的基础,把人脸画成黑鬼也就不难了。需要做的就是对图片进行两次Scharr滤波,并把两次结果叠加。将最终结果叠加到原图中即可。

void evilImage(Mat& src, Mat& dst)
{Mat gray, mask;cvtColor(src, gray, CV_BGR2GRAY);const int MEDIAN_BLUR_FILTER_SIZE = 7;medianBlur(gray, gray, MEDIAN_BLUR_FILTER_SIZE);Mat edges, edges2;Scharr(gray, edges, CV_8U, 1, 0);Scharr(gray, edges2, CV_8U, 1, 0, -1);edges += edges2;const int EVIL_EDGE_THRESHOLD = 12;threshold(edges, mask, EVIL_EDGE_THRESHOLD, 255, THRESH_BINARY_INV);medianBlur(mask, mask, 3);imshow("1", mask);dst.setTo(0);src.copyTo(dst, mask);
}

这张黑鬼的图片看多了真是难受啊。

理论部分

作为本次实现的主角,这里重点讲述两个知识点:双边滤波和边缘检测。

双边滤波

大多数空域滤波器在工作过程中不可避免地会导致边缘模糊,如均值滤波、中值滤波、高斯滤波等。如何能够实现在滤除平坦区域噪声时,保留边缘的锐化程度呢?这个时候就需要用到双边滤波器了。

首先给出双边滤波的解析式:

这里不对公式进行数学推导,仅阐释其物理含义。

其中k为归一化系数,c为空域权重项,s为值域权重项,均为高斯函数形式。普通的高斯滤波器仅考虑到像素位置对最终结果的影响,在处理过程中会模糊边缘。双边滤波在此基础上引入像素强度因素。像素强度差值越大,权重越小。换句话说,双边滤波将尽可能地只考虑邻域内强度相仿的像素对最后结果的影响。以此来保证边缘的锐化程度。

结合OpenCV中的函数原型

CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d,double sigmaColor, double sigmaSpace,int borderType = BORDER_DEFAULT )

可以看到除了输入输出图像之外,还要指定

d:滤波尺寸, 实时应用建议不超过5,离线应用建议不超过9。否则的话会很慢。

sigmaColor、sigmaSpace:由于两个权重项都是高斯函数,sigma越大,滤波效果越明显。<10时,效果不明显,>150将会产生很强的卡通效果。

boardType:用来指定补充边缘以外的像素值的方法。

其实所谓卡通化,就是使图像色彩更加平坦。为了达到这个目的,也可以采用多次滤波的方式。正如代码里写的那样。

这里直接盗张图,展示一下多次滤波后的效果

参考资料:http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html

边缘检测

在卡通化的过程中,用到了拉普拉斯滤波,黑化的过程中用到了Scharr滤波。这两种滤波器都属于边缘检测技术。

先抛开各种滤波名称不管。首先考虑一个问题,如果让你自己检测一个图像的边缘,你会如何做?要知道这个问题的答案,首先要明确什么样的点才是边缘点。现在我们有一个像素点,通过这个点水平画一条线,如果该点左边相邻的像素点的灰度值与右边相邻的像素点的灰度值相差很大,那么这是一个水平方向上的边缘点。为了使边缘突出,应有这样的处理方法:一个像素点两侧相邻的像素点灰度差值越大,滤波后该点的灰度值越大,借以凸显边缘。如果认可了这种做法,那应该很快能够得到以下的卷积模板(以下均以水平方向为例):

其实就是梯度越大,灰度越大。进一步考虑,像素点的水平相邻像素应该有更大的权重。于是,有了Sobel算子

再进一步,Sobel算子有什么缺点呢?在实际情况下,很多时候是由于图像的边缘不够清晰,所以才需要进行边缘的锐化。考虑一个不够清晰,灰度值渐变的边缘。一阶导数对其的响应是一个恒定值,而Sobel算子就是一阶滤波器。这意味着Sobel得到的图像边缘宽度将与渐变宽度一致。这有时是我们不想得到的结果。这也是一阶算子的缺点:滤波后的边缘较宽

如何解决这个问题呢?答案就在二阶导数!于是拉普拉斯算子登场了。

拉普拉斯算子解决了上述问题。对于渐变边缘,仍可保证较细的边缘。但与此同时,拉普拉斯引入了另外一问题:双边缘。什么情况?为了更清晰地理解上述论述,继续盗个图。

参考资料:《数字图像处理与机器视觉》

从上图还可以看出,拉普拉斯算子与渐变边缘的起点和终点都将产生锐化效果。另外,对于散点噪声滤除效果也并不好。所以,在使用时往往会首先进行低通滤波,然后再进行边缘锐化。

好了,还剩下一个Scharr算子。是酱紫滴。

也是一阶滤波器,只不过在Sobel算子的基础上进一步增大了左右邻域的权重值。同时增大了对比度,使我们的边缘更加狂野。所以针对黑化图片的需求,Scharr更加适合。

边缘检测的另外一种经典算法是Canny检测。具体的理论知识,会放到后面的章节阐述。仅需要说明的是,对于本次实验,由于Canny算法得到的边缘太细,所以不适合实验需求,没有采用。

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

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

    第一章 基于Android的图片卡通化及肤色改变 一.基于Visual Studio的图片卡通化 二.基于Visual Studio的肤色改变 三.Android代码移植 最近由于工作比较忙,很抱歉没 ...

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

    转载请注明出处:https://blog.csdn.net/mymottoissh/article/details/86723580 第一章 基于Android的图片卡通化及肤色改变 一.基于Visu ...

  3. 《RabbitMQ 实战指南》第一章 RabbitMQ 简介

    <RabbitMQ 实战指南>第一章 RabbitMQ 简介 文章目录 <RabbitMQ 实战指南>第一章 RabbitMQ 简介 一.什么是消息中间件 二.消息中间件的作用 ...

  4. Spring入门到精通:第一章 基础入门:1.Spring框架概述

    Spring入门到精通:第一章 基础入门:1.Spring框架概述 前言:为什么要学习Spring框架? 为什么要学习Spring框架呐? (1)使用广泛:现在很多的企业都有Spring的影子,不管是 ...

  5. 李艺《微信小程序全栈开发实战》(第一章)

    李艺<微信小程序全栈开发实战>(第一章) 双线程运行机制 小程序的特点及开发能力 小程序的特点 小程序的开发能力 开发小程序的一般流程 小程序的运行机制 小程序双线程 视图的持续更新是如何 ...

  6. 1. Vue从入门到精通(第一章 vue核心)

    Vue从入门到精通(第一章 vue核心) 第一章 Vue核心 1. Vue简介 1.1 Vue是什么? 1.2 Vue的作者以及迭代版本 1.3 Vue的特点 2. 搭建Vue开发环境 2.1 安装V ...

  7. 基于Android的手机订餐系统设计与实现(三)

    基于Android的手机订餐系统设计与实现(三) 文章目录 基于Android的手机订餐系统设计与实现(三) 一.HomeFragmet中的listItem的点击事件. 1.gain() 2.建立*C ...

  8. 基于ONNX的人物卡通化

    照片进行卡通化,是研究的重点.主要原理是检测人头分割.然后卡通化网络.直接ONNX调用,得到卡通化的效果. 效果如下图所示: C++和PYTHON都能实现,具体效果见下面链接 照片卡通化,只需要OPE ...

  9. 2022 最新 Android 基础教程,从开发入门到项目实战【b站动脑学院】学习笔记——第一章:Android开发环境搭建

    第 1 章 Android开发环境搭建 本章介绍了如何在个人电脑上搭建Android开发环境,主要包括:Android开发的发展历史是怎样的.Android Studio的开发环境是如何搭建的.如何创 ...

最新文章

  1. Java 高并发_JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程 没有项目经验的朋友不要错过!...
  2. 怎么让程序后台运行_CPU中的程序是怎么运行起来的?
  3. 使用Amazon Web Services(EC2)
  4. php sql desc,PHP SQL 查询封装
  5. java 线程只执行一次_java – 如何确保方法只执行一次并且只从一个线程执行?...
  6. 常见数据库默认端口号
  7. 一加8系列再次开售 全渠道销售额破亿
  8. 基于tensorflow+RNN的MNIST数据集手写数字分类
  9. Java服务器多站点,java客户端web服务器连接到多个web服务器
  10. testng多线程并行执行测试
  11. 构建LVS+Keepalived高可用群集
  12. ug冲模标准件库_ug标准件库免费
  13. hibernate 二级缓存 处理
  14. 建筑结构抗震设计新技术
  15. 机器学习基本概念-有监督学习和无监督学习
  16. Job for DmServiceDMSERVER.service failed because the control process exited with error code. Se
  17. sun存储的串口连接管理_修改SUN设备管理IP的步骤
  18. 计算机的语言栏怎么更改,win7电脑语言栏不见了如何修复
  19. flappy brid
  20. SAP S4 FI 后台详细配置教程文档 PART2 (财务会计的基本设置篇)

热门文章

  1. python实现对森林生物量进行随机森林回归预测
  2. c6387警告解决方案
  3. 云和恩墨携MogDB等全系产品亮相第十届PostgreSQL中国技术大会
  4. c语言const unsigned char,char * /const char */unsigned char * 转换
  5. php-90坦克大乱斗源码 v1.1(源码)
  6. java syslog解析_syslog日志格式解析
  7. Mr. Holmes(福尔摩斯先生观后感)--尖锐直白了一生,却以谎言结尾。
  8. 作为uboot到kernel中DTS DTSI DTB等关系
  9. Ubuntu 20.04 保姆级安装教程
  10. python培训机构怎么比较靠谱