博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Meanshift filter实现简单图片的卡通化效果
阅读量:6266 次
发布时间:2019-06-22

本文共 7787 字,大约阅读时间需要 25 分钟。

    利用Meanshift filter和canny边缘检测的效果,可以实现简单的图片的卡通化效果。简单的说,就是用Meanshift filter的结果减去canny算法的结果得到卡通化的效果。

  代码如下:

#include <opencv2/core/core.hpp>

#include <opencv2/highgui//highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>

using namespace std;
using namespace cv;

int main()

{
    cv::Mat img = cv::imread("../lenna.jpg");
    cv::namedWindow("image");
    cv::imshow("image", img);

    cv::Mat img1;

    img1 = img.clone();

    //meanshift filter

    cv::pyrMeanShiftFiltering(img1.clone(), img1, 10, 30);
    cv::namedWindow("image1");
    cv::imshow("image1", img1);

    cv::Mat img2;

    cv::Mat img3;
    cv::Mat img4;

    //canny

    cv::cvtColor(img, img2, CV_BGR2GRAY);
    cv::Canny(img2, img3, 150, 150);
    cv::cvtColor(img3, img4, CV_GRAY2BGR);

    cv::namedWindow("image4");

    cv::imshow("image4", img4);

    //卡通化的图片

    img4 = img1 - img4;
    cv::namedWindow("image4_1");
    cv::imshow("image4_1", img4);

    cv::waitKey(0);
}

下面分别为,原始图像,meanshift filter后的图像,canny边缘图像,以及最终的卡通化图像。

img_89bc09ca8da3f27efdd300175e27823a.jpg

下面我们看看meanshift filter算法的原理。

 

在OpenCV中,meanshift filter函数为 pyrMeanShiftFiltering, 它的函数调用格式如下:

C++: void pyrMeanShiftFiltering(InputArray src, OutputArray dst, double sp, double sr, int maxLevel=1, TermCriteriatermcrit=TermCriteria( TermCriteria::MAX_ITER+TermCriteria::EPS,5,1) )

Parameters:

  • src – The source 8-bit, 3-channel image. //三通道的输入图像
  • dst – The destination image of the same format and the same size as the source. //相同尺寸格式输出图像
  • sp – The spatial window radius.  //空间域半径
  • sr – The color window radius.  //颜色域半径
  • maxLevel – Maximum level of the pyramid for the segmentation. //分割用金字塔的最大层数
  • termcrit – Termination criteria: when to stop meanshift iterations. //迭代终止的条件

算法的描述大致如下:

对于输入图像的每个像素点(X,Y) ,在它的半径为sp的空间域,执行meanshift迭代算法,

(x,y): X- \texttt{sp} \le x  \le X+ \texttt{sp} , Y- \texttt{sp} \le y  \le Y+ \texttt{sp} , ||(R,G,B)-(r,g,b)||   \le \texttt{sr}

像素点(X,Y)的颜色值为(R,G,B), 它的空间邻域点(x,y)的颜色值为(r,g,b),如果点(x,y)的到(X,Y)的颜色距离小于sr,则满足条件,最终我们求得满足条件点的平均空间坐标(X’,Y’)以及平均颜色向量(R',G',B'),并把它们作为下一次迭代的输入。

(X,Y)~(X',Y'), (R,G,B)~(R',G',B').

迭代结果后,我们把最初输入位置的颜色值用最终迭代的颜色值代替。

I(X,Y) <- (R*,G*,B*)

算法代码如下:

#include 
#include
#include
#include
using namespace std;using namespace cv;//forward声明void gMeanShift(int x0, int y0, uchar *sptr, uchar *dptr, int sstep, cv::Size size, int sp, int sr, int maxIter, float eps, int *tab);void gMeanShiftFilter(const cv::Mat src, cv::Mat &dst, int sp, int sr, cv::TermCriteria crit = cv::TermCriteria( cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 5, 1));int main(){ cv::Mat img = cv::imread("../lenna.jpg"); cv::namedWindow("image"); cv::imshow("image", img); cv::Mat img1; img1 = img.clone(); //meanshift filter //转化图像为4BGRA 4通道格式 cv::cvtColor(img1.clone(), img1, CV_BGR2BGRA); gMeanShiftFilter(img1.clone(), img1, 10, 30); cv::cvtColor(img1.clone(), img1, CV_BGRA2BGR); //meanshift filter result image cv::namedWindow("image1"); cv::imshow("image1", img1); cv::Mat img2; cv::Mat img3; cv::Mat img4; //canny cv::cvtColor(img, img2, CV_BGR2GRAY); cv::Canny(img2, img3, 150, 150); cv::cvtColor(img3, img4, CV_GRAY2BGR); cv::namedWindow("image4"); cv::imshow("image4", img4); img4 = img1 - img4; cv::namedWindow("image4_1"); cv::imshow("image4_1", img4); cv::waitKey(0);}void gMeanShift(int x0, int y0, uchar *sptr, uchar *dptr, int sstep, cv::Size size, int sp, int sr, int maxIter, float eps, int *tab){ int isr2 = sr * sr; int c0, c1, c2, c3; int iter; uchar *ptr = NULL; uchar *pstart = NULL; int revx = 0, revy = 0; c0 = sptr[0]; c1 = sptr[1]; c2 = sptr[2]; c3 = sptr[3]; /**************************************************************************** * Iterate meanshift procedure * ****************************************************************************/ for (iter = 0; iter < maxIter; iter++) { int count = 0; int s0 = 0, s1 = 0, s2 = 0, sx = 0, sy = 0; /**************************************************************************** * mean shift: process pixels in window (p-sigmaSp)x(p+sigmaSp) * ****************************************************************************/ int minx = x0 - sp; int miny = y0 - sp; int maxx = x0 + sp; int maxy = y0 + sp; /**************************************************************************** * Deal with the image boundary. * ****************************************************************************/ if (minx < 0) { minx = 0; } if (miny < 0) { miny = 0; } if (maxx >= size.width) { maxx = size.width - 1; } if (maxy >= size.height) { maxy = size.height - 1; } if (iter == 0) { pstart = sptr; } else { pstart = pstart + revy * sstep + (revx << 2); //point to the new position } ptr = pstart; //point to the start in the row ptr = ptr + (miny - y0) * sstep + ((minx - x0) << 2); for (int y = miny; y <= maxy; y++, ptr += sstep - ((maxx - minx + 1) << 2)) { int rowCount = 0; int temp, temp1; temp1 = (maxx - minx + 1) << 2; temp = sstep - ((maxx - minx + 1) << 2); int x = minx; for (; x <= maxx; x++, ptr += 4) { int t0 = ptr[0], t1 = ptr[1], t2 = ptr[2]; if (tab[t0 - c0 + 255] + tab[t1 - c1 + 255] + tab[t2 - c2 + 255] <= isr2) { s0 += t0; s1 += t1; s2 += t2; sx += x; rowCount++; } } if (rowCount == 0) { continue; } count += rowCount; sy += y * rowCount; } if (count == 0) { break; } int x1 = sx / count; int y1 = sy / count; s0 = s0 / count; s1 = s1 / count; s2 = s2 / count; bool stopFlag = (x0 == x1 && y0 == y1) || (abs(x1 - x0) + abs(y1 - y0) + tab[s0 - c0 + 255] + tab[s1 - c1 + 255] + tab[s2 - c2 + 255] <= eps); /**************************************************************************** * Revise the pointer corresponding to the new (y0,x0) * ****************************************************************************/ // revx = x1 - x0; revy = y1 - y0; x0 = x1; y0 = y1; c0 = s0; c1 = s1; c2 = s2; if (stopFlag) { break; } } dptr[0] = (uchar)c0; dptr[1] = (uchar)c1; dptr[2] = (uchar)c2; dptr[3] = (uchar)c3;}void gMeanShiftFilter(const cv::Mat src, cv::Mat &dst, int sp, int sr, cv::TermCriteria crit){ if (src.empty()) { cout << "Source is null" << endl; } if (!(crit.type & cv::TermCriteria::MAX_ITER)) { crit.maxCount = 5; } int maxIter = std::min(std::max(crit.maxCount, 1), 100); float eps; if (!(crit.type & cv::TermCriteria::EPS)) { eps = 1.f; } eps = (float)std::max(crit.epsilon, 0.0); int tab[512]; for (int i = 0; i < 512; i++) { tab[i] = (i - 255) * (i - 255); } uchar *sptr = src.data; uchar *dptr = dst.data; int sstep = (int)src.step; int dstep = (int)dst.step; cv::Size size = src.size(); for (int i = 0; i < size.height; i++, sptr += sstep - (size.width << 2), dptr += dstep - (size.width << 2)) { int tt, tt1; tt = size.width << 2; tt1 = sstep - (size.width << 2); for (int j = 0; j < size.width; j++, sptr += 4, dptr += 4) { gMeanShift(j, i, sptr, dptr, sstep, size, sp, sr, maxIter, eps, tab); } }}

转载地址:http://hocpa.baihongyu.com/

你可能感兴趣的文章
html5游戏开发-简单tiger机
查看>>
Codeforces 712C Memory and De-Evolution
查看>>
编写的windows程序,崩溃时产生crash dump文件的办法
查看>>
Ural2110 : Remove or Maximize
查看>>
Django REST framework 的TokenAuth认证及外键Serializer基本实现
查看>>
《ArcGIS Runtime SDK for Android开发笔记》——问题集:如何解决ArcGIS Runtime SDK for Android中文标注无法显示的问题(转载)...
查看>>
Spring Boot日志管理
查看>>
动态注册HttpModule管道,实现global.asax功能
查看>>
使用 ES2015 编写 Gulp 构建
查看>>
[转]Using NLog for ASP.NET Core to write custom information to the database
查看>>
BZOJ 4766: 文艺计算姬 [矩阵树定理 快速乘]
查看>>
MySQL 的instr函数
查看>>
Hibernate的核心对象关系映射
查看>>
接口与抽象类的使用选择
查看>>
if __name__ == '__main__'
查看>>
CF 375D. Tree and Queries【莫队 | dsu on tree】
查看>>
Maven最佳实践 划分模块 配置多模块项目 pom modules
查看>>
Hadoop学习笔记——WordCount
查看>>
Unity应用架构设计(4)——设计可复用的SubView和SubViewModel(Part 1)
查看>>
Java-Spring-获取Request,Response对象
查看>>