Hi guys,
I performed the training of an SVM based on the code below. I used C # for familiarity with the language. I tried doing with Java, directly on Android, but I had some problems as described in this question, mainly due to the absence of the class BOWImgDescriptorExtractor:
C# Trainning
public class Training
{
KAZE extractor;
BFMatcher bFMatcher;
BOWKMeansTrainer bOWKMeansTrainer;
BOWImgDescriptorExtractor bOWImgDescriptorExtractor;
Mat descriptorsExtractor;
Mat descriptorsBOWImgDescriptorExtractor;
int dictionarySize = 32;
Dictionary<int, int> images;
List<int> imagesType = new List<int> { 0, 1 };
public Training()
{
extractor = new KAZE(true, true);
bFMatcher = new BFMatcher(DistanceType.L2);
bOWKMeansTrainer = new BOWKMeansTrainer(
dictionarySize, new MCvTermCriteria(10, 0.001),
1, KMeansInitType.PPCenters);
bOWImgDescriptorExtractor =
new BOWImgDescriptorExtractor(extractor, bFMatcher);
descriptorsExtractor = new Mat();
descriptorsBOWImgDescriptorExtractor =
new Mat(0, dictionarySize, DepthType.Cv32F, 1);
images = new Dictionary<int, int>();
images.Add(0, 15);
images.Add(1, 17);
}
public void Train()
{
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
path = Directory.GetParent(Directory.GetParent(path).ToString()).ToString();
// Step 1
for (var i = 0; i < imagesType.Count; i++)
{
var type = imagesType[i];
for (var j = 1; j <= images[type]; j++)
{
var file = $@"{path}\Train\{type} ({j}).jpg";
var image = new Image<Bgr, Byte>(file);
MKeyPoint[] keyPoints = extractor.Detect(image);
Mat descriptors = new Mat();
extractor.Compute(image, new VectorOfKeyPoint(keyPoints), descriptors);
descriptorsExtractor.PushBack(descriptors);
}
}
bOWKMeansTrainer.Add(descriptorsExtractor);
// Step 2
int count = bOWKMeansTrainer.DescriptorCount;
Console.WriteLine($"Clustering {count} descriptors");
Mat dictionary = new Mat();
bOWKMeansTrainer.Cluster(dictionary);
bOWImgDescriptorExtractor.SetVocabulary(dictionary);
// Step 3
Matrix<int> labels;
List<int> listLabels = new List<int>();
for (var i = 0; i < imagesType.Count; i++)
{
var type = imagesType[i];
for (var j = 1; j <= images[type]; j++)
{
var file = $@"{path}\Train\{type} ({j}).jpg";
var image = new Image<Bgr, Byte>(file);
MKeyPoint[] keyPoints = extractor.Detect(image);
Mat descriptors = new Mat();
bOWImgDescriptorExtractor.Compute(image, new VectorOfKeyPoint(keyPoints), descriptors);
descriptorsBOWImgDescriptorExtractor.PushBack(descriptors);
listLabels.Add(type);
}
}
labels = new Matrix<int>(listLabels.ToArray());
// Step 4
SVM svm = new SVM();
svm.SetKernel(SVM.SvmKernelType.Rbf);
svm.Type = SVM.SvmType.CSvc;
svm.Gamma = 0.50625000000000009;
svm.C = 312.50000000000000;
svm.TermCriteria = new MCvTermCriteria(100, 0.000001);
bool result = svm.Train(
descriptorsBOWImgDescriptorExtractor,
Emgu.CV.ML.MlEnum.DataLayoutType.RowSample,
labels);
svm.Save("output.xml");
/////////////////////////////////////////////
var file1 = $@"{path}\Train\{1} ({18}).jpg";
var img = new Image<Bgr, Byte>(file1);
MKeyPoint[] keypoints = null;
var bowDescriptor = new Mat();
keypoints = extractor.Detect(img);
bOWImgDescriptorExtractor.Compute(
img,
new VectorOfKeyPoint(keypoints),
bowDescriptor);
var response = svm.Predict(bowDescriptor);
Console.WriteLine($"Result {response}");
}
}
Question
http://answers.opencv.org/question/199980/problem-when-training-svm-with-orb-descriptors-android/
As suggested, I conducted the training using KAZE (UpRight) and BagOfWords. After generating the output.xml file, I loaded it on Android and tried to sort a simple image.
Output.xml
https://pastebin.com/fDm8Ynnx
This is the code I'm using to sort the image:
private void predict(Mat image) {
Mat grayImage = new Mat();
Imgproc.cvtColor(image, grayImage, Imgproc.COLOR_BGR2GRAY);
KAZE kaze = KAZE.create();
kaze.setUpright(true);
kaze.setExtended(true);
MatOfKeyPoint keyPoints = new MatOfKeyPoint();
kaze.detect(grayImage, keyPoints);
MatOfFloat descriptors = new MatOfFloat();
kaze.compute(grayImage, keyPoints, descriptors);
try {
float result = svm.predict(descriptors);
} catch(Exception e) {
Log.d(TAG, e.getMessage());
}
}
However, I have obtained this exception:
error: (-215:Assertion failed) samples.cols == var_count && samples.type() == CV_32F in function 'virtual float cv::ml::SVMImpl::predict(cv::InputArray, cv::OutputArray, int) const
I've tried several settings, but they all give me the same error.
descriptors.convertTo(descriptors, CvType.CV_32F);
try {
float result = svm.predict(descriptors);
} catch(Exception e) {
Log.d(TAG, e.getMessage());
}
Mat mat = descriptors.reshape(1, 1);
try {
float result = svm.predict(mat);
} catch(Exception e) {
Log.d(TAG, e.getMessage());
}
Mat mat = descriptors.reshape(descriptors.cols(), 1);
try {
float result = svm.predict(mat);
} catch(Exception e) {
Log.d(TAG, e.getMessage());
}
-----------------------------------------------------------------
Mat testMat = image.clone().reshape(image.cols(),1);
testMat.convertTo(testMat, CvType.CV_32F);
try {
float result = svm.predict(testMat);
} catch(Exception e) {
Log.d(TAG, e.getMessage());
}
In this case:
1 - Since I'm only using one image, I do not need to use BagOfWords. I can directly use the descriptors obtained by KAZE. Is this correct?
2 - In some examples that I found in other languages, people directly use the Mat object of the loaded image. Is it possible to call svm.predict using this Mat object?
Thank you!