实现原理
OpenCV中有自带的stylization函数,使图像呈漫画状态,可是有点不太像,于是自己重写了一个。定义Cartoon函数,有4个输入参数:src为输入图像;clevel为轮廓参数,调整可改变轮廓的数量;d为双边滤波中的参数d,sigma同时控制双边滤波中的参数sigmaColor和sigmaSpace;size为漫画同色区域的尺寸大小,该值越大,则同色区域面积越大。
对图像进行中值滤波减少噪声,用Canny提取轮廓线并适当膨胀,反转后均值滤波处理下;对原图进行双边滤波,平滑感更强,符合漫画特征;将处理后的轮廓线点乘双边滤波图,以达到轮廓线加深的效果。
话不多说,上代码。
功能函数代码
// 漫画效果 cv::Mat Cartoon(cv::Mat src, double clevel,int d,double sigma,int size) { // 中值滤波 cv::Mat m; cv::medianBlur(src, m, 7); // 提取轮廓 cv::Mat c; clevel = max(40., min(80., clevel)); cv::Canny(m, c, clevel, clevel *3); // 轮廓膨胀加深 cv::Mat k = getStructuringElement(MORPH_RECT, Size(2, 2)); cv::dilate(c, c, k); // 反转 c = c / 255; c = 1 - c; // 类型转化 cv::Mat cf; c.convertTo(cf, CV_32FC1); // 均值滤波 cv::blur(cf, cf, Size(5, 5)); // 双边滤波 cv::Mat srcb; d = max(0, min(10, d)); sigma = max(10., min(250., sigma)); cv::bilateralFilter(src, srcb, d, sigma, sigma); size = max(10, min(25, size)); cv::Mat temp = srcb / size; temp = temp * size; // 通道合并 Mat c3; Mat cannyChannels[] = { cf, cf, cf }; merge(cannyChannels, 3, c3); // 类型转化 Mat tempf; temp.convertTo(tempf, CV_32FC3); // 图像相乘 multiply(tempf, c3, tempf); // 类型转化 tempf.convertTo(temp, CV_8UC3); return temp; }
C++测试代码
#include <iostream> #include "opencv2/core.hpp" #include "opencv2/highgui.hpp" #include "opencv2/imgproc.hpp" using namespace cv; using namespace std; cv::Mat Cartoon(cv::Mat src, double clevel, int d, double sigma, int size); int main() { cv::Mat src = imread("test1.jpg"); double clevel = 80.; int d = 5; double sigma = 150.; int size = 20; cv::Mat result = Cartoon(src, clevel,d,sigma,size); imshow("src", src); imshow("result", result); waitKey(0); return 0; } // 漫画效果 cv::Mat Cartoon(cv::Mat src, double clevel,int d,double sigma,int size) { // 中值滤波 cv::Mat m; cv::medianBlur(src, m, 7); // 提取轮廓 cv::Mat c; clevel = max(40., min(80., clevel)); cv::Canny(m, c, clevel, clevel *3); // 轮廓膨胀加深 cv::Mat k = getStructuringElement(MORPH_RECT, Size(2, 2)); cv::dilate(c, c, k); // 反转 c = c / 255; c = 1 - c; // 类型转化 cv::Mat cf; c.convertTo(cf, CV_32FC1); // 均值滤波 cv::blur(cf, cf, Size(5, 5)); // 双边滤波 cv::Mat srcb; d = max(0, min(10, d)); sigma = max(10., min(250., sigma)); cv::bilateralFilter(src, srcb, d, sigma, sigma); size = max(10, min(25, size)); cv::Mat temp = srcb / size; temp = temp * size; // 通道合并 Mat c3; Mat cannyChannels[] = { cf, cf, cf }; merge(cannyChannels, 3, c3); // 类型转化 Mat tempf; temp.convertTo(tempf, CV_32FC3); // 图像相乘 multiply(tempf, c3, tempf); // 类型转化 tempf.convertTo(temp, CV_8UC3); return temp; }
测试效果
图1 原图
图2 漫画效果(重)
图3 漫画效果(轻)
函数对输入的参数作了限制,阈值是由我个人大量测试后设置的,因人而异。clevel阈值40-80,d阈值0-10,sigma阈值10-250,size阈值10-25。效果相比stylization函数一类的方法已经好很多了,后面给大家做一期stylization函数,看看恐怖风漫画是什么样子的哈哈。
如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!