I am using opencv and C++ although i'm beginner. I am trying to detect and count faces from a set of image using Haarcascade .
I only want to get the number of faces on each image .
how can i edit this code to get the number of faces on image????
// Function detectAndDisplay
void detectAndDisplay(Mat frame)
{
std::vector<Rect> faces;
Mat frame_gray;
Mat crop;
Mat res;
Mat gray;
string text;
stringstream sstm;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
equalizeHist(frame_gray, frame_gray);
// Detect faces
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));
// Set Region of Interest
cv::Rect roi_b;
cv::Rect roi_c;
size_t ic = 0; // ic is index of current element
int ac = 0; // ac is area of current element
size_t ib = 0; // ib is index of biggest element
int ab = 0; // ab is area of biggest element
for (ic = 0; ic < faces.size(); ic++) // Iterate through all current elements (detected faces)
{
roi_c.x = faces[ic].x;
roi_c.y = faces[ic].y;
roi_c.width = (faces[ic].width);
roi_c.height = (faces[ic].height);
ac = roi_c.width * roi_c.height; // Get the area of current element (detected face)
roi_b.x = faces[ib].x;
roi_b.y = faces[ib].y;
roi_b.width = (faces[ib].width);
roi_b.height = (faces[ib].height);
ab = roi_b.width * roi_b.height; // Get the area of biggest element, at beginning it is same as "current" element
if (ac > ab)
{
ib = ic;
roi_b.x = faces[ib].x;
roi_b.y = faces[ib].y;
roi_b.width = (faces[ib].width);
roi_b.height = (faces[ib].height);
}
crop = frame(roi_b);
resize(crop, res, Size(128, 128), 0, 0, INTER_LINEAR); // This will be needed later while saving images
cvtColor(crop, gray, CV_BGR2GRAY); // Convert cropped image to Grayscale
// Form a filename
filename = "";
stringstream ssfn;
ssfn << filenumber << ".png";
filename = ssfn.str();
filenumber++;
imwrite(filename, gray);
printf("filename");
Point pt1(faces[ic].x, faces[ic].y); // Display detected faces on main window - live stream from camera
Point pt2((faces[ic].x + faces[ic].height), (faces[ic].y + faces[ic].width));
rectangle(frame, pt1, pt2, Scalar(0, 255, 0), 2, 8, 0);
}
// Show image
/*sstm << "Crop area size: " << roi_b.width << "x" << roi_b.height << " Filename: " << filename;
text = sstm.str();
putText(frame, text, cvPoint(30, 30), FONT_HERSHEY_COMPLEX_SMALL, 0.8, cvScalar(0, 0, 255), 1, CV_AA);
imshow("original", frame);
if (!crop.empty())
{
imshow("detected", crop);
}
else
destroyWindow("detected");*/
}
modified your posted code sample to just return the number of detected faces in the image...
// Function to count the detected faces in your image
void countFacesInImage(Mat frame)
{
std::vector<Rect> faces;
Mat frame_gray;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
equalizeHist(frame_gray, frame_gray);
// Detect faces
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 2, 0 | CASCADE_SCALE_IMAGE, Size(30, 30));
return faces.size();
}
if you want to get an impression of which faces were detected and which weren't you can add this code before the return:
cv::Mat tmpImage = frame.clone();
for(unsigned int i=0; i<faces.size(); ++i)
{
cv::rectangle(tmpImage, faces[i], cv::Scalar(0,255,0), 2);
}
cv::imshow("faces", tmpImage);
cv::waitKey(0);
after each image you have to press a key with active window "faces". You can change to cv::waitKey(n) to wait n milliseconds instead of the need to press a key.
I had to do something similar and used the example of a CascadeClassifier on the OpenCV website.
The rough steps to follow are:
Load all images you want to process.
For each image, apply the CascadeClassifier as in the example, you will need to pass a std::vector<cv::Rect> as parameter. After detection, this vector will contain the location of all detected objects (in your case, faces).
For each image, return the size of the vector to know the number of faces that were detected.
To be honest, the example I linked is something you could have found on your own without much effort.
Related
I was looking at this tutorial, and it said "You can make a symmetric face, by averaging a face and its mirror reflection." - and there was an example of Obama's face being made symmetrical. I tried doing the same with openCV and C++, but these are the results I'm getting using the following code:
Mat3b getMean(const vector<Mat3b>& images) {
Mat m(images[0].rows, images[0].cols, CV_64FC3); // Create a 0 initialized image to use as accumulator
m.setTo(Scalar(0, 0, 0, 0)); //set all image elements to 0
Mat temp; // Use a temp image to hold the conversion of each input image to CV_64FC3
for (int i = 0; i < images.size(); ++i) { //loop through the images
images[i].convertTo(temp, CV_64FC3); // Convert the input images to CV_64FC3...
m += temp; //...so you can accumulate
}
m.convertTo(m, CV_8U, 1. / images.size()); // Convert back to CV_8UC3 type, applying the division to get the actual mean
return m;
}
int main() {
Mat img1 = imread("E:/barack-obama.jpg"), img2, img4;
resize(img1, img1, Size(0.4 * img1.cols, 0.4 * img1.rows), 1, 1, INTER_LINEAR);
flip(img1, img2, +1);
vector<Mat3b> imgs;
imgs.push_back(img1);
imgs.push_back(img2);
Mat3b img3 = getMean(imgs); // Compute the mean
//img3 = (img1 + img2)*0.5;
double alpha = 0.5, beta;
beta = (1.0 - alpha);
addWeighted(img1, alpha, img2, beta, 0.0, img4);
imshow("Original", img1);
imshow("getMean", img3);
imshow("AddWeighted", img4);
waitKey(0);
}
I need to get contour from hand image, usually I process image with 4 steps:
get raw RGB gray image from 3 channels to 1 channel:
cvtColor(sourceGrayImage, sourceGrayImage, COLOR_BGR2GRAY);
use Gaussian blur to filter gray image:
GaussianBlur(sourceGrayImage, sourceGrayImage, Size(3,3), 0);
binary gray image, I split image by height, normally I split image to 6 images by its height, then each one I do threshold process:
// we split source picture to binaryImageSectionCount(here it's 8) pieces by its height,
// then we for every piece, we do threshold,
// and at last we combine them agin to binaryImage
const binaryImageSectionCount = 8;
void GetBinaryImage(Mat &grayImage, Mat &binaryImage)
{
// get every partial gray image's height
int partImageHeight = grayImage.rows / binaryImageSectionCount;
for (int i = 0; i < binaryImageSectionCount; i++)
{
Mat partialGrayImage;
Mat partialBinaryImage;
Rect partialRect;
if (i != binaryImageSectionCount - 1)
{
// if it's not last piece, Rect's height should be partImageHeight
partialRect = Rect(0, i * partImageHeight, grayImage.cols, partImageHeight);
}
else
{
// if it's last piece, Rect's height should be (grayImage.rows - i * partImageHeight)
partialRect = Rect(0, i * partImageHeight, grayImage.cols, grayImage.rows - i * partImageHeight);
}
Mat partialResource = grayImage(partialRect);
partialResource.copyTo(partialGrayImage);
threshold( partialGrayImage, partialBinaryImage, 0, 255, THRESH_OTSU);
// combin partial binary image to one piece
partialBinaryImage.copyTo(binaryImage(partialRect));
///*stringstream resultStrm;
//resultStrm << "partial_" << (i + 1);
//string string = resultStrm.str();
//imshow(string, partialBinaryImage);
//waitKey(0);*/
}
imshow("result binary image.", binaryImage);
waitKey(0);
return;
}
use findcontour to get biggest area contour:
vector<vector<Point> > contours;
findContours(binaryImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
normally it works well,
But for some low quality gray image, it doesn't work,like below:
the complete code is here:
#include <opencv2/imgproc/imgproc.hpp>
#include<opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
// we split source picture to binaryImageSectionCount(here it's 8) pieces by its height,
// then we for every piece, we do threshold,
// and at last we combine them agin to binaryImage
const binaryImageSectionCount = 8;
void GetBinaryImage(Mat &grayImage, Mat &binaryImage)
{
// get every partial gray image's height
int partImageHeight = grayImage.rows / binaryImageSectionCount;
for (int i = 0; i < binaryImageSectionCount; i++)
{
Mat partialGrayImage;
Mat partialBinaryImage;
Rect partialRect;
if (i != binaryImageSectionCount - 1)
{
// if it's not last piece, Rect's height should be partImageHeight
partialRect = Rect(0, i * partImageHeight, grayImage.cols, partImageHeight);
}
else
{
// if it's last piece, Rect's height should be (grayImage.rows - i * partImageHeight)
partialRect = Rect(0, i * partImageHeight, grayImage.cols, grayImage.rows - i * partImageHeight);
}
Mat partialResource = grayImage(partialRect);
partialResource.copyTo(partialGrayImage);
threshold( partialGrayImage, partialBinaryImage, 0, 255, THRESH_OTSU);
// combin partial binary image to one piece
partialBinaryImage.copyTo(binaryImage(partialRect));
///*stringstream resultStrm;
//resultStrm << "partial_" << (i + 1);
//string string = resultStrm.str();
//imshow(string, partialBinaryImage);
//waitKey(0);*/
}
imshow("result binary image.", binaryImage);
waitKey(0);
return;
}
int main(int argc, _TCHAR* argv[])
{
// get image path
string imgPath("C:\\Users\\Alfred\\Desktop\\gray.bmp");
// read image
Mat src = imread(imgPath);
imshow("Source", src);
//medianBlur(src, src, 7);
cvtColor(src, src, COLOR_BGR2GRAY);
imshow("gray", src);
// do filter
GaussianBlur(src, src, Size(3,3), 0);
// binary image
Mat threshold_output(src.rows, src.cols, CV_8UC1, Scalar(0, 0, 0));
GetBinaryImage(src, threshold_output);
imshow("binaryImage", threshold_output);
// get biggest contour
vector<vector<Point> > contours;
findContours(threshold_output,contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
int biggestContourIndex = 0;
int maxContourArea = -1000;
for (int i = 0; i < contours.size(); i++)
{
if (contourArea(contours[i]) > maxContourArea)
{
maxContourArea = contourArea(contours[i]);
biggestContourIndex = i;
}
}
// show biggest contour
Mat biggestContour(threshold_output.rows, threshold_output.cols, CV_8UC1, Scalar(0, 0, 0));
drawContours(biggestContour, contours, biggestContourIndex, cv::Scalar(255,255,255), 2, 8, vector<Vec4i>(), 0, Point());
imshow("maxContour", biggestContour);
waitKey(0);
}
could anybody please help me to get a better hand contour result?
thanks!!!
I have the code snippet in python, you can follow the same approach in C:
img = cv2.imread(x, 1)
cv2.imshow("img",img)
imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("gray",imgray)
#Code for histogram equalization
equ = cv2.equalizeHist(imgray)
cv2.imshow('equ', equ)
#Code for contrast limited adaptive histogram equalization
#clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
#cl2 = clahe.apply(imgray)
#cv2.imshow('clahe2', cl2)
This is the result I obtained:
If you're image is horribly bad you could try the code that I commented involving contrast limited adaptive histogram equalization.
I'm trying to detect multiple faces from video file according to user input.
The code detects the number of the desired faces but sometimes the detection "jumps" to someone else - different from the first frame.
The video file contains 5-6 faces and the user can select a number from 1 to 4.
The program should to detect the first X faces and tracked throughout the video, in addition, the program opens another window with the detected faces.
Here is my algorithm code:
capture >> cap_img;
waitKey(2);
cvtColor(cap_img, gray_img, CV_BGR2GRAY);
equalizeHist(gray_img, gray_img);
while (1)
{
capture >> cap_img;
waitKey(2);
cvtColor(cap_img, gray_img, CV_BGR2GRAY);
equalizeHist(gray_img, gray_img);
// Detect faces
face_cascade.detectMultiScale(gray_img, faces, 1.1, 5,0 | CV_HAAR_SCALE_IMAGE | CV_HAAR_DO_CANNY_PRUNING | CASCADE_SCALE_IMAGE, Size(1, 1));
// Set Region of Interest
cv::Rect reg_b;
cv::Rect reg_c;
int i = 0; // i is index of current element
int ac = 0; // ac is area of current element
int ib = 0; // ib is index of biggest element
int ab = 0; // ab is area of biggest element
if (numOfFaces > faces.size())
{
numOfFaces = faces.size();
}
for (i = 0; i < numOfFaces; i++) // Iterate through all current elements (detected faces)
{
reg_c.x = faces[i].x;
reg_c.y = faces[i].y;
reg_c.width = (faces[i].width);
reg_c.height = (faces[i].height);
// Get the area of current element (detected face), at beginning it is same as "current" element
ac = reg_c.width * reg_c.height;
reg_b.x = faces[ib].x;
reg_b.y = faces[ib].y;
reg_b.width = (faces[ib].width);
reg_b.height = (faces[ib].height);
ab = reg_b.width * reg_b.height;
// Get the area of biggest element, at beginning it is same as "current" element
if (ac > ab)
{
ib = i;
reg_b.x = faces[ib].x;
reg_b.y = faces[ib].y;
reg_b.width = (faces[ib].width);
reg_b.height = (faces[ib].height);
}
crop = cap_img(reg_b);
resize(crop, res, Size(128, 128), 0, 0, INTER_LINEAR); // This will be needed later while saving images
cvtColor(crop, gray, CV_BGR2GRAY); // Convert cropped image to Grayscale
Point pt1(faces[i].x, faces[i].y); // Display detected faces on main window - live stream from camera
Point pt2((faces[i].x + faces[i].height), (faces[i].y + faces[i].width));
rectangle(cap_img, pt1, pt2, Scalar(0, 255, 0), 2, 8, 0);
}
// Show image
resize(cap_img, cap_img, Size(cap_img.cols / 2, cap_img.rows / 2)); // to half size or even smaller
imshow("original", cap_img);
if (!crop.empty())
{
imshow("detected", crop);
}
else
destroyWindow("detected");
}
"numOfFaces" is the number of faces to detect.
What am I doing wrong?
I'm trying to detect this circle
within an image with several other circles .
How would you go about doing something like this? I tried combining both colour ranges but this didn't work.
Here's my current code:
// Threshold for yellow colour on the Drop-off point
int bLowH = 25;
int bHighH = 79;
int bLowS = 0;
int bHighS = 121;
int bLowV = 87;
int bHighV = 196;
// Threshold values for red colour on the Drop-off point
int gLowH = 148;
int gHighH = 180;
int gLowS = 54;
int gHighS = 255;
int gLowV = 96;
int gHighV = 247;
Mat imgHSV;
Mat yellowRange;
Mat redRange;
cvtColor(frame, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV
//Threshold the images.. Only Keep The threshold values for the dropoff point
inRange(imgHSV, Scalar(bLowH, bLowS, bLowV), Scalar(bHighH, bHighS, bHighV), yellowRange);
inRange(imgHSV, Scalar(gLowH, gLowS, gLowV), Scalar(gHighH, gHighS, gHighV), redRange);
// combine both images and slightly blur...
Mat dropoff_image;
addWeighted(yellowRange, 1.0, redRange, 1.0, 0.0, dropoff_image);
GaussianBlur(dropoff_image, dropoff_image, Size(9, 9), 2, 2);
// Hough Transform to detect circle
vector<Vec3f> dropoff;
HoughCircles(dropoff_image, dropoff, CV_HOUGH_GRADIENT, 1, dropoff_image.rows / 8, 100, 20, 0, 0);
if (dropoff.size() == 0)
{
cout << "No dropoff circle found" << endl;
exit(-1);
}
for (size_t current_circle = 0; current_circle < dropoff.size(); ++current_circle)
{
cout << "circle found" << endl;
Point center(round(dropoff[current_circle][0]), round(dropoff[current_circle][1]));
int radius = round(dropoff[current_circle][2]);
circle(frame, center, radius, Scalar(0, 255, 0), 5);
imshow("Gaussian", dropoff_image);
}
friends, could you please help with my questions?
I am using opencv in c++.
I am randomly cropping a small picture from a camera view. I want to find the word located at the bottom of this cropped picture, and this word should also be penetrated by the vertical center line (imaginary) of this cropped picture. please see the following code :
char* my_word = do_ocr(my_cropped_image);
and the do_ocr function is like this:
char* do_ocr(cv::Mat im)
{
cv::Mat gray;
cv::cvtColor(im, gray, CV_BGR2GRAY);
// ...other image pre-processing here...
// Pass it to Tesseract API
tesseract::TessBaseAPI tess;
tess.Init(NULL, "eng", tesseract::OEM_DEFAULT);
tess.SetPageSegMode(tesseract::PSM_SINGLE_BLOCK);
tess.SetImage((uchar*)gray.data, gray.cols, gray.rows, 1, gray.cols);
// Get the text
char* out = tess.GetUTF8Text();
std::cout << out << std::endl;
return out;
}
The following is the schematic diagram and some samples of my_cropped_image :
my_cropped_image sample # 1, the letter "preceding" should be detected:
my_cropped_image sample # 2, the letter "advantageous" should be detected:
my_cropped_image sample # 3, the letter "Correlation" should be detected:
my_cropped_image sample # 4, the letter "density" should be detected:
my_cropped_image sample # 5, the letter "time" should be detected:
I'll appreciate the helps from you to update my do_ocr function.
Thank you and have a great day!
Are these the results you were looking for?
Methodology:
1) Binaryze the image, white is foreground. Here is simply done with img = img < 150;. You can use more sophisticated methods, like adaptiveThreshold.
You get something like:
2) Apply a open morphological operation, so that all the letters in a single word for a single blob:
3) Find the rectangle of each connected component:
4) Take the bottom one, in the center.
Here the full code:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
Mat3b dbg;
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
cvtColor(img, dbg, COLOR_GRAY2BGR);
Mat3b result;
cvtColor(img, result, COLOR_GRAY2BGR);
Mat1b img2;
img2 = img < 150;
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5,3));
morphologyEx(img2, img2, MORPH_DILATE, kernel);
// Apply a small border
copyMakeBorder(img2, img2, 5, 5, 5, 5, BORDER_CONSTANT, Scalar(0));
// Take the bounding boxes of all connected components
vector<vector<Point>> contours;
findContours(img2.clone(), contours, CV_RETR_LIST, CHAIN_APPROX_NONE);
int minArea = 60;
vector<Rect> rects;
for (int i = 0; i < contours.size(); ++i)
{
Rect r = boundingRect(contours[i]);
if (r.area() >= minArea)
{
// Account for border
r -= Point(5,5);
rects.push_back(r);
}
}
int middle = img.cols / 2;
// Keep bottom rect, containig middle point
if (rects.empty()) return -1;
Rect word;
for (int i = 1; i < rects.size(); ++i)
{
Point pt(middle, rects[i].y + rects[i].height/2);
if (rects[i].contains(pt))
{
if (rects[i].y > word.y)
{
word = rects[i];
}
}
}
// Show results
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
for (int i = 0; i < rects.size(); ++i)
{
rectangle(res, rects[i], Scalar(0, 255, 0));
}
rectangle(result, word, Scalar(0, 0, 255), 2);
imshow("Rects", res);
imshow("Result", result);
waitKey();
return 0;
}