1. 程式人生 > >AI人臉自動美型演算法

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. def bias_variable(shape,name):

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

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

  6. if padding=="SAME" :

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

  8. else:

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

  10. def max_pool(x, kSize, Strides):

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

  12. def compute_cost(Z3, Y):

  13. """

  14. Computes the cost

  15. Arguments:

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

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

  18. Returns:

  19. cost - Tensor of the cost function

  20. """

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

  22. return cost

  23. def initialize_parameters():

  24. tf.set_random_seed(1)

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

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

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

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

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

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

  31. W4 = tf.cast(weight_variable([14*12*128,500],"W4"), dtype = tf.float32)

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

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

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

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

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

  37. parameters = {"W1":W1,

  38. "b1":b1,

  39. "W2":W2,

  40. "b2":b2,

  41. "W3":W3,

  42. "b3":b3,

  43. "W4":W4,

  44. "b4":b4,

  45. "W5":W5,

  46. "b5":b5,

  47. "W6":W6,

  48. "b6":b6}

  49. return parameters

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

  51. #frist convolution layer

  52. w_conv1 = parameters["W1"]

  53. b_conv1 = parameters["b1"]

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

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

  56. #second convolution layer

  57. w_conv2 = parameters["W2"]

  58. b_conv2 = parameters["b2"]

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

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

  61. #third convolution layer

  62. w_conv3 = parameters["W3"]

  63. b_conv3 = parameters["b3"]

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

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

  66. #full convolution layer

  67. w_fc1 = parameters["W4"]

  68. b_fc1 = parameters["b4"]

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

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

  71. w_fc2 = parameters["W5"]

  72. b_fc2 = parameters["b5"]

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

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

  75. w_fc3 = parameters["W6"]

  76. b_fc3 = parameters["b6"]

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

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

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

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

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

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

  83. #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. static void setDstPoints(const vector<PointD> &qdst,vector<PointD> &oldDotL, int* nPoint) {

  9. *nPoint = qdst.size();

  10. oldDotL.clear();

  11. oldDotL.reserve(*nPoint);

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

  13. }

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

  15. double v21, double v22) {

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

  17. }

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

  19. PointD lt, rb;

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

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

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

  23. i++) {

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

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

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

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

  28. }

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

  30. }

  31. 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)

  32. {

  33. int i, j, k;

  34. PointD swq, qstar, newP, tmpP;

  35. double sw;

  36. double ratio;

  37. if (preScale) {

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

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

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

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

  42. }

  43. }

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

  45. if (nPoint < 2) {

  46. //rDx.setTo(0);

  47. //rDy.setTo(0);

  48. return;

  49. }

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

  51. double miu_r;

  52. for (i = 0;; i += gridSize) {

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

  54. i = tarW - 1;

  55. else if (i >= tarW)

  56. break;

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

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

  59. j = tarH - 1;

  60. else if (j >= tarH)

  61. break;

  62. sw = 0;

  63. swp.x = swp.y = 0;

  64. swq.x = swq.y = 0;

  65. newP.x = newP.y = 0;

  66. curV.x = i;

  67. curV.y = j;

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

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

  70. if (alpha == 1)

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

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

  73. else

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

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

  76. -alpha);

  77. sw = sw + w[k];

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

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

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

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

  82. }

  83. if (k == nPoint) {

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

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

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

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

  88. // Calc miu_r

  89. double s1 = 0, s2 = 0;

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

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

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

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

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

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

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

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

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

  99. }

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

  101. curV.x -= pstar.x;

  102. curV.y -= pstar.y;

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

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

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

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

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

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

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

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

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

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

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

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

  115. newP.x += tmpP.x;

  116. newP.y += tmpP.y;

  117. }

  118. newP.x += qstar.x;

  119. newP.y += qstar.y;

  120. } else {

  121. newP = newDotL[k];

  122. }

  123. if (preScale) {

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

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

  126. } else {

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

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

  129. }

  130. }

  131. }

  132. delete[] w;

  133. if (preScale!=0) {

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

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

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

  137. }

  138. }

  139. }

  140. 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)

  141. {

  142. int i, j, k;

  143. PointD swq, qstar, newP, tmpP;

  144. double sw;

  145. double ratio;

  146. if (preScale) {

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

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

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

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

  151. }

  152. }

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

  154. if (nPoint < 2) {

  155. return;

  156. }

  157. PointD swp, pstar, curV, curVJ, Pi, PiJ;

  158. double miu_s;

  159. for (i = 0;; i += gridSize) {

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

  161. i = tarW - 1;

  162. else if (i >= tarW)

  163. break;

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

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

  166. j = tarH - 1;

  167. else if (j >= tarH)

  168. break;

  169. sw = 0;

  170. swp.x = swp.y = 0;

  171. swq.x = swq.y = 0;

  172. newP.x = newP.y = 0;

  173. curV.x = i;

  174. curV.y = j;

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

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

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

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

  179. sw = sw + w[k];

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

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

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

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

  184. }

  185. if (k == nPoint) {

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

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

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

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

  190. // Calc miu_s

  191. miu_s = 0;

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

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

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

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

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

  197. }

  198. curV.x -= pstar.x;

  199. curV.y -= pstar.y;

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

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

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

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

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

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

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

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

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

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

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

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

  212. newP.x += tmpP.x;

  213. newP.y += tmpP.y;

  214. }

  215. newP.x += qstar.x;

  216. newP.y += qstar.y;

  217. } else {

  218. newP = newDotL[k];

  219. }

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

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

  222. }

  223. }

  224. delete[] w;

  225. if (preScale!=0) {

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

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

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

  229. }

  230. }

  231. }

  232. 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)

  233. {

  234. int i, j;

  235. double di, dj;

  236. double nx, ny;

  237. int nxi, nyi, nxi1, nyi1;

  238. double deltaX, deltaY;

  239. double w, h;

  240. int ni, nj;

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

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

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

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

  245. w = h = gridSize;

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

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

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

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

  250. deltaX =

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

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

  253. deltaY =

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

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

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

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

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

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

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

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

  262. nxi = int(nx);

  263. nyi = int(ny);

  264. nxi1 = ceil(nx);

  265. nyi1 = ceil(ny);

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

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

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

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

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

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

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

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

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

  275. }

  276. }

  277. return 0;

  278. };

  279. 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)

  280. {

  281. int srcW = width;

  282. int srcH = height;

  283. int tarW = outW;

  284. double alpha = 1;

  285. int nPoint;

  286. int len = tarH * tarW;

  287. vector<PointD> oldDotL, newDotL;

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

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

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

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

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

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

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

  295. if(method!=0)

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

  297. else

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

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

  300. if(rDx != NULL)

  301. free(rDx);

  302. if(rDy != NULL)

  303. free(rDy);

  304. };

  305. 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)

  306. {

  307. int res = 0;

  308. vector<PointD> qDst;

  309. vector<PointD> qSrc;

  310. PointD point = {0};

  311. int len = 0;

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

  313. {

  314. len = (i << 1);

  315. point.x = srcPoint[len];

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

  317. qSrc.push_back(point);

  318. point.x = dragPoint[len];

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

  320. qDst.push_back(point);

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

  322. return res;

  323. };

上述過程就是人臉自動美型的演算法邏輯,對於不同的人像照片,會自動判斷大眼矯正或者小眼矯正,瘦臉或者胖臉等等,綜合達到一個相對完美的結果。