MLS算法 C++实现
这段时间要做一个美颜软件作为结课的一个小作业,其中有一个功能是瘦脸,关于这方面看了一些资料,做了一个小Demo。因为我看现有的基于OpenCv做的MLS好像还没有人发,所以尽管做的很粗糙还是决定记录一下,以供大家参考学习。
先上一波效果图
第一张是原图像;第二张是打完标记点之后的图像,深蓝色记为p点也就是原图像的点,浅蓝色记为q点,是原图像变形后的点;第三张是经过MLS变形处理之后的图像了,图像畸变有点大主要是面部的标记点需要优化一下。
具体标记点是怎么得出的之后会写文章介绍,这篇就不涉及了,感兴趣的可以去搜索Facelandmark看看这方面的资料就可以了。
这篇文章没有理论部分,只是单纯的代码实现,基于Image Deformation Using Moving Least Squares这篇文章写的C++和Opencv代码,想深入了解的直接去看论文就可以了。
数学部分
公式一
其中v是图像中的点p是变形前的点q是变形后的点,w是权重。
公式二
是图像中控制点的点集,
是原图像中的点,
我们在程序中取1。
公式三
公式四
公式五
公式六
以上就是整个数学过程的计算,先完成公式二至六,最后通过公式一输出新的点的坐标。
编程部分
公式二
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);
}
公式三
公式四
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;
公式五
公式六
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;
}
代码实现如下
其中使用了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;
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;
}
公式一
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);
}