Can I increase number of SVM training label in OpenCV? - c++

I'm building OCR application with Visual Studio 2010, C++, SVM in OpenCV. It's ok when I train SVM with under 181 different labels but fails when over 181 labels. Below is IDE and OpenCV error message and my code. Please help me, thank you so much!
IDE error message
First-chance exception at 0x771e4b32 in OCR.exe: Microsoft C++
exception: cv::Exception at memory location 0x0081da74.. The thread
'Win32 Thread' (0xdac) has exited with code -1073741510 (0xc000013a).
The program '[2512] OCR.exe: Native' has exited with code -1073741510
(0xc000013a).
OpenCV error message
......\src\opencv\modules\core\src\datastructs.cpp:332: error: (-211)
requested size is negative or too big
SVM's configuration
CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::LINEAR;
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);
SVM.train( training_vectors, training_labels, cv::Mat(), cv::Mat(), params );

libSVM uses "one vs all" technique to represent multi-class problem using the binary SVM classifier. It means, that if you have N (>2) labels, libSVM will generate N distinct classifiers, each with different data labeling (so it expresses the "one vs all" scheme). This can lead to the memory problems that you are experiencing. Some other models like for example neural networks or knn can represent the multi-class classification without such overhead. So if your data is to large to treat it the way libsvm does you have at least three possible options:
Change SVM to some other model, which can directly addresss mutli-label classification
Try to use other, lighter implementation of the library, especially that opencv does not use the most recent implementation of libsvm (it could help, but does not have to)
You can manually do the "one vs all" implementation and save each separate model. This way you should avoid the memory problems, as at any time you will allocate at most as much memory as it is needed for a binary problem. At the end you just load your models from file and apply simple voting scheme. If saved models are too big, it means that your model overfitted (in SVMs the overfitting is usually expressed as too big number of support vectors, which are in fact the only thing needed to define the model - so if the model has too many sv's to load them to memory it means that it is most probably incorrectly trained and you should try different parameters/kernels)

Related

Can an ONNX network be incompatible with onnxruntime?

I am having trouble running inference on an ONNX model, either by making (tiny) adjustments to this Windows ML tutorial, or by implementing my own ONNX Runtime code following their MNIST Tutorial. As I understand it, Windows ML makes use of ONNX Runtime, so both efforts probably end up in the same place... and probably generating the same underlying exception for the same reason.
The exceptions thrown are either unintelligible (a second exception thrown during exception handling by the looks...) or not identified at all. It makes me wonder if the network itself is faulty or incompatible in some sense. The network was produced by taking a saved Tensorflow/Keras model and running this conversion:
python -m tf2onnx.convert --saved-model MyNet --output MyNet.onnx --inputs-as-nchw mobilenetv2_1_00_224_input:0
The result is a network that is rendered by Netron with the following input & output stages:
Is there anything about this network that is obviously incompatible with ONNX Runtime? Any suggestions on how to push past either/both of these exceptions?
Turns out that in my attempt to adapt Windows ML example, I had the output shape wrong - in that example, the output shape is 1 x 1000 x 1 x 1. I had copied/pasted this, and just modified the 1000 to suit. Clearly the network above needs a 1 x 10 shape....

extracting output data from typed_output_tensor in TFlite

Thanks in advance for your support.
I'm trying to get the output of a tensor after the inference on a .tflite U-net neural network. I'm using Tensorflow lite image classification code as a baseline.
I need to adapt the code for a segmentation task. My question is how I can access the output of the inferenced model (which 128x128x1) and write the result into an image?
I already debugged the code and explored many different approaches. Unfortunately, I'm not confident with the C++ language. What I found is that the command: interpreter->typed_output_tensor<float>(0) should be what I need, as also referenced here: https://www.tensorflow.org/lite/guide/inference#loading_a_model. However, I cannot access the 128x128 tensor generated by the network.
You can find the code at the address: https://github.com/tensorflow/tensorflow/blob/770481fb3e9126f9a29db5667f528e450d54d719/tensorflow/lite/examples/label_image/label_image.cc
The interesting part is here (lines 217 -224):
const float threshold = 0.001f;
std::vector<std::pair<float, int>> top_results;
int output = interpreter->outputs()[0];
TfLiteIntArray* output_dims = interpreter->tensor(output)->dims;
// assume output dims to be something like (1, 1, ... ,size)
auto output_size = output_dims->data[output_dims->size - 1];
I expect the values saved in an image or an alternative way of saving the output tensor

Error when using Decision Trees in OpenCV 3.0.0-rc1

I am doing some machine learning in OpenCV and i'm using Decision Trees. I am currently using OpenCV 3.0.0-rc1. Whenever i attempt to train Decision Trees with my training data and labels, i get either
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
or
Segmentation fault
Depending on what i put into setMaxDepth(); if the number is larger than 22, it's bad_alloc, else it's seg fault.
Here's my source code:
//import data
Mat trainData=imread("/home/jetson/Documents/CB/ml/td.jpg",CV_LOAD_IMAGE_GRAYSCALE);
Mat labels=imread("/home/jetson/Documents/CB/ml/lab.jpg",CV_LOAD_IMAGE_GRAYSCALE);
//convert to the right type
trainData.convertTo(trainData,CV_32FC1);
labels.convertTo(labels,CV_32SC1);
transpose(trainData,trainData);
Ptr<ml::TrainData> tData = ml::TrainData::create(trainData, ml::ROW_SAMPLE, labels);
cout <<"Training data ready\n";
Ptr<ml::DTrees> dec_trees = ml::DTrees::create();
//params
dec_trees->setMaxDepth(1);
dec_trees->setMinSampleCount(10);
dec_trees->setRegressionAccuracy(0.01f);
dec_trees->setUseSurrogates(false);
dec_trees->setMaxCategories(2);
dec_trees->setCVFolds(10);
dec_trees->setUse1SERule(true);
dec_trees->setTruncatePrunedTree(true);
dec_trees->setPriors(Mat());
cout <<"Params set\n";
dec_trees->train(tData);
cout <<"Done!\n";`
In addition to this, when i try to train a SVM model with the same data, using the same steps (below) it works just fine.
Ptr<ml::SVM> svm = ml::SVM::create();
//params
svm->setType(ml::SVM::C_SVC);
svm->setKernel(ml::SVM::POLY);
svm->setGamma(3);
svm->setDegree(0.1);
cout <<"Params set\n";
svm->train(tData);
cout <<"Done!\n";
I need to point out that the error occurs when i try to train the model. I'm using the default parameters for decision trees, as suggested on the OpenCV documentation page.
Does anybody know what's wrong here and how to go about fixing my problem?
Thanks in advance.
EDIT: I upgraded OpenCV to version 3.0.0 and the issues stay the same

Opencv classifier update error

I am getting an error while trying to update the CvBoost classifier in OpenCV, the error i am getting is as follows
OpenCV Error: Bad argument (The new training data must have the same types and the input and output variables and the same categories for categorical variables) in CvDTreeTrainData::set_data, file /home/bsoni/Downloads/OpenCV-2.4.1/modules/ml/src/tree.cpp, line 172
Basically i am working on a 2 class problem and initially i train the classifier with a set of SURF features. So the process is that i initially train the classifier using a set of surf descriptors.
data.surf_features are a set of 128 bit SURF descriptors
data.surf_classes are a set of class labels which are either +1 or -1
Initially i train the classifier using
void train()
{
CvBoostParams params(CvBoost::REAL,80,0.95,2,false,0);
aSurfBoost.train(data.surf_features,CV_ROW_SAMPLE,data.surf_classes,Mat(),Mat(),Mat(),Mat(),params,false);
}
following that i try to re-train the classifier using the code below
void train()
{
CvBoostParams params(CvBoost::REAL,80,0.95,2,false,0);
aSurfBoost.train(data.surf_features,CV_ROW_SAMPLE,data.surf_classes,Mat(),Mat(),Mat(),Mat(),params,true);
}
the only think i am changing is setting the update parameter to true.
I have checked the Mat.type of the descriptors and in both cases they are the exact same thing.
any suggestions solutions or possibly even workarounds would be welcome.

OpenCV (C++) using SIFT descriptors increases the number of detected features?

I have a bit confusing situation while using the SIFT descriptors implementation from OpenCV.
I'm trying to test various feature detector + descriptor calculation methods, so I am using a combination of cv::FeatureDetector and cv::DescriptorExtractor interfaces which allow me to simply change between different detector methods and descriptors.
When calling cv::DescriptorExtractor::compute(...) (the variant for a single image), the documentation says that it is possible for the number of key points given to the algorithm to decrease if it is impossible to calculate their descriptors, and I understand how and why that is done.
But, what happens to me is that the number of key points after the descriptor computations actually increases. It is clearly so, and I'm not trying to stop it from happening, I am just hoping for an explanation as to why (just an intuitive description would be cool, altough I appreciate anything more that that).
I've got layers upon layers of wrappers around the actual OpenCV that don't have any code (just setting up some local non-OpenCV flags), so here's the OpenCV code that's being called at the bottom of it all:
cv::Ptr<cv::FeatureDetector> dect = cv::FeatureDetector::create("MSER");
cv::Mat input = cv::imread("someImg.ppm", 0);
std::vector<cv::KeyPoint> keypoints;
dect->detect(input, keypoints);
cv::Ptr<cv::DescriptorExtractor>deEx=cv::DescriptorCalculator::create("SIFT");
std::cout << "before computing, feats size " << keypoints.size() << std::endl;
// code to print out 10 features
cv::Mat desc;
deEx->compute(input, keypoints, desc);
std::cout << "after computing, feats size " << keypoints.size() << std::endl;
// code to print out 10 features
I've printed out the first 10 key points just before and after the descriptor calculations, so here are some concrete numbers as an example:
before computing, feats size 379
feat[0]: 10.7584 39.9262 176.526 0 12.5396
feat[1]: 48.2209 207.904 275.091 0 11.1319
feat[2]: 160.894 313.781 170.278 0 9.63786
feat[3]: 166.061 239.115 158.33 0 19.5027
feat[4]: 150.043 233.088 171.887 0 11.9569
feat[5]: 262.323 322.173 188.103 0 8.65429
feat[6]: 189.501 183.462 177.396 0 12.3069
feat[7]: 218.135 253.027 171.763 0 123.069
feat[8]: 234.508 353.236 173.281 0 11.8375
feat[9]: 234.404 394.079 176.23 0 8.99652
after computing, feats size 463
feat[0]: 10.7584 39.9262 13.1313 0 12.5396
feat[1]: 48.2209 207.904 69.0472 0 11.1319
feat[2]: 48.2209 207.904 107.438 0 11.1319
feat[3]: 160.894 313.781 9.57937 0 9.63786
feat[4]: 166.061 239.115 166.144 0 19.5027
feat[5]: 150.043 233.088 78.8696 0 11.9569
feat[6]: 262.323 322.173 167.259 0 8.65429
feat[7]: 189.501 183.462 -1.49394 0 12.3069
feat[8]: 218.135 253.027 -117.067 3 123.069
feat[9]: 218.135 253.027 7.44055 3 123.069
I can see from this example that the original feat[1] and feat[7] have spanned in to two new key points each, but I do not see any logical explanation for the compute method to do that :(
The printout I have given here is from using MSER for detection of keypoints, and then trying to calculate SIFT descriptors, but the same increase in size also happens with STAR, SURF, and SIFT (i.e. DoG) keypoints detected. I didn't try to change the SIFT descriptor in to something else, but if someone thinks it's relevant to the question, I'll try it and edit it in my question.
First of all, as you can see in the documentation cv::DescriptorExtractor::compute take a std::vector<cv::Keypoints> in argument which in non const. It means that this vector can be modified by cv::DescriptorExtractor::compute.
In practice, KeyPointsFilter::runByImageBorder and KeyPointsFilter::runByKeypointSize (two non-const functions) will be applied to the vector and will remove the keypoint for which a descriptor cannot be computed. No re-extraction of keypoints will be done.
You should post the few lines of code you are using for further diagnostic.
--
Well, I finally found where the problem occurs: cv::SiftDescriptorExtractor::compute method calls SIFT::operator() which (re)calculate orientation of features and also duplicates the points with several dominant orientations.
Solution could be to change descriptorParams.recalculateAngles to false.
Looks like it is due to OpenCV using Rob Hess's SIFT implementation, which sometimes duplicates the keypoints with more than one dominant orientation.
Looking around the OpenCV reported bugs did the trick, the issue was reported here.
It is not a bug, the behavior was not corrected in the newer versions but instead just documented. Since I am obliged to the OpenCV version I am using right now (v2.1), it did not occur to me to look at newer documentation for additional behavior since the behavior described in the old one made sense to me.
This is not a bug but by design:
SIFT returns multiple interest points at the same location with different orientations if there is not clearly a single dominant orientation. Usually, up to three (depending on the actual image patch) orientations are estimated.