OpenCV——證件照自動摳圖

今天去交社保,要白底的電子版照片,我目前手頭就是一個藍底的,又不想手動摳圖,因而想作一個自動換背景的程序。c++

先上效果:算法

具體分三步,第一步是大致的背景轉換,把藍色變爲白色:ide

void colortransfer(cv::Mat image) //藍背景轉白背景,有邊緣殘留
{
    int Diff;
    int num_row = image.rows;
    int num_col = image.cols;
    for (int r = 0; r < num_row; r++)
    {
        cv::Vec3b *data = image.ptr<cv::Vec3b>(r);
        for (int c = 0; c < num_col; c++)
        {
            Diff = data[c][0] - (data[c][1] + data[c][2]) / 2; //藍色檢測
            if (Diff > 60 && data[c][0]>150)//藍色份量比GR份量的平均值高60且藍色份量大於150
            {
                data[c][0] = 255; 
                data[c][1] = 255;
                data[c][2] = 255;
            }
        }
    }    
}

可是這步出來以後效果不太好,能夠看出來有很明顯的藍色邊緣,不少藍底照片由於照的時候和背景過近,致使邊緣顏色變化this

 

接下來就是對這些深色的藍邊進行一個處理,這裏採用了檢測藍邊->中值濾波器的方法:spa

void Optimization(cv::Mat image)   //去邊緣殘留
{
    int num_row = image.rows;
    int num_col = image.cols;
    for (int i = 1; i < num_row-1; i++)
    {
        cv::Vec3b *last_r = image.ptr<cv::Vec3b>(i-1);
        cv::Vec3b *data = image.ptr<cv::Vec3b>(i);
        cv::Vec3b *next_r = image.ptr<cv::Vec3b>(i+1);
        for (int j = 1; j < num_col-1; j++)
        {    
            if (data[j][0]>90 && data[j][0] - data[j][1]>9 && data[j][0] - data[j][2]>9)
            {
                int stat;
                cv::Vec3b Temp;
                cv::Vec3b array[9] = { last_r[j - 1], last_r[j], last_r[j + 1], data[j - 1], data[j], data[j + 1], next_r[j - 1], next_r[j], next_r[j + 1] };
                do
                {
                    stat = 0;
                    for (int m = 0; m < 8; m++)
                    {
                        if (array[m][0] + array[m][1] + array[m][2]> array[m + 1][0] + array[m + 1][1] + array[m + 1][2])
                        {
                            Temp=array[m + 1];
                            array[m + 1] = array[m];
                            array[m] = Temp;
                            stat = 1;
                        }

                    }
                } while (stat == 1);
                data[j][0] = array[7][0];  
                data[j][1] = array[7][1];
                data[j][2] = array[7][2];
            }
        }
    }
}

中間用了冒泡算法進行排序,結果以下:3d

邊緣基本去除了,可是頭髮這邊顯得很是不真實,沒有邊緣的毛躁感。這裏咱們檢測到頭髮邊緣後,對邊緣區域進行一個低通濾波和顏色的加白處理,讓邊緣有層次感:code

void hairilization(cv::Mat image) //毛躁化
{
    int num_row = image.rows / 3;
    int num_col = image.cols;
    for (int i = 2; i < num_row - 2; i=i+5)
    {
        cv::Vec3b *last_sec_r = image.ptr<cv::Vec3b>(i - 2);
        cv::Vec3b *last_r = image.ptr<cv::Vec3b>(i - 1);
        cv::Vec3b *data = image.ptr<cv::Vec3b>(i);
        cv::Vec3b *next_r = image.ptr<cv::Vec3b>(i + 1);
        cv::Vec3b *next_sec_r = image.ptr<cv::Vec3b>(i + 2);
        for (int j = 2; j < num_col; j = j + 5)
        {
            int count = 0;// check how many 255point in this area(boundary)
            cv::Vec3b array[5][5] = { last_sec_r[j - 2], last_sec_r[j - 1], last_sec_r[j], last_sec_r[j + 1], last_sec_r[j + 2], \
                            last_r[j - 2], last_r[j - 1], last_r[j], last_r[j + 1], last_r[j + 2],\
                            data[j - 2], data[j - 1], data[j], data[j + 1], data[j + 2],\
                            next_r[j - 2], next_r[j - 1], next_r[j], next_r[j + 1], next_r[j + 2], \
                        next_sec_r[j - 2], next_sec_r[j - 1], next_sec_r[j], next_sec_r[j + 1], next_sec_r[j + 2]};
            for (int r = 0; r < 5; r++)
            {
                for (int c = 0; c < 5; c++)
                {
                    if (array[r][c][1] >= 251)
                        count++;
                }
            }
            if (count >= 7 &&count<=18) //說明是頭髮邊緣,開始處理
            {
                
                last_r[j - 1] = 1 / 9 * (array[0][0] + array[0][1] + array[0][2] + array[1][0] + array[1][1] + array[1][2] + array[2][0] + array[2][1] + array[2][2]) + cv::Vec3b(100, 100, 100);
                last_r[j] = 1 / 9 * (array[0][1] + array[0][2] + array[0][3] + array[1][1] + array[1][2] + array[1][3] + array[2][1] + array[2][2] + array[2][3]) + cv::Vec3b(80, 80, 80);
                last_r[j + 1] = 1 / 9 * (array[0][2] + array[0][3] + array[0][4] + array[1][2] + array[1][3] + array[1][4] + array[2][2] + array[2][3] + array[2][4]) + cv::Vec3b(100, 100, 100);
                
                data[j - 1] = (1 / 9 * array[1][0] + 1 / 9 * array[1][1] + 1 / 9 * array[1][2] + 1 / 9 * array[2][0] + 1 / 9 * array[2][1] + 1 / 9 * array[2][2] + 1 / 9 * array[3][0] + 1 / 9 * array[3][1] + 1 / 9 * array[3][2]) + cv::Vec3b(80, 80, 80);
                data[j] = (1 / 9 * array[1][1] + 1 / 9 * array[1][2] + 1 / 9 * array[1][3] + 1 / 9 * array[2][1] + 1 / 9 * array[2][2] + 1 / 9 * array[2][3] + 1 / 9 * array[3][1] + 1 / 9 * array[3][2] + 1 / 9 * array[3][3]) + cv::Vec3b(80, 80, 80);
                data[j + 1] =  (1 / 9 * array[1][2] + 1 / 9 * array[1][3] + 1 / 9 * array[1][4] + 1 / 9 * array[2][2] + 1 / 9 * array[2][3] + 1 / 9 * array[2][4] + 1 / 9 * array[3][2] + 1 / 9 * array[3][3] + 1 / 9 * array[3][4]) + cv::Vec3b(80, 80, 80);
                
                data[j - 1] =  (1 / 9 * array[2][0] + 1 / 9 * array[2][1] + 1 / 9 * array[2][2] + 1 / 9 * array[3][0] + 1 / 9 * array[3][1] + 1 / 9 * array[3][2] + 1 / 9 * array[4][0] + 1 / 9 * array[4][1] + 1 / 9 * array[4][2]) + cv::Vec3b(100, 100, 100);
                data[j] =  (1 / 9 * array[2][1] + 1 / 9 * array[2][2] + 1 / 9 * array[2][3] + 1 / 9 * array[3][1] + 1 / 9 * array[3][2] + 1 / 9 * array[3][3] + 1 / 9 * array[4][1] + 1 / 9 * array[4][2] + 1 / 9 * array[4][3]) + cv::Vec3b(80, 80, 80);
                data[j + 1] =  (1 / 9 * array[2][2] + 1 / 9 * array[2][3] + 1 / 9 * array[2][4] + 1 / 9 * array[3][2] + 1 / 9 * array[3][3] + 1 / 9 * array[3][4] + 1 / 9 * array[4][2] + 1 / 9 * array[4][3] + 1 / 9 * array[4][4]) + cv::Vec3b(100, 100, 100);
                
            }
        }
    }
}
View Code

這部分代碼比較長,的因此隱藏了。blog

結果以下:排序

相比於:io

 能夠看到頭髮的邊緣上有很大提高,由於均值濾波把邊緣的頭髮變淺,造成了一個層次感,模擬了在白幕照相的效果。

相關文章
相關標籤/搜索