• Meanshift filter实现简单图片的卡通化效果


        利用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边缘图像,以及最终的卡通化图像。

    下面我们看看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- 	exttt{sp} le x  le X+ 	exttt{sp} , Y- 	exttt{sp} le y  le Y+ 	exttt{sp} , ||(R,G,B)-(r,g,b)||   le 	exttt{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 <opencv2/core/core.hpp>
    #include <opencv2/highgui//highgui.hpp>
    #include <opencv2/imgproc/imgproc.hpp>
    
    #include <iostream>
    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);
            }
        }
    }

  • 相关阅读:
    [Swift]LeetCode96. 不同的二叉搜索树 | Unique Binary Search Trees
    [Swift]LeetCode95. 不同的二叉搜索树 II | Unique Binary Search Trees II
    [Swift]LeetCode94. 二叉树的中序遍历 | Binary Tree Inorder Traversal
    [Swift]LeetCode93. 复原IP地址 | Restore IP Addresses
    [Swift]LeetCode92. 反转链表 II | Reverse Linked List II
    [Swift]LeetCode91. 解码方法 | Decode Ways
    [Swift]LeetCode90. 子集 II | Subsets II
    谈谈我对P2P网络借贷的一些看法
    辣妈萌宝面试心得体会
    辣妈萌宝面试心得体会
  • 原文地址:https://www.cnblogs.com/mikewolf2002/p/5615209.html
Copyright © 2020-2023  润新知