├── README.md ├── face_landmark.cpp ├── face_landmark.py ├── model ├── VanFace.caffemodel └── landmark_deploy.prototxt ├── result ├── 1.jpg ├── 1_result.jpg ├── 2.jpg ├── 2_result.jpg ├── 4.jpg ├── 4_result.jpg ├── 7.jpg ├── 7_result.jpg ├── 8.jpg └── 8_result.jpg ├── rotation_image.cpp └── train ├── DataRow.py ├── Demo.py ├── NormlizedMSE.py ├── adam_solver.prototxt ├── landmark_train.prototxt └── mainloop.py /README.md: -------------------------------------------------------------------------------- 1 | # face-landmark-localization 2 | 3 | This is a project predict face landmarks (68 points). 4 | - Model Size : 3.3MB. 5 | - Speed : 5ms per face on i5-2.7GHz CPU. 6 | 7 | ## Install 8 | - [caffe](https://github.com/BVLC/caffe) 9 | - [dlib face detector](http://dlib.net/) 10 | - add libdlib.a to your project lib path 11 | - add face_lardmark.cpp to your caffe project example folder 12 | - opencv

13 | 14 | ## Usage 15 | 16 | - CPP Demo : ./face_lardmark 17 | - Python Demo: python face_landmark.py (created by [jiangwqcooler](https://github.com/jiangwqcooler)) 18 | 19 | ## notice 20 | - This model was trained on dlib face detector. Other face detector may not get landmark result as good as dlib 21 | 22 | ## Result 23 | ![](result/7_result.jpg) 24 | ![](result/8_result.jpg) 25 | -------------------------------------------------------------------------------- /face_landmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "dlib/image_processing/frontal_face_detector.h" 14 | #include "dlib/image_io.h" 15 | 16 | #include 17 | 18 | using namespace caffe; 19 | using namespace std; 20 | using namespace cv; 21 | using namespace dlib; 22 | 23 | int main() 24 | { 25 | string network = "/Users/CaffeMac/CaffeMac/Models/landmark_deploy.prototxt"; 26 | string weights = "/Users/CaffeMac/CaffeMac/Models/landmark.caffemodel"; 27 | string img = "/Users/CaffeMac/CaffeMac/test.jpg"; 28 | Net *net = new Net(network,TEST); 29 | 30 | net->CopyTrainedLayersFrom(weights); 31 | 32 | Caffe::set_mode(Caffe::CPU); 33 | 34 | Mat image = imread(img); 35 | 36 | frontal_face_detector detector = get_frontal_face_detector(); 37 | 38 | 39 | array2d< bgr_pixel> arrimg(image.rows, image.cols); 40 | for(int i=0; i < image.rows; i++) 41 | { 42 | 43 | for(int j=0; j < image.cols; j++) 44 | { 45 | 46 | arrimg[i][j].blue = image.at< cv::Vec3b>(i,j)[0]; 47 | arrimg[i][j].green=image.at< cv::Vec3b>(i,j)[1]; 48 | arrimg[i][j].red = image.at< cv::Vec3b>(i,j)[2]; 49 | } 50 | } 51 | 52 | //开始检测,返回一系列的边界框 53 | clock_t start; 54 | int cnt = 0; 55 | start = clock(); 56 | std::vector dets = detector(arrimg); 57 | start = clock() -start; 58 | printf("detection time is: %f ms\n", (double(start))/CLOCKS_PER_SEC*1000); 59 | //cout<<"time is "<(0,0); 82 | sd = tmp_sd.at(0,0); 83 | 84 | img3 = (img3 - m)/(0.000001 + sd); 85 | 86 | if (img3.channels() * img3.rows * img3.cols != net->input_blobs()[0]->count()) 87 | LOG(FATAL) << "Incorrect " << image << ", resize to correct dimensions.\n"; 88 | // prepare data into array 89 | float *data = (float*)malloc( img3.rows * img3.cols * sizeof(float)); 90 | 91 | int pix_count = 0; 92 | 93 | for (int i = 0; i < img3.rows; ++i) { 94 | for (int j = 0; j < img3.cols; ++j) { 95 | float pix = img3.at(i, j); 96 | float* p = (float*)(data); 97 | p[pix_count] = pix; 98 | ++pix_count; 99 | } 100 | } 101 | 102 | std::vector*> in_blobs = net->input_blobs(); 103 | in_blobs[0]->Reshape(1, 1, img3.rows, img3.cols); 104 | net->Reshape(); 105 | 106 | in_blobs[0]->set_cpu_data((float*)data); 107 | Timer total_timer; 108 | total_timer.Start(); 109 | 110 | net->Forward(); 111 | cout << " total time = " << total_timer.MicroSeconds() / 1000 < > feature_blob = net->blob_by_name("Dense3");//获取该层特征 113 | 114 | float feat_dim = feature_blob->count() / feature_blob->num();//计算特征维度 115 | cout << feat_dim << endl; 116 | const float* data_ptr = (const float *)feature_blob->cpu_data();//特征块数据 117 | 118 | 119 | std::vector feat2; 120 | 121 | for (int i = 0; i < feat_dim; i++) 122 | { 123 | feat2.push_back(*data_ptr); 124 | if (i < feat_dim - 1) 125 | data_ptr++; 126 | } 127 | 128 | for(int i = 0;i < feat_dim/2;i++) 129 | { 130 | Point x = Point(int(feat2[2*i]*(tmp.right() - tmp.left()) + tmp.left()),int(feat2[2*i + 1]*(tmp.bottom() - tmp.top()) + tmp.top())); 131 | cv::circle(image, x, 0.1, Scalar(0, 0, 255), 4, 8, 0); 132 | } 133 | free(data); 134 | } 135 | imshow("result", image); 136 | imwrite("/Users/CaffeMac/CaffeMac/result.jpg", image); 137 | free(net); 138 | 139 | image.release(); 140 | return 0; 141 | 142 | } 143 | -------------------------------------------------------------------------------- /face_landmark.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | import time 5 | import pprint 6 | 7 | import caffe 8 | import dlib 9 | import cv2 10 | import numpy as np 11 | 12 | def file_list_fn(path): 13 | 14 | file_list = [] 15 | files = os.listdir(path) 16 | for f in files: 17 | file_list.append(f) 18 | return file_list 19 | 20 | net_work_path = '/home/code/face-landmark/model/landmark_deploy.prototxt' 21 | weight_path = '/home/code/face-landmark/model/VanFace.caffemodel' 22 | images_dir = '/home/code/face-landmark/images/' 23 | result_dir = '/home/code/face-landmark/results/' 24 | 25 | image_list = file_list_fn(images_dir) 26 | caffe.set_mode_cpu() 27 | net = caffe.Net(net_work_path, weight_path, caffe.TEST) 28 | net.name = 'FaceThink_face_landmark_test' 29 | 30 | detector = dlib.get_frontal_face_detector() 31 | 32 | total_detecting_time = 0.0 33 | total_landmark_time = 0.0 34 | face_total = 0.0 35 | for image in image_list: 36 | print("Processing file: {}".format(image)) 37 | img = cv2.imread(images_dir + image) 38 | 39 | # The 1 in the second argument indicates that we should upsample the image 40 | # 1 time. This will make everything bigger and allow us to detect more 41 | # faces. 42 | det_start_time = time.time() 43 | dets = detector(img, 1) 44 | det_end_time = time.time() 45 | det_time = det_end_time - det_start_time 46 | total_detecting_time += det_time 47 | print "Detecting time is {}".format(det_time) 48 | print "Number of faces detected: {}".format(len(dets)) 49 | for i, d in enumerate(dets): 50 | print "Detection {}: Left: {} Top: {} Right: {} Bottom: {}".format( 51 | i, d.left(), d.top(), d.right(), d.bottom()) 52 | 53 | for index, det in enumerate(dets): 54 | face_total += 1 55 | x1 = det.left() 56 | y1 = det.top() 57 | x2 = det.right() 58 | y2 = det.bottom() 59 | if x1 < 0: x1 = 0 60 | if y1 < 0: y1 = 0 61 | if x2 > img.shape[1]: x2 = img.shape[1] 62 | if y2 > img.shape[0]: y2 = img.shape[0] 63 | cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) 64 | roi = img[y1:y2 + 1, x1:x2 + 1, ] 65 | gary_img = cv2.cvtColor(roi, cv2.COLOR_RGB2GRAY) 66 | w = 60 67 | h = 60 68 | 69 | print image 70 | res = cv2.resize(gary_img, (w, h), 0.0, 0.0, interpolation=cv2.INTER_CUBIC) 71 | resize_mat = np.float32(res) 72 | 73 | m = np.zeros((w, h)) 74 | sd = np.zeros((w, h)) 75 | mean, std_dev = cv2.meanStdDev(resize_mat, m, sd) 76 | new_m = mean[0][0] 77 | new_sd = std_dev[0][0] 78 | new_img = (resize_mat - new_m) / (0.000001 + new_sd) 79 | 80 | if new_img.shape[0] != net.blobs['data'].data[0].shape or new_img.shape[1] != net.blobs['data'].data[1].shape: 81 | print "Incorrect " + image + ", resize to correct dimensions." 82 | 83 | net.blobs['data'].data[...] = new_img 84 | landmark_time_start = time.time() 85 | out = net.forward() 86 | landmark_time_end = time.time() 87 | landmark_time = landmark_time_end - landmark_time_start 88 | total_landmark_time += landmark_time 89 | print "landmark time is {}".format(landmark_time) 90 | points = net.blobs['Dense3'].data[0].flatten() 91 | 92 | point_pair_l = len(points) 93 | for i in range(point_pair_l / 2): 94 | x = points[2*i] * (x2 - x1) + x1 95 | y = points[2*i+1] * (y2 - y1) + y1 96 | cv2.circle(img, (int(x), int(y)), 1, (0, 0, 255), 2) 97 | 98 | cv2.imwrite(result_dir + image, img) 99 | 100 | print total_detecting_time 101 | print total_landmark_time 102 | print face_total 103 | per_face_det_time = total_detecting_time / face_total 104 | per_face_landmark_time = total_landmark_time / face_total 105 | 106 | per_image_det_time = total_detecting_time / len(image_list) 107 | per_image_landmark_time = total_landmark_time / len(image_list) 108 | 109 | print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~' 110 | print "per face detecting time is {}".format(per_face_det_time) 111 | print "per face landmark time is {}".format(per_face_landmark_time) 112 | print "per image detecting time is {}".format(per_image_det_time) 113 | print "per image detecting time is {}".format(per_image_landmark_time) 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /model/VanFace.caffemodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/model/VanFace.caffemodel -------------------------------------------------------------------------------- /model/landmark_deploy.prototxt: -------------------------------------------------------------------------------- 1 | name: "landmark" 2 | input: "data" 3 | input_dim: 1 4 | input_dim: 1 5 | input_dim: 60 6 | input_dim: 60 7 | ######################################## 8 | # the actual net 9 | # layer 1 10 | layer { 11 | name: "Conv1" 12 | type: "Convolution" 13 | bottom: "data" 14 | top: "Conv1" 15 | param { 16 | lr_mult: 1 17 | decay_mult: 1 18 | } 19 | param { 20 | lr_mult: 2 21 | decay_mult: 0 22 | } 23 | convolution_param { 24 | num_output: 20 25 | pad: 2 26 | kernel_size: 5 27 | stride: 1 28 | weight_filler { 29 | type: "xavier" 30 | std: 0.1 31 | } 32 | bias_filler { 33 | type: "constant" 34 | value: 0.2 35 | } 36 | } 37 | } 38 | 39 | layer { 40 | name: "ActivationTangH1" 41 | bottom: "Conv1" 42 | top: "ActivationTangH1" 43 | type: "TanH" 44 | } 45 | 46 | layer { 47 | name: "ActivationAbs1" 48 | bottom: "ActivationTangH1" 49 | top: "Abs1" 50 | type: "AbsVal" 51 | } 52 | 53 | layer { 54 | name: "Pool1" 55 | type: "Pooling" 56 | bottom: "Abs1" 57 | top: "Pool1" 58 | pooling_param { 59 | pool: MAX 60 | kernel_size: 2 61 | stride: 2 62 | } 63 | } 64 | 65 | layer { 66 | name: "Conv2" 67 | type: "Convolution" 68 | bottom: "Pool1" 69 | top: "Conv2" 70 | param { 71 | lr_mult: 1 72 | decay_mult: 1 73 | } 74 | param { 75 | lr_mult: 2 76 | decay_mult: 0 77 | } 78 | convolution_param { 79 | num_output: 48 80 | pad: 2 81 | kernel_size: 5 82 | stride: 1 83 | weight_filler { 84 | type: "xavier" 85 | std: 0.1 86 | } 87 | bias_filler { 88 | type: "constant" 89 | value: 0.2 90 | } 91 | } 92 | } 93 | 94 | layer { 95 | name: "ActivationTangH2" 96 | bottom: "Conv2" 97 | top: "ActivationTangH2" 98 | type: "TanH" 99 | } 100 | 101 | layer { 102 | name: "ActivationAbs2" 103 | bottom: "ActivationTangH2" 104 | top: "Abs2" 105 | type: "AbsVal" 106 | } 107 | 108 | 109 | layer { 110 | name: "Pool2" 111 | type: "Pooling" 112 | bottom: "Abs2" 113 | top: "Pool2" 114 | pooling_param { 115 | pool: MAX 116 | kernel_size: 2 117 | stride: 2 118 | } 119 | } 120 | 121 | # layer 3 122 | layer { 123 | name: "Conv3" 124 | type: "Convolution" 125 | bottom: "Pool2" 126 | top: "Conv3" 127 | param { 128 | lr_mult: 1 129 | decay_mult: 1 130 | } 131 | param { 132 | lr_mult: 2 133 | decay_mult: 0 134 | } 135 | convolution_param { 136 | num_output: 64 137 | pad: 0 138 | kernel_size: 3 139 | stride: 1 140 | weight_filler { 141 | type: "xavier" 142 | std: 0.1 143 | } 144 | bias_filler { 145 | type: "constant" 146 | value: 0.2 147 | } 148 | } 149 | } 150 | 151 | 152 | layer { 153 | name: "ActivationTangH3" 154 | bottom: "Conv3" 155 | top: "ActivationTangH3" 156 | type: "TanH" 157 | } 158 | 159 | layer { 160 | name: "ActivationAbs3" 161 | bottom: "ActivationTangH3" 162 | top: "Abs3" 163 | type: "AbsVal" 164 | } 165 | 166 | layer { 167 | name: "Pool3" 168 | type: "Pooling" 169 | bottom: "Abs3" 170 | top: "Pool3" 171 | pooling_param { 172 | pool: MAX 173 | kernel_size: 3 174 | stride: 2 175 | } 176 | } 177 | 178 | # layer 4 179 | layer { 180 | name: "Conv4" 181 | type: "Convolution" 182 | bottom: "Pool3" 183 | top: "Conv4" 184 | param { 185 | lr_mult: 1 186 | decay_mult: 1 187 | } 188 | param { 189 | lr_mult: 2 190 | decay_mult: 0 191 | } 192 | convolution_param { 193 | num_output: 80 194 | pad: 0 195 | kernel_size: 3 196 | stride: 1 197 | weight_filler { 198 | type: "xavier" 199 | std: 0.1 200 | } 201 | bias_filler { 202 | type: "constant" 203 | value: 0.2 204 | } 205 | } 206 | } 207 | 208 | 209 | layer { 210 | name: "ActivationTangH4" 211 | bottom: "Conv4" 212 | top: "ActivationTangH4" 213 | type: "TanH" 214 | } 215 | 216 | layer { 217 | name: "ActivationAbs4" 218 | bottom: "ActivationTangH4" 219 | top: "Abs4" 220 | type: "AbsVal" 221 | } 222 | 223 | 224 | ######################################## 225 | 226 | layer { 227 | name: "Dense1" 228 | type: "InnerProduct" 229 | bottom: "Abs4" 230 | top: "Dense1" 231 | param { 232 | lr_mult: 1 233 | decay_mult: 1 234 | } 235 | param { 236 | lr_mult: 2 237 | decay_mult: 0 238 | } 239 | inner_product_param { 240 | num_output: 512 241 | weight_filler { 242 | type: "xavier" 243 | } 244 | bias_filler { 245 | type: "constant" 246 | value: 0 247 | } 248 | } 249 | } 250 | 251 | 252 | layer { 253 | name: "ActivationTangH5" 254 | bottom: "Dense1" 255 | top: "ActivationTangH5" 256 | type: "TanH" 257 | } 258 | 259 | layer { 260 | name: "ActivationAbs5" 261 | bottom: "ActivationTangH5" 262 | top: "Abs5" 263 | type: "AbsVal" 264 | } 265 | 266 | 267 | layer { 268 | name: "Dense3" 269 | type: "InnerProduct" 270 | bottom: "Abs5" 271 | top: "Dense3" 272 | param { 273 | lr_mult: 1 274 | decay_mult: 1 275 | } 276 | param { 277 | lr_mult: 2 278 | decay_mult: 0 279 | } 280 | inner_product_param { 281 | num_output: 136 282 | weight_filler { 283 | type: "xavier" 284 | } 285 | bias_filler { 286 | type: "constant" 287 | value: 0 288 | } 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /result/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/1.jpg -------------------------------------------------------------------------------- /result/1_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/1_result.jpg -------------------------------------------------------------------------------- /result/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/2.jpg -------------------------------------------------------------------------------- /result/2_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/2_result.jpg -------------------------------------------------------------------------------- /result/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/4.jpg -------------------------------------------------------------------------------- /result/4_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/4_result.jpg -------------------------------------------------------------------------------- /result/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/7.jpg -------------------------------------------------------------------------------- /result/7_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/7_result.jpg -------------------------------------------------------------------------------- /result/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/8.jpg -------------------------------------------------------------------------------- /result/8_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda-lab/face-landmark/34c8a893fee03eb3b6e5b90576fb335bbfc99571/result/8_result.jpg -------------------------------------------------------------------------------- /rotation_image.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | using namespace std; 8 | using namespace cv; 9 | #define BYTE unsigned char 10 | 11 | Mat ImageRotate(Mat & src, const CvPoint &_center, double angle) 12 | { 13 | CvPoint2D32f center; 14 | center.x = float(_center.x); 15 | center.y = float(_center.y); 16 | 17 | //计算二维旋转的仿射变换矩阵 18 | Mat M = getRotationMatrix2D(center, angle, 1); 19 | 20 | // rotate 21 | Mat dst; 22 | warpAffine(src, dst, M, cvSize(src.cols, src.rows), CV_INTER_LINEAR); 23 | return dst; 24 | } 25 | 26 | // 获取指定像素点放射变换后的新的坐标位置 27 | CvPoint getPointAffinedPos(const CvPoint &src, const CvPoint center, double angle) 28 | { 29 | CvPoint dst; 30 | int x = src.x - center.x; 31 | int y = src.y - center.y; 32 | 33 | dst.x = cvRound(x * cos(angle) + y * sin(angle) + center.x); 34 | dst.y = cvRound(-x * sin(angle) + y * cos(angle) + center.y); 35 | return dst; 36 | } 37 | 38 | 39 | 40 | 41 | int main(int argc, const char * argv[]) 42 | { 43 | struct dirent *ptr; 44 | DIR *dir; 45 | string PATH = "/home/workspace/Dataset/68/helen/trainset/"; 46 | dir=opendir(PATH.c_str()); 47 | vector files; 48 | cout << "文件列表: "<< endl; 49 | while((ptr=readdir(dir))!=NULL) 50 | { 51 | 52 | //跳过'.'和'..'两个目录 53 | if(ptr->d_name[0] == '.') 54 | continue; 55 | //cout << ptr->d_name << endl; 56 | string tmp = ptr->d_name; 57 | long k = tmp.find(".png",0); 58 | long m = tmp.find(".jpg",0); 59 | if(k > 0 || m > 0) 60 | { 61 | files.push_back(ptr->d_name); 62 | } 63 | } 64 | closedir(dir); 65 | 66 | for (int i = 0; i < files.size(); ++i) 67 | { 68 | cout << files[i] << endl; 69 | string filename = "/home/workspace/Dataset/68/helen/trainset/" + files[i]; 70 | Mat img = imread(filename); 71 | cout << filename << endl; 72 | CvPoint center; 73 | center.x = img.cols / 2; 74 | center.y = img.rows / 2; 75 | 76 | 77 | double angle = 9; 78 | 79 | Mat dst = ImageRotate(img, center, angle); 80 | 81 | 82 | //读取pts文件 83 | long idx = filename.find(".", 0); 84 | printf("---- %ld ----\n",filename.size()); 85 | string pre_path = filename.erase(idx); 86 | pre_path.insert(idx, ".pts"); 87 | ifstream ifs(pre_path); 88 | 89 | 90 | string rotate_pts = "/home/workspace/Dataset/train(helen+ibug+lfpw+afw)/img_09/" + files[i]; 91 | long idx2 = rotate_pts.find(".", 0); 92 | rotate_pts.erase(idx2); 93 | 94 | string out_pts = rotate_pts.insert(idx2, "_09.pts"); 95 | ofstream outfile(out_pts,ios::out); 96 | 97 | float x; 98 | float y; 99 | char drop; 100 | int cnt = 0; 101 | string temp; 102 | getline(ifs,temp); //跳过前三行 103 | outfile << temp << endl; 104 | getline(ifs,temp); 105 | outfile << temp << endl; 106 | getline(ifs,temp); 107 | outfile << temp << endl; 108 | 109 | while (!ifs.eof()) 110 | { 111 | if (ifs>>x>>y) 112 | { 113 | CvPoint tmp; 114 | tmp.x = x; 115 | tmp.y = y; 116 | 117 | CvPoint l2 = getPointAffinedPos(tmp, center, angle * CV_PI / 180); 118 | //circle(dst,l2,1,CV_RGB(0,255,255),2); 119 | outfile.precision(3); 120 | outfile.setf(ios::fixed); 121 | outfile << l2.x << " " << l2.y << endl; 122 | } 123 | else 124 | { 125 | ifs.clear(); 126 | ifs.sync(); 127 | ifs>>drop; 128 | } 129 | 130 | cnt++; 131 | if(cnt >= 68) 132 | { 133 | cnt = 0; 134 | break; 135 | } 136 | } 137 | 138 | outfile.close(); 139 | //imshow("src", dst); 140 | string rotate_img = "/home/workspace/Dataset/train(helen+ibug+lfpw+afw)/img_09/" + files[i]; 141 | rotate_img.erase(idx2); 142 | string out_img = rotate_img.insert(idx2, "_09.jpg"); 143 | 144 | imwrite(out_img,dst); 145 | 146 | img.release(); 147 | dst.release(); 148 | 149 | //waitKey(0); 150 | 151 | } 152 | 153 | 154 | return 0; 155 | } 156 | -------------------------------------------------------------------------------- /train/DataRow.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Dec 4 22:30:29 2015 4 | 5 | @author: Ishay Tubi 6 | """ 7 | 8 | import os 9 | import cv2 10 | import numpy as np 11 | import sys 12 | import csv 13 | import time 14 | def getGitRepFolder(): 15 | # import subprocess 16 | # return subprocess.Popen(['git', 'rev-parse', '--show-toplevel'], stdout=subprocess.PIPE).communicate()[0].rstrip() 17 | return '/home/ly/workspace/Vanilla' 18 | 19 | def mse_normlized(groundTruth, pred): 20 | delX = groundTruth[78]-groundTruth[84] 21 | delY = groundTruth[79]-groundTruth[85] 22 | interOc = (1e-6+(delX*delX + delY*delY))**0.5 # Euclidain distance 23 | diff = (pred-groundTruth)**2 24 | sumPairs = (diff[0::2]+diff[1::2])**0.5 # Euclidian distance 25 | return (sumPairs / interOc) # normlized 26 | 27 | 28 | 29 | 30 | class RetVal: 31 | pass ## A generic class to return multiple values without a need for a dictionary. 32 | 33 | def createDataRowsFromCSV(csvFilePath, csvParseFunc, DATA_PATH, limit = sys.maxint): 34 | ''' Returns a list of DataRow from CSV files parsed by csvParseFunc, 35 | DATA_PATH is the prefix to add to the csv file names, 36 | limit can be used to parse only partial file rows. 37 | ''' 38 | data = [] # the array we build 39 | validObjectsCounter = 0 40 | 41 | with open(csvFilePath, 'r') as csvfile: 42 | 43 | reader = csv.reader(csvfile, delimiter=' ') 44 | for row in reader: 45 | d = csvParseFunc(row, DATA_PATH) 46 | if d is not None: 47 | data.append(d) 48 | validObjectsCounter += 1 49 | if (validObjectsCounter > limit ): # Stop if reached to limit 50 | return data 51 | return data 52 | 53 | def createBoxRowsFromCSV(csvFilePath, DATA_PATH, limit = sys.maxint): 54 | ''' Returns a list of DataRow from CSV files parsed by csvParseFunc, 55 | DATA_PATH is the prefix to add to the csv file names, 56 | limit can be used to parse only partial file rows. 57 | ''' 58 | data = [] # the array we build 59 | validObjectsCounter = 0 60 | with open(csvFilePath, 'r') as csvfile: 61 | 62 | reader = csv.reader(csvfile, delimiter=' ') 63 | for row in reader: 64 | # print "row: ", row 65 | box = BBox() 66 | box.path = os.path.join(DATA_PATH, row[0]).replace("\\", "/") 67 | #print box.path 68 | box.left = row[1] 69 | box.top = row[2] 70 | box.right = row[3] 71 | box.bottom = row[4] 72 | if box.right>0 and box.bottom>0 : 73 | data.append(box) 74 | validObjectsCounter += 1 75 | if (validObjectsCounter > limit ): # Stop if reached to limit 76 | return data 77 | return data 78 | 79 | def getValidWithBBox(dataRows, boxRows=[]): 80 | ''' Returns a list of valid DataRow of a given list of dataRows 81 | ''' 82 | import dlib 83 | import random 84 | R=RetVal() 85 | 86 | R.outsideLandmarks = 0 87 | R.noImages = 0 88 | R.noFacesAtAll = 0 89 | R.couldNotMatch = 0 90 | detector=dlib.get_frontal_face_detector() 91 | 92 | validRow=[] 93 | for rowIdx, dataRow in enumerate(dataRows): 94 | if dataRow.image is None or len(dataRow.image)==0: 95 | R.noImages += 1 96 | continue 97 | #print 'landmarks ', dataRow.landmarks() 98 | lmd_xy = dataRow.landmarks().reshape([-1,2]) 99 | 100 | #left, top = lmd_xy.min( axis=0 ) 101 | #right, bot = lmd_xy.max( axis=0 ) 102 | #left, top = lmd_xy[37] 103 | #right, bot = lmd_xy[46] 104 | left, top0 = lmd_xy[36] 105 | right, top1 = lmd_xy[45] 106 | mid, bot = lmd_xy[57] 107 | 108 | top = max([top0, top1]) 109 | left = min([left, mid]) 110 | right = max([right, mid]) 111 | #left, top = lmd_xy.min(axis=0) 112 | #right, bot = lmd_xy.max(axis=0) 113 | #print "landmark box: ", left, top, right , bot 114 | 115 | 116 | if not boxRows == []: 117 | dets = [boxRows[rowIdx]] 118 | #print "box: ",boxRows[rowIdx].left, boxRows[rowIdx].top, boxRows[rowIdx].right, boxRows[rowIdx].bottom 119 | else: 120 | dets = detector( np.array(dataRow.image, dtype = 'uint8' ), 1); 121 | #dets = [BBox.BBoxFromLTRB(left, top, right, bot)] 122 | 123 | 124 | det_bbox = None # the valid bbox if found 125 | #print R.couldNotMatch 126 | for det in dets: 127 | if not boxRows == []: 128 | det_box = BBox.BBoxFromLTRB(float(det.left), float(det.top), float(det.right), float(det.bottom)) 129 | # print "det_box: ",det_box.left, det_box.top, det_box.right, det_box.bottom 130 | else: 131 | det_box = BBox.BBoxFromLTRB(det.left(), det.top(), det.right(), det.bottom()) 132 | #print det.left(), det.top(), det.right(), det.bottom() 133 | #det_box = BBox.BBoxFromLTRB(det[0], det[1], det[0]+det[2], det[1]+det[3]) 134 | #det_box = det 135 | 136 | # Does all landmarks fit into this box? 137 | if top >= det_box.top and bot<= det_box.bottom and left>=det_box.left and right<=det_box.right: 138 | # det_bbox = det_box 139 | # height = det_box.bottom - det_box.top 140 | 141 | # det_bbox.top = det_box.top - height * 0.1 142 | # det_bbox.bottom = det_box.bottom + height*0.25 143 | # weight = det_box.right - det_box.left 144 | # det_bbox.left = det_box.left - weight*0.15 145 | # det_bbox.right = det_box.right + weight*0.15 146 | 147 | # center random shift 148 | tx = random.uniform(-0.04,0.04) 149 | ty = random.uniform(-0.04,0.04) 150 | det_box.offset(tx*det_box.width(), ty*det_box.height()) 151 | 152 | # scale random 153 | s = random.uniform(-0.04,0.04) 154 | #s = 0.1#random.uniform(-0.05,0.1) 155 | #s = random.uniform(0.0,0.03) 156 | det_bbox = det_box 157 | height = det_box.bottom - det_box.top 158 | det_bbox.top = det_box.top - height * s#0.1 159 | det_bbox.bottom = det_box.bottom + height*s#0.1 160 | weight = det_box.right - det_box.left 161 | det_bbox.left = det_box.left - weight*s#0.1 162 | det_bbox.right = det_box.right + weight*s#0.1 163 | # print "det_bbox: ",det_bbox.left, det_bbox.top, det_bbox.right, det_bbox.bottom 164 | 165 | if det_bbox is None: 166 | if len(dets)>0: 167 | R.couldNotMatch += 1 # For statistics, dlib found faces but they did not match our landmarks. 168 | else: 169 | R.noFacesAtAll += 1 # dlib found 0 faces. 170 | else: 171 | dataRow.fbbox = det_bbox # Save the bbox to the data row 172 | #if det_bbox.left<0 or det_bbox.top<0 or det_bbox.right>dataRow.image.shape[0] or det_bbox.bottom>dataRow.image.shape[1]: 173 | if det_bbox.left<0 or det_bbox.top<0 or det_bbox.right>dataRow.image.shape[1] or det_bbox.bottom>dataRow.image.shape[0]: 174 | #print 'det_bbox: ', det_bbox 175 | #print 'detb: ', detb 176 | #print 'image.shape: ', dataRow.image.shape 177 | R.outsideLandmarks += 1 # Saftey check, make sure nothing goes out of bound. 178 | else: 179 | validRow.append(dataRow) 180 | 181 | 182 | return validRow,R 183 | 184 | def writeHD5(dataRows, outputPath, setTxtFilePATH, meanTrainSet, stdTrainSet , IMAGE_SIZE=60, mirror=False): 185 | ''' Create HD5 data set for caffe from given valid data rows. 186 | if mirror is True, duplicate data by mirroring. 187 | ''' 188 | from numpy import zeros 189 | import h5py 190 | 191 | if mirror: 192 | BATCH_SIZE = len(dataRows) *2 193 | else: 194 | BATCH_SIZE = len(dataRows) 195 | 196 | #HD5Images = zeros([BATCH_SIZE, 3, IMAGE_SIZE, IMAGE_SIZE], dtype='float32') 197 | HD5Images = zeros([BATCH_SIZE, 1, IMAGE_SIZE, IMAGE_SIZE], dtype='float32') 198 | HD5Landmarks = zeros([BATCH_SIZE, 136], dtype='float32') 199 | #prefix = os.path.join(ROOT, 'caffeData', 'hd5', 'train') 200 | setTxtFile = open(setTxtFilePATH, 'w') 201 | 202 | 203 | i = 0 204 | 205 | for dataRowOrig in dataRows: 206 | if i % 1000 == 0 or i >= BATCH_SIZE-1: 207 | print "Processing row %d " % (i+1) 208 | 209 | if not hasattr(dataRowOrig, 'fbbox'): 210 | print "Warning, no fbbox" 211 | continue 212 | 213 | dataRow = dataRowOrig.copyCroppedByBBox(dataRowOrig.fbbox) # Get a cropped scale copy of the data row 214 | scaledLM = dataRow.landmarksScaledMinus05_plus05() 215 | image = dataRow.image.astype('f4') 216 | #image = (image-meanTrainSet)/(1.e-6 + stdTrainSet) 217 | image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY).reshape(1,IMAGE_SIZE,IMAGE_SIZE) 218 | m, s = cv2.meanStdDev(image) 219 | image = (image-m)/(1.e-6 + s) 220 | 221 | #HD5Images[i, :] = cv2.split(image) # split interleaved (40,40,3) to (3,40,40) 222 | HD5Images[i, :] = image 223 | HD5Landmarks[i,:] = scaledLM 224 | i+=1 225 | 226 | if mirror: 227 | dataRow = dataRowOrig.copyCroppedByBBox(dataRowOrig.fbbox).copyMirrored() # Get a cropped scale copy of the data row 228 | scaledLM = dataRow.landmarksScaledMinus05_plus05() 229 | image = dataRow.image.astype('f4') 230 | #image = (image-meanTrainSet)/(1.e-6 + stdTrainSet) 231 | image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY).reshape(1,IMAGE_SIZE,IMAGE_SIZE) 232 | m, s = cv2.meanStdDev(image) 233 | image = (image-m)/(1.e-6 + s) 234 | 235 | #HD5Images[i, :] = cv2.split(image) # split interleaved (40,40,3) to (3,40,40) 236 | HD5Images[i, :] = image 237 | HD5Landmarks[i,:] = scaledLM 238 | i+=1 239 | 240 | 241 | with h5py.File(outputPath, 'w') as T: 242 | T.create_dataset("X", data=HD5Images) 243 | T.create_dataset("landmarks", data=HD5Landmarks) 244 | 245 | setTxtFile.write(outputPath+"\n") 246 | setTxtFile.flush() 247 | setTxtFile.close() 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | class ErrorAcum: # Used to count error per landmark 256 | def __init__(self): 257 | self.errorPerLandmark = np.zeros(68, dtype ='f4') 258 | self.itemsCounter = 0 259 | self.failureCounter = 0 260 | 261 | def __repr__(self): 262 | return '%f mean error, %d items, %d failures %f precent' % (self.meanError().mean()*100, self.itemsCounter, self.failureCounter, float(self.failureCounter)/self.itemsCounter if self.itemsCounter>0 else 0) 263 | 264 | 265 | def add(self, groundTruth, pred): 266 | normlized = mse_normlized(groundTruth, pred) 267 | self.errorPerLandmark += normlized 268 | self.itemsCounter +=1 269 | if normlized.mean() > 0.1: 270 | # Count error above 10% as failure 271 | self.failureCounter +=1 272 | 273 | def meanError(self): 274 | if self.itemsCounter > 0: 275 | return self.errorPerLandmark/self.itemsCounter 276 | else: 277 | return self.errorPerLandmark 278 | 279 | def __add__(self, x): 280 | ret = ErrorAcum() 281 | ret.errorPerLandmark = self.errorPerLandmark + x.errorPerLandmark 282 | ret.itemsCounter = self.itemsCounter + x.itemsCounter 283 | ret.failureCounter = self.failureCounter + x.failureCounter 284 | return ret 285 | 286 | def plot(self): 287 | from matplotlib.pylab import show, plot, stem 288 | pass 289 | 290 | 291 | class BBox: # Bounding box 292 | 293 | @staticmethod 294 | def BBoxFromLTRB(l, t, r, b): 295 | return BBox(l, t, r, b) 296 | 297 | @staticmethod 298 | def BBoxFromXYWH_array(xywh): 299 | return BBox(xywh[0], xywh[1], +xywh[0]+xywh[2], xywh[1]+xywh[3]) 300 | 301 | @staticmethod 302 | def BBoxFromXYWH(x,y,w,h): 303 | return BBox(x,y, x+w, y+h) 304 | 305 | def top_left(self): 306 | return (self.top, self.left) 307 | 308 | def left_top(self): 309 | return (self.left, self.top) 310 | 311 | def bottom_right(self): 312 | return (self.bottom, self.right) 313 | 314 | def right_bottom(self): 315 | return (self.right, self.bottom) 316 | 317 | def right_top(self): 318 | return (self.right, self.top) 319 | 320 | def relaxed(self, clip ,relax=3): #@Unused 321 | from numpy import array 322 | _A = array 323 | maxWidth, maxHeight = clip[0], clip[1] 324 | 325 | nw, nh = self.size()*(1+relax)*.5 326 | center = self.center() 327 | offset=_A([nw,nh]) 328 | lefttop = center - offset 329 | rightbot= center + offset 330 | 331 | self.left, self.top = int( max( 0, lefttop[0] ) ), int( max( 0, lefttop[1]) ) 332 | self.right, self.bottom = int( min( rightbot[0], maxWidth ) ), int( min( rightbot[1], maxHeight ) ) 333 | return self 334 | 335 | def clip(self, maxRight, maxBottom): 336 | self.left = max(self.left, 0) 337 | self.top = max(self.top, 0) 338 | self.right = min(self.right, maxRight) 339 | self.bottom = min(self.bottom, maxBottom) 340 | 341 | def size(self): 342 | from numpy import array 343 | return array([self.width(), self.height()]) 344 | 345 | def center(self): 346 | from numpy import array 347 | return array([(self.left+self.right)/2, (self.top+self.bottom)/2]) 348 | 349 | def __init__(self,left=0, top=0, right=0, bottom=0, path=''): 350 | self.left = left 351 | self.top = top 352 | self.right = right 353 | self.bottom = bottom 354 | self.path = path 355 | 356 | def width(self): 357 | return self.right - self.left 358 | 359 | def height(self): 360 | return self.bottom - self.top 361 | 362 | def xywh(self): 363 | return self.left, self.top, self.width(), self.height() 364 | 365 | def offset(self, x, y): 366 | self.left += x 367 | self.right += x 368 | self.top += y 369 | self.bottom += y 370 | 371 | def scale(self, rx, ry): 372 | self.left *= rx 373 | self.right *= rx 374 | self.top *= ry 375 | self.bottom *= ry 376 | 377 | def __repr__(self): 378 | return 'left(%.1f), top(%.1f), right(%.1f), bottom(%.1f) w(%d) h(%d)' % (self.left, self.top, self.right, self.bottom,self.width(), self.height()) 379 | 380 | def makeInt(self): 381 | self.left = int(self.left) 382 | self.top = int(self.top) 383 | self.right = int(self.right) 384 | self.bottom = int(self.bottom) 385 | return self 386 | 387 | 388 | 389 | class DataRow: 390 | global TrainSetMean 391 | global TrainSetSTD 392 | 393 | IMAGE_SIZE = 60 394 | def __init__(self, path='', p1=(0, 0, ), p2=(0, 0), p3=(0, 0), p4=(0, 0), p5=(0, 0),p6=(0, 0, ), p7=(0, 0), p8=(0, 0), p9=(0, 0), p10=(0, 0), 395 | p11=(0, 0, ), p12=(0, 0), p13=(0, 0), p14=(0, 0), p15=(0, 0),p16=(0, 0, ), p17=(0, 0), p18=(0, 0), p19=(0, 0), p20=(0, 0), 396 | p21=(0, 0, ), p22=(0, 0), p23=(0, 0), p24=(0, 0), p25=(0, 0),p26=(0, 0, ), p27=(0, 0), p28=(0, 0), p29=(0, 0), p30=(0, 0), 397 | p31=(0, 0, ), p32=(0, 0), p33=(0, 0), p34=(0, 0), p35=(0, 0),p36=(0, 0, ), p37=(0, 0), p38=(0, 0), p39=(0, 0), p40=(0, 0), 398 | p41=(0, 0, ), p42=(0, 0), p43=(0, 0), p44=(0, 0), p45=(0, 0),p46=(0, 0, ), p47=(0, 0), p48=(0, 0), p49=(0, 0), p50=(0, 0), 399 | p51=(0, 0, ), p52=(0, 0), p53=(0, 0), p54=(0, 0), p55=(0, 0),p56=(0, 0, ), p57=(0, 0), p58=(0, 0), p59=(0, 0), p60=(0, 0), 400 | p61=(0, 0, ), p62=(0, 0), p63=(0, 0), p64=(0, 0), p65=(0, 0),p66=(0, 0, ), p67=(0, 0), p68=(0, 0)): 401 | self.image = cv2.imread(path) 402 | self.p1 = p1 403 | self.p2 = p2 404 | self.p3 = p3 405 | self.p4 = p4 406 | self.p5 = p5 407 | self.p6 = p6 408 | self.p7 = p7 409 | self.p8 = p8 410 | self.p9 = p9 411 | self.p10 = p10 412 | self.p11 = p11 413 | self.p12 = p12 414 | self.p13 = p13 415 | self.p14 = p14 416 | self.p15 = p15 417 | self.p16 = p16 418 | self.p17 = p17 419 | self.p18 = p18 420 | self.p19 = p19 421 | self.p20 = p20 422 | self.p21 = p21 423 | self.p22 = p22 424 | self.p23 = p23 425 | self.p24 = p24 426 | self.p25 = p25 427 | self.p26 = p26 428 | self.p27 = p27 429 | self.p28 = p28 430 | self.p29 = p29 431 | self.p30 = p30 432 | self.p31 = p31 433 | self.p32 = p32 434 | self.p33 = p33 435 | self.p34 = p34 436 | self.p35 = p35 437 | self.p36 = p36 438 | self.p37 = p37 439 | self.p38 = p38 440 | self.p39 = p39 441 | self.p40 = p40 442 | self.p41 = p41 443 | self.p42 = p42 444 | self.p43 = p43 445 | self.p44 = p44 446 | self.p45 = p45 447 | self.p46 = p46 448 | self.p47 = p47 449 | self.p48 = p48 450 | self.p49 = p49 451 | self.p50 = p50 452 | self.p51 = p51 453 | self.p52 = p52 454 | self.p53 = p53 455 | self.p54 = p54 456 | self.p55 = p55 457 | self.p56 = p56 458 | self.p57 = p57 459 | self.p58 = p58 460 | self.p59 = p59 461 | self.p60 = p60 462 | self.p61 = p61 463 | self.p62 = p62 464 | self.p63 = p63 465 | self.p64 = p64 466 | self.p65 = p65 467 | self.p66 = p66 468 | self.p67 = p67 469 | self.p68 = p68 470 | 471 | self.name = os.path.split(path)[-1] 472 | self.sx = 1. 473 | self.sy = 1. 474 | self.offsetX = 0. 475 | self.offsetY = 0. 476 | 477 | def __repr__(self): 478 | return '{} le:{},{} re:{},{} nose:{},{}, lm:{},{} rm:{},{}'.format( 479 | self.name, 480 | self.p1[0], self.p1[1], 481 | self.p2[0], self.p2[1], 482 | self.p3[0], self.p3[1], 483 | self.p4[0], self.p4[1], 484 | self.p5[0], self.p5[1], 485 | self.p6[0], self.p6[1], 486 | self.p7[0], self.p7[1], 487 | self.p8[0], self.p8[1], 488 | self.p9[0], self.p9[1], 489 | self.p10[0], self.p10[1], 490 | self.p11[0], self.p11[1], 491 | self.p12[0], self.p12[1], 492 | self.p13[0], self.p13[1], 493 | self.p14[0], self.p14[1], 494 | self.p15[0], self.p15[1], 495 | self.p16[0], self.p16[1], 496 | self.p17[0], self.p17[1], 497 | self.p18[0], self.p18[1], 498 | self.p19[0], self.p19[1], 499 | self.p20[0], self.p20[1], 500 | self.p21[0], self.p21[1], 501 | self.p22[0], self.p22[1], 502 | self.p23[0], self.p23[1], 503 | self.p24[0], self.p24[1], 504 | self.p25[0], self.p25[1], 505 | self.p26[0], self.p26[1], 506 | self.p27[0], self.p27[1], 507 | self.p28[0], self.p28[1], 508 | self.p29[0], self.p29[1], 509 | self.p30[0], self.p20[1], 510 | self.p31[0], self.p31[1], 511 | self.p32[0], self.p32[1], 512 | self.p33[0], self.p33[1], 513 | self.p34[0], self.p34[1], 514 | self.p35[0], self.p35[1], 515 | self.p36[0], self.p36[1], 516 | self.p37[0], self.p37[1], 517 | self.p38[0], self.p38[1], 518 | self.p39[0], self.p39[1], 519 | self.p40[0], self.p40[1], 520 | self.p51[0], self.p51[1], 521 | self.p52[0], self.p52[1], 522 | self.p53[0], self.p53[1], 523 | self.p54[0], self.p54[1], 524 | self.p55[0], self.p55[1], 525 | self.p56[0], self.p56[1], 526 | self.p57[0], self.p57[1], 527 | self.p58[0], self.p58[1], 528 | self.p59[0], self.p59[1], 529 | self.p60[0], self.p60[1], 530 | self.p61[0], self.p61[1], 531 | self.p62[0], self.p62[1], 532 | self.p63[0], self.p63[1], 533 | self.p64[0], self.p64[1], 534 | self.p65[0], self.p65[1], 535 | self.p66[0], self.p66[1], 536 | self.p67[0], self.p67[1], 537 | self.p68[0], self.p68[1] 538 | ) 539 | 540 | def setLandmarks(self,landMarks): 541 | """ 542 | @landMarks : np.array 543 | set the landmarks from array 544 | """ 545 | self.p1 = landMarks[0:2] 546 | self.p2 = landMarks[2:4] 547 | self.p3 = landMarks[4:6] 548 | self.p4 = landMarks[6:8] 549 | self.p5 = landMarks[8:10] 550 | self.p6 = landMarks[10:12] 551 | self.p7 = landMarks[12:14] 552 | self.p8 = landMarks[14:16] 553 | self.p9 = landMarks[16:18] 554 | self.p10 = landMarks[18:20] 555 | self.p11 = landMarks[20:22] 556 | self.p12 = landMarks[22:24] 557 | self.p13 = landMarks[24:26] 558 | self.p14 = landMarks[26:28] 559 | self.p15 = landMarks[28:30] 560 | self.p16 = landMarks[30:32] 561 | self.p17 = landMarks[32:34] 562 | self.p18 = landMarks[34:36] 563 | self.p19 = landMarks[36:38] 564 | self.p20 = landMarks[38:40] 565 | self.p21 = landMarks[40:42] 566 | self.p22 = landMarks[42:44] 567 | self.p23 = landMarks[44:46] 568 | self.p24 = landMarks[46:48] 569 | self.p25 = landMarks[48:50] 570 | self.p26 = landMarks[50:52] 571 | self.p27 = landMarks[52:54] 572 | self.p28 = landMarks[54:56] 573 | self.p29 = landMarks[56:58] 574 | self.p30 = landMarks[58:60] 575 | self.p31 = landMarks[60:62] 576 | self.p32 = landMarks[62:64] 577 | self.p33 = landMarks[64:66] 578 | self.p34 = landMarks[66:68] 579 | self.p35 = landMarks[68:70] 580 | self.p36 = landMarks[70:72] 581 | self.p37 = landMarks[72:74] 582 | self.p38 = landMarks[74:76] 583 | self.p39 = landMarks[76:78] 584 | self.p40 = landMarks[78:80] 585 | self.p41 = landMarks[80:82] 586 | self.p42 = landMarks[82:84] 587 | self.p43 = landMarks[84:86] 588 | self.p44 = landMarks[86:88] 589 | self.p45 = landMarks[88:90] 590 | self.p46 = landMarks[90:92] 591 | self.p47 = landMarks[92:94] 592 | self.p48 = landMarks[94:96] 593 | self.p49 = landMarks[96:98] 594 | self.p50 = landMarks[98:100] 595 | self.p51 = landMarks[100:102] 596 | self.p52 = landMarks[102:104] 597 | self.p53 = landMarks[104:106] 598 | self.p54 = landMarks[106:108] 599 | self.p55 = landMarks[108:110] 600 | self.p56 = landMarks[110:112] 601 | self.p57 = landMarks[112:114] 602 | self.p58 = landMarks[114:116] 603 | self.p59 = landMarks[116:118] 604 | self.p60 = landMarks[118:120] 605 | self.p61 = landMarks[120:122] 606 | self.p62 = landMarks[122:124] 607 | self.p63 = landMarks[124:126] 608 | self.p64 = landMarks[126:128] 609 | self.p65 = landMarks[128:130] 610 | self.p66 = landMarks[130:132] 611 | self.p67 = landMarks[132:134] 612 | self.p68 = landMarks[134:136] 613 | def landmarks(self): 614 | # return numpy float array with ordered values 615 | stright = [ 616 | self.p1[0],self.p1[1], 617 | self.p2[0],self.p2[1], 618 | self.p3[0],self.p3[1], 619 | self.p4[0],self.p4[1], 620 | self.p5[0],self.p5[1], 621 | self.p6[0],self.p6[1], 622 | self.p7[0],self.p7[1], 623 | self.p8[0],self.p8[1], 624 | self.p9[0],self.p9[1], 625 | self.p10[0],self.p10[1], 626 | self.p11[0],self.p11[1], 627 | self.p12[0],self.p12[1], 628 | self.p13[0],self.p13[1], 629 | self.p14[0],self.p14[1], 630 | self.p15[0],self.p15[1], 631 | self.p16[0],self.p16[1], 632 | self.p17[0],self.p17[1], 633 | self.p18[0],self.p18[1], 634 | self.p19[0],self.p19[1], 635 | self.p20[0],self.p20[1], 636 | self.p21[0],self.p21[1], 637 | self.p22[0],self.p22[1], 638 | self.p23[0],self.p23[1], 639 | self.p24[0],self.p24[1], 640 | self.p25[0],self.p25[1], 641 | self.p26[0],self.p26[1], 642 | self.p27[0],self.p27[1], 643 | self.p28[0],self.p28[1], 644 | self.p29[0],self.p29[1], 645 | self.p30[0],self.p30[1], 646 | self.p31[0],self.p31[1], 647 | self.p32[0],self.p32[1], 648 | self.p33[0],self.p33[1], 649 | self.p34[0],self.p34[1], 650 | self.p35[0],self.p35[1], 651 | self.p36[0],self.p36[1], 652 | self.p37[0],self.p37[1], 653 | self.p38[0],self.p38[1], 654 | self.p39[0],self.p39[1], 655 | self.p40[0],self.p40[1], 656 | self.p41[0],self.p41[1], 657 | self.p42[0],self.p42[1], 658 | self.p43[0],self.p43[1], 659 | self.p44[0],self.p44[1], 660 | self.p45[0],self.p45[1], 661 | self.p46[0],self.p46[1], 662 | self.p47[0],self.p47[1], 663 | self.p48[0],self.p48[1], 664 | self.p49[0],self.p49[1], 665 | self.p50[0],self.p50[1], 666 | self.p51[0],self.p51[1], 667 | self.p52[0],self.p52[1], 668 | self.p53[0],self.p53[1], 669 | self.p54[0],self.p54[1], 670 | self.p55[0],self.p55[1], 671 | self.p56[0],self.p56[1], 672 | self.p57[0],self.p57[1], 673 | self.p58[0],self.p58[1], 674 | self.p59[0],self.p59[1], 675 | self.p60[0],self.p60[1], 676 | self.p61[0],self.p61[1], 677 | self.p62[0],self.p62[1], 678 | self.p63[0],self.p63[1], 679 | self.p64[0],self.p64[1], 680 | self.p65[0],self.p65[1], 681 | self.p66[0],self.p66[1], 682 | self.p67[0],self.p67[1], 683 | self.p68[0],self.p68[1]] 684 | 685 | return np.array(stright, dtype='f4') 686 | 687 | def landmarksScaledMinus05_plus05(self): 688 | # return numpy float array with ordered values 689 | #return self.landmarks().astype('f4')/40. - 0.5 690 | return self.landmarks().astype('f4')/60. 691 | 692 | def scale(self, sx, sy): 693 | self.sx *= sx 694 | self.sy *= sy 695 | 696 | self.p1 = (self.p1[0]*sx, self.p1[1]*sy) 697 | self.p2 = (self.p2[0]*sx, self.p2[1]*sy) 698 | self.p3 = (self.p3[0]*sx, self.p3[1]*sy) 699 | self.p4 = (self.p4[0]*sx, self.p4[1]*sy) 700 | self.p5 = (self.p5[0]*sx, self.p5[1]*sy) 701 | self.p6 = (self.p6[0]*sx, self.p6[1]*sy) 702 | self.p7 = (self.p7[0]*sx, self.p7[1]*sy) 703 | self.p8 = (self.p8[0]*sx, self.p8[1]*sy) 704 | self.p9 = (self.p9[0]*sx, self.p9[1]*sy) 705 | self.p10 = (self.p10[0]*sx, self.p10[1]*sy) 706 | self.p11 = (self.p11[0]*sx, self.p11[1]*sy) 707 | self.p12 = (self.p12[0]*sx, self.p12[1]*sy) 708 | self.p13 = (self.p13[0]*sx, self.p13[1]*sy) 709 | self.p14 = (self.p14[0]*sx, self.p14[1]*sy) 710 | self.p15 = (self.p15[0]*sx, self.p15[1]*sy) 711 | self.p16 = (self.p16[0]*sx, self.p16[1]*sy) 712 | self.p17 = (self.p17[0]*sx, self.p17[1]*sy) 713 | self.p18 = (self.p18[0]*sx, self.p18[1]*sy) 714 | self.p19 = (self.p19[0]*sx, self.p19[1]*sy) 715 | self.p20 = (self.p20[0]*sx, self.p20[1]*sy) 716 | self.p21 = (self.p21[0]*sx, self.p21[1]*sy) 717 | self.p22 = (self.p22[0]*sx, self.p22[1]*sy) 718 | self.p23 = (self.p23[0]*sx, self.p23[1]*sy) 719 | self.p24 = (self.p24[0]*sx, self.p24[1]*sy) 720 | self.p25 = (self.p25[0]*sx, self.p25[1]*sy) 721 | self.p26 = (self.p26[0]*sx, self.p26[1]*sy) 722 | self.p27 = (self.p27[0]*sx, self.p27[1]*sy) 723 | self.p28 = (self.p28[0]*sx, self.p28[1]*sy) 724 | self.p29 = (self.p29[0]*sx, self.p29[1]*sy) 725 | self.p30 = (self.p30[0]*sx, self.p30[1]*sy) 726 | self.p31 = (self.p31[0]*sx, self.p31[1]*sy) 727 | self.p32 = (self.p32[0]*sx, self.p32[1]*sy) 728 | self.p33 = (self.p33[0]*sx, self.p33[1]*sy) 729 | self.p34 = (self.p34[0]*sx, self.p34[1]*sy) 730 | self.p35 = (self.p35[0]*sx, self.p35[1]*sy) 731 | self.p36 = (self.p36[0]*sx, self.p36[1]*sy) 732 | self.p37 = (self.p37[0]*sx, self.p37[1]*sy) 733 | self.p38 = (self.p38[0]*sx, self.p38[1]*sy) 734 | self.p39 = (self.p39[0]*sx, self.p39[1]*sy) 735 | self.p40 = (self.p40[0]*sx, self.p40[1]*sy) 736 | self.p41 = (self.p41[0]*sx, self.p41[1]*sy) 737 | self.p42 = (self.p42[0]*sx, self.p42[1]*sy) 738 | self.p43 = (self.p43[0]*sx, self.p43[1]*sy) 739 | self.p44 = (self.p44[0]*sx, self.p44[1]*sy) 740 | self.p45 = (self.p45[0]*sx, self.p45[1]*sy) 741 | self.p46 = (self.p46[0]*sx, self.p46[1]*sy) 742 | self.p47 = (self.p47[0]*sx, self.p47[1]*sy) 743 | self.p48 = (self.p48[0]*sx, self.p48[1]*sy) 744 | self.p49 = (self.p49[0]*sx, self.p49[1]*sy) 745 | self.p50 = (self.p50[0]*sx, self.p50[1]*sy) 746 | self.p51 = (self.p51[0]*sx, self.p51[1]*sy) 747 | self.p52 = (self.p52[0]*sx, self.p52[1]*sy) 748 | self.p53 = (self.p53[0]*sx, self.p53[1]*sy) 749 | self.p54 = (self.p54[0]*sx, self.p54[1]*sy) 750 | self.p55 = (self.p55[0]*sx, self.p55[1]*sy) 751 | self.p56 = (self.p56[0]*sx, self.p56[1]*sy) 752 | self.p57 = (self.p57[0]*sx, self.p57[1]*sy) 753 | self.p58 = (self.p58[0]*sx, self.p58[1]*sy) 754 | self.p59 = (self.p59[0]*sx, self.p59[1]*sy) 755 | self.p60 = (self.p60[0]*sx, self.p60[1]*sy) 756 | self.p61 = (self.p61[0]*sx, self.p61[1]*sy) 757 | self.p62 = (self.p62[0]*sx, self.p62[1]*sy) 758 | self.p63 = (self.p63[0]*sx, self.p63[1]*sy) 759 | self.p64 = (self.p64[0]*sx, self.p64[1]*sy) 760 | self.p65 = (self.p65[0]*sx, self.p65[1]*sy) 761 | self.p66 = (self.p66[0]*sx, self.p66[1]*sy) 762 | self.p67 = (self.p67[0]*sx, self.p67[1]*sy) 763 | self.p68 = (self.p68[0]*sx, self.p68[1]*sy) 764 | 765 | 766 | if hasattr(self, 'prediction'): 767 | self.prediction = self.prediction.reshape(-1, 2)*[sx, sy] 768 | 769 | return self 770 | 771 | def offsetCropped(self, offset=(0., 0.)): 772 | """ given the cropped values - offset the positions by offset 773 | """ 774 | self.offsetX -= offset[0] 775 | self.offsetY -= offset[1] 776 | 777 | if hasattr(self, 'prediction'): 778 | self.prediction = self.prediction.reshape(-1,2)-offset 779 | 780 | 781 | self.p1 = (self.p1[0]-offset[0], self.p1[1]-offset[1]) 782 | self.p2 = (self.p2[0]-offset[0], self.p2[1]-offset[1]) 783 | self.p3 = (self.p3[0]-offset[0], self.p3[1]-offset[1]) 784 | self.p4 = (self.p4[0]-offset[0], self.p4[1]-offset[1]) 785 | self.p5 = (self.p5[0]-offset[0], self.p5[1]-offset[1]) 786 | self.p6 = (self.p6[0]-offset[0], self.p6[1]-offset[1]) 787 | self.p7 = (self.p7[0]-offset[0], self.p7[1]-offset[1]) 788 | self.p8 = (self.p8[0]-offset[0], self.p8[1]-offset[1]) 789 | self.p9 = (self.p9[0]-offset[0], self.p9[1]-offset[1]) 790 | self.p10 = (self.p10[0]-offset[0], self.p10[1]-offset[1]) 791 | self.p11 = (self.p11[0]-offset[0], self.p11[1]-offset[1]) 792 | self.p12 = (self.p12[0]-offset[0], self.p12[1]-offset[1]) 793 | self.p13 = (self.p13[0]-offset[0], self.p13[1]-offset[1]) 794 | self.p14 = (self.p14[0]-offset[0], self.p14[1]-offset[1]) 795 | self.p15 = (self.p15[0]-offset[0], self.p15[1]-offset[1]) 796 | self.p16 = (self.p16[0]-offset[0], self.p16[1]-offset[1]) 797 | self.p17 = (self.p17[0]-offset[0], self.p17[1]-offset[1]) 798 | self.p18 = (self.p18[0]-offset[0], self.p18[1]-offset[1]) 799 | self.p19 = (self.p19[0]-offset[0], self.p19[1]-offset[1]) 800 | self.p20 = (self.p20[0]-offset[0], self.p20[1]-offset[1]) 801 | self.p21 = (self.p21[0]-offset[0], self.p21[1]-offset[1]) 802 | self.p22 = (self.p22[0]-offset[0], self.p22[1]-offset[1]) 803 | self.p23 = (self.p23[0]-offset[0], self.p23[1]-offset[1]) 804 | self.p24 = (self.p24[0]-offset[0], self.p24[1]-offset[1]) 805 | self.p25 = (self.p25[0]-offset[0], self.p25[1]-offset[1]) 806 | self.p26 = (self.p26[0]-offset[0], self.p26[1]-offset[1]) 807 | self.p27 = (self.p27[0]-offset[0], self.p27[1]-offset[1]) 808 | self.p28 = (self.p28[0]-offset[0], self.p28[1]-offset[1]) 809 | self.p29 = (self.p29[0]-offset[0], self.p29[1]-offset[1]) 810 | self.p30 = (self.p30[0]-offset[0], self.p30[1]-offset[1]) 811 | self.p31 = (self.p31[0]-offset[0], self.p31[1]-offset[1]) 812 | self.p32 = (self.p32[0]-offset[0], self.p32[1]-offset[1]) 813 | self.p33 = (self.p33[0]-offset[0], self.p33[1]-offset[1]) 814 | self.p34 = (self.p34[0]-offset[0], self.p34[1]-offset[1]) 815 | self.p35 = (self.p35[0]-offset[0], self.p35[1]-offset[1]) 816 | self.p36 = (self.p36[0]-offset[0], self.p36[1]-offset[1]) 817 | self.p37 = (self.p37[0]-offset[0], self.p37[1]-offset[1]) 818 | self.p38 = (self.p38[0]-offset[0], self.p38[1]-offset[1]) 819 | self.p39 = (self.p39[0]-offset[0], self.p39[1]-offset[1]) 820 | self.p40 = (self.p40[0]-offset[0], self.p40[1]-offset[1]) 821 | self.p41 = (self.p41[0]-offset[0], self.p41[1]-offset[1]) 822 | self.p42 = (self.p42[0]-offset[0], self.p42[1]-offset[1]) 823 | self.p43 = (self.p43[0]-offset[0], self.p43[1]-offset[1]) 824 | self.p44 = (self.p44[0]-offset[0], self.p44[1]-offset[1]) 825 | self.p45 = (self.p45[0]-offset[0], self.p45[1]-offset[1]) 826 | self.p46 = (self.p46[0]-offset[0], self.p46[1]-offset[1]) 827 | self.p47 = (self.p47[0]-offset[0], self.p47[1]-offset[1]) 828 | self.p48 = (self.p48[0]-offset[0], self.p48[1]-offset[1]) 829 | self.p49 = (self.p49[0]-offset[0], self.p49[1]-offset[1]) 830 | self.p50 = (self.p50[0]-offset[0], self.p50[1]-offset[1]) 831 | self.p51 = (self.p51[0]-offset[0], self.p51[1]-offset[1]) 832 | self.p52 = (self.p52[0]-offset[0], self.p52[1]-offset[1]) 833 | self.p53 = (self.p53[0]-offset[0], self.p53[1]-offset[1]) 834 | self.p54 = (self.p54[0]-offset[0], self.p54[1]-offset[1]) 835 | self.p55 = (self.p55[0]-offset[0], self.p55[1]-offset[1]) 836 | self.p56 = (self.p56[0]-offset[0], self.p56[1]-offset[1]) 837 | self.p57 = (self.p57[0]-offset[0], self.p57[1]-offset[1]) 838 | self.p58 = (self.p58[0]-offset[0], self.p58[1]-offset[1]) 839 | self.p59 = (self.p59[0]-offset[0], self.p59[1]-offset[1]) 840 | self.p60 = (self.p60[0]-offset[0], self.p60[1]-offset[1]) 841 | self.p61 = (self.p61[0]-offset[0], self.p61[1]-offset[1]) 842 | self.p62 = (self.p62[0]-offset[0], self.p62[1]-offset[1]) 843 | self.p63 = (self.p63[0]-offset[0], self.p63[1]-offset[1]) 844 | self.p64 = (self.p64[0]-offset[0], self.p64[1]-offset[1]) 845 | self.p65 = (self.p65[0]-offset[0], self.p65[1]-offset[1]) 846 | self.p66 = (self.p66[0]-offset[0], self.p66[1]-offset[1]) 847 | self.p67 = (self.p67[0]-offset[0], self.p67[1]-offset[1]) 848 | self.p68 = (self.p68[0]-offset[0], self.p68[1]-offset[1]) 849 | 850 | return self 851 | 852 | def inverseScaleAndOffset(self, landmarks): 853 | """ computes the inverse scale and offset of input data according to the inverse scale factor and inverse offset factor 854 | """ 855 | from numpy import array; _A = array ; # Shothand 856 | 857 | ret = _A(landmarks.reshape(-1,2)) *_A([1./self.sx, 1./self.sy]) 858 | ret += _A([-self.offsetX, -self.offsetY]) 859 | return ret 860 | 861 | @staticmethod 862 | def DataRowFromNameBoxInterlaved(row, root=''): # lfw_5590 + net_7876 (interleaved) 863 | ''' 864 | name , bounding box(w,h), left eye (x,y) ,right eye (x,y)..nose..left mouth,..right mouth 865 | ''' 866 | d = DataRow() 867 | #print 'row: ', row 868 | d.path = os.path.join(root, row[0]).replace("\\", "/") 869 | print d.path 870 | d.name = os.path.split(d.path)[-1] 871 | 872 | d.image = cv2.imread(d.path) 873 | 874 | d.p1 = (float(row[1]), float(row[2])) 875 | d.p2 = (float(row[3]), float(row[4])) 876 | d.p3 = (float(row[5]), float(row[6])) 877 | d.p4 = (float(row[7]), float(row[8])) 878 | d.p5 = (float(row[9]), float(row[10])) 879 | d.p6 = (float(row[11]), float(row[12])) 880 | d.p7 = (float(row[13]), float(row[14])) 881 | d.p8 = (float(row[15]), float(row[16])) 882 | d.p9 = (float(row[17]), float(row[18])) 883 | d.p10 = (float(row[19]), float(row[20])) 884 | d.p11 = (float(row[21]), float(row[22])) 885 | d.p12 = (float(row[23]), float(row[24])) 886 | d.p13 = (float(row[25]), float(row[26])) 887 | d.p14 = (float(row[27]), float(row[28])) 888 | d.p15 = (float(row[29]), float(row[30])) 889 | d.p16 = (float(row[31]), float(row[32])) 890 | d.p17 = (float(row[33]), float(row[34])) 891 | d.p18 = (float(row[35]), float(row[36])) 892 | d.p19 = (float(row[37]), float(row[38])) 893 | d.p20 = (float(row[39]), float(row[40])) 894 | d.p21 = (float(row[41]), float(row[42])) 895 | d.p22 = (float(row[43]), float(row[44])) 896 | d.p23 = (float(row[45]), float(row[46])) 897 | d.p24 = (float(row[47]), float(row[48])) 898 | d.p25 = (float(row[49]), float(row[50])) 899 | d.p26 = (float(row[51]), float(row[52])) 900 | d.p27 = (float(row[53]), float(row[54])) 901 | d.p28 = (float(row[55]), float(row[56])) 902 | d.p29 = (float(row[57]), float(row[58])) 903 | d.p30 = (float(row[59]), float(row[60])) 904 | d.p31 = (float(row[61]), float(row[62])) 905 | d.p32 = (float(row[63]), float(row[64])) 906 | d.p33 = (float(row[65]), float(row[66])) 907 | d.p34 = (float(row[67]), float(row[68])) 908 | d.p35 = (float(row[69]), float(row[70])) 909 | d.p36 = (float(row[71]), float(row[72])) 910 | d.p37 = (float(row[73]), float(row[74])) 911 | d.p38 = (float(row[75]), float(row[76])) 912 | d.p39 = (float(row[77]), float(row[78])) 913 | d.p40 = (float(row[79]), float(row[80])) 914 | d.p41 = (float(row[81]), float(row[82])) 915 | d.p42 = (float(row[83]), float(row[84])) 916 | d.p43 = (float(row[85]), float(row[86])) 917 | d.p44 = (float(row[87]), float(row[88])) 918 | d.p45 = (float(row[89]), float(row[90])) 919 | d.p46 = (float(row[91]), float(row[92])) 920 | d.p47 = (float(row[93]), float(row[94])) 921 | d.p48 = (float(row[95]), float(row[96])) 922 | d.p49 = (float(row[97]), float(row[98])) 923 | d.p50 = (float(row[99]), float(row[100])) 924 | d.p51 = (float(row[101]), float(row[102])) 925 | d.p52 = (float(row[103]), float(row[104])) 926 | d.p53 = (float(row[105]), float(row[106])) 927 | d.p54 = (float(row[107]), float(row[108])) 928 | d.p55 = (float(row[109]), float(row[110])) 929 | d.p56 = (float(row[111]), float(row[112])) 930 | d.p57 = (float(row[113]), float(row[114])) 931 | d.p58 = (float(row[115]), float(row[116])) 932 | d.p59 = (float(row[117]), float(row[118])) 933 | d.p60 = (float(row[119]), float(row[120])) 934 | d.p61 = (float(row[121]), float(row[122])) 935 | d.p62 = (float(row[123]), float(row[124])) 936 | d.p63 = (float(row[125]), float(row[126])) 937 | d.p64 = (float(row[127]), float(row[128])) 938 | d.p65 = (float(row[129]), float(row[130])) 939 | d.p66 = (float(row[131]), float(row[132])) 940 | d.p67 = (float(row[133]), float(row[134])) 941 | d.p68 = (float(row[135]), float(row[136])) 942 | 943 | return d 944 | 945 | @staticmethod 946 | def DataRowFromMTFL(row, root=''): 947 | ''' 948 | --x1...x5,y1...y5: the locations for left eye, right eye, nose, left mouth corner, right mouth corner. 949 | ''' 950 | d = DataRow() 951 | if len(row[0]) <= 1: 952 | # bug in the files, it has spaces seperating them, skip it 953 | row=row[1:] 954 | 955 | if len(row)<10: 956 | print 'error parsing ', row 957 | return None 958 | 959 | d.path = os.path.join(root, row[0]).replace("\\", "/") 960 | d.name = os.path.split(d.path)[-1] 961 | d.image = cv2.imread(d.path) 962 | 963 | if d.image is None: 964 | print 'Error reading image', d.path 965 | return None 966 | 967 | d.p1 = (float(row[1]), float(row[2])) 968 | d.p2 = (float(row[3]), float(row[4])) 969 | d.p3 = (float(row[5]), float(row[6])) 970 | d.p4 = (float(row[7]), float(row[8])) 971 | d.p5 = (float(row[9]), float(row[10])) 972 | d.p6 = (float(row[11]), float(row[12])) 973 | d.p7 = (float(row[13]), float(row[14])) 974 | d.p8 = (float(row[15]), float(row[16])) 975 | d.p9 = (float(row[17]), float(row[18])) 976 | d.p10 = (float(row[19]), float(row[20])) 977 | d.p11 = (float(row[21]), float(row[22])) 978 | d.p12 = (float(row[23]), float(row[24])) 979 | d.p13 = (float(row[25]), float(row[26])) 980 | d.p14 = (float(row[27]), float(row[28])) 981 | d.p15 = (float(row[29]), float(row[30])) 982 | d.p16 = (float(row[31]), float(row[32])) 983 | d.p17 = (float(row[33]), float(row[34])) 984 | d.p18 = (float(row[35]), float(row[36])) 985 | d.p19 = (float(row[37]), float(row[38])) 986 | d.p20 = (float(row[39]), float(row[40])) 987 | d.p21 = (float(row[41]), float(row[42])) 988 | d.p22 = (float(row[43]), float(row[44])) 989 | d.p23 = (float(row[45]), float(row[46])) 990 | d.p24 = (float(row[47]), float(row[48])) 991 | d.p25 = (float(row[49]), float(row[50])) 992 | d.p26 = (float(row[51]), float(row[52])) 993 | d.p27 = (float(row[53]), float(row[54])) 994 | d.p28 = (float(row[55]), float(row[56])) 995 | d.p29 = (float(row[57]), float(row[58])) 996 | d.p30 = (float(row[59]), float(row[60])) 997 | d.p31 = (float(row[61]), float(row[62])) 998 | d.p32 = (float(row[63]), float(row[64])) 999 | d.p33 = (float(row[65]), float(row[66])) 1000 | d.p34 = (float(row[67]), float(row[68])) 1001 | d.p35 = (float(row[69]), float(row[70])) 1002 | d.p36 = (float(row[71]), float(row[72])) 1003 | d.p37 = (float(row[73]), float(row[74])) 1004 | d.p38 = (float(row[75]), float(row[76])) 1005 | d.p39 = (float(row[77]), float(row[78])) 1006 | d.p40 = (float(row[79]), float(row[80])) 1007 | d.p41 = (float(row[81]), float(row[82])) 1008 | d.p42 = (float(row[83]), float(row[84])) 1009 | d.p43 = (float(row[85]), float(row[86])) 1010 | d.p44 = (float(row[87]), float(row[88])) 1011 | d.p45 = (float(row[89]), float(row[90])) 1012 | d.p46 = (float(row[91]), float(row[92])) 1013 | d.p47 = (float(row[93]), float(row[94])) 1014 | d.p48 = (float(row[95]), float(row[96])) 1015 | d.p49 = (float(row[97]), float(row[98])) 1016 | d.p50 = (float(row[99]), float(row[100])) 1017 | d.p51 = (float(row[101]), float(row[102])) 1018 | d.p52 = (float(row[103]), float(row[104])) 1019 | d.p53 = (float(row[105]), float(row[106])) 1020 | d.p54 = (float(row[107]), float(row[108])) 1021 | d.p55 = (float(row[109]), float(row[110])) 1022 | d.p56 = (float(row[111]), float(row[112])) 1023 | d.p57 = (float(row[113]), float(row[114])) 1024 | d.p58 = (float(row[115]), float(row[116])) 1025 | d.p59 = (float(row[117]), float(row[118])) 1026 | d.p60 = (float(row[119]), float(row[120])) 1027 | d.p61 = (float(row[121]), float(row[122])) 1028 | d.p62 = (float(row[123]), float(row[124])) 1029 | d.p63 = (float(row[125]), float(row[126])) 1030 | d.p64 = (float(row[127]), float(row[128])) 1031 | d.p65 = (float(row[129]), float(row[130])) 1032 | d.p66 = (float(row[131]), float(row[132])) 1033 | d.p67 = (float(row[133]), float(row[134])) 1034 | d.p68 = (float(row[135]), float(row[136])) 1035 | 1036 | 1037 | 1038 | return d 1039 | 1040 | @staticmethod 1041 | def DataRowFromAFW(anno, root=''): # Assume data comming from parsed anno-v7.mat file. 1042 | name = str(anno[0][0]) 1043 | bbox = anno[1][0][0] 1044 | # yaw, pitch, roll = anno[2][0][0][0] 1045 | lm = anno[3][0][0] # 6 landmarks 1046 | 1047 | if np.isnan(lm).any(): 1048 | return None # Fail 1049 | 1050 | d = DataRow() 1051 | d.path = os.path.join(root, name).replace("\\", "/") 1052 | d.name = os.path.split(d.path)[-1] 1053 | d.image = cv2.imread(d.path) 1054 | 1055 | 1056 | d.leftEye = (float(lm[0][0]), float(lm[0][1])) 1057 | d.rightEye = (float(lm[1][0]), float(lm[1][1])) 1058 | d.middle = (float(lm[2][0]), float(lm[2][1])) 1059 | d.leftMouth = (float(lm[3][0]), float(lm[3][1])) 1060 | # skip point 4 middle mouth - We take 0 left eye, 1 right eye, 2 nose, 3 left mouth, 5 right mouth 1061 | d.rightMouth = (float(lm[5][0]), float(lm[5][1])) 1062 | 1063 | 1064 | return d 1065 | 1066 | @staticmethod 1067 | def DataRowFromPrediction(p, path='', image=None): 1068 | d = DataRow(path) 1069 | p = (p+0.5)*60. # scale from -0.5..+0.5 to 0..40 1070 | 1071 | d.p1 = (p[0], p[1]) 1072 | d.p2 = (p[2], p[3]) 1073 | d.p3 = (p[4], p[5]) 1074 | d.p4 = (p[6], p[7]) 1075 | d.p5 = (p[8], p[9]) 1076 | 1077 | return d 1078 | 1079 | def drawLandmarks(self, r=3, color=255, other=None, title=None): 1080 | M = self.image 1081 | if hasattr(self, 'prediction'): 1082 | for x,y in self.prediction.reshape(-1,2): 1083 | cv2.circle(M, (int(x), int(y)), r, (0,200,0), -1) 1084 | 1085 | cv2.circle(M, (int(self.p1[0]), int(self.p1[1])), r, color, -1) 1086 | cv2.circle(M, (int(self.p2[0]), int(self.p2[1])), r, color, -1) 1087 | cv2.circle(M, (int(self.p3[0]), int(self.p3[1])), r, color, -1) 1088 | cv2.circle(M, (int(self.p4[0]), int(self.p4[1])), r, color, -1) 1089 | cv2.circle(M, (int(self.p5[0]), int(self.p5[1])), r, color, -1) 1090 | cv2.circle(M, (int(self.p6[0]), int(self.p6[1])), r, color, -1) 1091 | cv2.circle(M, (int(self.p7[0]), int(self.p7[1])), r, color, -1) 1092 | cv2.circle(M, (int(self.p8[0]), int(self.p8[1])), r, color, -1) 1093 | cv2.circle(M, (int(self.p9[0]), int(self.p9[1])), r, color, -1) 1094 | cv2.circle(M, (int(self.p10[0]), int(self.p10[1])), r, color, -1) 1095 | cv2.circle(M, (int(self.p11[0]), int(self.p11[1])), r, color, -1) 1096 | cv2.circle(M, (int(self.p12[0]), int(self.p12[1])), r, color, -1) 1097 | cv2.circle(M, (int(self.p13[0]), int(self.p13[1])), r, color, -1) 1098 | cv2.circle(M, (int(self.p14[0]), int(self.p14[1])), r, color, -1) 1099 | cv2.circle(M, (int(self.p15[0]), int(self.p15[1])), r, color, -1) 1100 | cv2.circle(M, (int(self.p16[0]), int(self.p16[1])), r, color, -1) 1101 | cv2.circle(M, (int(self.p17[0]), int(self.p17[1])), r, color, -1) 1102 | cv2.circle(M, (int(self.p18[0]), int(self.p18[1])), r, color, -1) 1103 | cv2.circle(M, (int(self.p19[0]), int(self.p19[1])), r, color, -1) 1104 | if hasattr(self, 'fbbox'): 1105 | #cv2.rectangle(M, self.fbbox.top_left(), self.fbbox.bottom_right(), color) 1106 | # cv2.rectangle(M, self.fbbox.left_top(), self.fbbox.right_bottom(), color) 1107 | cv2.rectangle(M, (int(self.fbbox.left), int(self.fbbox.top)), (int(self.fbbox.right), int(self.fbbox.bottom)), color, 2) 1108 | 1109 | 1110 | det_bbox = self.fbbox 1111 | det_box = self.fbbox 1112 | #height = (det_box.bottom - det_box.top)/1.35 1113 | #det_bbox.top = det_box.top + height * 0.1 1114 | #det_bbox.bottom = det_box.bottom - height*0.25 1115 | #weight = (det_box.right - det_box.left)/1.3 1116 | #det_bbox.left = det_box.left + weight*0.15 1117 | #det_bbox.right = det_box.right - weight*0.15 1118 | height = (det_box.bottom - det_box.top)/1.2 1119 | det_bbox.top = det_box.top + height * 0.1 1120 | det_bbox.bottom = det_box.bottom - height*0.1 1121 | weight = (det_box.right - det_box.left)/1.2 1122 | det_bbox.left = det_box.left + weight*0.1 1123 | det_bbox.right = det_box.right - weight*0.1 1124 | 1125 | cv2.rectangle(M, (int(det_bbox.left), int(det_bbox.top)), (int(det_bbox.right), int(det_bbox.bottom)), (0,200,0), 2) 1126 | return M 1127 | 1128 | def show(self, r=3, color=255, other=None, title=None): 1129 | M = self.drawLandmarks(r, color, other, title) 1130 | if title is None: 1131 | title = self.name 1132 | # my debug 1133 | #cv2.imshow(title, M) 1134 | 1135 | return M 1136 | 1137 | def makeInt(self): 1138 | self.p1 = (int(self.p1[0]), int(self.p1[1])) 1139 | self.p2 = (int(self.p2[0]), int(self.p2[1])) 1140 | self.p3 = (int(self.p3[0]), int(self.p3[1])) 1141 | self.p4 = (int(self.p4[0]), int(self.p4[1])) 1142 | self.p5 = (int(self.p5[0]), int(self.p5[1])) 1143 | self.p6 = (int(self.p6[0]), int(self.p6[1])) 1144 | self.p7 = (int(self.p7[0]), int(self.p7[1])) 1145 | self.p8 = (int(self.p8[0]), int(self.p8[1])) 1146 | self.p9 = (int(self.p9[0]), int(self.p9[1])) 1147 | self.p10 = (int(self.p10[0]), int(self.p10[1])) 1148 | self.p11 = (int(self.p11[0]), int(self.p11[1])) 1149 | self.p12 = (int(self.p12[0]), int(self.p12[1])) 1150 | self.p13 = (int(self.p13[0]), int(self.p13[1])) 1151 | self.p14 = (int(self.p14[0]), int(self.p14[1])) 1152 | self.p15 = (int(self.p15[0]), int(self.p15[1])) 1153 | self.p16 = (int(self.p16[0]), int(self.p16[1])) 1154 | self.p17 = (int(self.p17[0]), int(self.p17[1])) 1155 | self.p18 = (int(self.p18[0]), int(self.p18[1])) 1156 | self.p19 = (int(self.p19[0]), int(self.p19[1])) 1157 | self.p20 = (int(self.p20[0]), int(self.p20[1])) 1158 | self.p21 = (int(self.p21[0]), int(self.p21[1])) 1159 | self.p22 = (int(self.p22[0]), int(self.p22[1])) 1160 | self.p23 = (int(self.p23[0]), int(self.p23[1])) 1161 | self.p24 = (int(self.p24[0]), int(self.p24[1])) 1162 | self.p25 = (int(self.p25[0]), int(self.p25[1])) 1163 | self.p26 = (int(self.p26[0]), int(self.p26[1])) 1164 | self.p27 = (int(self.p27[0]), int(self.p27[1])) 1165 | self.p28 = (int(self.p28[0]), int(self.p28[1])) 1166 | self.p29 = (int(self.p29[0]), int(self.p29[1])) 1167 | self.p30 = (int(self.p30[0]), int(self.p30[1])) 1168 | self.p31 = (int(self.p31[0]), int(self.p31[1])) 1169 | self.p32 = (int(self.p32[0]), int(self.p32[1])) 1170 | self.p33 = (int(self.p33[0]), int(self.p33[1])) 1171 | self.p34 = (int(self.p34[0]), int(self.p34[1])) 1172 | self.p35 = (int(self.p35[0]), int(self.p35[1])) 1173 | self.p36 = (int(self.p36[0]), int(self.p36[1])) 1174 | self.p37 = (int(self.p37[0]), int(self.p37[1])) 1175 | self.p38 = (int(self.p38[0]), int(self.p38[1])) 1176 | self.p39 = (int(self.p39[0]), int(self.p39[1])) 1177 | self.p40 = (int(self.p40[0]), int(self.p40[1])) 1178 | self.p41 = (int(self.p41[0]), int(self.p41[1])) 1179 | self.p42 = (int(self.p42[0]), int(self.p42[1])) 1180 | self.p43 = (int(self.p43[0]), int(self.p43[1])) 1181 | self.p44 = (int(self.p44[0]), int(self.p44[1])) 1182 | self.p45 = (int(self.p45[0]), int(self.p45[1])) 1183 | self.p46 = (int(self.p46[0]), int(self.p46[1])) 1184 | self.p47 = (int(self.p47[0]), int(self.p47[1])) 1185 | self.p48 = (int(self.p48[0]), int(self.p48[1])) 1186 | self.p49 = (int(self.p49[0]), int(self.p49[1])) 1187 | self.p50 = (int(self.p50[0]), int(self.p50[1])) 1188 | self.p51 = (int(self.p51[0]), int(self.p51[1])) 1189 | self.p52 = (int(self.p52[0]), int(self.p52[1])) 1190 | self.p53 = (int(self.p53[0]), int(self.p53[1])) 1191 | self.p54 = (int(self.p54[0]), int(self.p54[1])) 1192 | self.p55 = (int(self.p55[0]), int(self.p55[1])) 1193 | self.p56 = (int(self.p56[0]), int(self.p56[1])) 1194 | self.p57 = (int(self.p57[0]), int(self.p57[1])) 1195 | self.p58 = (int(self.p58[0]), int(self.p58[1])) 1196 | self.p59 = (int(self.p59[0]), int(self.p59[1])) 1197 | self.p60 = (int(self.p60[0]), int(self.p60[1])) 1198 | self.p61 = (int(self.p61[0]), int(self.p61[1])) 1199 | self.p62 = (int(self.p62[0]), int(self.p62[1])) 1200 | self.p63 = (int(self.p63[0]), int(self.p63[1])) 1201 | self.p64 = (int(self.p64[0]), int(self.p64[1])) 1202 | self.p65 = (int(self.p65[0]), int(self.p65[1])) 1203 | self.p66 = (int(self.p66[0]), int(self.p66[1])) 1204 | self.p67 = (int(self.p67[0]), int(self.p67[1])) 1205 | self.p68 = (int(self.p68[0]), int(self.p68[1])) 1206 | 1207 | return self 1208 | 1209 | def copyCroppedByBBox(self,fbbox, siz=np.array([60.,60.])): 1210 | """ 1211 | @ fbbox : BBox 1212 | Returns a copy with cropped, scaled to size 1213 | """ 1214 | fbbox.makeInt() # assume BBox class 1215 | if fbbox.width()<10 or fbbox.height()<10: 1216 | print "Invalid bbox size:",fbbox 1217 | return None 1218 | #print "fbbox: ", fbbox 1219 | faceOnly = self.image[fbbox.top : fbbox.bottom, fbbox.left:fbbox.right, :] 1220 | scaled = DataRow() 1221 | scaled.image = cv2.resize(faceOnly, (int(siz[0]), int(siz[1]))) 1222 | scaled.setLandmarks(self.landmarks()) 1223 | """ @scaled: DataRow """ 1224 | scaled.offsetCropped(fbbox.left_top()) # offset the landmarks 1225 | ry, rx = siz/faceOnly.shape[:2] 1226 | scaled.scale(rx, ry) 1227 | 1228 | return scaled 1229 | 1230 | def copyMirrored(self): 1231 | ''' 1232 | Return a copy with mirrored data (and mirrored landmarks). 1233 | ''' 1234 | import numpy 1235 | _A=numpy.array 1236 | ret = DataRow() 1237 | ret.image=cv2.flip(self.image.copy(),1) 1238 | # Now we mirror the landmarks and swap left and right 1239 | width = ret.image.shape[0] 1240 | ret.p1 = _A([width-self.p17[0], self.p17[1]]) # Toggle left\right eyes position and mirror x axis only 1241 | ret.p2 = _A([width-self.p16[0], self.p16[1]]) 1242 | ret.p3 = _A([width-self.p15[0], self.p15[1]]) 1243 | ret.p4 = _A([width-self.p14[0], self.p14[1]]) # Toggle mouth positions and mirror x axis only 1244 | ret.p5 = _A([width-self.p13[0], self.p13[1]]) 1245 | ret.p6 = _A([width-self.p12[0], self.p12[1]]) 1246 | ret.p7 = _A([width-self.p11[0], self.p11[1]]) 1247 | ret.p8 = _A([width-self.p10[0], self.p10[1]]) 1248 | ret.p9 = _A([width-self.p9[0], self.p9[1]]) 1249 | ret.p10 = _A([width-self.p8[0], self.p8[1]]) 1250 | ret.p11 = _A([width-self.p7[0], self.p7[1]]) # Toggle left\right eyes position and mirror x axis only 1251 | ret.p12 = _A([width-self.p6[0], self.p6[1]]) 1252 | ret.p13 = _A([width-self.p5[0], self.p5[1]]) 1253 | ret.p14 = _A([width-self.p4[0], self.p4[1]]) # Toggle mouth positions and mirror x axis only 1254 | ret.p15 = _A([width-self.p3[0], self.p3[1]]) 1255 | ret.p16 = _A([width-self.p2[0], self.p2[1]]) 1256 | ret.p17 = _A([width-self.p1[0], self.p1[1]]) 1257 | ret.p18 = _A([width-self.p27[0], self.p27[1]]) 1258 | ret.p19 = _A([width-self.p26[0], self.p26[1]]) 1259 | ret.p20 = _A([width-self.p25[0], self.p25[1]]) 1260 | ret.p21 = _A([width-self.p24[0], self.p24[1]]) 1261 | ret.p22 = _A([width-self.p23[0], self.p23[1]]) 1262 | ret.p23 = _A([width-self.p22[0], self.p22[1]]) 1263 | ret.p24 = _A([width-self.p21[0], self.p21[1]]) 1264 | ret.p25 = _A([width-self.p20[0], self.p20[1]]) 1265 | ret.p26 = _A([width-self.p19[0], self.p19[1]]) 1266 | ret.p27 = _A([width-self.p18[0], self.p18[1]]) 1267 | ret.p28 = _A([width-self.p28[0], self.p28[1]]) 1268 | ret.p29 = _A([width-self.p29[0], self.p29[1]]) 1269 | ret.p30 = _A([width-self.p30[0], self.p30[1]]) 1270 | ret.p31 = _A([width-self.p31[0], self.p31[1]]) # Toggle left\right eyes position and mirror x axis only 1271 | ret.p32 = _A([width-self.p36[0], self.p36[1]]) 1272 | ret.p33 = _A([width-self.p35[0], self.p35[1]]) 1273 | ret.p34 = _A([width-self.p34[0], self.p34[1]]) # Toggle mouth positions and mirror x axis only 1274 | ret.p35 = _A([width-self.p33[0], self.p33[1]]) 1275 | ret.p36 = _A([width-self.p32[0], self.p32[1]]) 1276 | ret.p37 = _A([width-self.p46[0], self.p46[1]]) 1277 | ret.p38 = _A([width-self.p45[0], self.p45[1]]) 1278 | ret.p39 = _A([width-self.p44[0], self.p44[1]]) 1279 | ret.p40 = _A([width-self.p43[0], self.p43[1]]) 1280 | ret.p41 = _A([width-self.p48[0], self.p48[1]]) # Toggle left\right eyes position and mirror x axis only 1281 | ret.p42 = _A([width-self.p47[0], self.p47[1]]) 1282 | ret.p43 = _A([width-self.p40[0], self.p40[1]]) 1283 | ret.p44 = _A([width-self.p39[0], self.p39[1]]) # Toggle mouth positions and mirror x axis only 1284 | ret.p45 = _A([width-self.p38[0], self.p38[1]]) 1285 | ret.p46 = _A([width-self.p37[0], self.p37[1]]) 1286 | ret.p47 = _A([width-self.p42[0], self.p42[1]]) 1287 | ret.p48 = _A([width-self.p41[0], self.p41[1]]) 1288 | ret.p49 = _A([width-self.p55[0], self.p55[1]]) 1289 | ret.p50 = _A([width-self.p54[0], self.p54[1]]) 1290 | ret.p51 = _A([width-self.p53[0], self.p53[1]]) # Toggle left\right eyes position and mirror x axis only 1291 | ret.p52 = _A([width-self.p52[0], self.p52[1]]) 1292 | ret.p53 = _A([width-self.p51[0], self.p51[1]]) 1293 | ret.p54 = _A([width-self.p50[0], self.p50[1]]) # Toggle mouth positions and mirror x axis only 1294 | ret.p55 = _A([width-self.p49[0], self.p49[1]]) 1295 | ret.p56 = _A([width-self.p60[0], self.p60[1]]) 1296 | ret.p57 = _A([width-self.p59[0], self.p59[1]]) 1297 | ret.p58 = _A([width-self.p58[0], self.p58[1]]) 1298 | ret.p59 = _A([width-self.p57[0], self.p57[1]]) 1299 | ret.p60 = _A([width-self.p56[0], self.p56[1]]) 1300 | ret.p61 = _A([width-self.p65[0], self.p65[1]]) # Toggle left\right eyes position and mirror x axis only 1301 | ret.p62 = _A([width-self.p64[0], self.p64[1]]) 1302 | ret.p63 = _A([width-self.p63[0], self.p63[1]]) 1303 | ret.p64 = _A([width-self.p62[0], self.p62[1]]) # Toggle mouth positions and mirror x axis only 1304 | ret.p65 = _A([width-self.p61[0], self.p61[1]]) 1305 | ret.p66 = _A([width-self.p68[0], self.p68[1]]) 1306 | ret.p67 = _A([width-self.p67[0], self.p67[1]]) 1307 | ret.p68 = _A([width-self.p66[0], self.p66[1]]) 1308 | 1309 | return ret 1310 | 1311 | @staticmethod 1312 | def dummyDataRow(): 1313 | ''' Returns a dummy dataRow object to play with 1314 | ''' 1315 | return DataRow('/Users/ishay/Dev/VanilaCNN/data/train/lfw_5590/Abbas_Kiarostami_0001.jpg', 1316 | leftEye=(106.75, 108.25), 1317 | rightEye=(143.75,108.75) , 1318 | middle = (131.25, 127.25), 1319 | leftMouth = (106.25, 155.25), 1320 | rightMouth =(142.75,155.25) 1321 | ) 1322 | 1323 | 1324 | 1325 | class Predictor: 1326 | ROOT = getGitRepFolder() 1327 | 1328 | def preprocess(self, resized, landmarks): 1329 | #ret = resized.astype('f4') 1330 | #ret -= self.mean 1331 | #ret /= (1.e-6+ self.std) 1332 | #return ret, (landmarks/40.)-0.5 1333 | grayImg = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY).astype('f4') 1334 | m, s = cv2.meanStdDev(grayImg) 1335 | grayImg = (grayImg-m)/(1.e-6 + s) 1336 | return grayImg, landmarks/60. 1337 | 1338 | def predict(self, resized): 1339 | """ 1340 | @resized: image 40,40 already pre processed 1341 | """ 1342 | #self.net.blobs['data'].data[...] = cv2.split(resized) 1343 | self.net.blobs['data'].data[...] = resized.reshape(1,1,60,60) 1344 | prediction = self.net.forward()['Dense3'][0] 1345 | return prediction 1346 | 1347 | def __init__(self, protoTXTPath, weightsPath): 1348 | import caffe 1349 | caffe.set_mode_cpu() 1350 | self.net = caffe.Net(protoTXTPath, weightsPath, caffe.TEST) 1351 | self.mean = cv2.imread(os.path.join(Predictor.ROOT, 'trainMean.png')).astype('float') 1352 | self.mean = cv2.resize(self.mean, (60,60), interpolation=cv2.INTER_CUBIC) 1353 | self.std = cv2.imread(os.path.join(Predictor.ROOT,'trainSTD.png')).astype('float') 1354 | self.std = cv2.resize(self.std, (60,60), interpolation=cv2.INTER_CUBIC) 1355 | 1356 | 1357 | -------------------------------------------------------------------------------- /train/Demo.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | _A = np.array # A shortcut to creating arrays in command line 4 | import os 5 | import cv2 6 | import sys 7 | 8 | sys.path.append(os.path.join('/home/ly/workspace/Vanilla-40', 'python')) # Assume git root directory 9 | 10 | ########################### PATHS TO SET #################### 11 | # Either define CAFFE_ROOT in your enviroment variables or set it here 12 | CAFFE_ROOT = os.environ.get('CAFFE_ROOT','~/caffe/distribute') 13 | sys.path.append(CAFFE_ROOT+'/python') # Add caffe python path so we can import it 14 | import caffe 15 | 16 | #Import helper functions 17 | from DataRow import DataRow, BBox, Predictor 18 | 19 | 20 | PATH_TO_WEIGHTS = os.path.join('/home/ly/workspace/Vanilla-40', 'caffeData', 'snapshots_60_medium_ibug_0.04c_0.04s', 'snap_iter_80000.caffemodel') 21 | #PATH_TO_WEIGHTS = os.path.join('/home/ly/workspace/Vanilla-40', 'caffeData', 'snapshots-40-unit-cvt1-inner', 'snap_iter_670000.caffemodel') 22 | PATH_TO_DEPLOY_TXT = os.path.join('/home/ly/workspace/Vanilla-40', 'ZOO', 'vanilla_deploy_60_medium.prototxt') 23 | predictor = Predictor(protoTXTPath=PATH_TO_DEPLOY_TXT, weightsPath=PATH_TO_WEIGHTS) 24 | 25 | # Make sure dlib python path exists on PYTHONPATH else "pip install dlib" if needed. 26 | import dlib 27 | detector=dlib.get_frontal_face_detector() # Load dlib's face detector 28 | 29 | 30 | img_path = os.path.join('/home/ly/workspace/Vanilla-40/TestSet','helen_3139620200_1.jpg') 31 | 32 | 33 | img = cv2.imread(img_path) 34 | #dets = detector( np.array(img, dtype = 'uint8' ), 1) 35 | dets = [[1]] 36 | 37 | for det in dets: 38 | 39 | det_box = BBox.BBoxFromLTRB(51, 238, 545, 656) #helen_3139620200_1 40 | 41 | 42 | print "det_box: ",det_box.left, det_box.top, det_box.right, det_box.bottom 43 | 44 | #det_box = det_bbox 45 | #det_box.offset(-0.1*det_box.width(), 0.0*det_box.height()) 46 | det_bbox = det_box 47 | 48 | 49 | scaledImage = DataRow() 50 | scaledImage.image = img 51 | scaledImage.fbbox = det_bbox 52 | dataRow60 = scaledImage.copyCroppedByBBox(det_bbox) 53 | image, lm = predictor.preprocess(dataRow60.image, dataRow60.landmarks()) 54 | prediction = predictor.predict(image) 55 | #dataRow60.prediction = (prediction+0.5)*60. # Scale -0.5..+0.5 to 0..60 56 | dataRow60.prediction = prediction*60. # Scale -0.5..+0.5 to 0..60 57 | scaledImage.prediction = dataRow60.inverseScaleAndOffset(dataRow60.prediction) # Scale up to the original image scale 58 | ALIGNED_DATA_PATH = os.path.join('/home/ly/workspace/Vanilla-40/TestSet', os.path.basename(img_path)[:-4]+'_reuslt.jpg') 59 | aligned_im = scaledImage.show() 60 | print ALIGNED_DATA_PATH 61 | print aligned_im.shape 62 | cv2.imwrite(ALIGNED_DATA_PATH, aligned_im) 63 | 64 | -------------------------------------------------------------------------------- /train/NormlizedMSE.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Fri Dec 4 22:57:25 2015 4 | 5 | @author:Ishay Tubi 6 | """ 7 | 8 | import caffe 9 | import numpy as np 10 | 11 | 12 | class NormlizedMSE(caffe.Layer): 13 | """ 14 | Compute the normlized MSE Loss 15 | """ 16 | def setup(self, bottom, top): 17 | # check input pair 18 | if len(bottom) != 2: 19 | raise Exception("Need two inputs to compute distance.") 20 | 21 | def reshape(self, bottom, top): 22 | # check input dimensions match 23 | 24 | if bottom[0].count != bottom[1].count: 25 | raise Exception("Inputs must have the same dimension.") 26 | # difference is shape of inputs 27 | self.diff = np.zeros_like(bottom[0].data, dtype=np.float32) 28 | # self.diff = np.zeros(bottom[0].count,dtype='float32') 29 | # loss output is scalar 30 | top[0].reshape(1) 31 | # print('NormlizedMSE bottom shape ',bottom[0].shape[0],bottom[0].shape[1]) 32 | 33 | def forward(self,bottom, top): 34 | '''Mean square error of landmark regression normalized w.r.t. 35 | inter-ocular distance 36 | ''' 37 | # Lets assume batch size is 16 in this example remarks 38 | # input size is (16,10) 39 | y_true = bottom[1].data # Assume second entry is the labled data 40 | y_pred = bottom[0].data 41 | 42 | #eye_indices = left eye x, left eye y, right eye x, right eye y, nose x, left mouth, right mouth 43 | #delX = y_true[:,78]-y_true[:,84] # del X size 16 44 | #delY = y_true[:,79]-y_true[:,85] # del y size 16 45 | delX = y_true[:,72]-y_true[:,90] # del X size 16 46 | delY = y_true[:,73]-y_true[:,91] # del y size 16 47 | 48 | 49 | #distXLeft = y_true[:,78]-y_true[:,54] 50 | #distYLeft = y_true[:,79]-y_true[:,55] 51 | #distXRight = y_true[:,84]-y_true[:,54] 52 | #distYRight = y_true[:,85]-y_true[:,55] 53 | #distLeft = (1e-6+(distXLeft*distXLeft + distYLeft*distYLeft)**0.5) 54 | #distRight = (1e-6+(distXRight*distXRight + distYRight*distYRight)**0.5) 55 | #dist = np.vstack((distLeft,distRight)) 56 | #maxDist = dist.max(axis=0) 57 | #minDist = dist.min(axis=0) 58 | #ratio = (minDist/maxDist) 59 | 60 | self.interOc = (1e-6+(delX*delX + delY*delY)**0.5).T # Euclidain distance 61 | #self.interOc = self.interOc + (1e-6+(delX1*delX1 + delY1*delY1)**0.5).T # Euclidain distance 62 | #self.interOc = self.interOc/2.0 63 | #self.interOc = 1.0 64 | #self.interOc = (ratio*(1e-6+(delX*delX + delY*delY)**0.5)).T # Euclidain distance 65 | #self.interOc = ratio 66 | 67 | # Cannot multiply shape (16,10) by (16,1) so we transpose to (10,16) and (1,16) 68 | diff = (y_pred-y_true).T # Transpose so we can divide a (16,10) array by (16,1) 69 | 70 | self.diff[...] = (diff/self.interOc).T # We transpose back to (16,10) 71 | top[0].data[...] = np.sum(self.diff**2) / bottom[0].num / 2. # Loss is scalar 72 | 73 | # loss = np.zeros((bottom[0].num), dtype=np.float32) 74 | # for k in xrange(0,68): 75 | # delX = y_pred[:,k*2]-y_true[:,k*2] # del X size 16 76 | # delY = y_pred[:,k*2+1]-y_true[:,k*2+1] # del y size 16 77 | # loss = loss + (delX*delX + delY*delY)**0.5 78 | # loss = loss.T 79 | # top[0].data[...] = np.sum(loss/self.interOc) / bottom[0].num/68 80 | 81 | def backward(self, top, propagate_down, bottom): 82 | 83 | for i in range(2): 84 | if not propagate_down[i]: 85 | continue 86 | if i == 0: 87 | sign = 1 88 | else: 89 | sign = -1 90 | bottom[i].diff[...] = sign * self.diff / bottom[i].num 91 | # print(bottom[i].diff[...]) 92 | 93 | ################################## 94 | 95 | class EuclideanLossLayer(caffe.Layer): 96 | #ORIGINAL EXAMPLE 97 | def setup(self, bottom, top): 98 | # check input pair 99 | if len(bottom) != 2: 100 | raise Exception("Need two inputs to compute distance.") 101 | 102 | def reshape(self, bottom, top): 103 | # check input dimensions match 104 | if bottom[0].count != bottom[1].count: 105 | raise Exception("Inputs must have the same dimension.") 106 | # difference is shape of inputs 107 | self.diff = np.zeros_like(bottom[0].data, dtype=np.float32) 108 | # loss output is scalar 109 | top[0].reshape(1) 110 | 111 | def forward(self, bottom, top): 112 | self.diff[...] = bottom[0].data - bottom[1].data 113 | top[0].data[...] = np.sum(self.diff**2) / bottom[0].num / 2. 114 | 115 | def backward(self, top, propagate_down, bottom): 116 | for i in range(2): 117 | if not propagate_down[i]: 118 | continue 119 | if i == 0: 120 | sign = 1 121 | else: 122 | sign = -1 123 | bottom[i].diff[...] = sign * self.diff / bottom[i].num 124 | 125 | 126 | 127 | if __name__ =="__main__": 128 | print '---------------------------' 129 | net=caffe.Net('/home/ly/workspace/Vanilla-40/ZOO/vanilla_train.prototxt', '/home/ly/workspace/Vanilla-40/ZOO/vanilaCNN.caffemodel',caffe.TRAIN) 130 | prediction = net.forward()['loss'][0] 131 | print 'lose ', prediction 132 | 133 | -------------------------------------------------------------------------------- /train/adam_solver.prototxt: -------------------------------------------------------------------------------- 1 | net: "ZOO/train_60_medium.prototxt" 2 | type: "Adam" 3 | test_iter: 30 4 | test_interval: 5000 5 | base_lr: 0.000001 6 | momentum: 0.9 7 | momentum2: 0.999 8 | lr_policy: "fixed" 9 | gamma:0.8 10 | stepsize:100000 11 | display: 2500 12 | max_iter: 1500000 13 | snapshot: 5000 14 | snapshot_prefix: "caffeData/snapshots/snap" 15 | solver_mode: GPU 16 | -------------------------------------------------------------------------------- /train/landmark_train.prototxt: -------------------------------------------------------------------------------- 1 | name: "vanila" 2 | 3 | layer { 4 | type: "HDF5Data" 5 | top: "X" # same name as given in create_dataset! 6 | top: "landmarks" 7 | hdf5_data_param { 8 | source: "/home/ly/workspace/Vanilla-40/caffeData/train.txt" # do not give the h5 files directly, but the list. 9 | batch_size: 30 10 | } 11 | include { phase:TRAIN } 12 | } 13 | 14 | layer { 15 | type: "HDF5Data" 16 | top: "X" # same name as given in create_dataset! 17 | top: "landmarks" 18 | hdf5_data_param { 19 | source: "/home/ly/workspace/Vanilla-40/caffeData/test.txt" # do not give the h5 files directly, but the list. 20 | batch_size: 10 21 | } 22 | include { phase:TEST } 23 | } 24 | 25 | layer { 26 | name: "Conv1" 27 | type: "Convolution" 28 | bottom: "X" 29 | top: "Conv1" 30 | param { 31 | lr_mult: 1 32 | decay_mult: 1 33 | } 34 | param { 35 | lr_mult: 2 36 | decay_mult: 0 37 | } 38 | convolution_param { 39 | num_output: 20 40 | pad: 2 41 | kernel_size: 5 42 | stride: 1 43 | weight_filler { 44 | type: "xavier" 45 | std: 0.1 46 | } 47 | bias_filler { 48 | type: "constant" 49 | value: 0.2 50 | } 51 | } 52 | } 53 | 54 | layer { 55 | name: "ActivationTangH1" 56 | bottom: "Conv1" 57 | top: "ActivationTangH1" 58 | type: "TanH" 59 | } 60 | 61 | layer { 62 | name: "ActivationAbs1" 63 | bottom: "ActivationTangH1" 64 | top: "Abs1" 65 | type: "AbsVal" 66 | } 67 | 68 | layer { 69 | name: "Pool1" 70 | type: "Pooling" 71 | bottom: "Abs1" 72 | top: "Pool1" 73 | pooling_param { 74 | pool: MAX 75 | kernel_size: 2 76 | stride: 2 77 | } 78 | } 79 | 80 | layer { 81 | name: "Conv2" 82 | type: "Convolution" 83 | bottom: "Pool1" 84 | top: "Conv2" 85 | param { 86 | lr_mult: 1 87 | decay_mult: 1 88 | } 89 | param { 90 | lr_mult: 2 91 | decay_mult: 0 92 | } 93 | convolution_param { 94 | num_output: 48 95 | pad: 2 96 | kernel_size: 5 97 | stride: 1 98 | weight_filler { 99 | type: "xavier" 100 | std: 0.1 101 | } 102 | bias_filler { 103 | type: "constant" 104 | value: 0.2 105 | } 106 | } 107 | } 108 | 109 | layer { 110 | name: "ActivationTangH2" 111 | bottom: "Conv2" 112 | top: "ActivationTangH2" 113 | type: "TanH" 114 | } 115 | 116 | layer { 117 | name: "ActivationAbs2" 118 | bottom: "ActivationTangH2" 119 | top: "Abs2" 120 | type: "AbsVal" 121 | } 122 | 123 | 124 | layer { 125 | name: "Pool2" 126 | type: "Pooling" 127 | bottom: "Abs2" 128 | top: "Pool2" 129 | pooling_param { 130 | pool: MAX 131 | kernel_size: 2 132 | stride: 2 133 | } 134 | } 135 | 136 | # layer 3 137 | layer { 138 | name: "Conv3" 139 | type: "Convolution" 140 | bottom: "Pool2" 141 | top: "Conv3" 142 | param { 143 | lr_mult: 1 144 | decay_mult: 1 145 | } 146 | param { 147 | lr_mult: 2 148 | decay_mult: 0 149 | } 150 | convolution_param { 151 | num_output: 64 152 | pad: 0 153 | kernel_size: 3 154 | stride: 1 155 | weight_filler { 156 | type: "xavier" 157 | std: 0.1 158 | } 159 | bias_filler { 160 | type: "constant" 161 | value: 0.2 162 | } 163 | } 164 | } 165 | 166 | 167 | layer { 168 | name: "ActivationTangH3" 169 | bottom: "Conv3" 170 | top: "ActivationTangH3" 171 | type: "TanH" 172 | } 173 | 174 | layer { 175 | name: "ActivationAbs3" 176 | bottom: "ActivationTangH3" 177 | top: "Abs3" 178 | type: "AbsVal" 179 | } 180 | 181 | layer { 182 | name: "Pool3" 183 | type: "Pooling" 184 | bottom: "Abs3" 185 | top: "Pool3" 186 | pooling_param { 187 | pool: MAX 188 | kernel_size: 3 189 | stride: 2 190 | } 191 | } 192 | 193 | # layer 4 194 | layer { 195 | name: "Conv4" 196 | type: "Convolution" 197 | bottom: "Pool3" 198 | top: "Conv4" 199 | param { 200 | lr_mult: 1 201 | decay_mult: 1 202 | } 203 | param { 204 | lr_mult: 2 205 | decay_mult: 0 206 | } 207 | convolution_param { 208 | num_output: 80 209 | pad: 0 210 | kernel_size: 3 211 | stride: 1 212 | weight_filler { 213 | type: "xavier" 214 | std: 0.1 215 | } 216 | bias_filler { 217 | type: "constant" 218 | value: 0.2 219 | } 220 | } 221 | } 222 | 223 | 224 | layer { 225 | name: "ActivationTangH4" 226 | bottom: "Conv4" 227 | top: "ActivationTangH4" 228 | type: "TanH" 229 | } 230 | 231 | layer { 232 | name: "ActivationAbs4" 233 | bottom: "ActivationTangH4" 234 | top: "Abs4" 235 | type: "AbsVal" 236 | } 237 | 238 | 239 | ######################################## 240 | 241 | layer { 242 | name: "Dense1" 243 | type: "InnerProduct" 244 | bottom: "Abs4" 245 | top: "Dense1" 246 | param { 247 | lr_mult: 1 248 | decay_mult: 1 249 | } 250 | param { 251 | lr_mult: 2 252 | decay_mult: 0 253 | } 254 | inner_product_param { 255 | num_output: 512 256 | weight_filler { 257 | type: "xavier" 258 | } 259 | bias_filler { 260 | type: "constant" 261 | value: 0 262 | } 263 | } 264 | } 265 | 266 | 267 | layer { 268 | name: "ActivationTangH5" 269 | bottom: "Dense1" 270 | top: "ActivationTangH5" 271 | type: "TanH" 272 | } 273 | 274 | layer { 275 | name: "ActivationAbs5" 276 | bottom: "ActivationTangH5" 277 | top: "Abs5" 278 | type: "AbsVal" 279 | } 280 | 281 | 282 | layer { 283 | name: "Dense3" 284 | type: "InnerProduct" 285 | bottom: "Abs5" 286 | top: "Dense3" 287 | param { 288 | lr_mult: 1 289 | decay_mult: 1 290 | } 291 | param { 292 | lr_mult: 2 293 | decay_mult: 0 294 | } 295 | inner_product_param { 296 | num_output: 136 297 | weight_filler { 298 | type: "xavier" 299 | } 300 | bias_filler { 301 | type: "constant" 302 | value: 0 303 | } 304 | } 305 | } 306 | 307 | layer { 308 | type: 'Python' 309 | name: 'loss' 310 | top: 'loss' 311 | bottom: 'Dense3' 312 | bottom: 'landmarks' 313 | python_param { 314 | # the module name -- usually the filename -- that needs to be in $PYTHONPATH 315 | module: 'python.NormlizedMSE' 316 | # the layer name -- the class name in the module 317 | layer: 'NormlizedMSE' 318 | } 319 | # set loss weight so Caffe knows this is a loss layer. 320 | # since PythonLayer inherits directly from Layer, this isn't automatically 321 | # known to Caffe 322 | loss_weight: 1 323 | } 324 | -------------------------------------------------------------------------------- /train/mainloop.py: -------------------------------------------------------------------------------- 1 | 2 | import numpy as np 3 | _A = np.array # A shortcut to creating arrays in command line 4 | import os 5 | import cv2 6 | import sys 7 | from pickle import load, dump 8 | from zipfile import ZipFile 9 | from urllib import urlretrieve 10 | 11 | #Import helper functions 12 | from DataRow import DataRow, ErrorAcum, Predictor, getGitRepFolder, createDataRowsFromCSV, getValidWithBBox, writeHD5 13 | 14 | ########################### PATHS TO SET #################### 15 | # Either define CAFFE_ROOT in your enviroment variables or set it here 16 | CAFFE_ROOT = os.environ.get('CAFFE_ROOT','~/caffe/distribute') 17 | sys.path.append(CAFFE_ROOT+'/python') # Add caffe python path so we can import it 18 | import caffe 19 | 20 | ROOT = getGitRepFolder() # ROOT is the git root folder . 21 | sys.path.append(os.path.join(ROOT, 'python')) # Assume git root directory 22 | #DATA_PATH = os.path.join(ROOT, 'data') 23 | CSV_TEST = os.path.join(ROOT, 'data', 'mtflTestImageList.txt') 24 | 25 | 26 | # my debug celeba 27 | DATA_PATH = '' 28 | 29 | 30 | MEAN_TRAIN_SET = cv2.imread(os.path.join(ROOT, 'trainMean.png')).astype('f4') 31 | MEAN_TRAIN_SET = cv2.resize(MEAN_TRAIN_SET,(60,60),interpolation=cv2.INTER_CUBIC) 32 | STD_TRAIN_SET = cv2.imread(os.path.join(ROOT, 'trainSTD.png')).astype('f4') 33 | STD_TRAIN_SET = cv2.resize(STD_TRAIN_SET,(60,60),interpolation=cv2.INTER_CUBIC) 34 | 35 | AFW_DATA_PATH = os.path.join(ROOT, 'data', 'afwTestImages') 36 | AFW_MAT_PATH = os.path.join(ROOT, 'data', 'anno-v7.mat') 37 | 38 | 39 | PATH_TO_WEIGHTS = os.path.join('/home/ly/workspace/Vanilla-40', 'caffeData', 'snapshots', 'snap_iter_5910000.caffemodel') 40 | PATH_TO_DEPLOY_TXT = os.path.join('/home/ly/workspace/Vanilla-40', 'ZOO', 'vanilla_deploy_large.prototxt') 41 | 42 | ########################### STEPS TO RUN #################### 43 | AFW_STEPS =['downloadAFW', 44 | 'createAFW_TestSet', 45 | 'testAFW_TestSet'] # Steps needed for AFW 46 | 47 | AFLW_STEPS=['downloadAFLW', 48 | 'testSetHD5', 49 | 'testSetPickle', 50 | 'trainSetHD5', 51 | 'calcTrainSetMean', # depends on trainSetHD5 52 | 'createAFLW_TestSet', 53 | 'testAFLW_TestSet', 54 | 'testErrorMini'] # Steps needed for AFLW 55 | 56 | #STEPS =['trainSetHD5', 'calcTrainSetMean'] # AFLW_STEPS+AFW_STEPS # Run AFLW and AFW steps 57 | 58 | #STEPS = ['createAFW_TestSet'] 59 | #STEPS = ['testAFW_TestSet'] 60 | 61 | #STEPS = ['createAFLW_TestSet'] 62 | #STEPS = ['createAFLW_TestSet'] 63 | STEPS = ['testAFLW_TestSet'] 64 | 65 | ########################################## SCRIPT STEPS ################################################## 66 | 67 | if 'downloadAFW' in STEPS: 68 | theurl = 'https://www.ics.uci.edu/~xzhu/face/AFW.zip' 69 | filename = ROOT+'/AFW.zip' 70 | if os.path.isfile(filename): 71 | print "AFW.zip already downloaded" 72 | else: 73 | print "Downloading "+theurl + " ....." 74 | name, hdrs = urlretrieve(theurl, filename) 75 | print "Finished downloading AFW....." 76 | 77 | print "Extracting AFW zip file......" 78 | folderPATH = ROOT+'/data' 79 | with ZipFile(filename) as theOpenedFile: 80 | theOpenedFile.extractall(folderPATH) 81 | theOpenedFile.close() 82 | 83 | if 'downloadAFLW' in STEPS: 84 | theurl='http://mmlab.ie.cuhk.edu.hk/archive/CNN/data/train.zip' 85 | filename = ROOT+'/AFLW.zip' 86 | if os.path.isfile(filename): 87 | print "AFLW.zip training data already downloaded" 88 | else: 89 | print "Downloading "+theurl + " ....." 90 | name, hdrs = urlretrieve(theurl, filename) 91 | print "Finished downloading AFLW....." 92 | 93 | print 'Extracting zip data...' 94 | folderPATH = ROOT+'/data' 95 | with ZipFile(filename) as theOpenedFile: 96 | theOpenedFile.extractall(folderPATH) 97 | theOpenedFile.close() 98 | print "Done extracting AFW zip folder" 99 | 100 | if 'testSetHD5' in STEPS or 'testSetPickle' in STEPS: 101 | print "Creating test set....." 102 | dataRowsTest_CSV = createDataRowsFromCSV(CSV_TEST , DataRow.DataRowFromNameBoxInterlaved, DATA_PATH) 103 | print "Finished reading %d rows from test" % len(dataRowsTest_CSV) 104 | dataRowsTestValid,R = getValidWithBBox(dataRowsTest_CSV) 105 | print "Original test:",len(dataRowsTest_CSV), "Valid Rows:", len(dataRowsTestValid), " noFacesAtAll", R.noFacesAtAll, " outside:", R.outsideLandmarks, " couldNotMatch:", R.couldNotMatch 106 | if 'testSetPickle' in STEPS: 107 | with open('testSet.pickle','w') as f: 108 | dump(dataRowsTestValid,f) 109 | print "Finished dumping to testSet.pickle" 110 | # Also save mini test set for debug 111 | with open('testSetMini.pickle','w') as f: 112 | dump(dataRowsTestValid[0:10],f) 113 | print "Finished dumping to testSetMini.pickle" 114 | 115 | 116 | if 'testSetHD5' in STEPS: 117 | writeHD5(dataRowsTestValid, ROOT+'/caffeData/hd5/test.hd5', ROOT+'/caffeData/test.txt', MEAN_TRAIN_SET, STD_TRAIN_SET) 118 | print "Finished writing test to caffeData/test.txt" 119 | 120 | 121 | DEBUG = True 122 | if 'testErrorMini' in STEPS: 123 | with open('testSetMini.pickle','r') as f: 124 | dataRowsTrainValid = load(f) 125 | 126 | testErrorMini=ErrorAcum() 127 | predictor = Predictor(protoTXTPath=PATH_TO_DEPLOY_TXT, weightsPath=PATH_TO_WEIGHTS) 128 | for i, dataRow in enumerate(dataRowsTrainValid): 129 | dataRow40 = dataRow.copyCroppedByBBox(dataRow.fbbox).copyMirrored() 130 | image, lm_0_5 = predictor.preprocess(dataRow40.image, dataRow40.landmarks()) 131 | prediction = predictor.predict(image) 132 | testErrorMini.add(lm_0_5, prediction) 133 | dataRow40.prediction = (prediction+0.5)*40. # Scale -0.5..+0.5 to 0..40 134 | 135 | if DEBUG: 136 | dataRow.prediction = dataRow40.inverseScaleAndOffset(dataRow40.prediction) # Scale up to the original image scale 137 | dataRow.show() 138 | if i>40: 139 | print "Debug breaked after %d rows:" % i 140 | 141 | 142 | print "Test Error mini:", testErrorMini 143 | 144 | 145 | 146 | if 'trainSetHD5' in STEPS: 147 | dataRowsTrain_CSV = createDataRowsFromCSV(CSV_TRAIN, DataRow.DataRowFromNameBoxInterlaved, DATA_PATH) 148 | print "Finished reading %d rows from training data. Parsing BBox...." % len(dataRowsTrain_CSV) 149 | dataRowsTrainValid,R = getValidWithBBox(dataRowsTrain_CSV) 150 | print "Original train:",len(dataRowsTrain_CSV), "Valid Rows:", len(dataRowsTrainValid), " noFacesAtAll", R.noFacesAtAll, " outside:", R.outsideLandmarks, " couldNotMatch:", R.couldNotMatch 151 | dataRowsTrain_CSV=[] # remove from memory 152 | 153 | writeHD5(dataRowsTrainValid, ROOT+'/caffeData/hd5/train.hd5', ROOT+'/caffeData/train.txt', MEAN_TRAIN_SET, STD_TRAIN_SET ,mirror=True) 154 | print "Finished writing train to caffeData/train.txt" 155 | 156 | 157 | #%% Calculate train mean image - Assume data was read to dataRowsTrainValid 158 | if 'calcTrainSetMean' in STEPS: 159 | print ('Calculating train data mean value') 160 | meanTrainSet = np.zeros([40,40,3], dtype='double') 161 | for dataRow in dataRowsTrainValid: 162 | meanTrainSet += dataRow.copyCroppedByBBox(dataRow.fbbox).image.astype('double') 163 | 164 | MEAN_TRAIN_SET = meanTrainSet / len(dataRowsTrainValid) 165 | cv2.imwrite(os.path.join(ROOT, 'trainMean.png'), (MEAN_TRAIN_SET).astype('uint8')) 166 | print ('Finished Calculating train data mean value to file trainMean.png', MEAN_TRAIN_SET.mean()) 167 | 168 | print ('Calculating train data std value') 169 | 170 | stdTrainSet = np.zeros([40,40,3], dtype='double') 171 | for dataRow in dataRowsTrainValid: 172 | diff = dataRow.copyCroppedByBBox(dataRow.fbbox).image.astype('double') - MEAN_TRAIN_SET 173 | stdTrainSet += diff*diff 174 | 175 | stdTrainSet /= len(dataRowsTrainValid) 176 | STD_TRAIN_SET = stdTrainSet**0.5 177 | cv2.imwrite(os.path.join(ROOT, 'trainSTD.png'), (STD_TRAIN_SET).astype('uint8')) 178 | print 'Finished Calculating train data std value to file trainSTD.png with mean', STD_TRAIN_SET.mean() 179 | else: 180 | MEAN_TRAIN_SET = cv2.imread(os.path.join(ROOT, 'trainMean.png')).astype('f4') 181 | STD_TRAIN_SET = cv2.imread(os.path.join(ROOT, 'trainSTD.png')).astype('f4') 182 | 183 | 184 | # Run the same caffe test set using python 185 | DEBUG = False # Set this to true if you wish to plot the images 186 | if 'testError' in STEPS: 187 | with open('testSet.pickle','r') as f: 188 | dataRowsTrainValid = load(f) 189 | 190 | testError=ErrorAcum() 191 | predictor = Predictor(protoTXTPath=PATH_TO_DEPLOY_TXT, weightsPath=PATH_TO_WEIGHTS) 192 | for i, dataRow in enumerate(dataRowsTrainValid): 193 | dataRow40 = dataRow.copyCroppedByBBox(dataRow.fbbox) 194 | image, lm40 = predictor.preprocess(dataRow40.image, dataRow40.landmarks()) 195 | prediction = predictor.predict(image) 196 | testError.add(lm40, prediction) 197 | dataRow40.prediction = (prediction+0.5)*40. 198 | 199 | if DEBUG and i%40 ==0: 200 | dataRow.prediction = dataRow40.inverseScaleAndOffset(dataRow40.prediction) 201 | dataRow.show() 202 | break 203 | 204 | 205 | print "Test Error:", testError 206 | 207 | 208 | 209 | # AFW test - Make the pickle data set 210 | if 'createAFW_TestSet' in STEPS: 211 | print "Parsing AFW anno-v7.mat ....." 212 | from scipy.io import loadmat 213 | annotaions = loadmat(AFW_MAT_PATH)['anno'] 214 | dataRowsAFW = [] 215 | 216 | for anno in annotaions: 217 | dataRow = DataRow.DataRowFromAFW(anno, AFW_DATA_PATH) 218 | if dataRow is not None: 219 | dataRowsAFW.append(dataRow) 220 | print "Finished parsing anno-v7.mat with total rows:", len(dataRowsAFW) 221 | annotaions = None # remove from memory 222 | 223 | dataRowsAFW_Valid, R=getValidWithBBox(dataRowsAFW) 224 | print "Original AFW:",len(dataRowsAFW), "Valid Rows:", len(dataRowsAFW_Valid), " No faces at all", R.noFacesAtAll, " illegal landmarks:", R.outsideLandmarks, " Could not match:", R.couldNotMatch 225 | dataRowsAFW = None # remove from Memory 226 | with open('afwTestSet.pickle','w') as f: 227 | dump(dataRowsAFW_Valid, f) 228 | print "Data saved to afwTestSet.pickle" 229 | 230 | 231 | DEBUG = True 232 | if 'testAFW_TestSet' in STEPS: 233 | with open('afwTestSet.pickle','r') as f: 234 | dataRowsAFW_Valid = load(f) 235 | 236 | testErrorAFW=ErrorAcum() 237 | predictor = Predictor(protoTXTPath=PATH_TO_DEPLOY_TXT, weightsPath=PATH_TO_WEIGHTS) 238 | for i, dataRow in enumerate(dataRowsAFW_Valid): 239 | dataRow40 = dataRow.copyCroppedByBBox(dataRow.fbbox) 240 | # my debug 241 | #CROP_DATA_PATH = os.path.join(ROOT, 'data', 'afwTestCropImages', str(i) + '.jpg') 242 | #cv2.imwrite(CROP_DATA_PATH, dataRow40.image) 243 | 244 | image, lm_0_5 = predictor.preprocess(dataRow40.image, dataRow40.landmarks()) 245 | prediction = predictor.predict(image) 246 | testErrorAFW.add(lm_0_5, prediction) 247 | dataRow40.prediction = (prediction+0.5)*40. # Scale -0.5..+0.5 to 0..40 248 | 249 | if DEBUG: 250 | dataRow.prediction = dataRow40.inverseScaleAndOffset(dataRow40.prediction) # Scale up to the original image scale 251 | # my debug 252 | #ALIGNED_DATA_PATH = os.path.join(ROOT, 'data', 'mineAFWAlignedTestImages', str(i) + '.jpg') 253 | ALIGNED_DATA_PATH = os.path.join('/home/ly/workspace/Vanilla-40', 'data', 'mineAFWAlignedTestImages', str(i) + '.jpg') 254 | aligned_im = dataRow.show() 255 | print ALIGNED_DATA_PATH 256 | print aligned_im.shape 257 | cv2.imwrite(ALIGNED_DATA_PATH, aligned_im) 258 | 259 | 260 | print "Test Error AFW:", testErrorAFW 261 | 262 | 263 | 264 | # Create the MTFL benchmark 265 | if 'createAFLW_TestSet' in STEPS: 266 | #MTFL_LINK = 'http://mmlab.ie.cuhk.edu.hk/projects/TCDCN/data/MTFL.zip' 267 | #MTFL_ZIP = ROOT+"/MTFL.zip" 268 | #if os.path.isfile(MTFL_ZIP): 269 | # print "MTFL.zip already downloaded" 270 | #else: 271 | # print "Downloading:"+MTFL_ZIP+" from url:"+MTFL_LINK+"....." 272 | # urlretrieve(MTFL_LINK, MTFL_ZIP) 273 | # print "Finished download. Extracting file....." 274 | # with ZipFile(MTFL_ZIP) as f: 275 | # f.extractall(ROOT+'/data') 276 | # print "Done extracting MTFL" 277 | # f.close() 278 | 279 | 280 | #dataRowsMTFL_CSV = createDataRowsFromCSV(CSV_MTFL , DataRow.DataRowFromMTFL, AFLW_PATH) 281 | 282 | # my debug 283 | #dataRowsMTFL_CSV = createDataRowsFromCSV(CSV_TRAIN , DataRow.DataRowFromNameBoxInterlaved, DATA_PATH) 284 | dataRowsMTFL_CSV = createDataRowsFromCSV(CSV_MTFL , DataRow.DataRowFromNameBoxInterlaved, DATA_PATH) 285 | 286 | print "Finished reading %d rows from test" % len(dataRowsMTFL_CSV) 287 | dataRowsMTFLValid,R = getValidWithBBox(dataRowsMTFL_CSV) 288 | print "Original test:",len(dataRowsMTFL_CSV), "Valid Rows:", len(dataRowsMTFLValid), " No faces at all", R.noFacesAtAll, " Illegal landmarks:", R.outsideLandmarks, " Could not match:", R.couldNotMatch 289 | with open('testSetMTFL.pickle','w') as f: 290 | dump(dataRowsMTFLValid,f) 291 | print "Finished dumping to testSetMTFL.pickle" 292 | 293 | 294 | # Run AFLW benchmark 295 | DEBUG = True 296 | if 'testAFLW_TestSet' in STEPS: 297 | print "Running AFLW benchmark........." 298 | with open('testSetMTFL-det1.pickle','r') as f: 299 | dataRowsAFW_Valid = load(f) 300 | print "%d rows in AFLW benchmark ....." % len(dataRowsAFW_Valid) 301 | testErrorAFLW=ErrorAcum() 302 | predictor = Predictor(protoTXTPath=PATH_TO_DEPLOY_TXT, weightsPath=PATH_TO_WEIGHTS) 303 | for i, dataRow in enumerate(dataRowsAFW_Valid): 304 | dataRow40 = dataRow.copyCroppedByBBox(dataRow.fbbox) 305 | image, lm_0_5 = predictor.preprocess(dataRow40.image, dataRow40.landmarks()) 306 | prediction = predictor.predict(image) 307 | testErrorAFLW.add(lm_0_5, prediction) 308 | dataRow40.prediction = (prediction+0.5)*60. # Scale -0.5..+0.5 to 0..40 309 | 310 | # if DEBUG and i%40 == 0: 311 | if DEBUG: 312 | dataRow.prediction = dataRow40.inverseScaleAndOffset(dataRow40.prediction) # Scale up to the original image scale 313 | # my debug 314 | #ALIGNED_DATA_PATH = os.path.join(ROOT, 'data', 'mineAFLWAlignedTestImages', str(i) + '.jpg') 315 | ALIGNED_DATA_PATH = os.path.join('/home/ly/workspace/Vanilla-40', 'data', 'mineAFLWAlignedTestImages', str(i) + '.jpg') 316 | aligned_im = dataRow.show() 317 | print ALIGNED_DATA_PATH 318 | print aligned_im.shape 319 | cv2.imwrite(ALIGNED_DATA_PATH, aligned_im) 320 | 321 | 322 | print "Test Error AFLW:", testErrorAFLW 323 | 324 | 325 | if 'findSnapshotWithMinError' in STEPS: 326 | pass 327 | --------------------------------------------------------------------------------