OpenCV 学习记录7 图像实时磨皮及皮肤检测

前言

今天学习了OpenCV里一些滤波的用法,感觉获益颇深,尤其是其中的双边滤波,完全可以达到磨皮的效果,原来还很纠结要如何实现磨皮美白,原来双边滤波就行了,当然如果要保留一些细节的话可能还需要用其他函数。
废话不说,上代码:


#include 
#include
#include
#include 

using namespace std;
using namespace cv;


Mat g_srcImage,  g_dstImage5;
int g_nBilateralFilterValue = 15;  //双边滤波参数值,不同数值效果不一样


int main()
{


    VideoCapture capture(0); //从摄像头中获取图像
    while (1)
    {
        if (char(waitKey(1)) == 'q')  break; //长按1ms q键退出;

        capture >> g_srcImage;
        if (!g_srcImage.data) { printf("Oh,no,读取srcImage错误~!\n"); return false; }

        g_dstImage5 = g_srcImage.clone();

        //显示原图
        namedWindow("原图窗口", 1);
        imshow("原图窗口", g_srcImage);

        namedWindow("双边滤波", 1);
        bilateralFilter(g_srcImage, g_dstImage5, g_nBilateralFilterValue, g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);
        imshow("双边滤波", g_dstImage5);
        waitKey(30);

    }

    return 0;
}

关于滤波的学习主要是参考浅墨大大的文章拉,其中一篇文章结尾给了一个用tracebar来调节参数进行均值滤波、方波滤波、高斯滤波、双边滤波的代码,可以很直观的理解各种滤波的效果大概是什么样。附上文章传送门:浅墨大大的滤波教程


因为项目中是要照镜子一样的效果,所以肯定是需要实时同步的,所以设置了用摄像头来读取,处理速度还可以,基本上可以达到像手机美颜APP那种效果,还是不错的。

皮肤检测是利用RGB转换成YCrCb,然后根据CrCb的范围来确认图像中那一块区域是皮肤,原理是因为人的肤色受亮度影响很大,用CrCb来定像素点简单明了而且效果还可以,但环境的光亮程度对检测还是有一定影响,代码是将判定为皮肤的像素点变成白色,否侧变成黑色,然后显示结果图片,上代码:

#include   
#include   
#include   
#include   
#include   
#include   

using namespace std;
using namespace cv;

int main()
{
    VideoCapture cap(0);//摄像头读取

    if (!cap.isOpened())
    {
        return -1;
    }
    namedWindow("result");
    namedWindow("frame");
    Mat frame;
    Mat result, tmp;
    Mat Y, Cr, Cb;
    Mat channels[3];//这里很多教程分离通道都是用向量,但不知道为什么会报错所以改成数组了
    char c;
    while (1) {

        if (c = waitKey(1) == 'q') break; //按1ms q键退出

        cap >> frame;                     //读取视频帧  
        frame.copyTo(tmp);                  //拷贝备份  
                                            /*转换颜色空间并分割颜色通道*/
        cvtColor(tmp, tmp, CV_BGR2YCrCb);
        split(tmp, channels);

        Y = channels[0];
        Cr = channels[1];
        Cb = channels[2];

        result.create(frame.rows, frame.cols, CV_8UC1);//创建等大小图像

        for (int j = 1; j < Y.rows - 1; j++)
        {
            uchar* currentCr = Cr.ptr< uchar>(j);
            uchar* currentCb = Cb.ptr< uchar>(j);
            uchar* current = result.ptr< uchar>(j);
            //uchar* frameCurrent = frame.ptr(j);//加上这行和下面的frameCurrent行会有灵异效果
            for (int i = 1; i < Y.cols - 1; i++)
            { // 这个Cr、Cb的范围根据实际环境要进行调节效果会好点
                if ((currentCr[i] > 125) && (currentCr[i] < 155) && (currentCb[i] > 105) && (currentCb[i] < 125))
                {
                    current[i] = 255;
                //  frameCurrent[i] = 255;//灵异效果
                }
                else
                {
                    current[i] = 0;
                }
            }
        }

        imshow("frame", frame);
        imshow("result", result);

    }

    return 0;
}

本来想着皮肤检测和双边滤波一起使用可以达到只对皮肤进行磨皮而不影响环境效果的,但是实际不知道该如何结合起来,再思考思考吧。

还有一个嘴唇上色代码,但因为受环境光线影响很大基本算是不能用,原理还是像皮肤检测一样限定区域来定位嘴唇区域,在办公室调参数的时候效果还行,回宿舍后同样的参数就上色效果一塌糊涂了,也上个代码:


#include   
#include   
#include   
#include   
#include   
#include   

using namespace std;
using namespace cv;

int main()
{
    VideoCapture cap(0);//摄像头读取

    if (!cap.isOpened())
    {
        return -1;
    }
    namedWindow("result");
    namedWindow("frame");
    Mat frame;
    Mat  tmp;
    double Y, I, Q;

    char c;
    while (1) {

        if (c = waitKey(1) == 'q') break; //按1ms q键退出

        cap >> frame;                     //读取视频帧  
        frame.copyTo(tmp);                  //拷贝备份  

        int B, G, R;
        for (int j = 0; j < tmp.rows; j++)
        {

            for (int i = 0; i < tmp.cols; i++)
            {
                B = tmp.at(j, i)[0];
                G = tmp.at(j, i)[1];
                R = tmp.at(j, i)[2];
                Y = 0.299 * R + 0.587 * G + 0.114 * B;
                I = 0.596 * R - 0.275 * G - 0.321 * B;
                Q = 0.212 * R - 0.523 * G + 0.311 * B;
                if ( //Y>20 && Y<120 //调节这YIQ的范围来确定嘴唇
                    //&& I>5 && I<20
                    //&& Q>0 && Q<30 
                    Y>20 && Y<220
                    && I>5 && I<20
                    && Q>0 && Q<10
                    && R>10 && R<120 //加个RGB范围来去掉无关区域
                    && B>10 && B<100
                    && G>20 && G<100
                    )
                {
                    tmp.at(j, i)[0] = 255;
                    tmp.at(j, i)[1] = 0;
                    tmp.at(j, i)[2] = 255;

                }
            }
        }

        imshow("frame", frame);
        imshow("result", tmp);

    }

    return 0;
}

这个上色方法虽然是一个思路但并不可行,换个环境就会一大片都给上色了,而且不能保留纹理做到真正涂口红的样子,所以这个方法基本不能用了,看到有篇文章的上色效果很棒,有空继续好好研究下。

你可能感兴趣的:(OpenCV学习笔记)