AI人脸自动美型算法

人脸智能美型包含如下两个部分:

①人脸轮廓自动调整

②五官自动修正

人脸轮廓自动修正:对人脸大小,胖瘦进行自动调整,目前app中常用的瘦脸只是其中一个特例而已;

五官自动修正:包含眼睛大小自动调整,鼻子形状位置修正,眉毛位置修正以及嘴巴形状、大小和位置自动修正等等。App中常用的大眼和立体修鼻功能,也属于其中一个特例;

人脸智能美型和磨皮美白结合使用,就是所谓的智能美颜。

人脸智能美型算法逻辑如下:

1,构建平均脸

针对男女分别构建正脸平均脸,如下图所示:

2,性别识别

这一步需要将用户的人像照片进行性别识别,根据识别结果分别选择男/女平均脸数据。

性别识别可以参考本人博客

核心代码如下:


 
  1. def weight_variable(shape,name):

  2. return tf.Variable(tf.truncated_normal(shape, stddev = 0.1),name=name)

  3.  
  4. def bias_variable(shape,name):

  5. return tf.Variable(tf.constant(0.1, shape = shape),name=name)

  6.  
  7. def conv2d(x,w,padding="SAME"):

  8. if padding=="SAME" :

  9. return tf.nn.conv2d(x, w, strides = [1,1,1,1], padding = "SAME")

  10. else:

  11. return tf.nn.conv2d(x, w, strides = [1,1,1,1], padding = "VALID")

  12.  
  13. def max_pool(x, kSize, Strides):

  14. return tf.nn.max_pool(x, ksize = http://i5z.wikidot.com/ [1,kSize,kSize,1],strides = [1,Strides,Strides,1], padding = "SAME")

  15.  
  16. def compute_cost(Z3, Y):

  17. """

  18. Computes the cost

  19.  
  20. Arguments:

  21. Z3 -- output of forward propagation (output of the last LINEAR unit), of shape (6, number of examples)

  22. Y -- "true" labels vector placeholder, same shape as Z3

  23.  
  24. Returns:

  25. cost - Tensor of the cost function

  26. """

  27. cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=Z3, labels=Y))

  28. return cost

  29.  
  30. def initialize_parameters():

  31. tf.set_random_seed(1)

  32. W1 = tf.cast(weight_variable([5,5,1,32],"W1"), dtype = tf.float32)

  33. b1 = tf.cast(bias_variable([32],"b1"), dtype = tf.float32)

  34. W2 = tf.cast(weight_variable([5,5,32,64],"W2"), dtype = tf.float32)

  35. b2 = tf.cast(bias_variable([64],"b2"), dtype = tf.float32)

  36. W3 = tf.cast(weight_variable([5,5,64,128],"W3"), dtype = tf.float32)

  37. b3 = tf.cast(bias_variable([128],"b3"), dtype = tf.float32)

  38. http://w0o.wikidot.com/
  39. W4 = tf.cast(weight_variable([14*12*128,500],"W4"), dtype = tf.float32)

  40. b4 = tf.cast(bias_variable([500],"b4"), dtype = tf.float32)

  41. W5 = tf.cast(weight_variable([500,500],"W5"), dtype = tf.float32)

  42. b5 = tf.cast(bias_variable([500],"b5"), dtype = tf.float32)

  43. W6 = tf.cast(weight_variable([500,2],"W6"), dtype = tf.float32)

  44. b6 = tf.cast(bias_variable([2],"b6"), dtype = tf.float32)

  45. parameters = {"W1":W1,

  46. "b1":b1,

  47. "W2":W2,

  48. "b2":b2,

  49. "W3":W3,

  50. "b3":b3,

  51. "W4":W4,

  52. "b4":b4,

  53. "W5":W5,

  54. "b5":b5,

  55. "W6":W6,

  56. "b6":b6}

  57. return parameters

  58.  
  59. def cnn_net(x, parameters, keep_prob = 1.0):

  60. #frist convolution layer

  61. w_conv1 = parameters["W1"]

  62. b_conv1 = parameters["b1"]

  63. h_conv1 = tf.nn.relu(conv2d(x,w_conv1) + b_conv1) #output size 112x92x32

  64. h_pool1 = max_pool(h_conv1,2,2) #output size 56x46x32

  65.  
  66. #second convolution layer

  67. w_conv2 = parameters["W2"]

  68. b_conv2 = parameters["b2"]

  69. h_conv2 = tf.nn.relu(conv2d(h_pool1, w_conv2) + b_conv2) #output size 56x46x64

  70. h_pool2 = max_pool(h_conv2,2,2) #output size 28x23x64

  71.  
  72. #third convolution layer

  73. w_conv3 = parameters["W3"]

  74. b_conv3 = parameters["b3"]

  75. h_conv3 = tf.nn.relu(conv2d(h_pool2,w_conv3 http://j6u.wikidot.com/ ) + b_conv3) #output size 28x23x128

  76. h_pool3 = max_pool(h_conv3,2,2) #output size 14x12x128

  77.  
  78. #full convolution layer

  79. w_fc1 = parameters["W4"]

  80. b_fc1 = parameters["b4"]

  81. h_fc11 = tf.reshape(h_pool3,[-1,14*12*128])

  82. h_fc1 = tf.nn.relu(tf.matmul(h_fc11,w_fc1) + b_fc1)

  83.  
  84. w_fc2 = parameters["W5"]

  85. b_fc2 = parameters["b5"]

  86. h_fc2 = tf.nn.relu(tf.matmul(h_fc1,w_fc2)+b_fc2)

  87. h_fc2_drop = tf.nn.dropout(h_fc2,keep_prob)

  88.  
  89. w_fc3 = parameters["W6"]

  90. b_fc3 = parameters["b6"]

  91. y_conv = tf.matmul(h_fc2_drop, w_fc3) + b_fc3

  92. #y_conv = tf.nn.softmax(tf.matmul(h_fc2_drop, w_fc3) + b_fc3)

  93. #rmse = tf.sqrt(tf.reduce_mean(tf.square(y_ - y_conv)))

  94. #cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels = y, logits = y_conv))

  95. #train_step = tf.train.GradientDescentOptimizer(0.001).minimize(cross_entropy)

  96. #correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y,1))

  97. #accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

3,将用户照片人脸映射到平均脸

这一步主要根据用户照片人脸关键点和平均脸的人脸关键点,加上一定的映射算法将用户照片对齐到平均脸中,可以参考仿射变换等。

关键点可以使用商汤科技或者FACE++等人脸SDK。

效果如下图所示:

4,计算用户人脸和平均脸的距离D

此处计算规则可以使用欧氏距离等,距离D表示用户人脸到完美人脸的差。

距离的计算还需要参考人脸的旋转角度信息,根据人脸旋转角度对距离进行加权处理,以此来适应各种角度的用户人脸照片。

5,根据D对用户人脸进行不同程度的变形,得到智能美型结果

此处变形可以使用MLS、三角网格变形等等。

MLS详细算法与代码,可参考博客

MLS核心代码如下:


 
  1. static void setSrcPoints(const vector<PointD> &qsrc, vector<PointD> &newDotL, int* nPoint) {

  2. *nPoint = qsrc.size();

  3. newDotL.clear();

  4. newDotL.reserve(*nPoint);

  5. for (size_t i = 0; i < qsrc.size(); i++)

  6. newDotL.push_back(qsrc[i]);

  7. }

  8.  
  9. static void setDstPoints(const vector<PointD> &qdst,vector<PointD> &oldDotL, int* nPoint) {

  10. *nPoint = qdst.size();

  11. oldDotL.clear();

  12. oldDotL.reserve(*nPoint);

  13.  
  14. for (size_t i = 0; i < qdst.size(); i++) oldDotL.push_back(qdst[i]);

  15. }

  16. static double bilinear_interp(double x, double y, double v11, double v12,

  17. double v21, double v22) {

  18. return (v11 * (1 - y) + v12 * y) * (1 - x) + (v21 * (1 - y) + v22 * y) * x;

  19. }

  20.  
  21. static double calcArea(const vector<PointD> &V) {

  22. PointD lt, rb;

  23. lt.x = lt.y = 1e10;

  24. rb.x = rb.y = -1e10;

  25. for (vector<PointD >::const_iterator i = V.begin(); i != V.end();

  26. i++) {

  27. if (i->x < lt.x) lt.x = i->x;

  28. if (i->x > rb.x) rb.x = i->x;

  29. if (i->y < lt.y) lt.y = i->y;

  30. if (i->y > rb.y) rb.y = i->y;

  31. }

  32. return (rb.x - lt.x) * (rb.y - lt.y);

  33. }

  34. static void calcDelta_rigid(int srcW, int srcH, int tarW, int tarH, double alpha, int gridSize, int nPoint, int preScale, double *rDx, double *rDy, vector<PointD> &oldDotL, vector<PointD> &newDotL)

  35. {

  36. int i, j, k;

  37. PointD swq, qstar, newP, tmpP;

  38. double sw;

  39.  
  40. double ratio;

  41.  
  42. if (preScale) {

  43. ratio = sqrt(calcArea(newDotL) / calcArea(oldDotL));

  44. for (i = 0; i < nPoint; i++) {

  45. newDotL[i].x *= 1 / ratio;

  46. newDotL[i].y *= 1 / ratio;

  47. }

  48. }

  49. double *w = new double[nPoint];

  50.  
  51. if (nPoint < 2) {

  52. //rDx.setTo(0);

  53. //rDy.setTo(0);

  54. return;

  55. }

  56. PointD swp, pstar, curV, curVJ, Pi, PiJ, Qi;

  57. double miu_r;

  58.  
  59. for (i = 0;; i += gridSize) {

  60. if (i >= tarW && i < tarW + gridSize - 1)

  61. i = tarW - 1;

  62. else if (i >= tarW)

  63. break;

  64. for (j = 0;; j += gridSize) {

  65. if (j >= tarH && j < tarH + gridSize - 1)

  66. j = tarH - 1;

  67. else if (j >= tarH)

  68. break;

  69. sw = 0;

  70. swp.x = swp.y = 0;

  71. swq.x = swq.y = 0;

  72. newP.x = newP.y = 0;

  73. curV.x = i;

  74. curV.y = j;

  75. for (k = 0; k < nPoint; k++) {

  76. if ((i == oldDotL[k].x) && j == oldDotL[k].y) break;

  77. if (alpha == 1)

  78. w[k] = 1 / ((i - oldDotL[k].x) * (i - oldDotL[k].x) +

  79. (j - oldDotL[k].y) * (j - oldDotL[k].y));

  80. else

  81. w[k] = pow((i - oldDotL[k].x) * (i - oldDotL[k].x) +

  82. (j - oldDotL[k].y) * (j - oldDotL[k].y),

  83. -alpha);

  84. sw = sw + w[k];

  85. swp.x = swp.x + w[k] * oldDotL[k].x;

  86. swp.y = swp.y + w[k] * oldDotL[k].y;

  87. swq.x = swq.x + w[k] * newDotL[k].x;

  88. swq.y = swq.y + w[k] * newDotL[k].y;

  89. }

  90. if (k == nPoint) {

  91. pstar.x = (1 / sw) * swp.x;

  92. pstar.y = (1 / sw) * swp.y;

  93. qstar.x = 1 / sw * swq.x;

  94. qstar.y = 1 / sw * swq.y;

  95. // Calc miu_r

  96. double s1 = 0, s2 = 0;

  97. for (k = 0; k < nPoint; k++) {

  98. if (i == oldDotL[k].x && j == oldDotL[k].y) continue;

  99. Pi.x = oldDotL[k].x - pstar.x;

  100. Pi.y = oldDotL[k].y - pstar.y;

  101. PiJ.x = -Pi.y, PiJ.y = Pi.x;

  102. Qi.x = newDotL[k].x - qstar.x;

  103. Qi.y = newDotL[k].y - qstar.y;

  104. s1 += w[k] * (Qi.x*Pi.x+Qi.y*Pi.y);

  105. s2 += w[k] * (Qi.x*PiJ.x+Qi.y*PiJ.y);

  106. }

  107. miu_r = sqrt(s1 * s1 + s2 * s2);

  108. curV.x -= pstar.x;

  109. curV.y -= pstar.y;

  110. http://wi1.wikidot.com/
  111. curVJ.x = -curV.y, curVJ.y = curV.x;

  112.  
  113. for (k = 0; k < nPoint; k++) {

  114. if (i == oldDotL[k].x && j == oldDotL[k].y) continue;

  115. Pi.x = oldDotL[k].x - pstar.x;

  116. Pi.y = oldDotL[k].y - pstar.y;

  117. PiJ.x = -Pi.y, PiJ.y = Pi.x;

  118. tmpP.x = (Pi.x*curV.x+Pi.y*curV.y)* newDotL[k].x -

  119. (PiJ.x*curV.x+PiJ.y*curV.y)* newDotL[k].y;

  120. tmpP.y = -(Pi.x*curVJ.x+Pi.y*curVJ.y) * newDotL[k].x +

  121. (PiJ.x*curVJ.x+PiJ.y*curVJ.y) * newDotL[k].y;

  122. tmpP.x *= w[k] / miu_r;

  123. tmpP.y *= w[k] / miu_r;

  124. newP.x += tmpP.x;

  125. newP.y += tmpP.y;

  126. }

  127. newP.x += qstar.x;

  128. newP.y += qstar.y;

  129. } else {

  130. newP = newDotL[k];

  131. }

  132.  
  133. if (preScale) {

  134. rDx[j * tarW + i] = newP.x * ratio - i;

  135. rDy[j * tarW + i] = newP.y * ratio - j;

  136. } else {

  137. rDx[j * tarW + i] = newP.x - i;

  138. rDy[j * tarW + i] = newP.y - j;

  139. }

  140. }

  141. }

  142. delete[] w;

  143.  
  144. if (preScale!=0) {

  145. for (i = 0; i < nPoint; i++){

  146. newDotL[i].x *= ratio;

  147. newDotL[i].y *= ratio;

  148. }

  149. }

  150. }

  151. static void calcDelta_Similarity(int srcW, int srcH, int tarW, int tarH, double alpha, int gridSize, int nPoint, int preScale, double *rDx, double *rDy, vector<PointD> &oldDotL, vector<PointD> &newDotL)

  152. {

  153. int i, j, k;

  154.  
  155. PointD swq, qstar, newP, tmpP;

  156. double sw;

  157.  
  158. double ratio;

  159.  
  160. if (preScale) {

  161. ratio = sqrt(calcArea(newDotL) / calcArea(oldDotL));

  162. for (i = 0; i < nPoint; i++) {

  163. newDotL[i].x *= 1 / ratio;

  164. newDotL[i].y *= 1 / ratio;

  165. }

  166. }

  167. double *w = new double[nPoint];

  168.  
  169. if (nPoint < 2) {

  170. return;

  171. }

  172.  
  173. PointD swp, pstar, curV, curVJ, Pi, PiJ;

  174. double miu_s;

  175.  
  176. for (i = 0;; i += gridSize) {

  177. if (i >= tarW && i < tarW + gridSize - 1)

  178. i = tarW - 1;

  179. else if (i >= tarW)

  180. break;

  181. for (j = 0;; j += gridSize) {

  182. if (j >= tarH && j < tarH + gridSize - 1)

  183. j = tarH - 1;

  184. else if (j >= tarH)

  185. break;

  186. sw = 0;

  187. swp.x = swp.y = 0;

  188. swq.x = swq.y = 0;

  189. newP.x = newP.y = 0;

  190. curV.x = i;

  191. curV.y = j;

  192. for (k = 0; k < nPoint; k++) {

  193. if ((i == oldDotL[k].x) && j == oldDotL[k].y) break;

  194. w[k] = 1 / ((i - oldDotL[k].x) * (i - oldDotL[k].x) +

  195. (j - oldDotL[k].y) * (j - oldDotL[k].y));

  196. sw = sw + w[k];

  197. swp.x = swp.x + w[k] * oldDotL[k].x;

  198. swp.y = swp.y + w[k] * oldDotL[k].y;

  199. swq.x = swq.x + w[k] * newDotL[k].x;

  200. swq.y = swq.y + w[k] * newDotL[k].y;

  201. }

  202. if (k == nPoint) {

  203. pstar.x = (1 / sw) * swp.x;

  204. pstar.y = (1 / sw) * swp.y;

  205. qstar.x = 1 / sw * swq.x;

  206. qstar.y = 1 / sw * swq.y;

  207. // Calc miu_s

  208. miu_s = 0;

  209. for (k = 0; k < nPoint; k++) {

  210. if (i == oldDotL[k].x && j == oldDotL[k].y) continue;

  211.  
  212. Pi.x = oldDotL[k].x - pstar.x;

  213. Pi.y = oldDotL[k].y - pstar.y;

  214. miu_s += w[k] * (Pi.x*Pi.x+Pi.y*Pi.y);

  215. }

  216.  
  217. curV.x -= pstar.x;

  218. curV.y -= pstar.y;

  219. curVJ.x = -curV.y, curVJ.y = curV.x;

  220.  
  221. for (k = 0; k < nPoint; k++) {

  222. if (i == oldDotL[k].x && j == oldDotL[k].y) continue;

  223.  
  224. Pi.x = oldDotL[k].x - pstar.x;

  225. Pi.y = oldDotL[k].y - pstar.y;

  226. PiJ.x = -Pi.y, PiJ.y = Pi.x;

  227.  
  228. tmpP.x = (Pi.x*curV.x+Pi.y*curV.y) * newDotL[k].x -

  229. (PiJ.x*curV.x+PiJ.y*curV.y) * newDotL[k].y;

  230. tmpP.y = -(Pi.x*curVJ.x+Pi.y*curVJ.y) * newDotL[k].x +

  231. (PiJ.x*curVJ.x+PiJ.y*curVJ.y) * newDotL[k].y;

  232. tmpP.x *= w[k] / miu_s;

  233. tmpP.y *= w[k] / miu_s;

  234. newP.x += tmpP.x;

  235. newP.y += tmpP.y;

  236. }

  237. newP.x += qstar.x;

  238. newP.y += qstar.y;

  239. } else {

  240. newP = newDotL[k];

  241. }

  242.  
  243. rDx[j * tarW + i] = newP.x - i;

  244. rDy[j * tarW + i] = newP.y - j;

  245. }

  246. }

  247.  
  248. delete[] w;

  249. if (preScale!=0) {

  250. for (i = 0; i < nPoint; i++){

  251. newDotL[i].x *= ratio;

  252. newDotL[i].y *= ratio;

  253. }

  254. }

  255. }

  256. static int GetNewImg(unsigned char* oriImg, int width, int height, int stride, unsigned char* tarImg, int tarW, int tarH, int tarStride, int gridSize, double* rDx, double* rDy, double transRatio)

  257. {

  258. int i, j;

  259. double di, dj;

  260. double nx, ny;

  261. int nxi, nyi, nxi1, nyi1;

  262. double deltaX, deltaY;

  263. double w, h;

  264. int ni, nj;

  265. int pos, posa, posb, posc, posd;

  266. for (i = 0; i < tarH; i += gridSize)

  267. for (j = 0; j < tarW; j += gridSize) {

  268. ni = i + gridSize, nj = j + gridSize;

  269. w = h = gridSize;

  270. if (ni >= tarH) ni = tarH - 1, h = ni - i + 1;

  271. if (nj >= tarW) nj = tarW - 1, w = nj - j + 1;

  272. for (di = 0; di < h; di++)

  273. for (dj = 0; dj < w; dj++) {

  274. deltaX =

  275. bilinear_interp(di / h, dj / w, rDx[i * tarW + j], rDx[i * tarW + nj],

  276. rDx[ni * tarW + j], rDx[ni * tarW + nj]);

  277. deltaY =

  278. bilinear_interp(di / h, dj / w, rDy[i * tarW + j], rDy[i * tarW + nj],

  279. rDy[ni * tarW + j], rDy[ni * tarW + nj]);

  280. nx = j + dj + deltaX * transRatio;

  281. ny = i + di + deltaY * transRatio;

  282. if (nx > width - 1) nx = width - 1;

  283. if (ny > height - 1) ny = height - 1;

  284. if (nx < 0) nx = 0;

  285. if (ny < 0) ny = 0;

  286. nxi = int(nx);

  287. nyi = int(ny);

  288. nxi1 = ceil(nx);

  289. nyi1 = ceil(ny);

  290. pos = (int)(i + di) * tarStride + ((int)(j + dj) << 2);

  291. posa = nyi * stride + (nxi << 2);

  292. posb = nyi * stride + (nxi1 << 2);

  293. posc = nyi1 * stride + (nxi << 2);

  294. posd = nyi1 * stride + (nxi1 << 2);

  295. tarImg[pos] = (unsigned char)bilinear_interp(ny - nyi, nx - nxi, oriImg[posa], oriImg[posb], oriImg[posc], oriImg[posd]);

  296. tarImg[pos + 1] = (unsigned char)bilinear_interp(ny - nyi, nx - nxi, oriImg[posa + 1],oriImg[posb + 1], oriImg[posc + 1], oriImg[posd + 1]);

  297. tarImg[pos + 2] = (unsigned char)bilinear_interp(ny - nyi, nx - nxi, oriImg[posa + 2],oriImg[posb + 2], oriImg[posc + 2], oriImg[posd + 2]);

  298. tarImg[pos + 3] = (unsigned char)bilinear_interp(ny - nyi, nx - nxi, oriImg[posa + 3],oriImg[posb + 3], oriImg[posc + 3], oriImg[posd + 3]);

  299. }

  300. }

  301. return 0;

  302. };

  303.  
  304. static void MLSImageWrapping(unsigned char* oriImg,int width, int height, int stride,const vector<PointD > &qsrc, const vector<PointD > &qdst, unsigned char* tarImg, int outW, int outH, int outStride, double transRatio, int preScale, int gridSize, int method)

  305. {

  306. int srcW = width;

  307. int srcH = height;

  308. int tarW = outW;

  309. int tarH = outH;http://mi2.wikidot.com/

  310. double alpha = 1;

  311. int nPoint;

  312. int len = tarH * tarW;

  313. vector<PointD> oldDotL, newDotL;

  314. double *rDx = NULL,*rDy = NULL;

  315. setSrcPoints(qsrc,newDotL,&nPoint);

  316. setDstPoints(qdst,oldDotL,&nPoint);

  317. rDx = (double*)malloc(sizeof(double) * len);

  318. rDy = (double*)malloc(sizeof(double) * len);

  319. memset(rDx, 0, sizeof(double) * len);

  320. memset(rDy, 0, sizeof(double) * len);

  321. if(method!=0)

  322. calcDelta_Similarity(srcW, srcH, tarW, tarH, alpha, gridSize, nPoint, preScale, rDx, rDy, oldDotL, newDotL);

  323. else

  324. calcDelta_rigid(srcW, srcH, tarW, tarH, alpha, gridSize, nPoint, preScale, rDx, rDy, oldDotL, newDotL);

  325. GetNewImg(oriImg, srcW, srcH, stride, tarImg, tarW, tarH, outStride, gridSize, rDx, rDy, transRatio);

  326. if(rDx != NULL)

  327. free(rDx);

  328. if(rDy != NULL)

  329. free(rDy);

  330. };

  331. int f_TMLSImagewarpping(unsigned char* srcData, int width ,int height, int stride, unsigned char* dstData, int outW, int outH, int outStride, int srcPoint[], int dragPoint[], int pointNum, double intensity, int preScale, int gridSize, int method)

  332. {

  333. int res = 0;

  334. vector<PointD> qDst;

  335. vector<PointD> qSrc;

  336. PointD point = {0};

  337. int len = 0;

  338. for(int i = 0; i < pointNum; i++)

  339. {

  340. len = (i << 1);

  341. point.x = srcPoint[len];

  342. point.y = srcPoint[len + 1];

  343. qSrc.push_back(point);

  344. point.x = dragPoint[len];

  345. point.y = dragPoint[len + 1];

  346. qDst.push_back(point);

  347. http://kci.wikidot.com/ }

  348. MLSImageWrapping(srcData, width, height, stride, qSrc, qDst, dstData, outW, outH, outStride, intensity, preScale,gridSize, method);

  349. return res;

  350. };

上述过程就是人脸自动美型的算法逻辑,对于不同的人像照片,会自动判断大眼矫正或者小眼矫正,瘦脸或者胖脸等等,综合达到一个相对完美的结果。

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试

关闭