C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ OpenCV图片动漫化

C++ opencv将图片动漫化介绍

作者:Lammyzp

大家好,本篇文章主要讲的是C++ opencv将图片动漫化介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下

最近对图像处理十分感兴趣,也学着用opencv 实现各种简单的图像处理,因此,有了下面的实验,就是将照片处理成漫画的风格。

对照片进行动漫话一般需要四个步骤
1、边缘检测
2、将边缘检测得到的边缘 以黑色的形式贴在原来的画上。
3、对贴了边缘的图进行双边滤波,双边滤波可以较好的滤波的同时保留边缘。
4、修改图像的颜色的饱和度,本文采用的是将RGB转化为HSI空间,然后调整S分量。

边缘检测

对于边缘检测,本文采用的是canny算法
此文中将低阈值设定在70,高阈值则为70*3。
执行后的结果为:

这里写图片描述

贴边缘图到原图

将边缘图以黑色贴到原图上,原图上非边缘区域仍然为原来的颜色,动漫就是边缘很明显,且边缘不是很多,不注重细节,因此这里将边缘贴上面当作边缘,后续利用双倍滤波将图中的其他相对小的细节边缘去掉。针对纹理贴图主要用到下面这个函数:

// 将边缘检测后的图 cannyImage 边以黑色的形式贴在原图 image上。
void pasteEdge(Mat &image, Mat &outImg, IplImage cannyImage)
{
    Mat cannyMat;
    //将IplImage转化为Mat
    cannyMat = cvarrToMat(&cannyImage); 
    //颜色反转
    cannyMat = cannyMat < 100;
    image.copyTo(outImg, cannyMat);
}

执行后的效果如下:

这里写图片描述

双边滤波

双边滤波(Bilateral filter)在图像美化,美颜上有广泛的运用,是一种可以保边去噪的滤波器,由两个函数构成。为了节约时间,这里就借用一张图来充当介绍了

这里写图片描述

opencv也对此有函数调用:

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

前面2个参数为输入图像,输出图像,d为双倍滤波的算子大小,sigmacolor ,sigmaSpace是2个滤波函数的nameda值(这里节约时间不打符号了)
本文相关代码:

    // 双边滤波
    Mat binateMat;
    bilateralFilter(pasteEdgeMat, binateMat, 10, 50, 50, BORDER_DEFAULT);

执行后的结果如下:

这里写图片描述

HSI空间修改饱和度

关于HSI颜色空间这里就不详细介绍了,大家可以百度下,很多文章介绍,后续我也可能总结一下各个颜色空间,并且与rgb转换方法。主要思路:将贴有边缘 且 双边滤波后的图像 转化为 HSI 空间,而将S分量增大到原来的SRadio倍,然后将HSI空间图像转化回Rgb,并显示。

将颜色空间转化HSI,并增加S分量为原来的sRadio倍,主要是使用了下面这个函数:

// 将image 像素转化到 HSI 空间,并调整S 即颜色的饱和度,
void changeSImage(Mat &image, IplImage &outImg, float sRadio)
{
    int rows = image.rows;
    int cols = image.cols;
    // 三个HSI空间数据矩阵
    CvMat* HSI_H = cvCreateMat(rows, cols, CV_32FC1);
    CvMat* HSI_S = cvCreateMat(rows, cols, CV_32FC1);
    CvMat* HSI_I = cvCreateMat(rows, cols, CV_32FC1);

    // 原始图像数据指针, HSI矩阵数据指针
    uchar* data;

    // rgb分量
    int img_r, img_g, img_b;
    int min_rgb;  // rgb分量中的最小值
    // HSI分量
    float fHue, fSaturation, fIntensity;
    int channels = image.channels();
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            data = image.ptr<uchar>(i);
            data = data + j*channels;
            img_b = *data;
            data++;
            img_g = *data;
            data++;
            img_r = *data;

            // Intensity分量[0, 1]
            fIntensity = (float)((img_b + img_g + img_r) / 3) / 255;

            // 得到RGB分量中的最小值
            float fTemp = img_r < img_g ? img_r : img_g;
            min_rgb = fTemp < img_b ? fTemp : img_b;
            // Saturation分量[0, 1]
            fSaturation = 1 - (float)(3 * min_rgb) / (img_r + img_g + img_b);

            // 计算theta角
            float numerator = (img_r - img_g + img_r - img_b) / 2;
            float denominator = sqrt(
                pow((img_r - img_g), 2) + (img_r - img_b)*(img_g - img_b));

            // 计算Hue分量
            if (denominator != 0)
            {
                float theta = acos(numerator / denominator) * 180 / 3.14;

                if (img_b <= img_g)
                {
                    fHue = theta;
                }
                else
                {
                    fHue = 360 - theta;
                }
            }
            else
            {
                fHue = 0;
            }

            // 赋值
            cvmSet(HSI_H, i, j, fHue);
            cvmSet(HSI_S, i, j, fSaturation * sRadio);
            cvmSet(HSI_I, i, j, fIntensity);
        }
    }
    outImg = *HSI2RGBImage(HSI_H, HSI_S, HSI_I);    
}

HSI2RGBImage(HSI_H, HSI_S, HSI_I)是将三个分类的Mat 合并并转化为BGR的图,函数如下:

IplImage* HSI2RGBImage(CvMat* HSI_H, CvMat* HSI_S, CvMat* HSI_I)
{
    IplImage * RGB_Image = cvCreateImage(cvGetSize(HSI_H), IPL_DEPTH_8U, 3);

    int iB, iG, iR;
    for (int i = 0; i < RGB_Image->height; i++)
    {
        for (int j = 0; j < RGB_Image->width; j++)
        {
            // 该点的色度H
            double dH = cvmGet(HSI_H, i, j);
            // 该点的色饱和度S
            double dS = cvmGet(HSI_S, i, j);
            // 该点的亮度
            double dI = cvmGet(HSI_I, i, j);

            double dTempB, dTempG, dTempR;
            // RG扇区
            if (dH < 120 && dH >= 0)
            {
                // 将H转为弧度表示
                dH = dH * 3.1415926 / 180;
                dTempB = dI * (1 - dS);
                dTempR = dI * (1 + (dS * cos(dH)) / cos(3.1415926 / 3 - dH));
                dTempG = (3 * dI - (dTempR + dTempB));
            }
            // GB扇区
            else if (dH < 240 && dH >= 120)
            {
                dH -= 120;

                // 将H转为弧度表示
                dH = dH * 3.1415926 / 180;

                dTempR = dI * (1 - dS);
                dTempG = dI * (1 + dS * cos(dH) / cos(3.1415926 / 3 - dH));
                dTempB = (3 * dI - (dTempR + dTempG));
            }
            // BR扇区
            else
            {
                dH -= 240;

                // 将H转为弧度表示
                dH = dH * 3.1415926 / 180;

                dTempG = dI * (1 - dS);
                dTempB = dI * (1 + (dS * cos(dH)) / cos(3.1415926 / 3 - dH));
                dTempR = (3 * dI - (dTempG + dTempB));
            }

            iB = dTempB * 255;
            iG = dTempG * 255;
            iR = dTempR * 255;

            cvSet2D(RGB_Image, i, j, cvScalar(iB, iG, iR));
        }
    }

    return RGB_Image;
}

执行后就大功告成了,效果如下:

这里写图片描述

后续:

上述执行基本完成了照片的漫画风格,但看到天空的云的一些边缘泰国刺眼,本着好玩的性子,去掉了第一步和第二步,直接图原图进行了双边滤波和增加颜色饱和度,感觉图清晰,自然了些,但漫画风格也少了些,具体如何见下图:

这里写图片描述

github地址:https://github.com/hurtnotbad/cartoon

总结

到此这篇关于C++ opencv将图片动漫化介绍的文章就介绍到这了,更多相关C++ OpenCV图片动漫化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文