I' m trying to create my own SVM classifier, and i stuck at the beginning. I have two sets of data for positive and negative images, and i want to create training matrix, to pass it into SVM. So i read this fundamental post, and tried to do as it says:
char* path_positive, path_negative;
int numPos, numNeg;
int imageWidth=130;
int imageHeight=160;
numPos= 176;
numNeg= 735;
path_positive= "C:\\SVM\\positive\\";
path_negative= "C:\\SVM\negative\\";
Mat classes(numPos+numNeg, 1, CV_32FC1);
Mat trainingMat(numPos+numNeg, imageWidth*imageHeight, CV_32FC1 );
vector<int> trainingLabels;
for(int file_num=0; file_num < numPos; file_num++)
{
stringstream ss(stringstream::in | stringstream::out);
ss << path_positive << file_num << ".jpg";
Mat img = imread(ss.str(), CV_LOAD_IMAGE_GRAYSCALE);
int ii = 0; // Current column in training_mat
for (int i = 0; i<img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
trainingMat.at<float>(file_num,ii++) = trainingMat.at<uchar>(i,j);
}
}
trainingLabels.push_back(1);
}
But when i start this code i got Assertation failderror:
I know, that i made some stupid mistake, because i'm very new to openCV.
Thanks for any help.
EDIT: The error occurs on this :
trainingMat.at(file_num,ii++) = trainingMat.at(i,j);
Related
i want to change brightness of image, only accessing pixel value.
not using opencv function(ex. convertTo)
input : image , num
num means constant value for brightness
here is my code and result looks wierd.
Is there any problem?
original
result
cv::Mat function(cv::Mat img, int num){
cv::Mat output;
output = cv::Mat::zeros(img.rows, img.cols, img.type());
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
for (int c = 0; c < img.channels(); c++)
{
output.at<cv::Vec3b>(i, j)[c] = img.at<cv::Vec3b>(i, j)[c] + num;
if (output.at<cv::Vec3b>(i, j)[c] > 255){
output.at<cv::Vec3b>(i, j)[c] = 255;
}
else if (output.at<cv::Vec3b>(i, j)[c] < 0)
{
output.at<cv::Vec3b>(i, j)[c] = 0;
}
}
}
}
cv::imshow("output", output);
cv::waitKey(0);
return img;
}
not using opencv function
that's somewhat silly, since your code is already using opencv's data structures.
trying to do so, you also reinvented the wheel, albeit a slightly square one ...
Check for overflow before assigning to output.
yea that's the problem. correct way to do it would be: assign the sum to something larger than uchar, and then check
else if (output.at<cv::Vec3b>(i, j)[c] < 0)
this will never happen, try to understand why
but please note, that your whole code (triple loop, omg !!!) could be rewritten as a simple:
Mat output = img + Scalar::all(num);
(faster, safer, & this will also saturate correctly !)
I am working on a project regarding the matching of consecutive images in order to find the panoramic image.
I found the keypoints with SIFT and the matches between couple of images using BFMatcher, but after removing the bad matches I am not able to keep the corresponding keypoints, or even to display the matches using cv::drawMatches because the program crashes.
The inital part of the code is the following, and it works.
("image" is a vector containing all the images)
cv::Mat descriptors;
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::Mat> descriptors_array;
std::vector<std::vector<cv::KeyPoint>> keypoints_array, reduced_keypoints_array;
cv::Ptr<cv::Feature2D> sift = cv::xfeatures2d::SIFT::create();
for(int i=0; i<N_images; i++){
sift->detectAndCompute(image.at(i), cv::Mat(), keypoints, descriptors);
keypoints_array.push_back(keypoints);
descriptors_array.push_back(descriptors);
}
std::vector<std::vector<cv::DMatch>> matches_array,
std::vector<cv::DMatch> matches, good_matches;
cv::Ptr<cv::BFMatcher> matcher = cv::BFMatcher::create(cv::NORM_L2, true);
for(int i=0; i<N_images-1; i++){
matcher->match(descriptors_array.at(i), descriptors_array.at(i+1), matches, cv::Mat());
for(int j=0; j<matches.size(); j++){
if (min_distance > matches.at(j).distance){
min_distance = matches.at(j).distance;
}
}
for( int k = 0; k <descriptors_array.at(i).rows; k++) {
if( matches[k].distance < 3*min_distance) {
good_matches.push_back(matches[k]);
}
}
matches_array.push_back(good_matches);
}
I am having problem with this code, when i want to just keep the good keypoints, which corresponds to the matches_array.
for(int i=0; i<keypoints_array.size()-1; i++){
reduced_keypoints_array.push_back(std::vector<cv::KeyPoint>());
for(int j=0; j<matches_array.at(i).size(); j++){
reduced_keypoints_array.at(i).push_back(cv::KeyPoint());
reduced_keypoints_array.at(i).at(j) = keypoints_array.at(i).at(matches_array.at(i).at(j).queryIdx);
}
}
And here i wanted to display the matches, but it also crashes because my matches are more than the keypoints since I couldn't reduce the number of keypoints.
cv::Mat out;
for(int i=0; i<keypoints_array.size()-1; i++){
cv::drawMatches(image.at(i), keypoints_array.at(i), image.at(i+1), keypoints_array.at(i+1),matches_array.at(i), out2, cv::Scalar::all(-1), cv::Scalar::all(-1), std::vector< char >(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
cv::imshow("matches", out);
cv::waitKey(0);
}
How can i keep only the corresponding keypoints? Or just draw the matches without removing the keypoints?
I edited some parts that I will mark in the code. I would not recommend the data structures you are using, since it is very hard to read them. Consider creating typedefs or structs when you have multiple vectors of vectors. I used ORB since I did not install SIFT at the moment. Here is an example for three images:
int main(int argc, char** argv)
{
// Reading my images and insert them into a vector
std::vector<cv::Mat> image;
cv::Mat img1 = cv::imread("1.png", cv::IMREAD_GRAYSCALE);
cv::Mat img2 = cv::imread("2.png", cv::IMREAD_GRAYSCALE);
cv::Mat img3 = cv::imread("3.png", cv::IMREAD_GRAYSCALE);
image.push_back(img1);
image.push_back(img2);
image.push_back(img3);
int N_images = (int)image.size();
cv::Mat descriptors;
std::vector<cv::KeyPoint> keypoints;
std::vector<cv::Mat> descriptors_array;
std::vector<std::vector<cv::KeyPoint>> keypoints_array, reduced_keypoints_array;
// Here I used ORB
cv::Ptr<cv::ORB> orb = cv::ORB::create();
for (int i = 0; i < N_images; i++) {
orb->detectAndCompute(image.at(i), cv::Mat(), keypoints, descriptors);
keypoints_array.push_back(keypoints);
descriptors_array.push_back(descriptors);
}
std::vector<std::vector<cv::DMatch>> matches_array;
std::vector<cv::DMatch> matches, good_matches;
cv::Ptr<cv::BFMatcher> matcher = cv::BFMatcher::create(cv::NORM_L2, true);
// I created a vector of pairs of keypoints to push them into an array similar to the good matches
std::vector<std::pair<cv::KeyPoint, cv::KeyPoint>> good_keypoint_pairs_array;
std::vector<std::vector<std::pair<cv::KeyPoint, cv::KeyPoint>>> keypoint_pairs_array;
float min_distance = 1000;
for (int i = 0; i < N_images-1 ; i++) {
matcher->match(descriptors_array[i], descriptors_array.at(i + 1), matches, cv::Mat());
// I left that part out since I got always a number of 0 matches, no matter which min_distance I used
/*for (int j = 0; j < matches.size(); j++) {
if (min_distance > matches.at(j).distance) {
min_distance = matches.at(j).distance;
}
}*/
for (int k = 0; k < descriptors_array.at(i).rows; k++) {
if (matches[k].distance < 3 * min_distance) {
good_keypoint_pairs_array.push_back(std::make_pair(keypoints_array.at(i).at(k), keypoints_array.at(i + 1).at(k)));
good_matches.push_back(matches[k]);
}
}
keypoint_pairs_array.push_back(good_keypoint_pairs_array);
matches_array.push_back(good_matches);
}
cv::Mat out;
// I create my keypoint vectors to use them for the cv::drawMatches function
std::vector<cv::KeyPoint> kp_1, kp_2;
for (int i = 0; i < keypoint_pairs_array.size(); ++i) {
for (int j = 0; j < keypoint_pairs_array[i].size(); ++j) {
kp_1.push_back(keypoint_pairs_array[i][j].first);
kp_2.push_back(keypoint_pairs_array[i][j].second);
}
cv::drawMatches(image.at(i), kp_1, image.at(i + 1), kp_2, matches_array.at(i), out, cv::Scalar::all(-1), cv::Scalar::all(-1), std::vector< char >(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
cv::imshow("matches", out);
cv::waitKey(0);
kp_1.clear();
kp_2.clear();
}
}
I am having problem with this code, when i want to just keep the good keypoints, which corresponds to the matches_array.
As you already mentioned, it is very important that the size of the std::vector<cv::DMatch> is always similar to the size of the std::vector<cv::KeyPoint> So you have to save your keypoints in the same loop where you use your number of matches as shown.
I have been working on MNIST Dataset to classify the handwritten digit images. I can read the images and calculate histograms of them. Then, I pushed back Mat of histograms into vector. I could not implement K-Means Clustering Algorithm method (kmeans()) because the first argument of method(InputArray) leads to some error
OpenCV 3.0 has been used.
vector<Mat> histogram_list;
//some implementations
int clusterCount = 10;
Mat labels, centers;
int attempts = 5;
kmeans(samplingHist(histogram_list, h_bins, s_bins), clusterCount, labels,
TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 10000, 0.0001),
attempts, KMEANS_PP_CENTERS, centers);
Is there any advice to solve this problem?
EDIT:
mat.int.hpp file. The error about assertion failed something.... and it returns me to that piece of code.
template<typename _Tp> inline_Tp& Mat::at(int i0, int i1) {
CV_DbgAssert(dims <= 2);
CV_DbgAssert(data);
CV_DbgAssert((unsigned)i0 < (unsigned)size.p[0]);
CV_DbgAssert((unsigned)(i1 * DataType<_Tp>::channels) < (unsigned)(size.p[1] * channels()));
CV_DbgAssert(CV_ELEM_SIZE1(DataType<_Tp>::depth) == elemSize1());
return ((_Tp*)(data + step.p[0] * i0))[i1];
}
I try to solve the problem with sampling to Mat from vector. However it could not help to solve it. Where do I make a mistake?
Mat samplingHist(vector<Mat> &vec, int h, int s) {
Mat samples(vec.size(), h * s, CV_32F);
for (int k = 0; k < vec.size(); k++)
for (int y = 0; y < h; y++)
for (int x = 0; x < s; x++)
samples.at<float>(k, y* s + x) = vec[k].at<float>(y, x);
return samples;
}
You are passing wrong parameters. The documentation of says InputArray data must be of type Mat or at least std::vector<cv::Point2f>, not std::vector<Mat>.
Take a look at this example:
http://docs.opencv.org/3.1.0/de/d63/kmeans_8cpp-example.html
I have tested the following two snippets and they give a different result. The second one is right. I do not understand why it is and I wonder if there is a bug in opencv?
A result matrix f_sub is different in those examples.
1)
Mat f = Mat::zeros(96,112,CV_8UC1);
randu(f,0,255);
Mat f_sub = f(cv::Rect(17,14,78,68));
//mat2File("f.mm",f,1);
//mat2File("f_sub.mm",f_sub,1);
exit(0);
2)
Mat f = Mat::zeros(96,112,CV_8UC1);
randu(f,0,255);
Mat f_sub = f(cv::Rect(17,14,78,68)).clone();
//mat2File("f.mm",f,1);
//mat2File("f_sub.mm",f_sub,1);
exit(0);
The mat2File is just print a mat into a file
void mat2File(string filename, Mat M, int y)
{
ofstream fout(filename.c_str());
//fout << M.rows<<" "<<M.cols<<endl;
uchar *M_ptr = (uchar*)M.ptr();
for(size_t i=0; i<M.rows; i++)
{
fout<<endl;
for(size_t j=0; j<M.cols; j++)
{
fout<< (size_t)M_ptr[i*M.cols+j]<<" ";
}
}
}
The mat2File seems to be a culprit.
M_ptr[i*M.cols+j] is incorrect for non-continuous matrices because pitch between matrix lines is greater than M.cols. You'd better use M.at<uchar>(y,x) to access Mat pixels.
I am working on fungus spores detection. I have about 359 positive images(cropped images of fungus particles) and 171 negative images(cropped images of dust particles).
the actual size of pos/nag images is same as the size of particle which is around 8x8 but for SVM training I have resize it to 30x30 and then have used resized images to train SVM classifier. I am directly training the SVM with images not with any features.
After training the classifier successfully I used hog.setSVMDetector(myclassifier) and hog.detectMultiScale but the found objects is zero.
Now my questions is what I am doing wrong in my code?
Please click on the below link for Nagative/Positive images which I used for SVM classifier, test images and the desired particles image.
https://drive.google.com/folderview?id=0B7yRjtOGywg7fnNJTXcxR2NYb3ItWTZ0UjYwUmt6YW96R0NHLWZQbVJucEk0bnJmcFJGS0E&usp=sharing
class LinearSVM: public CvSVM {
public:
void getSupportVector(std::vector<float>& support_vector) const;
};
void LinearSVM::getSupportVector(std::vector<float>& support_vector) const {
int sv_count = this->get_support_vector_count();
// mexPrintf("size : %d\n",support_vector.size());
const CvSVMDecisionFunc* df = this->decision_func;
const double* alphas = df[0].alpha;
double rho = df[0].rho;
int var_count = this->get_var_count();
support_vector.resize(var_count, 0);
for (unsigned int r = 0; r < (unsigned)sv_count; r++) {
float myalpha = alphas[r];
const float* v = this->get_support_vector(r);
for (int j = 0; j < var_count; j++,v++) {
support_vector[j] += (-myalpha) * (*v);
}
}
support_vector.push_back(rho);
}
////////////////////////////// main.cpp ///////////////////////////
int num_files = 359*171;
int img_area = 30*30;
Mat training_mat(num_files,img_area,CV_32FC1);
vector<int> training_Labels;
Mat labels(num_files,1,CV_32FC1);
int imagenum=0;
for (int pimageNum = 0; pimageNum < 359; pimageNum++)
{
// reading Positive Images from directory and resize it to 30x30
int ii = 0; // Current column in training_mat
for (int i = 0; i<posImage.rows; i++) {
for (int j = 0; j < posImage.cols; j++) {
training_mat.at<float>(imagenum,ii++) = posImage.at<uchar>(i,j);
imagenum++;
}
}
training_Labels.push_back(1.0);
}
for (int nimageNum = 0; nimageNum < 171; nimageNum++)
{
// reading Nagative Images from directory and resize it to 30x30
int ii = 0; // Current column in training_mat
for (int i = 0; i<nagImage.rows; i++) {
for (int j = 0; j < nagImage.cols; j++) {
training_mat.at<float>(imagenum,ii++) = nagImage.at<uchar>(i,j);
imagenum++;
}
}
training_Labels.push_back(-1.0);
}
Mat(training_Labels).copyTo(labels);
labels.convertTo(labels, CV_32FC1);
CvSVMParams SVM_params;
SVM_params.svm_type = CvSVM::C_SVC;
SVM_params.kernel_type = CvSVM::LINEAR; //CvSVM::LINEAR;
SVM_params.degree = 0;
SVM_params.gamma = 3;
SVM_params.coef0 = 0;
SVM_params.C = 1;
SVM_params.nu = 0;
SVM_params.p = 0;
SVM_params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 10, 0.03);
//Train SVM
LinearSVM svmClassifier;
svmClassifier.train(training_mat, labels, Mat(), Mat(), SVM_params);
svmClassifier.save("D:\\svmClassifier.yml");
HOGDescriptor hog;
hog.winSize = Size(8, 8);
std::vector<float> support_vector;
FileStorage fs;
fs.open("D:\\svmClassifier.yml", FileStorage::READ);
fs["support_vectors"] >> support_vector;
hog.setSVMDetector(support_vector);
vector< Rect> found,found_filtered;
Size padding(Size(0, 0));
Size winStride(Size(8, 8));
cv::Mat test=cv::imread("testimage.bmp",CV_LOAD_IMAGE_GRAYSCALE);
// actual size of test image is 1024x768 which I resize to 400x 300
hog.detectMultiScale(test, found, 0.0, winStride, padding, 1.01, 0);
cout<<""found particles"<< found.size() ; // it is always zero
size_t i, j;
for (i=0; i<found.size(); i++)
{
Rect r = found[i];
for (j=0; j<found.size(); j++)
if (j!=i && (r & found[j])==r)
break;
if (j==found.size())
found_filtered.push_back(r);
}
for (i=0; i<found_filtered.size(); i++)
{
Rect r = found_filtered[i];
r.x += cvRound(r.width*0.1);
r.width = cvRound(r.width*0.8);
r.y += cvRound(r.height*0.06);
r.height = cvRound(r.height*0.9);
rectangle(test, r.tl(), r.br(), cv::Scalar(0,255,0), 2);
}
imshow("detected particles", test);
waitKey(0);
I haven't managed to get your classifier to work, but I have found the cause of your current issue.
I have put up a gist here so you can check it out. I have used my own filereading methods to read in the images, so you will have to make one or two changes so that it works for you.
The reason that you get no found objects is because your classifier vector is empty. That is due to incorrect reading of the node from the yml file. 'support_vectors' is a subnode of 'my_svm'. You were trying to read it as a top-level node, and were getting back an empty vector.
There are also a couple of other errors in the filereading loops which I have fixed (marked FIXED) - at least, they seemed incorrect to me.
This still won't correctly classify (yet). There is some issue with the format of your support vectors array, which breaks the hog descriptor.
So you'll need to put a bit more work into this, but hopefully this moves you in the right direction.
I have added a slider to the output window. When you get the classifier to work, you can change the scaleFactor with the slider which should yield interesting variation in the results.