记录四图像处理之瘦脸 MLS算法 C++实现

 

MLS算法 C++实现

这段时间要做一个美颜软件作为结课的一个小作业,其中有一个功能是瘦脸,关于这方面看了一些资料,做了一个小Demo。因为我看现有的基于OpenCv做的MLS好像还没有人发,所以尽管做的很粗糙还是决定记录一下,以供大家参考学习。

先上一波效果图

第一张是原图像;第二张是打完标记点之后的图像,深蓝色记为p点也就是原图像的点,浅蓝色记为q点,是原图像变形后的点;第三张是经过MLS变形处理之后的图像了,图像畸变有点大主要是面部的标记点需要优化一下。

具体标记点是怎么得出的之后会写文章介绍,这篇就不涉及了,感兴趣的可以去搜索Facelandmark看看这方面的资料就可以了。

这篇文章没有理论部分,只是单纯的代码实现,基于Image Deformation Using Moving Least Squares这篇文章写的C++和Opencv代码,想深入了解的直接去看论文就可以了。

数学部分

f(v) = (v - p*)(\sum_{i} \hat{pi^{T}}Wi\hat{pi})^{-1}\sum_{j}Wj\hat{pj}^T\hat{qj} + q*     公式一

其中v是图像中的点p是变形前的点q是变形后的点,w是权重。

W_{i} = \frac{1}{\left | p_{i} - v \right |^{2\alpha }}                                                                  公式二

p_{i}是图像中控制点的点集,v是原图像中的点,\alpha我们在程序中取1。

p*=\frac{\sum_{i}W_{i}p_{i}}{\sum_{i}W_{i}}                                                                     公式三

q*=\frac{\sum_{i}W_{i}q_{i}}{\sum_{i}W_{i}}                                                                      公式四

\hat{p} = p_{i}-p*                                                                           公式五

\hat{q} = q_{i}-q*                                                                            公式六

以上就是整个数学过程的计算,先完成公式二至六,最后通过公式一输出新的点的坐标。

编程部分

W_{i} = \frac{1}{\left | p_{i} - v \right |^{2\alpha }}                                                                  公式二

C++代码如下

vector<float>W;
Point p_star, q_star = Point(0,0);
for(int i = 0; i <= p.size() - 1; i++)
{
    float temp;
    if(p[i] == V)
    {
        temp = INT_MAX;
        cout << "DBL_MAX = " << INT_MAX << endl;
    }
    else
    {
        temp = 1.0/(((p[i].x - V.x) * (p[i].x - V.x)) + ((p[i].y - V.y) * (p[i].y - V.y)));//aclculate weight W
            
    }
    W.push_back(temp);
}

 p*=\frac{\sum_{i}W_{i}p_{i}}{\sum_{i}W_{i}}                                                                     公式三

q*=\frac{\sum_{i}W_{i}q_{i}}{\sum_{i}W_{i}}                                                                      公式四

C++代码如下

    float px=0,py=0,qx=0,qy=0,W_sum=0;
    for(int i = 0; i <= W.size() - 1; i++)
    {
        px += W[i]*p[i].x;
        py += W[i]*p[i].y;


        qx += W[i]*q[i].x;
        qy += W[i]*q[i].y;
        //cout << "qx = " << qx << "  qy = "<< qy  <<endl;


        W_sum += W[i];
        //cout <<"W_sum = " << W_sum << endl;
    }

    //W_sum = accumulate(W.begin(), W.end(),0);

    p_star.x = px/W_sum;
    p_star.y = py/W_sum;

    q_star.x = qx/W_sum;
    q_star.y = qy/W_sum;

\hat{p} = p_{i}-p*                                                                           公式五

\hat{q} = q_{i}-q*                                                                            公式六

C++实现如下

    vector<Point> p_hat, q_hat;

    for(int i = 0; i <= p.size() - 1; i++)
    {
        p_hat.push_back(p[i] - p_star);
        q_hat.push_back(q[i] - q_star);
        //cout << "p_hat[i] = " << p_hat[i] << endl;
        //cout << "q_hat[i] = " << q_hat[i] << endl;


    }

(\sum_{i} \hat{pi^{T}}Wi\hat{pi})^{-1}

代码实现如下

其中使用了OpenCv的Mat类和Mat_类,不直接使用Mat类是因为Mat类做矩阵运算遇到负数会被截断为0。

    Mat pi_hat_t_ = Mat::zeros(2,1,CV_32FC1);
    Mat_<float> pi_hat_t = pi_hat_t_;

    Mat pi_hat_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> pi_hat = pi_hat_;

    Mat M_1_ = Mat::zeros(2,2,CV_32FC1);
    Mat_<float> M_1 = M_1_;


    for(int i = 0; i <= p_hat.size() - 1; i++)
    {
        pi_hat_t.at<float>(0,0) = p_hat[i].x;//将点转化为矩阵
        pi_hat_t.at<float>(1,0) = p_hat[i].y;
        //cout << "pi_hat_t" << pi_hat_t << endl;
        pi_hat.at<float>(0,0) = p_hat[i].x;
        pi_hat.at<float>(0,1) = p_hat[i].y;
        //cout << "pi_hat" << pi_hat<< endl;
        M_1 += pi_hat_t*W[i]*pi_hat;
        //cout << "W[i] = " << W[i] << endl;
        //cout <<"M_1" << M_1 << endl;
    }
    Mat_<float> M_1_inv = M_1.inv();
    M_1 = M_1_inv;

\sum_{j}Wj\hat{pj}^T\hat{qj}

C++代码实现如下与上片段类似

    Mat pj_hat_t_ = Mat::zeros(2,1,CV_32FC1);
    Mat_<float> pj_hat_t = pj_hat_t_;

    Mat qj_hat_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> qj_hat = qj_hat_;

    Mat M_2_ = Mat::zeros(2,2,CV_32FC1);
    Mat_<float> M_2 = M_2_;

    for(int j = 0; j <= q.size() -1; j++)
    {
        pj_hat_t.at<float>(0,0) = p_hat[j].x;
        pj_hat_t.at<float>(1,0) = p_hat[j].y;
        qj_hat.at<float>(0,0) = q_hat[j].x;
        qj_hat.at<float>(0,1) = q_hat[j].y;
        M_2 += W[j]*pj_hat_t*qj_hat;
    }

f(v) = (v - p*)(\sum_{i} \hat{pi^{T}}Wi\hat{pi})^{-1}\sum_{j}Wj\hat{pj}^T\hat{qj} + q*     公式一

    Mat_<float> M = M_1*M_2;//ok
    //cout << "M = " << M << endl;

    Point x_p_star = V - p_star;
    //cout << "V = " << V << endl;
    cout << "q_star = " << q_star << endl;
    //cout << "x_p_star = " << x_p_star << endl;



    Mat M_x_p_star_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> M_x_p_star = M_x_p_star_;

    M_x_p_star.at<float>(0,0) = x_p_star.x;
    M_x_p_star.at<float>(0,1) = x_p_star.y;

    Mat M_q_star_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> M_q_star = M_q_star_;

    M_q_star.at<float>(0,0) = q_star.x;
    M_q_star.at<float>(0,1) = q_star.y;

    Mat_<float> Lv = M_x_p_star * M + M_q_star;//公式一

以上代码中用到的M矩阵为仿射矩阵,可以替换为刚性变形矩阵之类的具体代码实现看论文也很好实现。

代码总览

NewPoint函数功能是根据映射关系 p与q生成返回新的Point坐标,MLS函数则是调用NewPoint函数遍历图像生成新的图像。

Point NewPoint(Point V, vector<Point> p, vector<Point> q)
{
    vector<float>W;
    Point p_star, q_star = Point(0,0);
    for(int i = 0; i <= p.size() - 1; i++)
    {
        float temp;
        if(p[i] == V)
        {
            temp = INT_MAX;
            cout << "DBL_MAX = " << INT_MAX << endl;
        }
        else
        {
            temp = 1.0/(((p[i].x - V.x) * (p[i].x - V.x)) + ((p[i].y - V.y) * (p[i].y - V.y)));//aclculate weight W
            //cout << "p[i] = " << p[i] << endl;
            //cout << "V = " << V << endl;
        }
        W.push_back(temp);
        //cout << "W[i] = " << W[i] << endl;
    }
    float px=0,py=0,qx=0,qy=0,W_sum=0;
    for(int i = 0; i <= W.size() - 1; i++)
    {
        px += W[i]*p[i].x;
        py += W[i]*p[i].y;


        qx += W[i]*q[i].x;
        qy += W[i]*q[i].y;
        //cout << "qx = " << qx << "  qy = "<< qy  <<endl;


        W_sum += W[i];
        //cout <<"W_sum = " << W_sum << endl;
    }

    //W_sum = accumulate(W.begin(), W.end(),0);

    p_star.x = px/W_sum;
    p_star.y = py/W_sum;

    q_star.x = qx/W_sum;
    q_star.y = qy/W_sum;
    //cout << "px = " << px << "  py = "<< py  <<endl;
    //cout <<"W_sum = " << W_sum << endl;
    //cout << "p_start = " << p_star << endl;
    //cout << "q_start = " << q_star << endl;




    vector<Point> p_hat, q_hat;

    for(int i = 0; i <= p.size() - 1; i++)
    {
        p_hat.push_back(p[i] - p_star);
        q_hat.push_back(q[i] - q_star);
        //cout << "p_hat[i] = " << p_hat[i] << endl;
        //cout << "q_hat[i] = " << q_hat[i] << endl;


    }
    Mat pi_hat_t_ = Mat::zeros(2,1,CV_32FC1);
    Mat_<float> pi_hat_t = pi_hat_t_;

    Mat pi_hat_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> pi_hat = pi_hat_;

    Mat M_1_ = Mat::zeros(2,2,CV_32FC1);
    Mat_<float> M_1 = M_1_;


    for(int i = 0; i <= p_hat.size() - 1; i++)
    {
        pi_hat_t.at<float>(0,0) = p_hat[i].x;
        pi_hat_t.at<float>(1,0) = p_hat[i].y;
        //cout << "pi_hat_t" << pi_hat_t << endl;
        pi_hat.at<float>(0,0) = p_hat[i].x;
        pi_hat.at<float>(0,1) = p_hat[i].y;
        //cout << "pi_hat" << pi_hat<< endl;
        M_1 += pi_hat_t*W[i]*pi_hat;
        //cout << "W[i] = " << W[i] << endl;
        //cout <<"M_1" << M_1 << endl;
    }
    Mat_<float> M_1_inv = M_1.inv();
    M_1 = M_1_inv;
    //cout <<"M_1_inv = " << M_1 << endl;

    Mat pj_hat_t_ = Mat::zeros(2,1,CV_32FC1);
    Mat_<float> pj_hat_t = pj_hat_t_;

    Mat qj_hat_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> qj_hat = qj_hat_;

    Mat M_2_ = Mat::zeros(2,2,CV_32FC1);
    Mat_<float> M_2 = M_2_;

    for(int j = 0; j <= q.size() -1; j++)
    {
        pj_hat_t.at<float>(0,0) = p_hat[j].x;
        pj_hat_t.at<float>(1,0) = p_hat[j].y;
        qj_hat.at<float>(0,0) = q_hat[j].x;
        qj_hat.at<float>(0,1) = q_hat[j].y;
        M_2 += W[j]*pj_hat_t*qj_hat;
    }
    //cout <<"M_2 = " << M_2 << endl;
    Mat_<float> M = M_1*M_2;//ok
    //cout << "M = " << M << endl;

    Point x_p_star = V - p_star;
    //cout << "V = " << V << endl;
    cout << "q_star = " << q_star << endl;
    //cout << "x_p_star = " << x_p_star << endl;



    Mat M_x_p_star_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> M_x_p_star = M_x_p_star_;

    M_x_p_star.at<float>(0,0) = x_p_star.x;
    M_x_p_star.at<float>(0,1) = x_p_star.y;

    Mat M_q_star_ = Mat::zeros(1,2,CV_32FC1);
    Mat_<float> M_q_star = M_q_star_;

    M_q_star.at<float>(0,0) = q_star.x;
    M_q_star.at<float>(0,1) = q_star.y;

    Mat_<float> Lv = M_x_p_star * M + M_q_star;
    cout << "Lv = " << Lv <<endl;
    //cout <<"Point LV" << Point(Lv.at<float>(0,0) ,  Lv.at<float>(0,1)) <<endl;

    return Point(Lv.at<float>(0,0) ,  Lv.at<float>(0,1));

}
void MLS(Mat src, Mat &dst, vector<Point> p, vector<Point> q)
{
    Mat res = Mat::zeros(src.rows,src.cols,CV_8UC3);
    for(int i = 0; i < src.rows; i++)
    {
        for(int j = 0; j < src.cols; j++)
        {
            Point old = Point(j, i);
            Point new_point = NewPoint(old, p, q);
            cout <<"old = " << old << endl;
            cout <<"new  = " << new_point << endl;

            dst.at<Vec3b>(i,j) = src.at<Vec3b>(new_point.y,new_point.x);
            //cout << "src.at<uchar> = " << src.at<Vec3b>(new_point.y,new_point.x) << endl;

        }
    }
    imshow("src",src);
    imshow("dst_msl",dst);

}

以上便是本文的所有内容,如有错误疏漏欢迎大家指出.

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值