QT5+OpenCV磨皮效果

【2017-04-01:完整项目文件GitHub


磨皮的详细内容请参考大神的博文:简单探讨可牛影像软件中具有肤质保留功能的磨皮算法及其实现细节。



根据上面文中所述,磨皮的主要公式为:Dest =(Src * (100 - Opacity) + (Src + 2 * GuassBlur(EPFFilter(Src) - Src + 128) - 256) * Opacity) /100


其中用到OpenCV的双边滤波:
bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace,int borderType=BORDER_DEFAULT );

以及OpenCV的高斯模糊:
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT );
注意ksize的宽和高必须是奇数


将公式转换为OpenCV代码如下;其中src为原图;dst为输出图片;输入参数value1为磨皮程度,value2为细节程度;尽量保持分步处理,合并步骤可能会出错
void buffing(int value1,int value2)
{
    int dx = value1*5;    //双边滤波参数之一
    double fc = value1*12.5; //双边滤波参数之一
    int p = 50;//透明度
    Mat temp1,temp2,temp3,temp4;
    
    //双边滤波
    bilateralFilter(src, temp1, dx, fc, fc);
    
    temp2 = (temp1 - src + 128);
    
    //高斯模糊
    GaussianBlur(temp2, temp3, Size(2*value2 - 1, 2*value2 - 1), 0, 0);

    temp4 = src + 2*temp3 - 255;

    dst = ( src*(100-p) + temp4*p ) / 100;
}


效果图

QT5+OpenCV磨皮效果_第1张图片



PS:磨皮加美白代码

  • mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

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

using namespace std;
using namespace cv;

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_horizontalSlider_valueChanged(int value);
    void on_horizontalSlider_2_valueChanged(int value);

    void on_openButton_clicked();
    void on_saveButton_clicked();

    void on_horizontalSlider_3_valueChanged(int value);

private:
    Ui::MainWindow *ui;

    Mat src,dst,result,white;

    void displayResult(Mat mat);

    int dx;
    double fc;

    QString fileName;

    int FMax(const int X, const int Y);
    int FMin(const int X, const int Y);
    //转换数组
    double  highlights_add[256], highlights_sub[256];
    double  midtones_add[256], midtones_sub[256];
    double  shadows_add[256], shadows_sub[256];
    void BalanceColor(Mat& bitmap,int value);
};

#endif // MAINWINDOW_H

  • mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
#include

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //初始化转换数组
    for (int i = 0; i < 256; i++)
    {
        highlights_add[i] = shadows_sub[255 - i] = (1.075 - 1 / ((double) i / 16.0 + 1));
        midtones_add[i] = midtones_sub[i] = 0.667 * (1 - (((double) i - 127.0) / 127.0)*(((double) i - 127.0) / 127.0));
        shadows_add[i] = highlights_sub[i] = 0.667 * (1 - (((double) i - 127.0) / 127.0)*(((double) i - 127.0) / 127.0));
    }

    dx = 0; //大小
    fc = 0; //方差

    ui->openButton->setStyleSheet("QPushButton{border: 3px solid grey;background:rgb(183,203,188);border-radius: 5px;}QPushButton:hover{border-color:rgb(183,203,188);}QPushButton:pressed{border-color:gray}");
    ui->saveButton->setStyleSheet("QPushButton{border: 3px solid grey;background:rgb(183,203,188);border-radius: 5px;}QPushButton:hover{border-color:rgb(183,203,188);}QPushButton:pressed{border-color:gray}");
}

MainWindow::~MainWindow()
{
    delete ui;
}

//QLabel显示mat图像
void MainWindow::displayResult(Mat mat)
{
    Mat rgb;
    QImage img;
    if(mat.channels() == 3)    // RGB image
    {
        cvtColor(mat,rgb,CV_BGR2RGB);
        img = QImage((const uchar*)(rgb.data),  //(const unsigned char*)
                     rgb.cols,rgb.rows,
                     rgb.cols*rgb.channels(),   //new add
                     QImage::Format_RGB888);
    }else                     // gray image
    {
        img = QImage((const uchar*)(mat.data),
                     mat.cols,mat.rows,
                     mat.cols*mat.channels(),    //new add
                     QImage::Format_Indexed8);
    }

    ui->showLabel->setPixmap(QPixmap::fromImage(img));
}

void MainWindow::on_horizontalSlider_valueChanged(int value)
{
    ui->dxLabel->setText(tr("%1").arg(value));
    if(fileName.isEmpty())
        return;
    dx = value*5;
    fc = value*12.5;
    //双边滤波
    bilateralFilter(src,dst,dx,fc,fc);
    ui->horizontalSlider_2->setValue(1);
    ui->horizontalSlider_3->setValue(0);
    displayResult(dst);
    result = dst.clone();
    white = dst.clone();
}

void MainWindow::on_horizontalSlider_2_valueChanged(int value)
{
    ui->fcLabel->setText(tr("%1").arg(value));
    if(fileName.isEmpty())
        return;
    Mat temp1,temp2,temp3,temp4;   
    ui->horizontalSlider_3->setValue(0);

    //Dest =(Src * (100 - Opacity) + (Src + 2 * GuassBlur(EPFFilter(Src) - Src + 128) - 256) * Opacity) /100 ;
    bilateralFilter(src,temp1,dx,fc,fc);
    temp2 = (temp1-src+128);
    GaussianBlur(temp2,temp3,Size((2*value-1),(2*value-1)),0,0);
    temp4 = src+2*temp3-255;
    int p = 50;//透明度
    dst = (src*(100-p) + temp4*p)/100;

    displayResult(dst);
    result = dst.clone();
    white = dst.clone();
}

void MainWindow::on_openButton_clicked()
{
    fileName = QFileDialog::getOpenFileName(this,tr("打开图片"),tr("."));
    if(fileName.isEmpty())
        return;
    src = imread(fileName.toStdString());
    result = src.clone();
    white = src.clone();

    ui->showLabel->setPixmap(QPixmap(fileName));
    ui->horizontalSlider->setValue(0);
    ui->horizontalSlider_2->setValue(1);
}

void MainWindow::on_saveButton_clicked()
{
    if(fileName.isEmpty())
        return;
    QString saveName = QFileDialog::getSaveFileName(this,tr("保存文件"),tr("."),"JPEG(*.jpg);;PNG(*.png);;BMP(*.bmp)");
    if(saveName.isEmpty())
        return;
    imwrite(saveName.toStdString(),result);
}

void MainWindow::on_horizontalSlider_3_valueChanged(int value)
{
    ui->whiteLabel->setText(tr("%1").arg(value));
    if(fileName.isEmpty())
        return;
    Mat temp = white.clone();
    BalanceColor(temp,value*15);  
    displayResult(temp);
    result = temp.clone();
}

int MainWindow::FMax(const int X, const int Y)
{
    return (X < Y ? Y : X);
}

int MainWindow::FMin(const int X, const int Y)
{
    return (Y < X ? Y : X);
}

void MainWindow::BalanceColor(Mat& bitmap,int value)
{
    int red, green, blue;
    unsigned char r_lookup[256],g_lookup[256],b_lookup[256];

    for (int i = 0; i < 256; i++)
    {
        red = i;
        green = i;
        blue = i;

        red += (int)( 0.0 * shadows_sub[red] + value * midtones_add[red] + 0.0 * highlights_sub[red]);
        red = FMax(0,FMin(0xFF,red));

        green += (int)( 0.0 * shadows_sub[green] + value * midtones_add[green] + 0.0 * highlights_sub[green]);
        green = FMax(0,FMin(0xFF,green));

        blue += (int)( 0.0 * shadows_sub[blue] + value * midtones_add[blue] + 0.0 * highlights_sub[blue]);
        blue = FMax(0,FMin(0xFF,blue));

        r_lookup[i] = (unsigned char)red;
        g_lookup[i] = (unsigned char)green;
        b_lookup[i] = (unsigned char)blue;
    }

    for (int row = 0; row < bitmap.rows; row++)
    {
        for (int col = 0; col < bitmap.cols; col++)
        {
            bitmap.at(row, col)[0] = b_lookup[bitmap.at(row, col)[0]];
            bitmap.at(row, col)[1] = g_lookup[bitmap.at(row, col)[1]];
            bitmap.at(row, col)[2] = r_lookup[bitmap.at(row, col)[2]];
        }
    }

}

  • main.cpp

#include "mainwindow.h"
#include 

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

QT5+OpenCV磨皮效果_第2张图片

你可能感兴趣的:(C/C++)