1. 程式人生 > >OpenCV 學習記錄7 影象實時磨皮及面板檢測

OpenCV 學習記錄7 影象實時磨皮及面板檢測

前言

今天學習了OpenCV裡一些濾波的用法,感覺獲益頗深,尤其是其中的雙邊濾波,完全可以達到磨皮的效果,原來還很糾結要如何實現磨皮美白,原來雙邊濾波就行了,當然如果要保留一些細節的話可能還需要用其他函式。
廢話不說,上程式碼:


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

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 <iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/features2d/features2d.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/calib3d/calib3d.hpp>  

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<uchar>(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 <iostream>  
#include <opencv2/core/core.hpp>  
#include <opencv2/features2d/features2d.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/imgproc/imgproc.hpp>  
#include <opencv2/calib3d/calib3d.hpp>  

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<Vec3b>(j, i)[0];
                G = tmp.at<Vec3b>(j, i)[1];
                R = tmp.at<Vec3b>(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<Vec3b>(j, i)[0] = 255;
                    tmp.at<Vec3b>(j, i)[1] = 0;
                    tmp.at<Vec3b>(j, i)[2] = 255;

                }
            }
        }

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

    }

    return 0;
}

這個上色方法雖然是一個思路但並不可行,換個環境就會一大片都給上色了,而且不能保留紋理做到真正塗口紅的樣子,所以這個方法基本不能用了,看到有篇文章的上色效果很棒,有空繼續好好研究下。