Laser line detection using OpenCV - c++

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

Related

How to send grayscale cv::Mat to gnuplot

community,
Using c++, opencv and gnuplot my goal is to showcase sobel values in a video between images with Depth of Field and ones without.
I've been saving the frame as cv::Mat, converted it into grayscale, blurred it with a 3x3 kernel and applied sobel on it. I normalized the result and it showed fine in imshow. With the help of gnuplot-iostream i want to create an 3d-image similiar to this one in gnuplot examplepicture, showing the intensity, which is between 0 and 255 after normalization.
Gnuplot doesnt seem to natively support cv::Mat, so i tried couple of ways to insert it, all creating just one line and/or wrong scaling. This is the code im using to convert it into an vector, which gnuplot seems to take with no issues.
if (sobelxy.isContinuous()) {
imgvec.assign(sobelxy.datastart, sobelxy.dataend);
}
else {
for (int i = 0; i < sobelxy.rows; i++) {
imgvec.insert(imgvec.end(), sobelxy.ptr<double>(i), sobelxy.ptr<double>(i) + sobelxy.cols);
}
}
I could access pixels one by one, however this is very performanceheavy and not well suited for a video, thus im wondering if theres an option to either preprocess the vector so that gnuplot gives me correct result or use certain parameters in gnuplot to read the vector correctly.
Thank you in advance.

OpenCV OCL logical indexing

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.

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.

efficient way to grayscale a frame without using OpenCV

i was capturing live video from my web camera to Mat objects.
is their any efficient way to convert a MAT object in to gray scaled image frame without using any API such as openCV...
I have tried it using openCV.
but i like to implement in to c++...is their any way to do it?
I would recommend you use OpenCV. OpenCV already contains optimized implementations for converting between various color spaces (i.e. even between RGB (actually BGR for OpenCV) to greyscale).
See for more details: http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html.
OpenCV is allready implemented in C++.
If you really want to implement you own for didactical purposes (I don't see any reason why you would do it otherwise) then the simple way to do it would be to iterate the R G B values in the Mat and apply the formula:
resultingVlue = 0.299 * R + 0.587 * G + 0.114 * B
(See also Stack overflow Question Converting RGB to grayscale/intensity for a more detailed discussion on why the R G B components typically get weighted differently)
Assuming here you want to convert RGB to gray. For other color space conversions, please look at the OpenCv documentation that also details how the transformations are done (see link provided above).
More so, OpenCV is open source. This means if you want to see how a optimal implementation might look like, you can download the source code and take a look.
Google tells me that you have to average the values of the R,G and B values of each pixel. Some algorithms are discussed here
http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
The simplest is to convert each color R, G and B values by the average (R+G+B)/3. Check the above links for the results of a few different averages.

OpenCV, C++: Distance between two points

For a group project, we are attempting to make a game, where functions are executed whenever a player forms a set of specific hand gestures in front of a camera. To process the images, we are using Open-CV 2.3.
During the image-processing we are trying to find the length between two points.
We already know this can be done very easily with Pythagoras law, though it is known that Pythagoras law requires much computer power, and we wish to do this as low-resource as possible.
We wish to know if there exist any build-in function within Open-CV or standard library for C++, which can handle low-resource calculations of the distance between two points.
We have the coordinates for the points, which are in pixel values (Of course).
Extra info:
Previous experience have taught us, that OpenCV and other libraries are heavily optimized. As an example, we attempted to change the RGB values of the live image feed from the camera with a for loop, going through each pixel. This provided with a low frame-rate output. Instead we decided to use an Open-CV build-in function instead, which instead gave us a high frame-rate output.
You should try this
cv::Point a(1, 3);
cv::Point b(5, 6);
double res = cv::norm(a-b);//Euclidian distance
As you correctly pointed out, there's an OpenCV function that does some of your work :)
(Also check the other way)
It is called magnitude() and it calculates the distance for you. And if you have a vector of more than 4 vectors to calculate distances, it will use SSE (i think) to make it faster.
Now, the problem is that it only calculate the square of the powers, and you have to do by hand differences. (check the documentation). But if you do them also using OpenCV functions it should be fast.
Mat pts1(nPts, 1, CV_8UC2), pts2(nPts, 1, CV_8UC2);
// populate them
Mat diffPts = pts1-pts2;
Mat ptsx, ptsy;
// split your points in x and y vectors. maybe separate them from start
Mat dist;
magnitude(ptsx, ptsy, dist); // voila!
The other way is to use a very fast sqrt:
// 15 times faster than the classical float sqrt.
// Reasonably accurate up to root(32500)
// Source: http://supp.iar.com/FilesPublic/SUPPORT/000419/AN-G-002.pdf
unsigned int root(unsigned int x){
unsigned int a,b;
b = x;
a = x = 0x3f;
x = b/x;
a = x = (x+a)>>1;
x = b/x;
a = x = (x+a)>>1;
x = b/x;
x = (x+a)>>1;
return(x);
}
This ought to a comment, but I haven't enough rep (50?) |-( so I post it as an answer.
What the guys are trying to tell you in the comments of your questions is that if it's only about comparing distances, then you can simply use
d=(dx*dx+dy*dy) = (x1-x2)(x1-x2) + (y1-y2)(y1-y2)
thus avoiding the square root. But you can't of course skip the square elevation.
Pythagoras is the fastest way, and it really isn't as expensive as you think. It used to be, because of the square-root. But modern processors can usually do this within a few cycles.
If you really need speed, use OpenCL on the graphics card for image processing.