OpenCV UMat operators - c++

With cv::Mat one can use ~ for cv::bitwise_not or > to compare 2 matrices.
But cv::UMat doesn't seem to have these operators, understandably you could simply do cv::bitwise_not(umat,umat) (though I've understood copying from a matrix to itself isn't very efficient, correct me if I'm wrong), but how can one compare 2 cv::UMat matrices, or a cv::UMat with a cv::Scalar?

TLDR use OpenCV compare function
You can use .getMat()
cv::UMat A = cv::Mat(1000, 1000, CV_8UC3), B = cv::UMat(1000, 1000, CV_8UC3);
cv::randu(A, Scalar::all(0), Scalar::all(255));
cv::randu(B, Scalar::all(0), Scalar::all(255));
cv::UMat C = A.getMat(cv::ACCESS_READ) > B.getMat(cv::ACCESS_READ);
But this doesn't use cv::UMats' hardware acceleration.
Instead you should just use OpenCV compare function
cv::UMat A = cv::Mat(1000, 1000, CV_8UC3), B = cv::UMat(1000, 1000, CV_8UC3);
cv::randu(A, Scalar::all(0), Scalar::all(255));
cv::randu(B, Scalar::all(0), Scalar::all(255));
cv::UMat C;
cv::compare(A, B, C, CMP_GT);

Not a very efficient answer probably, but from the top of my head...
Compare two cv::UMat you can convert them to a Mat and then use cv::bitwisenot.
Compare cv::UMat with cv::Scalar, you can convert the UMat to Mat and then use this:
Mat_<float> A(3,3); mf << 1,5,5,2,5,5,1,2,3;
// now use a simple MatExpr to get a mask:
Mat mask = (A == 5);
// show results:
cerr << A << endl;
cerr << mask << endl;
------------------------------
[1, 5, 5;
2, 5, 5;
1, 2, 3]
[0, 255, 255;
0, 255, 255;
0, 0, 0]
Depending on whether the Mat is 3,2 or 1 channel, adjust your Scalar.
Hope it helps!!

Related

opencv cornerSubPix Exception while converting python code to c++

I am trying to port this response to c++ but I am not able to get past this cryptic exception (see image below). Not sure what is the limiting factor. I imagine it is the image color format or the corners parameter but nothing seems to be working. If it is related to converting color format please provide a small code snippet.
The python code provided by Anubhav Singh is working great however I would like to develop in c++. Any help would be greatly appreciated.
I am using OpenCV04.2.0
void CornerDetection(){
std::string image_path = samples::findFile("../wing.png");
Mat img = imread(image_path);
Mat greyMat;
Mat dst;
cv::cvtColor(img, greyMat, COLOR_BGR2GRAY);
threshold(greyMat, greyMat, 0, 255, THRESH_BINARY | THRESH_OTSU);
cornerHarris(greyMat, dst, 9, 5, 0.04);
dilate(dst, dst,NULL);
Mat img_thresh;
threshold(dst, img_thresh, 0.32 * 255, 255, 0);
img_thresh.convertTo(img_thresh, CV_8UC1);
Mat labels = Mat();
Mat stats = Mat();
Mat centroids = Mat();
cv::connectedComponentsWithStats(img_thresh, labels, stats, centroids, 8, CV_32S);
TermCriteria criteria = TermCriteria(TermCriteria::EPS + TermCriteria::MAX_ITER, 30, 0.001);
std::vector<Point2f> corners = std::vector<Point2f>();
Size winSize = Size(5, 5);
Size zeroZone = Size(-1, -1);
cornerSubPix(greyMat, corners, winSize, zeroZone, criteria);
for (int i = 0; i < corners.size(); i++)
{
circle(img, Point(corners[i].x, corners[i].y), 5, Scalar(0, 255, 0), 2);
}
imshow("img", img);
waitKey();
destroyAllWindows();
}
The solution was to iterate over the centroids to build the corners vector before passing the corners variable to the cornerSubPix(...) function.
std::vector<Point2f> corners = std::vector<Point2f>();
for (int i = 0; i < centroids.rows; i++)
{
double x = centroids.at<double>(i, 0);
double y = centroids.at<double>(i, 1);
corners.push_back(Point2f(x, y));
}
The output of the solution is still not exactly what the python output is, regardless it fixed this question in case anyone else ran across this issue.

Creating Your Own Linear Filter

I am new to Opencv C++. I am trying to convolve a mask with an image. For this I want to create my own mask so that I can use the filter2D array function to convolve my mask with the image. The mask which I want to create is:
char mask [3][3]= {{-1,0,1},{-1,0,1},{-1,0,1}};
For this I have tried the code below(to generate this mask):-
Mat kernel(3,3, CV_8UC1, Scalar(-1,0,1));
i have printed the mask values as
std::cout << kernel;
but the answer which I am getting is
0, 0, 0;
0, 0, 0;
0, 0, 0
I am expecting the answer to be
-1, 0, 1;
-1, 0, 1;
-1, 0, 1
I know I am making a mistake in writing the channels properly. Can anyone help me understand what does the channel(CV_8UC1.... ) means and how to correct it and get the proper output.
CV_8UC1 means 1 channel, 8 bit, uchar image.
Scalar is used to set the value of each channel, not each pixel/coordinate.
Ex 1:
Mat kernel(3,3, CV_8UC1, Scalar::all(0))
would mean creating a 3X3 matrix with 0s and since it is of type CV_8UC1, you can mention only one value, in this case 0.
EX 2:
Mat kernel(3,3, CV_8UC3, Scalar(0,0,255))
means creating a 3X3 matrix with 3 channels since the type is CV_8UC3 and setting channel 1 to 0, channel 2 to 0, channel 3 to 255.
Hence for your purpose to set row values, you cannot use scalar.
Instead do this:
Mat C = (Mat_<double>(3,3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
Check this for more information.
Hope this helps!
You want to create a kernel with negative element for filter2D, then you should't use the data type of CV_8UC1. There is no negative value in unsigned type.
And Mat kernel(3,3, CV_8UC1, Scalar(-1,0,1)); means create a signal- channel-unsigned-char kernel. You set Scalar(-1,0,1) to kernel, then only the first element(that is double -1) is used, while saturate_cast<unchar>(-1) = 0.
Generally, use CV_32FC1 instead.
For example:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(){
float mask[9] = {-1,0,1, -1, 0, 1, -1,0,1};
Mat kernel(3,3, CV_32FC1);
memcpy(kernel.data, mask, sizeof(float)*9);
cout << kernel<<endl;
}
The result:
[-1, 0, 1;
-1, 0, 1;
-1, 0, 1]
A similar question:
How to create cv::Mat from buffer (array of T* data) using a template function?

How can I permute dimensions in cv::Mat from CxWxH to WxHxC?

How can I permute dimensions in cv::Mat from CxWxH to WxHxC (Width x Height x Channels)?
I.e. how can I convert Mat:
from cv::Mat frame (1000, 1000, CV_8UC3) = cv::imread("image.jpg", -1);
with dimesions: channels, width, height
to Mat with dimesions: width, height, channels
int sizes_inputs[] = { 3, 1000, 1000 };
cv::Mat out_image(3, sizes_inputs, CV_8UC);
Is there in OpenCV a ready-made fast function for such a conversion? Or should I implement this algorithm myself?
If you wish to treat the underlying data differently, then you can use reshape. The data is stored as BGRBGRBGR....
Otherwise you will have to shuffle the data yourself.
cv::reshape and cv::minChannels may be handy for this.
You can emulate it. I have not tested the code but something like this should do:
cv::Mat frame (1000, 1000, CV_8UC3) = cv::imread("image.jpg", -1);
int sizes_inputs[] = { 3, 1000, 1000 };
cv::Mat out_image(3, sizes_inputs, CV_8UC);
cv::Mat slices[] = {cv::Mat(1000, 1000, CV_8UC, out_image.ptr()),
cv::Mat(1000, 1000, CV_8UC, out_image.ptr() + 1000*1000),
cv::Mat(1000, 1000, CV_8UC, out_image.ptr() + 1000*1000*2)};
cv::split(frame, slices);

opencv houghcircles differences c c++

I'm introducing myself in OpenCV (in order for an software project at university) and found a tutorial for color circle detection which I adapted and tested. It was written with OpenCV 1 in C. So I tried to convert it to OpenCv 2 classes API and everything was fine, but I ran into one problem:
The C function cvHoughCircles produces other results than the C++ function HoughCircles.
The C version finds my test circle and has a low rate of false positives, but the C++ version has a significantly higher mistake rate.
//My C implementation
IplImage *img = cvQueryFrame( capture );
CvSize size = cvGetSize(img);
IplImage *hsv = cvCreateImage(size, IPL_DEPTH_8U, 3);
cvCvtColor(img, hsv, CV_BGR2HSV);
CvMat *mask = cvCreateMat(size.height, size.width, CV_8UC1);
cvInRangeS(hsv, cvScalar(107, 61, 0, 0), cvScalar(134, 255, 255, 0), mask);
/* Copy mask into a grayscale image */
IplImage *hough_in = cvCreateImage(size, 8, 1);
cvCopy(mask, hough_in, NULL);
cvSmooth(hough_in, hough_in, CV_GAUSSIAN, 15, 15, 0, 0);
cvShowImage("mask",hough_in);
/* Run the Hough function */
CvMemStorage *storage = cvCreateMemStorage(0);
CvSeq *circles = cvHoughCircles(hough_in, storage, CV_HOUGH_GRADIENT,
4, size.height/4, 100, 40, 0, 0);
// ... iterating over all found circles
this works pretty well
//My C++ implementation
cv::Mat img;
cap.read(img);
cv::Size size(img.cols,img.rows);
cv::Mat hsv(size, IPL_DEPTH_8U, 3);
cv::cvtColor(img, hsv, CV_BGR2HSV);
cv::Mat mask(size.height, size.width, CV_8UC1);
cv::inRange(hsv, cv::Scalar(107, 61, 0, 0), cv::Scalar(134, 255, 255, 0), mask);
GaussianBlur( mask, mask, cv::Size(15, 15), 0, 0 );
/* Run the Hough function */
imshow("mask",mask);
vector<cv::Vec3f> circles;
cv::HoughCircles(mask, circles, CV_HOUGH_GRADIENT,
4, size.height/4, 100, 140, 0, 0);
// ... iterating over all found circles
As you can see, I use same arguments to all calls. I tested this with a webcam and a static sample object.One requirement is to use OpenCV2 C++ API.
Does anybody know, why I get so different results under equivalent conditions?
Edit
The different threshold values was just a mistake when I tested to make results more equally. These screenshots are taken with threshold set to 40 for both versions:
Screenshots: (Sorry, cannot yet post images)
C and C++ version
I see Hough parameters in C version as "..., 100, 40, 0, 0); " while in C++ version as "... 100, 140, 0, 0);" This difference in thresholds probably explains the difference in results.

cv::gpu::HoughLines is not working properly

I am trying to detect some lines using Hough Transform on a cv::gpu::GpuMat structure. I have tried using both gpu::HoughLines and gpu::HoughLinesP but even with extremely low thresholds, I am not getting any results at all. During debugging, I see that the container which should contain the results (houghLines) has only zeros stored inside it. The code I have written is given below,
static cv::Mat drawHoughLinesOnMat (cv::gpu::GpuMat hough_Mat, cv::gpu::GpuMat houghLines)
{
cv::Mat output_Mat;
cv::cvtColor(cv::Mat(hough_Mat), output_Mat, CV_GRAY2BGR);
std::vector<cv::Vec4i> lines_vector;
if (!houghLines.empty())
{
lines_vector.resize(houghLines.cols);
cv::Mat temp_Mat (1, houghLines.cols, CV_8UC3, &lines_vector[0]);
houghLines.download (temp_Mat);
}
for (size_t i=0; i<lines_vector.size(); ++i)
{
cv::Vec4i l = lines_vector[i];
cv::line(output_Mat, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(0, 0, 255), 1, 8);
}
return output_Mat;
}
int main()
{
cv::Mat input = cv::imread(INPUT_DATA_1->c_str(), CV_LOAD_IMAGE_GRAYSCALE);
std::string imageType = getImgType(input.type());
cv::gpu::GpuMat mat_input(input), bil_out, mat_thresh, hough_lines;
cv::gpu::HoughLinesBuf hough_buffer;
int bilateral_thresh = 15; // 5 == 0.085s; 15 == 0.467s at run-time
cv::gpu::bilateralFilter(mat_input, bil_out, bilateral_thresh, bilateral_thresh*2, bilateral_thresh/2);
//cv::gpu::threshold(bil_out, mat_thresh, 10, 255, CV_THRESH_BINARY);
cv::gpu::Canny(bil_out, mat_thresh, 10, 60, 5);
cv::gpu::HoughLinesP(mat_thresh, hough_lines, hough_buffer, 1.0f, (float)(CV_PI/180.0f), 5, 1);
//cv::Mat test_hough(hough_lines);
cv::Mat hough_Mat = drawHoughLinesOnMat(mat_input, hough_lines);
cv::gpu::HoughLines(mat_thresh, hough_lines, 1.0f, (float)(CV_PI/180.0f), 1, true);
/*cv::Mat */hough_Mat = drawHoughLinesOnMat(mat_input, hough_lines);
return EXIT_SUCCESS
}
The image I am using is,
Could someone tell me what it is that I am doing wrong..? Thanks in advance.!
The output of the Canny filter is,
EDIT:
I have tested on the CPU version of HoughLines and it seems to work just fine.
EDIT_2:
The solution posted by #jet47 works perfectly.
You use incorrect code for downloading results from GPU back to CPU:
lines_vector.resize(houghLines.cols);
cv::Mat temp_Mat (1, houghLines.cols, CV_8UC3, &lines_vector[0]);
houghLines.download (temp_Mat);
You use incorrect type for temp_Mat - CV_8UC3, it must be CV_32SC4.
The correct code is:
lines_vector.resize(houghLines.cols);
cv::Mat temp_Mat(1, houghLines.cols, CV_32SC4, &lines_vector[0]);
houghLines.download(temp_Mat);
My guess is that the Method you are using is outdated (but im not entirely sure).
This is how i would do it(as demonstrated in this Example Code):
//d_src filled with your image somewhere
GpuMat d_lines;
{
Ptr<cuda::HoughSegmentDetector> hough = cuda::createHoughSegmentDetector(1.0f, (float) (CV_PI / 180.0f), 50, 5);
hough->detect(d_src, d_lines);
}
vector<Vec4i> lines_gpu;
if (!d_lines.empty())
{
lines_gpu.resize(d_lines.cols);
Mat h_lines(1, d_lines.cols, CV_32SC4, &lines_gpu[0]);
d_lines.download(h_lines);
}
for (size_t i = 0; i < lines_gpu.size(); ++i)
{
Vec4i l = lines_gpu[i];
line(dst_gpu, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 3, LINE_AA);
}
EDIT The above uses the OpenCv 3.0 Interface