OpenCV OCL logical indexing - c++

I am working on an algorithm with many computations done on a GPU. I'm working mainly with oclMat structures and am trying to avoid copying from CPU to GPU and vice versa, yet I cannot find an easy way to:
compare all elements in an ocl matrix to a specific single value (be it float or double, for instance) and create a logical matrix in accordance
create an oclMat matrix with a given size and type initialized with all elements to a specific value (for example all elements are float and equal to 1.234567)
For example:
cv::ocl::oclMat M1 =...
// DO STUFF WITH M1
cv::ocl::oclMat logicalM1 = M1>1.55; // compare directly to a single value
cv::ocl::oclMat logicalM2 = ... ; // i.e. I want a 100x100 CV_32FC1 matrix with all elements set to be equal to 1.234567
By reading the documentation, it seems using cv::ocl::compare only works with both matrices the same dimensions and type, so maybe my first request isn't feasible. On the other hand, I don't know how to initialize a specific matrix directly in ocl (with cv::Mat I know how it's done).
I assume an easy workaround exists, but haven't found one yet... Thanks!

You are right. Looks like cv::ocl::compare supports only two cv::oclMat on input.
But you can create oclMat filled with specific value as follows:
cv::ocl::oclMat logicalM2(M1.size(), M1.type);
logicalM2.setTo(cv::Scalar(1.234567));
cv::ocl::oclMat logicalM1;
cv::ocl::compare(M1, logicalM2, logicalM1, cv::CMP_GT);
P.S. Also I suggest you trying new OpenCV 3.0 with Transparent-API which makes processing on GPU using OpenCL much easier.

Related

How do images work in opencl kernel?

I'm trying to find ways to copy multidimensional arrays from host to device in opencl and thought an approach was to use an image... which can be 1, 2, or 3 dimensional objects. However I'm confused because when reading a pixle from an array, they are using vector datatypes. Normally I would think double pointer, but it doesn't sound like that is what is meant by vector datatypes. Anyway here are my questions:
1) What is actually meant to vector datatype, why wouldn't we just specify 2 or 3 indices when denoting pixel coordinates? It looks like a single value such as float2 is being used to denote coordinates, but that makes no sense to me. I'm looking at the function read_imageui and read_image.
2) Can the input image just be a subset of the entire image and sampler be the subset of the input image? I don't understand how the coordinates are actually specified here either since read_image() only seams to take a single value for input and a single value for sampler.
3) If doing linear algebra, should I just bite the bullet and translate 1-D array data from the buffer into multi-dim arrays in opencl?
4) I'm still interested in images, so even if what I want to do is not best for images, could you still explain questions 1 and 2?
Thanks!
EDIT
I wanted to refine my question and ask, in the following khronos documentation they define...
int4 read_imagei (
image2d_t image,
sampler_t sampler,
int2 coord)
But nowhere can I find what image2d_t's definition or structure is supposed to be. The samething for sampler_t and int2 coord. They seem like structs to me or pointers to structs since opencl is supposed to be based on ansi c, but what are the fields of these structs or how do I note the coord with what looks like a scala?! I've seen the notation (int2)(x,y), but that's not ansi c, that looks like scala, haha. Things seem conflicting to me. Thanks again!
In general you can read from images in three different ways:
direct pixel access, no sampling
sampling, normalized coordinates
sampling, integer coordinates
The first one is what you want, that is, you pass integer pixel coordinates like (10, 43) and it will return the contents of the image at that point, with no filtering whatsoever, as if it were a memory buffer. You can use the read_image*() family of functions which take no sampler_t param.
The second one is what most people want from images, you specify normalized image coords between 0 and 1, and the return value is the interpolated image color at the specified point (so if your coordinates specify a point in between pixels, the color is interpolated based on surrounding pixel colors). The interpolation, and the way out-of-bounds coordinates are handled, are defined by the configuration of the sampler_t parameter you pass to the function.
The third one is the same as the second one, except the texture coordinates are not normalized, and the sampler needs to be configured accordingly. In some sense the third way is closer to the first, and the only additional feature it provides is the ability to handle out-of-bounds pixel coordinates (for instance, by wrapping or clamping them) instead of you doing it manually.
Finally, the different versions of each function, e.g. read_imagef, read_imagei, read_imageui are to be used depending on the pixel format of your image. If it contains floats (in each channel), use read_imagef, if it contains signed integers (in each channel), use read_imagei, etc...
Writing to an image on the other hand is straightforward, there are write_image{f,i,ui}() functions that take an image object, integer pixel coordinates and a pixel color, all very easy.
Note that you cannot read and write to the same image in the same kernel! (I don't know if recent OpenCL versions have changed that). In general I would recommend using a buffer if you are not going to be using images as actual images (i.e. input textures that you sample or output textures that you write to only once at the end of your kernel).
About the image2d_t, sampler_t types, they are OpenCL "pseudo-objects" that you can pass into a kernel from C (they are reserved types). You send your image or your sampler from the C side into clSetKernelArg, and the kernel gets back a sampler_t or an image2d_t in the kernel's parameter list (just like you pass in a buffer object and it gets a pointer). The objects themselves cannot be meaningfully manipulated inside the kernel, they are just handles that you can send into the read_image/write_image functions, along with a few others.
As for the "actual" low-level difference between images and buffers, GPU's often have specially reserved texture memory that is highly optimized for "read often, write once" access patterns, with special texture sampling hardware and texture caches to optimize scatter reads, mipmaps, etc..
On the CPU there is probably no underlying difference between an image and a buffer, and your runtime likely implements both as memory arrays while enforcing image semantics.

OpenCV Linear SVM not training

I've been stuck on this for some time now. OpenCV's SVM implementation doesn't seem to work for a linear kernel. I'm fairly sure there's no bug in the code: when I change the kernel_type to RBF or POLY, keeping everything else as is, it works.
The reason I say it doesn't work is, I save the generated model and check it out. It shows support vector count as 1. Which is not the case in RBF or POLYnomial kernels.
There's nothing special about the code in itself, I've used OpenCV's SVM implementation before, but never a linear kernel. I tried setting the degree to 1 in a POLY kernel and it results in the same model. Which makes me believe something is buggy here.
The code structure, if required:
Mat trainingdata; //acquire from files. done and correct.
Mat testingdata; //acquire from files. done and correct again.
Mat labels; //corresponding labels. checked and correct.
SVM my_svm;
SVMParams my_params;
my_params.svm_type = SVM::C_SVC;
my_params.kernel_type = SVM::LINEAR; //or poly, with my_params.degree = 1.
my_param.C = 0.02; //doesn't matter if I set it to 20000, makes no difference.
my_svm.train( trainingdata, labels, Mat(), Mat(), my_params );
//train_auto(..) function with 10-fold cross-validation takes the same time as above (~2sec)!
Mat responses;
my_svm.predict( testingdata, responses );
//responses matrix is all wrong.
I have 500 samples from one class and 600 from the other class to test, and the correct classifications I get are: 1/500 and 597/600.
Craziest part:
I have done the same experiment with the same data on libSVM's MATLAB wrapper, and it works. Was just trying to do an OpenCV version of it.
It is not a bug that you always get only one support vector with linear CvSVM.
OpenCV optimizes a linear SVM down to one support vector.
The idea here is that the support vectors define the classification margin, and to do the actual classification only the separating hyperplane is needed and it can be defined by only one vector.
Parameter C doesn't matter if your training data is linearly separable. Maybe it is your case.

How to filter a single column mat with Gaussian in OpenCV

I have mat with only one column and 1600 rows. I want to filter it using a Gaussian.
I tried the following:
Mat AFilt=Mat(palm_contour.size(),1,CV_32F);
GaussianBlur(A,AFilt,cv::Size(20,1),3);
But I get the exact same values in AFilt (the filtered mat) and A. It looks like GaussianBlur has done nothing.
What's the problem here? How can I smooth a single-column mat with a Gaussian kernel?
I read about BaseColumnFilt, but haven't seen any usage examples so I'm not sure how to use them.
Any help given will be greatly appreciated as I don't have a clue.
I'm working with OpenCV 2.4.5 on windows 8 using Visual Studio 2012.
Thanks
Gil.
You have a single column but you are specifying the width of the gaussian to be big instead of specifying the height! OpenCV use row,col or x,y notation depending on the context. A general rule is whenever you use Point or Size, they behave like x,y and whenever the parameters are separate values they behave like row,col.
The kernel size should also be odd. If you specify the kernel size you can set sigma to zero to let OpenCV compute a suitable sigma value.
To conclude, this should work better:
GaussianBlur(A,AFilt,cv::Size(1,21),0);
The documentation og GaussianBlur says the kernel size must be odd, I would try using an odd size kernel and see if that makes any difference

Matlab griddata equivalent in C++

I am looking for a C++ equivalent to Matlab's griddata function, or any 2D global interpolation method.
I have a C++ code that uses Eigen 3. I will have an Eigen Vector that will contain x,y, and z values, and two Eigen matrices equivalent to those produced by Meshgrid in Matlab. I would like to interpolate the z values from the Vectors onto the grid points defined by the Meshgrid equivalents (which will extend past the outside of the original points a bit, so minor extrapolation is required).
I'm not too bothered by accuracy--it doesn't need to be perfect. However, I cannot accept NaN as a solution--the interpolation must be computed everywhere on the mesh regardless of data gaps. In other words, staying inside the convex hull is not an option.
I would prefer not to write an interpolation from scratch, but if someone wants to point me to pretty good (and explicit) recipe I'll give it a shot. It's not the most hateful thing to write (at least in an algorithmic sense), but I don't want to reinvent the wheel.
Effectively what I have is scattered terrain locations, and I wish to define a rectilinear mesh that nominally follows some distance beneath the topography for use later. Once I have the node points, I will be good.
My research so far:
The question asked here: MATLAB functions in C++ produced a close answer, but unfortunately the suggestion was not free (SciMath).
I have tried understanding the interpolation function used in Generic Mapping Tools, and was rewarded with a headache.
I briefly looked into the Grid Algorithms library (GrAL). If anyone has commentary I would appreciate it.
Eigen has an unsupported interpolation package, but it seems to just be for curves (not surfaces).
Edit: VTK has a matplotlib functionality. Presumably there must be an interpolation used somewhere in that for display purposes. Does anyone know if that's accessible and usable?
Thank you.
This is probably a little late, but hopefully it helps someone.
Method 1.) Octave: If you're coming from Matlab, one way is to embed the gnu Matlab clone Octave directly into the c++ program. I don't have much experience with it, but you can call the octave library functions directly from a cpp file.
See here, for instance. http://www.gnu.org/software/octave/doc/interpreter/Standalone-Programs.html#Standalone-Programs
griddata is included in octave's geometry package.
Method 2.) PCL: They way I do it is to use the point cloud library (http://www.pointclouds.org) and VoxelGrid. You can set x, and y bin sizes as you please, then set a really large z bin size, which gets you one z value for each x,y bin. The catch is that x,y, and z values are the centroid for the points averaged into the bin, not the bin centers (which is also why it works for this). So you need to massage the x,y values when you're done:
Ex:
//read in a list of comma separated values (x,y,z)
FILE * fp;
fp = fopen("points.xyz","r");
//store them in PCL's point cloud format
pcl::PointCloud<pcl::PointXYZ>::Ptr basic_cloud_ptr (new pcl::PointCloud<pcl::PointXYZ>);
int numpts=0;
double x,y,z;
while(fscanf(fp, "%lg, %lg, %lg", &x, &y, &z)!=EOF)
{
pcl::PointXYZ basic_point;
basic_point.x = x; basic_point.y = y; basic_point.z = z;
basic_cloud_ptr->points.push_back(basic_point);
}
fclose(fp);
basic_cloud_ptr->width = (int) basic_cloud_ptr->points.size ();
basic_cloud_ptr->height = 1;
// create object for result
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_filtered(new pcl::PointCloud<pcl::PointXYZ>());
// create filtering object and process
pcl::VoxelGrid<pcl::PointXYZ> sor;
sor.setInputCloud (basic_cloud_ptr);
//set the bin sizes here. (dx,dy,dz). for 2d results, make one of the bins larger
//than the data set span in that axis
sor.setLeafSize (0.1, 0.1, 1000);
sor.filter (*cloud_filtered);
So that cloud_filtered is now a point cloud that contains one point for each bin. Then I just make a 2-d matrix and go through the point cloud assigning points to their x,y bins if I want an image, etc. as would be produced by griddata. It works pretty well, and it's much faster than matlab's griddata for large datasets.

Laser line detection using OpenCV

I'm working on a project in which I need to detect a red laser line in an image. This is the strategy I have in mind.
Separate the R, G, B channels in the image.
Threshold the images at a high intensity value.
Using the 3 binary images generated, perform the element wise operation r && !g && !b. (&& is logical AND, ! is logical NOT).
The resulting matrix is a binary image with 1 on the regions where the laser was present.
This worked with a few test images on Matlab. But my problem is that this needs to be implemented using OpenCV in C/ C++.
I've tried going through most of the library functions, but there seems no intuitive/ simple way of working with binary images and performing logical operations on them.
Can someone please point me to the OpenCV functions/ methods that you think I might find useful? I've figured that cvThresholdImage can be used for thresholding, but that's pretty much about it.
So you already figured out steps 1 and 2 in openCV then? If you are just trying to use the logical operators, openCV gives you access to the raw data which you can then operate on with logical operators. Assuming you have already split into three channels and thresholded
//three binary images in the format you specified above
cv::Mat g;
cv::Mat b;
cv::Mat r;
uchar* gptr = g.data();
uchar* bptr = b.data();
uchar* rptr = r.data();
//assuming the matrix data is continuous you can just iterate straight through the data
if(g.isContinuous()&&r.isContinuous()&&b.isContinuous())
{
for(int i = 0; i < g.rows*g.cols; i++)
{
rptr[i] = rptr[i]&&!bptr[i]&&!gptr[i];
}
}
r now contains the output you described. You could also copy it into a new matrix if you don't want to overwrite r.
There are several ways of iterating through a cv::Mat and accessing all the data points and C++ provides all the logical operators you could want. To my knowledge openCV does not provide matrix logical operator functions but you could write your own very easily as shown above.
Edit
As suggested by QuentinGeissmann you could accomplish the same thing using the bitwise_not and bitwise_and functions. I was not aware that they existed. I suspect that using them would be slower because of the number of times the data must be iterated through but it could be done in less code.
cv::bitwise_not(g,g);
cv::bitwise_not(b,b);
cv::bitwise_and(b,g,b);
cv::bitwise_and(r,b,r);
//r now contains r&&!b&&!g