Instantly share code, notes, and snippets.
Last active
August 29, 2015 14:24
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save catree/1319a9b59f4f1df57da7 to your computer and use it in GitHub Desktop.
How to detect faces on an image and display number labels, intented to be used for school year group photo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/> | |
**/ | |
#include <iostream> | |
#include <algorithm> | |
#include <fstream> | |
#include <opencv2/opencv.hpp> | |
#define DEBUG 0 | |
/** | |
@author: Catree | |
@date: 2015/07/11 | |
**/ | |
typedef struct face_info_t { | |
cv::Point face_center; | |
cv::Size face_size; | |
cv::Point label_center; | |
cv::Size img_size; | |
face_info_t() : face_center(), face_size(), label_center(), img_size() | |
{ | |
} | |
face_info_t(const cv::Point ¢er, const cv::Size &size, const cv::Point &lbl_center, const cv::Size _img_size) | |
: face_center(center), face_size(size), label_center(lbl_center), img_size(_img_size) | |
{ | |
} | |
bool operator<(const face_info_t& face_info) const | |
{ | |
int top1 = (int) (face_center.y - face_size.height*0.5); | |
int bottom1 = (int) (face_center.y + face_size.height*0.5); | |
int top2 = (int) (face_info.face_center.y - face_info.face_size.height*0.5); | |
int bottom2 = (int) (face_info.face_center.y + face_info.face_size.height*0.5); | |
if( (top1 >= top2 && top1 <= bottom2) || (bottom1 >= top2 && bottom1 <= bottom2) || | |
(top2 >= top1 && top2 <= bottom1) || (bottom2 >= top1 && bottom2 <= bottom1) ) | |
{ | |
return (face_center.x < face_info.face_center.x); | |
} | |
return (face_center.y < face_info.face_center.y); | |
} | |
} face_info_t; | |
std::vector<face_info_t> remove_same_faces(const std::vector<face_info_t> &vectorOfFaces) | |
{ | |
std::vector<face_info_t> vectorOfUniqueFaces; | |
std::vector<size_t> vectorOfDuplicateIndexes; | |
for(size_t i = 0; i < vectorOfFaces.size(); i++) | |
{ | |
int top = (int) (vectorOfFaces[i].face_center.x - vectorOfFaces[i].face_size.width*0.5); | |
int left = (int) (vectorOfFaces[i].face_center.y - vectorOfFaces[i].face_size.height*0.5); | |
cv::Rect r1(top, left, vectorOfFaces[i].face_size.width, vectorOfFaces[i].face_size.height); | |
bool duplicate = false; | |
for(size_t j = i+1; j < vectorOfFaces.size(); j++) | |
{ | |
if(std::find(vectorOfDuplicateIndexes.begin(), vectorOfDuplicateIndexes.end(), j) == vectorOfDuplicateIndexes.end()) | |
{ | |
top = (int) (vectorOfFaces[j].face_center.x - vectorOfFaces[j].face_size.width*0.5); | |
left = (int) (vectorOfFaces[j].face_center.y - vectorOfFaces[j].face_size.height*0.5); | |
cv::Rect r2(top, left, vectorOfFaces[i].face_size.width, vectorOfFaces[i].face_size.height); | |
cv::Rect r_intersection = r1 & r2; | |
if(r_intersection.width > 0 && r_intersection.height > 0) | |
{ | |
duplicate = true; | |
vectorOfDuplicateIndexes.push_back(j); | |
} | |
} | |
} | |
if(!duplicate) | |
{ | |
vectorOfUniqueFaces.push_back(vectorOfFaces[i]); | |
} | |
} | |
return vectorOfUniqueFaces; | |
} | |
void display_number(cv::Mat &img, const int number, const cv::Point ¢er, const cv::Size &label_box_size=cv::Size(30,30), | |
const cv::Scalar label_box_bcg_color=cv::Scalar(255,255,255)) | |
{ | |
cv::Mat label_box(label_box_size, CV_8UC3); | |
label_box.setTo(label_box_bcg_color); | |
std::stringstream ss; | |
ss << number; | |
double factor = label_box_size.width / 30.0; | |
if(number < 10) | |
{ | |
cv::putText(label_box, ss.str(), cv::Point(label_box.cols/2-6*factor, label_box.rows/2+5*factor), cv::FONT_HERSHEY_PLAIN, factor, cv::Scalar(0,0,0), 2); | |
} | |
else | |
{ | |
cv::putText(label_box, ss.str(), cv::Point(label_box.cols/2-10*factor, label_box.rows/2+5*factor), cv::FONT_HERSHEY_PLAIN, factor, cv::Scalar(0,0,0), 2); | |
} | |
label_box.copyTo(img(cv::Rect(center.x, center.y, label_box.cols, label_box.rows))); | |
} | |
void detect_faces_and_display_labels(cv::Mat &img, cv::CascadeClassifier &face_cascade, const bool display_result=false) | |
{ | |
std::vector<cv::Rect> faces; | |
face_cascade.detectMultiScale(img, faces); | |
int size = std::min(img.rows, img.cols) / 24; | |
cv::Size label_box_size(size, size); | |
std::vector<face_info_t> vectorOfFaces(faces.size()); | |
for (size_t i = 0; i < faces.size(); i++) | |
{ | |
cv::Point center((int) (faces[i].x + faces[i].width*0.5), (int) (faces[i].y + faces[i].height*0.5)); | |
#if DEBUG | |
ellipse(img, center, cv::Size(faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, cv::Scalar(255, 0, 255), 2); | |
#endif | |
cv::Point label_box(center.x - size/2, center.y + faces[i].height - size/2); | |
vectorOfFaces[i] = face_info_t(center, faces[i].size(), label_box, img.size()); | |
} | |
std::sort (vectorOfFaces.begin(), vectorOfFaces.end()); | |
vectorOfFaces = remove_same_faces(vectorOfFaces); | |
int label = 1; | |
for(std::vector<face_info_t>::const_iterator it = vectorOfFaces.begin(); it != vectorOfFaces.end(); ++it, label++) | |
{ | |
display_number(img, label, it->label_center, label_box_size); | |
} | |
if(display_result) | |
{ | |
cv::imshow("Image with tags", img); | |
std::cout << "Press a key to continue..." << std::endl; | |
cv::waitKey(0); | |
} | |
} | |
cv::Mat read_image(const std::string &filepath) | |
{ | |
cv::Mat img = cv::imread(filepath); | |
return img; | |
} | |
void help() | |
{ | |
std::cout << "Option: -h <print this help>" << std::endl; | |
std::cout << "Option: -cascade <filepath for the cascade classifier learning file>" << std::endl; | |
std::cout << "Option: -image_list <filepath for the text file containing the images to process>" << std::endl; | |
std::cout << "Option: -display <display the result for each image>" << std::endl; | |
} | |
int main(int argc, char*argv[]) | |
{ | |
std::string casacade_filepath = "haarcascade_frontalface_alt2.xml"; | |
std::string image_list_filepath = "image_list.txt"; | |
bool display_result = false; | |
for(int i = 1; i < argc; i++) | |
{ | |
if(std::string(argv[i]) == "-h") | |
{ | |
help(); | |
} | |
else if(std::string(argv[i]) == "-cascade") | |
{ | |
if(i+1 < argc) | |
{ | |
casacade_filepath = argv[i+1]; | |
} | |
} | |
else if(std::string(argv[i]) == "-image_list") | |
{ | |
if(i+1 < argc) | |
{ | |
image_list_filepath = argv[i+1]; | |
} | |
} | |
else if(std::string(argv[i]) == "-display") | |
{ | |
display_result = true; | |
} | |
} | |
std::ifstream file(image_list_filepath.c_str()); | |
if(!file.is_open()) | |
{ | |
std::cerr << "Problem with the filepath for the learning file !" << std::endl; | |
return -1; | |
} | |
std::string line; | |
std::vector<std::string> vectorOfImageFilepaths; | |
while(getline(file, line)) | |
{ | |
vectorOfImageFilepaths.push_back(line); | |
} | |
cv::CascadeClassifier face_cascade; | |
if(!face_cascade.load(casacade_filepath)) | |
{ | |
std::cerr << "Problem with the filepath for cascade classifier learning file !" << std::endl; | |
return -1; | |
} | |
for(std::vector<std::string>::const_iterator it = vectorOfImageFilepaths.begin(); it != vectorOfImageFilepaths.end(); ++it) | |
{ | |
cv::Mat img = read_image(*it); | |
if(!img.empty()) | |
{ | |
std::string filenama_tagged = (*it) + "_tagged.jpg"; | |
detect_faces_and_display_labels(img, face_cascade, display_result); | |
std::cout << "Save image: " << filenama_tagged << std::endl; | |
cv::imwrite(filenama_tagged, img); | |
} | |
else | |
{ | |
std::cerr << "The image " << line << " cannot be read !" << std::endl; | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment