将一副图像转换成油画

原创性声明:以下代码是本人改写自C#语言编写的软件改写自PhotoSprite (Version 3.0 ,2006,由 联骏 编写),由使用OpenCV300编写。
先看一下效果

点击看大图 点击看大图

算法未作任何优化,优化算法可以看Photoshop 油画效果滤镜
算法原理也不用细说了,源码之前,了无秘密。

代码

cv::Mat OilPaint(cv::Mat I,int brushSize, int coarseness)
{
    assert(!I.empty());
    if (brushSize < 1) brushSize = 1;
    if (brushSize > 8) brushSize = 8;

    if (coarseness < 1) coarseness = 1;
    if (coarseness > 255) coarseness = 255;

    int width  = I.cols;
    int height = I.rows;

    int lenArray = coarseness + 1;
    int* CountIntensity = new int[lenArray];
    uint* RedAverage    = new uint[lenArray];
    uint* GreenAverage  = new uint[lenArray];
    uint* BlueAverage   = new uint[lenArray];

    /// 图像灰度化
    Mat gray;
    cvtColor(I,gray,COLOR_BGR2GRAY);


    /// 目标图像
    Mat dst = Mat::zeros(I.size(),I.type());

    for(int nY = 0;nY <height; nY++)
    {
        // 油画渲染范围上下边界
        int top = nY - brushSize;
        int bottom = nY+ brushSize+1;

        if(top<0) top = 0;
        if(bottom >=height) bottom = height - 1;

        for(int nX = 0;nX<width;nX++)
        {
            // 油画渲染范围左右边界
            int left = nX - brushSize;
            int right = nX +brushSize+1;

            if(left<0) left = 0;
            if(right>=width) right = width - 1;

            //初始化数组
            for(int i = 0;i <lenArray;i++)
            {
                CountIntensity[i] = 0;
                RedAverage[i] = 0;
                GreenAverage[i] = 0;
                BlueAverage[i] = 0;
            }


            // 下面这个内循环类似于外面的大循环
            // 也是油画特效处理的关键部分
            for(int j = top;j<bottom;j++)
            {
                for(int i = left;i<right;i++)
                {
                    uchar intensity = static_cast<uchar>(coarseness*gray.at<uchar>(j,i)/255.0);
                    CountIntensity[intensity]++;

                    RedAverage[intensity]  += I.at<Vec3b>(j,i)[2];
                    GreenAverage[intensity]+= I.at<Vec3b>(j,i)[1];
                    BlueAverage[intensity] += I.at<Vec3b>(j,i)[0];
                }
            }

            // 求最大值,并记录下数组索引
            uchar chosenIntensity = 0;
            int maxInstance = CountIntensity[0];
            for(int i=1;i<lenArray;i++)
            {
                if(CountIntensity[i]>maxInstance)
                {
                    chosenIntensity = (uchar)i;
                    maxInstance = CountIntensity[i];
                }
            }

            dst.at<Vec3b>(nY,nX)[2] = static_cast<uchar>(RedAverage[chosenIntensity] / static_cast<float>(maxInstance));
            dst.at<Vec3b>(nY,nX)[1] = static_cast<uchar>(GreenAverage[chosenIntensity] /  static_cast<float>(maxInstance));
            dst.at<Vec3b>(nY,nX)[0] = static_cast<uchar>(BlueAverage[chosenIntensity] /  static_cast<float>(maxInstance));
        }

    }

    delete [] CountIntensity;
    delete [] RedAverage;
    delete [] GreenAverage;
    delete [] BlueAverage;

#ifdef _DEBUG
    imshow("dst",dst);
    waitKey();
#endif

    return dst;
}

后续

但是这样的油画效果,还是感觉欠缺了什么。好吧,再拿了一张油画纹理渲染一下吧。其实比较简单你可以使用正片叠底混合算法就可以了。
先看一下效果。(其实你也可以认为这种效果不好看,囧)

点击看大图 点击看大图

再来一张
点击看大图 点击看大图

另一张
点击看大图 点击看大图

界面

点击看大图

界面致谢,人在旅途
好了,油画滤镜介绍完毕。

转载请保留以下信息

作者 日期 联系方式
风吹夏天 2015年10月31日 wincoder#qq.com
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章