I am trying to calibrate my Minoru stereo camera with OpenCV. The following code first calibrates the camera and then rectifies the results. When I run the code, the rectification results are somehow strange.
I've attached one of the results.
It would be very helpful, if somebody could look over my code.
Thanks Max
Code:
#include <opencv2/core/core.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/contrib/contrib.hpp>
#include "libcam.h"
#include <stdio.h>
#include <iostream>
using namespace cv;
using namespace std;
void loadImagePair(Mat &img1, Mat &img2, int i) {
stringstream ss1, ss2;
ss1 << "data/saves/" << i << "_1.png";
ss2 << "data/saves/" << i << "_2.png";
/*if (i < 10) {
ss1 << "data/martinperris_images/left0" << i << ".ppm";
ss2 << "data/martinperris_images/right0" << i << ".ppm";
} else {
ss1 << "data/martinperris_images/left" << i << ".ppm";
ss2 << "data/martinperris_images/right" << i << ".ppm";
}*/
img1 = imread(ss1.str());
img2 = imread(ss2.str());
}
int main(int argc, char** argv) {
// The camera properties
int w = 640;
int h = 480;
int fps = 20;
// The chessboard properties
CvSize chessboardSize(9, 6);
float squareSize = 1.0f;
// This should contain the physical location of each corner, but since we don't know them, we are assigning constant positions
vector<vector<Point3f> > objPoints;
// The chessboard corner points in the images
vector<vector<Point2f> > imagePoints1, imagePoints2;
vector<Point2f> corners1, corners2;
// The constant positions of each obj points
vector<Point3f> obj;
for (int x = 0; x < chessboardSize.width; x++) {
for (int y = 0; y < chessboardSize.height; y++) {
obj.push_back(Point3f(x * squareSize, y * squareSize, 0));
}
}
/*for (int i = 0; i < chessboardSize.width * chessboardSize.height; i++) {
obj.push_back(Point3f(i / chessboardSize.width, i % chessboardSize.height, 0.0f));
}*/
// The images, which are proceeded
Mat img1, img2;
// The grayscale versions of the images
Mat gray1, gray2;
// Get the image count
int imageCount;
cout << "How much images to load: " << endl;
cin >> imageCount;
// The image number of the current image (nullbased)
int i = 0;
// Whether the chessboard corners in the images were found
bool found1 = false, found2 = false;
while (i < imageCount) {
// Load the images
cout << "Attempting to load image pair " << i << endl;
loadImagePair(img1, img2, i);
cout << "Loaded image pair" << endl;
// Convert to grayscale images
cvtColor(img1, gray1, CV_BGR2GRAY);
cvtColor(img2, gray2, CV_BGR2GRAY);
// Find chessboard corners
found1 = findChessboardCorners(img1, chessboardSize, corners1, CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FILTER_QUADS);
found2 = findChessboardCorners(img2, chessboardSize, corners2, CV_CALIB_CB_ADAPTIVE_THRESH | CV_CALIB_CB_FILTER_QUADS);
cout << "found 1/2: " << found1 << "/" << found2 << endl;
// Find corners to subpixel accuracy
if (found1) {
cornerSubPix(gray1, corners1, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.1));
drawChessboardCorners(gray1, chessboardSize, corners1, found1);
}
if (found2) {
cornerSubPix(gray2, corners2, Size(11, 11), Size(-1, -1), TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 30, 0.1));
drawChessboardCorners(gray2, chessboardSize, corners2, found2);
}
// Store corners
if (found1 && found2) {
imagePoints1.push_back(corners1);
imagePoints2.push_back(corners2);
objPoints.push_back(obj);
cout << "Corners stored" << endl;
i++;
}
// Error
else {
cout << "Corners not found! Stopping" << endl;
return 0;
}
}
cout << "Starting calibration" << endl;
Mat CM1 = Mat(3, 3, CV_64F);
Mat CM2 = Mat(3, 3, CV_64F);
Mat D1 = Mat(1, 5, CV_64F);
Mat D2 = Mat(1, 5, CV_64F);
Mat R = Mat(3, 3, CV_64F);
Mat T = Mat(3, 1, CV_64F);
Mat E = Mat(3, 3, CV_64F);
Mat F = Mat(3, 3, CV_64F);
//stereoCalibrate(objPoints, imagePoints1, imagePoints2, CM1, D1, CM2, D2, img1.size(), R, T, E, F,
//CV_CALIB_SAME_FOCAL_LENGTH | CV_CALIB_ZERO_TANGENT_DIST, cvTermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 100, 1e-5));
stereoCalibrate(objPoints, imagePoints1, imagePoints2, CM1, D1, CM2, D2, img1.size(), R, T, E, F, 0,
cvTermCriteria(CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 100, 1e-5));
cout << "Done calibration" << endl;
cout << "Starting rectification" << endl;
Mat R1 = Mat(3, 3, CV_64F);
Mat R2 = Mat(3, 3, CV_64F);
Mat P1 = Mat(3, 4, CV_64F);
Mat P2 = Mat(3, 4, CV_64F);
Mat Q = Mat(4, 4, CV_64F);
stereoRectify(CM1, D1, CM2, D2, img1.size(), R, T, R1, R2, P1, P2, Q);
cout << "Done rectification" << endl;
cout << "Starting to store results" << endl;
FileStorage fs("stereocalib.yml", FileStorage::WRITE);
fs << "CM1" << CM1;
fs << "CM2" << CM2;
fs << "D1" << D1;
fs << "D2" << D2;
fs << "R" << R;
fs << "T" << T;
fs << "E" << E;
fs << "F" << F;
fs << "R1" << R1;
fs << "R2" << R2;
fs << "P1" << P1;
fs << "P2" << P2;
fs << "Q" << Q;
fs.release();
cout << "Done storing results" << endl;
cout << "Starting to apply undistort" << endl;
Mat map1x = Mat(img1.size().height, img1.size().width, CV_32F);
Mat map1y = Mat(img1.size().height, img1.size().width, CV_32F);
Mat map2x = Mat(img2.size().height, img2.size().width, CV_32F);
Mat map2y = Mat(img2.size().height, img2.size().width, CV_32F);
initUndistortRectifyMap(CM1, D1, R1, P1, img1.size(), CV_32FC1, map1x, map1y);
initUndistortRectifyMap(CM2, D2, R2, P2, img2.size(), CV_32FC1, map2x, map2y);
cout << "Done applying undistort" << endl;
// The rectified images
Mat imgU1 = Mat(img1.size(), img1.type()), imgU2 = Mat(img2.size(), img2.type());
// Show rectified images
i = 0;
while (i < imageCount) {
// Load the images
cout << "Attempting to load image pair " << i << endl;
loadImagePair(img1, img2, i);
cout << "Loaded image pair" << endl;
i++;
remap(img1, imgU1, map1x, map1y, INTER_LINEAR, BORDER_CONSTANT, Scalar());
remap(img2, imgU2, map2x, map2y, INTER_LINEAR, BORDER_CONSTANT, Scalar());
//remap(img1, imgU1, map1x, map1y, INTER_LINEAR, BORDER_DEFAULT);
//remap(img2, imgU2, map2x, map2y, INTER_LINEAR, BORDER_DEFAULT);
imshow("img1", img1);
imshow("img2", img2);
imshow("rec1", imgU1);
imshow("rec2", imgU2);
int key = waitKey(0);
if (key == 'q') {
break;
}
}
return 0;
}
Image:
Finally I found out, what the problem was. The images were good, but I defined the obj Vector the wrong way. It has to be:
// The constant positions of each obj points
vector<Point3f> obj;
for (int y = 0; y < chessboardSize.height; y++) {
for (int x = 0; x < chessboardSize.width; x++) {
obj.push_back(Point3f(y * squareSize, x * squareSize, 0));
}
}
Now I have a calibration result of 1.57324, which is quite good I think.
Related
I use opencv function cv::calibrateCamera to calibrate my camera, and i want to access the value return by index like this cameraMatrix.at<float>(2,2), but failed! The code and result is like this:
cv::Mat cameraMatrix = cv::Mat(3, 3, CV_32F);
cv::Mat distCoeffs = cv::Mat(1, 5, CV_32F);
cv::Mat rvecs;
cv::Mat tvecs;
double reprojectError;
reprojectError = cv::calibrateCamera(pointsOnBoard, pointsOnImg, imgSize,
cameraMatrix, distCoeffs, rvecs, tvecs);
std::cout << "Intrinsic matrix:" << std::endl;
std::cout << cameraMatrix << std::endl;
std::cout << "Value at [2,2]: " << cameraMatrix.at<float>(2,2) << std::endl;
The output is like this:
I have tried some code like this and everything is ok!
float tmp[] = {1,2,3,4,5,6,7,8,9};
cv Mat test = Mat(3,3,CV_32F,tmp);
std::cout << "Value at [2,2]: " << test.at<float>(2,2) << std::endl;
I tried to extract average pixel values (R, G, B) from the contoured image. However, my problem is when I applied the code below, something strange values were observed.
int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;
cv::imshow("Original", star);
cv::cvtColor(star, frame, CV_BGR2HSV);
cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
cv::Scalar temp = mean(mask);
cout << "avg_R: " << temp[2] << " \n"; // red value
cout << "avg_G: " << temp[1] << " \n"; // green value
cout << "avg_B: " << temp[0] << " \n\n"; // blue value
cv::imshow("result", result);
cv::waitKey(-1);
return 0;
}
And I got the correct images for the result like below.
I want to read pixel values only for yellow part, not for outside of mask.
And I have another code for read out pixel values in the yellow parts, but it showed the same result.
int main(){
cv::Mat star = imread("C:\\Users\\PC\\Desktop\\star\\starcircle.png");
cv::Mat mask = cv::Mat::zeros(star.rows, star.cols, CV_8UC1);
cv::Mat frame;
double b, g, r = 0.0;
cv::imshow("Original", star);
cv::cvtColor(star, frame, CV_BGR2HSV);
cv::inRange(frame, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
int hei = star.rows;
int wid = star.cols;
int corow = hei * wid;
double b, g, r = 0.0;
for (int x = 0; x < hei; x++) {
for (int y = 0; y < wid; y++) {
if (mask.at<unsigned char>(x, y) > 0) {
b += result.at<Vec3b>(x, y)[0];
g += result.at<Vec3b>(x, y)[1];
r += result.at<Vec3b>(x, y)[2];
}
else {
}
}
}
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
}
Please help me to revise the error.
Thank you in advance.
A few things:
Your variables names and Mat types are at least confusing. Use proper names for the variables, and use Mat_<T> whenever possible (I'd say always).
To get the mean you should divide by the number of pixels in the mask, not by total number of pixels.
you should consider using cv::mean
you need cv::waitKey() to actually see your cv::imshow
Check the code:
#include <opencv2\opencv.hpp>
int main()
{
cv::Mat3b star = cv::imread("path/to/image");
cv::imshow("Original", star);
cv::Mat3b hsv;
cv::cvtColor(star, hsv, cv::COLOR_BGR2HSV);
cv::Mat1b mask;
cv::inRange(hsv, cv::Scalar(29, 220, 220), cv::Scalar(30, 255, 255), mask);
cv::imshow("mask", mask);
// Change to 'false' to see how to use the 'cv::mask' approach
if (true)
{
double blue, green, red = 0.0;
int counter = 0;
for (int r = 0; r < star.rows; r++)
{
for (int c = 0; c < star.cols; c++)
{
if (mask(r, c) > 0)
{
++counter;
blue += star(r, c)[0];
green += star(r, c)[1];
red += star(r, c)[2];
}
}
}
// Avoid division by 0
if (counter > 0)
{
blue /= counter;
green /= counter;
red /= counter;
}
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
std::cout << "avg_R: " << red << " \n";
std::cout << "avg_G: " << green << " \n";
std::cout << "avg_B: " << blue << " \n\n";
}
else
{
cv::Scalar mean_value = cv::mean(star, mask);
double blue = mean_value[0];
double green = mean_value[1];
double red = mean_value[2];
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
std::cout << "avg_R: " << red << " \n"; // red value
std::cout << "avg_G: " << green << " \n"; // green value
std::cout << "avg_B: " << blue << " \n\n"; // blue value
}
cv::waitKey();
}
I see several errors in your code:
cv::Mat result = cv::Mat(star.rows, star.cols, CV_8UC1, star.type());
result.setTo(cv::Scalar(0, 0, 0));
star.copyTo(result, mask);
cv::Scalar temp = mean(mask);
If result is of type CV_8UC1 then you copyTo one channel? (The C1 from CV_8U means one channel). Then you use star.type() where the value to be set should be... You do also the mean to a mask, which will give you a scalar with only one channel set, since it is a binary image of type CV_8UC1... for it to work, it should be:
cv::Mat result = cv::Mat(star.rows, star.cols, star.type(), cv::Scalar::all(0));
star.copyTo(result, mask);
cv::Scalar temp = mean(result);
For the second part, it is ok to add it like that, however if you have not fixed the previous error... I think it should give you segmentation error at some point or weird results if you are lucky. Finally the result part you have this:
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
but corow should be the non zero points of the mask, so it should be:
corow = cv::countNonZero(mask);
cout << "$$ Red(R), Green(G), Blue(B) $$" << " \n\n";
cout << "avg_R: " << r / corow << " \n"; // red value
cout << "avg_G: " << g / corow << " \n"; // green value
cout << "avg_B: " << b / corow << " \n\n"; // blue value
if not it will give you a smaller number, since it is divided with a a number that includes the black points which do not contribute.
As an extra note, you should use more OpenCV functions... in this case cv::mean does the same thing, if not you can simplify it with sum and divide like:
cv::Scalar summed = cv::sum(result);
cv::Scalar mean = summed / static_cast<double>(cv::countNonZero(mask));
std::cout << "$$ Red(R), Green(G), Blue(B) $$" << std::endl << std::endl;
std::cout << "avg_R: " << mean[2] << std::endl; // red value
std::cout << "avg_G: " << mean[1] << std::endl; // green value
std::cout << "avg_B: " << mean[0] << std::endl << std::endl; // blue value
This is assuming you did the star.copyTo(result, mask); line
Read about cv::Mat::at: first row, second col. Not (x, y)!
See you on cv::mean: it can work with mask.
Right initialization: double b = 0.0, g = 0.0, r = 0.0;
int corow = 0; And inside loop ++corow if mask > 0.
In the OpenCV Tutorial
http://docs.opencv.org/master/d6/d6d/tutorial_mat_the_basic_image_container.html
is the following example for creating a Mat.
int sz[3] = {2,2,2};
Mat L(3,sz, CV_8UC(1), Scalar::all(0));
This works fine, but when i try to print the Mat my programm crashes.
cout << "L = " << endl << " " << L << endl << endl;
Why doesn't this work ?
Is there a way to do this without loops or splitting the Mat L ?
To print n-dim matrix you could use Matrix slice. Since 2d matrices are stored row by row, 3d matrices plane by plane and so on, you could use code:
cv::Mat sliceMat(cv::Mat L,int dim,std::vector<int> _sz)
{
cv::Mat M(L.dims - 1, std::vector<int>(_sz.begin() + 1, _sz.end()).data(), CV_8UC1, L.data + L.step[0] * 0);
return M;
}
To perform mat slice.For more dimensions you should make more slices. Example shows 3 and 4 dimension matrices:
std::cout << "3 dimensions" << std::endl;
std::vector<int> sz = { 3,3,3 };
cv::Mat L;
L.create(3, sz.data(), CV_8UC1);
L = cv::Scalar(255);
std::cout<< sliceMat(L, 1, sz);
std::cout << std::endl;
std::cout <<"4 dimensions"<< std::endl;
sz = { 5,4,3,5 };
L.create(4, sz.data(), CV_8UC1);
L = cv::Scalar(255);
std::cout << sliceMat(sliceMat(L, 1, sz),2, std::vector<int>(sz.begin() + 1, sz.end()));
end result screen
I am using OpenCV version 2.4.9 and Visual Studio 2015. I am sure all the dependencies between them are working, since other sample programs worked perfectly, using OpenCV libraries.
You can find here my code:
#include <opencv2/opencv.hpp>
#include <stdio.h>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/contrib/contrib.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>
#include <sstream>
using namespace cv;
using namespace std;
String face_cascade_name = "C:\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml";
String eye_cascade_name = "C:\\opencv\\sources\\data\\haarcascades\\haarcascade_eye.xml";
Mat faceDetect(Mat img);
CascadeClassifier face_cascade;
CascadeClassifier eyes_cascade;
using namespace cv;
using namespace std;
enum EmotionState_t {
SERIOUS = 0, // 0
SMILE, // 1
SURPRISED, // 2
};
static void read_csv(const string& filename, vector<Mat>& images,
vector<int>& labels, char separator = ';') {
std::ifstream file(filename.c_str(), ifstream::in);
if (!file) {
string error_message = "No valid input file was given, please check the given filename.";
CV_Error(CV_StsBadArg, error_message);
}
string line, path, classlabel;
while (getline(file, line)) {
stringstream liness(line);
getline(liness, path, separator);
getline(liness, classlabel);
if (!path.empty() && !classlabel.empty()) {
images.push_back(imread(path, 0));
labels.push_back(atoi(classlabel.c_str()));
}
}
}
int main(int argc, const char *argv[])
{
EmotionState_t emotion;
if (!face_cascade.load(face_cascade_name)) {
printf("--(!)Error loading\n"); return -1; };
if (!eyes_cascade.load(eye_cascade_name)) {
printf("--(!)Error loading\n"); return -1; };
// 0 is the ID of the built-in laptop camera, change if you want to useother camera
VideoCapture cap(0);
//check if the file was opened properly
if (!cap.isOpened())
{
std::cout << "Capture could not be opened succesfully" << endl;
return -1;
}
else
{
std::cout << "camera is ok.. Stay 2 ft away from your camera\n" << endl;
}
int w = 432;
int h = 240;
cap.set(CV_CAP_PROP_FRAME_WIDTH, w);
cap.set(CV_CAP_PROP_FRAME_HEIGHT, h);
Mat frame;
cap >> frame;
std::cout << "processing the image...." << endl;
Mat testSample = faceDetect(frame);
// Get the path to your CSV.
string fn_csv = "C:\\Users\\Omar\\Downloads\\test_canny\\my_csv.txt";
// These vectors hold the images and corresponding labels.
vector<Mat>* images;
images = new vector<Mat>;
vector<int>* labels;
labels = new vector<int>;
// Read in the data. This can fail if no valid
// input filename is given.
try
{
read_csv(fn_csv, *images, *labels);
}
catch (cv::Exception& e) {
cerr << "Error opening file \"" << fn_csv << "\". Reason: "
<< e.msg << endl;
// nothing more we can do
exit(1);
}
// Quit if there are not enough images for this demo.
if ((*images).size() <= 1)
{
string error_message = "This demo needs at least 2 images to work.Please add more images to your data set!";
CV_Error(CV_StsError, error_message);
}
// Get the height from the first image. We'll need this
// later in code to reshape the images to their original
// size:
int height = (*images)[0].rows;
// The following lines create an Fisherfaces model for
// face recognition and train it with the images and
// labels read from the given CSV file.
// If you just want to keep 10 Fisherfaces, then call
// the factory method like this:
//
// cv::createFisherFaceRecognizer(10);
//
// However it is not useful to discard Fisherfaces! Please
// always try to use _all_ available Fisherfaces for
// classification.
//
// If you want to create a FaceRecognizer with a
// confidence threshold (e.g. 123.0) and use _all_
// Fisherfaces, then call it with:
//
// cv::createFisherFaceRecognizer(0, 123.0);
//
Ptr<FaceRecognizer> model = createFisherFaceRecognizer();
model->train(*images, *labels);
// The following line predicts the label of a given
// test image:
int predictedLabel = model->predict(testSample);
// To get the confidence of a prediction call the model with:
//
// int predictedLabel = -1;
// double confidence = 0.0;
// model->predict(testSample, predictedLabel, confidence);
//
string result_message = format("Predicted class = %d", predictedLabel);
std::cout << result_message << endl;
// giving the result
switch (predictedLabel)
{
case SMILE:
std::cout << "You are happy!" << endl;
break;
case SURPRISED:
std::cout << "You are surprised!" << endl;
break;
case SERIOUS:
std::cout << "You are serious!" << endl;
break;
}
return 0;
}
Mat faceDetect(Mat img)
{
std::vector<Rect>* faces;
faces = new vector<Rect>;
std::vector<Rect>* eyes;
eyes = new vector<Rect>;
bool two_eyes = false;
bool any_eye_detected = false;
//detecting faces
face_cascade.detectMultiScale(img, *faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE,
Size(30, 30));
if ((*faces).size() == 0)
{
std::cout << "Try again.. I did not dectected any faces..." << endl;
exit(-1); // abort everything
}
Point p1 = Point(0, 0);
for (size_t i = 0; i < (*faces).size(); i++)
{
// we cannot draw in the image !!! otherwise will mess with the prediction
// rectangle( img, faces[i], Scalar( 255, 100, 0 ), 4, 8, 0 );
Mat frame_gray;
cvtColor(img, frame_gray, CV_BGR2GRAY);
// croping only the face in region defined by faces[i]
std::vector<Rect>* eyes;
eyes = new vector<Rect>;
Mat faceROI = frame_gray((*faces)[i]);
//In each face, detect eyes
eyes_cascade.detectMultiScale(faceROI, *eyes, 1.1, 3, 0
| CV_HAAR_SCALE_IMAGE, Size(30, 30));
for (size_t j = 0; j < (*eyes).size(); j++)
{
Point center((*faces)[i].x + (*eyes)[j].x + (*eyes)[j].width*0.5,
(*faces)[i].y + (*eyes)[j].y + (*eyes)[j].height*0.5);
// we cannot draw in the image !!! otherwise will mess with the prediction
// int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
// circle( img, center, radius, Scalar( 255, 0, 0 ), 4, 8, 0 );
if (j == 0)
{
p1 = center;
any_eye_detected = true;
}
else
{
two_eyes = true;
}
}
}
std::cout << "SOME DEBUG" << endl;
std::cout << "-------------------------" << endl;
std::cout << "faces detected:" << (*faces).size() << endl;
std::cout << "x: " << (*faces)[0].x << endl;
std::cout << "y: " << (*faces)[0].y << endl;
std::cout << "w: " << (*faces)[0].width << endl;
std::cout << "h: " << (*faces)[0].height << endl << endl;
Mat imageInRectangle;
imageInRectangle = img((*faces)[0]);
Size recFaceSize = imageInRectangle.size();
std::cout << recFaceSize << endl;
// for debug
imwrite("C:\\Users\\Omar\\Downloads\\test_canny\\imageInRectangle.jpg", imageInRectangle);
int rec_w = 0;
int rec_h = (*faces)[0].height * 0.64;
// checking the (x,y) for cropped rectangle
// based in human anatomy
int px = 0;
int py = 2 * 0.125 * (*faces)[0].height;
Mat cropImage;
std::cout << "faces[0].x:" << (*faces)[0].x << endl;
p1.x = p1.x - (*faces)[0].x;
std::cout << "p1.x:" << p1.x << endl;
if (any_eye_detected)
{
if (two_eyes)
{
std::cout << "two eyes detected" << endl;
// we have detected two eyes
// we have p1 and p2
// left eye
px = p1.x / 1.35;
}
else
{
// only one eye was found.. need to check if the
// left or right eye
// we have only p1
if (p1.x > recFaceSize.width / 2)
{
// right eye
std::cout << "only right eye detected" << endl;
px = p1.x / 1.75;
}
else
{
// left eye
std::cout << "only left eye detected" << endl;
px = p1.x / 1.35;
}
}
}
else
{
// no eyes detected but we have a face
px = 25;
py = 25;
rec_w = recFaceSize.width - 50;
rec_h = recFaceSize.height - 30;
}
rec_w = ((*faces)[0].width - px) * 0.75;
std::cout << "px :" << px << endl;
std::cout << "py :" << py << endl;
std::cout << "rec_w:" << rec_w << endl;
std::cout << "rec_h:" << rec_h << endl;
cropImage = imageInRectangle(Rect(px, py, rec_w, rec_h));
Size dstImgSize(70, 70); // same image size of db
Mat finalSizeImg;
resize(cropImage, finalSizeImg, dstImgSize);
// for debug
imwrite("C:\\Users\\Omar\\Downloads\\test_canny\\onlyface.jpg", finalSizeImg);
cvtColor(finalSizeImg, finalSizeImg, CV_BGR2GRAY);
return finalSizeImg;
}
I've debugged it and the error only pops up when I reach return 0 in the main method.
And here's an image of the error (too large to embed)
Any help will be appreciated.
Assume:
There are N files on disk that require face detection.
If I do it in "slow" mode all is well (faces are faces at their rects):
for(auto mat : getMyMats()) {
CascadeClassifier facer("haarcascade_frontalface_alt.xml");
vector<Rect> faces;
facer.detectMultiScale(mat, faces);
}
But if i try to cache and reuse CascadeClassifier - near by 50% image are detected very wrong - invalid place, size, count of faces:
CascadeClassifier facer("haarcascade_frontalface_alt.xml");
for(auto mat : getMyMats()) {
vector<Rect> faces;
facer.detectMultiScale(mat, faces);
}
What's wrong with CascadeClassifier reusing
Windows, OpenCV 3.0 both debug and release build.
SAMPLE CODE TO REPRODUCE:
// compiler - opencv/include referenced
// linker - opencv/lib referenced and
// opencv_core300.lib, opencv_imgproc300.lib, opencv_imgcodecs300.lib, opencv_objdetect300.lib (or world or debug libs) are referenced
// 1.png, 2.png, 3.png,
#include <vector>
#include <string>
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace cv;
using namespace std;
//count faces in load-every-time call
int no_reuse_call() {
int cnt = 0;
for (auto n : { "1.png","2.png","3.png" }) {
vector<Rect> faces{};
CascadeClassifier facer("haarcascade_frontalface_alt.xml");
Mat m = imread(n, CV_LOAD_IMAGE_GRAYSCALE);
facer.detectMultiScale(m, faces, 1.1, 3, CASCADE_SCALE_IMAGE, Size(50, 50));
cnt += faces.size(); //vector given to detectMultiScale cleared in detectMultiScale
cout << faces[0] << endl;
}
for (auto n : { "1.png","2.png","3.png" }) {
vector<Rect> faces{};
CascadeClassifier facer("haarcascade_frontalface_alt.xml");
Mat m = imread(n, CV_LOAD_IMAGE_GRAYSCALE);
facer.detectMultiScale(m, faces, 1.1, 3, CASCADE_SCALE_IMAGE, Size(50, 50));
cnt += faces.size(); //vector given to detectMultiScale cleared in detectMultiScale
cout << faces[0] << endl;
}
return cnt;
}
//coutn faces in one-instance call
int reuse_call() {
int cnt = 0;
CascadeClassifier facer("haarcascade_frontalface_alt.xml");
for (auto n : { "1.png","2.png","3.png" }) {
vector<Rect> faces{};
Mat m = imread(n, CV_LOAD_IMAGE_GRAYSCALE);
facer.detectMultiScale(m, faces, 1.1, 3, CASCADE_SCALE_IMAGE, Size(50, 50));
cnt += faces.size(); //vector given to detectMultiScale cleared in detectMultiScale
cout << faces[0] << endl;
}
for (auto n : { "1.png","2.png","3.png" }) {
vector<Rect> faces{};
Mat m = imread(n, CV_LOAD_IMAGE_GRAYSCALE);
facer.detectMultiScale(m, faces, 1.1, 3, CASCADE_SCALE_IMAGE, Size(50, 50));
cnt += faces.size(); //vector given to detectMultiScale cleared in detectMultiScale
cout << faces[0] << endl;
}
return cnt;
}
int reuse_call_reverse() {
int cnt = 0;
CascadeClassifier facer("haarcascade_frontalface_alt.xml");
for (auto n : {"3.png" ,"2.png", "1.png"}) {
vector<Rect> faces{};
Mat m = imread(n, CV_LOAD_IMAGE_GRAYSCALE);
facer.detectMultiScale(m, faces, 1.1, 3, CASCADE_SCALE_IMAGE, Size(50, 50));
cnt += faces.size(); //vector given to detectMultiScale cleared in detectMultiScale
cout << faces[0] << endl;
}
for (auto n : { "3.png" ,"2.png", "1.png" }) {
vector<Rect> faces{};
Mat m = imread(n, CV_LOAD_IMAGE_GRAYSCALE);
facer.detectMultiScale(m, faces, 1.1, 3, CASCADE_SCALE_IMAGE, Size(50, 50));
cnt += faces.size(); //vector given to detectMultiScale cleared in detectMultiScale
cout << faces[0] << endl;
}
return cnt;
}
int main() {
cout << "Created every image 2*3 files" << endl;
cout << no_reuse_call() << endl;
cout << "Created one time and files in alphabet order 2*3 files" << endl;
cout << reuse_call() << endl;
cout << "Created one time and files in contra - alphabet order 2*3 files" << endl;
cout << reuse_call_reverse() << endl;
cout << "You can see:" << endl;
cout << " if cascade is reused - detection in 3.png file is stabily wrong if it is processed after 1.png and 2.png" << endl;
cout << " it's depend on order - if 3.png is first in row it detected right even in second call after 1.png and 2.png" << endl;
cout << " it's random and based somehow on image - 1.png and 2.png are always detected validly" << endl;
return 0;
}
Look at https://github.com/Itseez/opencv/issues/5475
due to some reasones ocl in opencv 3.0.0 is not (stable?)
so author recomend to call setUseOpenCL(false); in programm - with this option all work well