convert vector of points to Mat (OpenCV ) - c++

my question is very similar to this one... I'm trying to extract a sub matrix from a grayscale image wich is a polygon by 5 points , and convert it to a Mat.
This does not work:
std::vector<Point> vert(5);
vert.push_back(pt1);
vert.push_back(pt2);
vert.push_back(pt3);
vert.push_back(pt4);
vert.push_back(pt5);
Mat matROI = Mat(vert);
It shows me the following error message:
OpenCV Error: Bad number of channels (Source image must have 1, 3 or 4 channels) in cvConvertImage, file /home/user/opencv-2.4.6.1/modules/highgui/src/utils.cpp, line 611
terminate called after throwing an instance of 'cv::Exception'
what(): /home/user/opencv-2.4.6.1/modules/highgui/src/utils.cpp:611: error: (-15) Source image must have 1, 3 or 4 channels in function cvConvertImage
I'm using OpenCV 2.4.6.1 and C++.
Thank you
Edit:
I will rephrase my question: my objective is to obtain the right side of the image.
I thought I'd see the image as a polygon because I have the coordinates of the vertices, and then transform the vector that has the vertices in a matrix (cvMat).
My thought is correct or is there a simpler way to get this submatrix?

Your code has two problems:
First:
std::vector<Point> vert(5);
creates a vector initially with 5 points, so after you use push_back() 5 times you have a vector of 10 points, the first 5 of of which are (0, 0).
Second:
Mat matROI = Mat(vert);
creates a 10x1 Mat (from a vector of 10 points) with TWO channels. Check that with:
cout << "matROI.channels()=" << matROI.channels() << endl;
If you have a code like:
imshow("Window", matROI);
it will pass matROI through to cvConvertImage() which has the following code (and this causes the error you are seeing):
if( src_cn != 1 && src_cn != 3 && src_cn != 4 )
CV_ERROR( CV_BadNumChannels, "Source image must have 1, 3 or 4 channels" );
Since matROI is a list of points, it doesn't make sense to pass it to imshow().
Instead, try this:
Mat img(image.rows, image.cols, CV_8UC1);
polylines(img, vert, true, Scalar(255)); // or perhaps 0
imshow("Window", img);
int c = waitKey(0);

Related

Opencv - How to get number of vertical lines present in image (count of lines)

Firstly I integrate OpenCV framework to XCode and All the OpenCV code is on ObjectiveC and I am using in Swift Using bridging header. I am new to OpenCV Framework and trying to achieve count of vertical lines from the image.
Here is my code:
First I am converting the image to GrayScale
+ (UIImage *)convertToGrayscale:(UIImage *)image {
cv::Mat mat;
UIImageToMat(image, mat);
cv::Mat gray;
cv::cvtColor(mat, gray, CV_RGB2GRAY);
UIImage *grayscale = MatToUIImage(gray);
return grayscale;
}
Then, I am detecting edges so I can find the line of gray color
+ (UIImage *)detectEdgesInRGBImage:(UIImage *)image {
cv::Mat mat;
UIImageToMat(image, mat);
//Prepare the image for findContours
cv::threshold(mat, mat, 128, 255, CV_THRESH_BINARY);
//Find the contours. Use the contourOutput Mat so the original image doesn't get overwritten
std::vector<std::vector<cv::Point> > contours;
cv::Mat contourOutput = mat.clone();
cv::findContours( contourOutput, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE );
NSLog(#"Count =>%lu", contours.size());
//For Blue
/*cv::GaussianBlur(mat, gray, cv::Size(11, 11), 0); */
UIImage *grayscale = MatToUIImage(mat);
return grayscale;
}
This both Function is written on Objective C
Here, I am calling both function Swift
override func viewDidLoad() {
super.viewDidLoad()
let img = UIImage(named: "imagenamed")
let img1 = Wrapper.convert(toGrayscale: img)
self.capturedImageView.image = Wrapper.detectEdges(inRGBImage: img1)
}
I was doing this for some days and finding some useful documents(Reference Link)
OpenCV - how to count objects in photo?
How to count number of lines (Hough Trasnform) in OpenCV
OPENCV Documents
https://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?#findcontours
Basically, I understand the first we need to convert this image to black and white, and then using cvtColor, threshold and findContours we can find the colors or lines.
I am attaching the image that vertical Lines I want to get.
Original Image
Output Image that I am getting
I got number of lines count =>10
I am not able to get accurate count here.
Please guide me on this. Thank You!
Since you want to detect the number of the vertical lines, there is a very simple approach I can suggest for you. You already got a clear output and I used this output in my code. Here are the steps before the code:
Preprocess the input image to get the lines clearly
Check each row and check until get a pixel whose value is higher than 100(threshold value I chose)
Then increase the line counter for that row
Continue on that line until get a pixel whose value is lower than 100
Restart from step 3 and finish the image for each row
At the end, check the most repeated element in the array which you assigned line numbers for each row. This number will be the number of vertical lines.
Note: If the steps are difficult to understand, think like this way:
" I am checking the first row, I found a pixel which is higher than
100, now this is a line edge starting, increase the counter for this
row. Search on this row until get a pixel smaller than 100, and then
research a pixel bigger than 100. when row is finished, assign the
line number for this row to a big array. Do this for all image. At the
end, since some lines looks like two lines at the top and also some
noises can occur, you should take the most repeated element in the big
array as the number of lines."
Here is the code part in C++:
#include <vector>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
int main()
{
cv::Mat img = cv::imread("/ur/img/dir/img.jpg",cv::IMREAD_GRAYSCALE);
std::vector<int> numberOfVerticalLinesForEachRow;
cv::Rect r(0,0,img.cols-10,200);
img = img(r);
bool blackCheck = 1;
for(int i=0; i<img.rows; i++)
{
int numberOfLines = 0;
for(int j=0; j<img.cols; j++)
{
if((int)img.at<uchar>(cv::Point(j,i))>100 && blackCheck)
{
numberOfLines++;
blackCheck = 0;
}
if((int)img.at<uchar>(cv::Point(j,i))<100)
blackCheck = 1;
}
numberOfVerticalLinesForEachRow.push_back(numberOfLines);
}
// In this part you need a simple algorithm to check the most repeated element
for(int k:numberOfVerticalLinesForEachRow)
std::cout<<k<<std::endl;
cv::namedWindow("WinWin",0);
cv::imshow("WinWin",img);
cv::waitKey(0);
}
Here's another possible approach. It relies mainly on the cv::thinning function from the extended image processing module to reduce the lines at a width of 1 pixel. We can crop a ROI from this image and count the number of transitions from 255 (white) to 0 (black). These are the steps:
Threshold the image using Otsu's method
Apply some morphology to clean up the binary image
Get the skeleton of the image
Crop a ROI from the center of the image
Count the number of jumps from 255 to 0
This is the code, be sure to include the extended image processing module (ximgproc) and also link it before compiling it:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/ximgproc.hpp> // The extended image processing module
// Read Image:
std::string imagePath = "D://opencvImages//";
cv::Mat inputImage = cv::imread( imagePath+"IN2Xh.png" );
// Convert BGR to Grayscale:
cv::cvtColor( inputImage, inputImage, cv::COLOR_BGR2GRAY );
// Get binary image via Otsu:
cv::threshold( inputImage, inputImage, 0, 255, cv::THRESH_OTSU );
The above snippet produces the following image:
Note that there's a little bit of noise due to the thresholding, let's try to remove those isolated blobs of white pixels by applying some morphology. Maybe an opening, which is an erosion followed by dilation. The structuring elements and iterations, though, are not the same, and these where found by experimentation. I wanted to remove the majority of the isolated blobs without modifying too much the original image:
// Apply Morphology. Erosion + Dilation:
// Set rectangular structuring element of size 3 x 3:
cv::Mat SE = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(3, 3) );
// Set the iterations:
int morphoIterations = 1;
cv::morphologyEx( inputImage, inputImage, cv::MORPH_ERODE, SE, cv::Point(-1,-1), morphoIterations);
// Set rectangular structuring element of size 5 x 5:
SE = cv::getStructuringElement( cv::MORPH_RECT, cv::Size(5, 5) );
// Set the iterations:
morphoIterations = 2;
cv::morphologyEx( inputImage, inputImage, cv::MORPH_DILATE, SE, cv::Point(-1,-1), morphoIterations);
This combination of structuring elements and iterations yield the following filtered image:
Its looking alright. Now comes the main idea of the algorithm. If we compute the skeleton of this image, we would "normalize" all the lines to a width of 1 pixel, which is very handy, because we could reduce the image to a 1 x 1 (row) matrix and count the number of jumps. Since the lines are "normalized" we could get rid of possible overlaps between lines. Now, skeletonized images sometimes produce artifacts near the borders of the image. These artifacts resemble thickened anchors at the first and last row of the image. To prevent these artifacts we can extend borders prior to computing the skeleton:
// Extend borders to avoid skeleton artifacts, extend 5 pixels in all directions:
cv::copyMakeBorder( inputImage, inputImage, 5, 5, 5, 5, cv::BORDER_CONSTANT, 0 );
// Get the skeleton:
cv::Mat imageSkelton;
cv::ximgproc::thinning( inputImage, imageSkelton );
This is the skeleton obtained:
Nice. Before we count jumps, though, we must observe that the lines are skewed. If we reduce this image directly to a one row, some overlapping could indeed happen between to lines that are too skewed. To prevent this, I crop a middle section of the skeleton image and count transitions there. Let's crop the image:
// Crop middle ROI:
cv::Rect linesRoi;
linesRoi.x = 0;
linesRoi.y = 0.5 * imageSkelton.rows;
linesRoi.width = imageSkelton.cols;
linesRoi.height = 1;
cv::Mat imageROI = imageSkelton( linesRoi );
This would be the new ROI, which is just the middle row of the skeleton image:
Let me prepare a BGR copy of this just to draw some results:
// BGR version of the Grayscale ROI:
cv::Mat colorROI;
cv::cvtColor( imageROI, colorROI, cv::COLOR_GRAY2BGR );
Ok, let's loop through the image and count the transitions between 255 and 0. That happens when we look at the value of the current pixel and compare it with the value obtained an iteration earlier. The current pixel must be 0 and the past pixel 255. There's more than a way to loop through a cv::Mat in C++. I prefer to use cv::MatIterator_s and pointer arithmetic:
// Set the loop variables:
cv::MatIterator_<cv::Vec3b> it, end;
uchar pastPixel = 0;
int jumpsCounter = 0;
int i = 0;
// Loop thru image ROI and count 255-0 jumps:
for (it = imageROI.begin<cv::Vec3b>(), end = imageROI.end<cv::Vec3b>(); it != end; ++it) {
// Get current pixel
uchar &currentPixel = (*it)[0];
// Compare it with past pixel:
if ( (currentPixel == 0) && (pastPixel == 255) ){
// We have a jump:
jumpsCounter++;
// Draw the point on the BGR version of the image:
cv::line( colorROI, cv::Point(i, 0), cv::Point(i, 0), cv::Scalar(0, 0, 255), 1 );
}
// current pixel is now past pixel:
pastPixel = currentPixel;
i++;
}
// Show image and print number of jumps found:
cv::namedWindow( "Jumps Found", CV_WINDOW_NORMAL );
cv::imshow( "Jumps Found", colorROI );
cv::waitKey( 0 );
std::cout<<"Jumps Found: "<<jumpsCounter<<std::endl;
The points where the jumps were found are drawn in red, and the number of total jumps printed is:
Jumps Found: 9

error: (-215:Assertion failed) m.dims <= 2 in function 'FormattedImpl' in cv::dnn

I am loading a pre-trained TensorFlow model in the opencv dnn module using the following code -
cv::dnn::Net net = cv::dnn::readNetFromTensorflow("frozen_inference_graph.pb",
"graph.pbtxt");
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); //Run model on GPU
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
Mat image = imread("img.jpeg");
Mat resized;
cv::resize(image, resized, cv::Size(300, 300));
cout<<resized.size()<<endl;
cout<<"Resized"<<endl;
auto input_image = dnn::blobFromImage(image, 1.0, cv::Size(300, 300),
cv::Scalar(127.5, 127.5, 127.5),
false, false, CV_32F);
cout<<"Now setting Input";
net.setInput(input_image);
auto detections = net.forward();
cout<<detections;
return 0;
However the I get the following error as mentioned in the question -
what(): OpenCV(4.4.0) /home/atharva/opencv-4.4.0/modules/core/src/out.cpp:87: error: (-215:Assertion failed) m.dims <= 2 in function 'FormattedImpl'
Could someone please point out what the mistake is?. I believe there is some problem in BlobFromImage as nothing after it is getting printed.
TIA
This error occurs because you are trying to print a cv::Mat to standard output that has more than 2 dimensions. With cv::dnn, the output after using net.forward() is 4-dimensional. However I have no idea what model you are using because the output structure of the blob is different depending on what task you are trying to do. If I had to guess you are doing some sort of object detection given your choice of variable names. In that case, usually the first dimension is the batch size and since you are using only one image, the batch size is 1. The second dimension is the number of channels in the output. As you're doing object detection on the image, this will also be size 1. The third and fourth dimensions are the number of rows and columns for the final output layer.
Going on faith, you can extract a 2D version of this cv::Mat to print out to standard output by doing:
cv::Mat output(detections.size[2], detections.size[3], CV_32F, detection.ptr<float>());
Now that this is a 2D matrix, you can print out this instead by doing std::cout << output << std::endl;.

OpenCV warpPerspective error : Bad number of channels

when using warpPerspective,
OpenCV Error: Bad number of channels (Source image must have 1, 3 or 4
channels) in cvConvertImage, file
/build/opencv-ys8xiq/opencv-2.4.9.1+dfsg/modules/highgui/src/utils.cpp,
line 622 terminate called after throwing an instance of
'cv::Exception' what():
/build/opencv-ys8xiq/opencv-2.4.9.1+dfsg/modules/highgui/src/utils.cpp:622:
error: (-15) Source image must have 1, 3 or 4 channels in function
cvConvertImage
But, the source image being used is 1 channel and has the desired size.
This code is basically to get the birdeye's view of an image.
cv::Mat warped;
std::vector<cv::Point2f> src ;
src.push_back(cv::Point2f(640, 470));
src.push_back(cv::Point2f(0, 470));
src.push_back(cv::Point2f(150, 250));
src.push_back(cv::Point2f(490, 250));
std::vector<cv::Point2f> dst ;
dst.push_back(cv::Point2f(640, 480));
dst.push_back(cv::Point2f(0, 480));
dst.push_back(cv::Point2f(0, 0));
dst.push_back(cv::Point2f(640, 0));
cv::Mat M = cv::getPerspectiveTransform(src,dst);
cv::warpPerspective(src, warped, M, image.size());
It was discussed in topic: https://stackoverflow.com/a/17863381
Short answer:
Use cv::perspectiveTransform or matrix multiplication for points and cv::warpPerspective for images
I hope it will help.

OpenCV fillConvexPoly function in C++ throws exception

I'm trying to fill a triangle in a mask using the fillConvexPoly function.
But I get the following error.
OpenCV Error: Assertion failed (points.checkVector(2, CV_32S) >= 0) in fillConvexPoly, file /home/iris/Downloads/opencv-3.1.0/modules/imgproc/src/drawing.cpp, line 2256
terminate called after throwing an instance of 'cv::Exception'
what(): /home/iris/Downloads/opencv-3.1.0/modules/imgproc/src/drawing.cpp:2256: error: (-215) points.checkVector(2, CV_32S) >= 0 in function fillConvexPoly
I call the function as like so,
cv::Mat mask = cv::Mat::zeros(r2.size(), CV_32FC3);
cv::fillConvexPoly(mask, trOutCroppedInt, cv::Scalar(1.0, 1.0, 1.0), 16, 0);
where the trOutCroppedInt defined like so,
std::vector<cv::Point> trOutCroppedInt
And I push 3 points in the vector,
[83, 46; 0, 48; 39, 0]
How should I correct this error?
When points.checkVector(2, CV_32S) >= 0) is encountered
This error may occur when the data type is more complex than CV_32S and the dimension is greater than two, for example all data type like vector<Point2f> can create the problem. As the result we can use fillConvexpoly according to the following steps:
1. Reading an Image with
cv::Mat src=cv::imread("what/ever/directory");
2. determine points
You must determine your points like in the following graphic
Thus, our code for this point is:
vector<cv::Point> point;
point.push_back(Point(163,146)); //point1
point.push_back(Point(100,148)); //point2
point.push_back(Point(100,110)); //point3
point.push_back(Point(139,110)); //point4
3.Use cv::fillConvexPoly function
Consider the image src and draw a polygon ((with the points)) on this image then code would be as follows:
cv::fillConvexPoly(src, //Image to be drawn on
point, //C-Style array of points
Scalar(255, 0, 0), //Color , BGR form
CV_AA, // connectedness, 4 or 8
0); // Bits of radius to treat as fraction
(so output image is as follows: before:left side - after:right side)

Access pixel value in a cvMat image in OpenCV

I have used Canny edge detector to successfully identify the edges of a given image. I'm struggling with finding specific points on this detected edge line.
My approach:
I used the cv::canny function in opencv and the output is stored in cv::Mat format. I want to iterate through the all values of the matrix and identify all those pixels where the edge is present so that I can detect the specific points on the detected edge line.
Function used:
cv::Canny(frame_gray,contours,50,150);
The output is stored in contours and it is of type CV_8UC3
To access the pixel value, have tried
contours.at<int>(i,j) != 0
and also
contours.at<uchar>(i,j) != 0
Will greatly appreciate help in the above. If the approach is correct and am missing something or else if i should try another approach
Thanks
Edit:
for(int i=0;i<img_width;i++)
{
if((int)contours.at<uchar>(i,neckcenter.y) > 0 )
{
Point multipoints(i,neckcenter.y);
circle( contours, multipoints, neckpoint, Scalar( 255, 0, 0 ),4, 8, 0 );
cout << (int)contours.at<uchar>(i,neckcenter.y) << endl;
}
}
I am using the above code which forms a small circle of radius 1 (defined by neckpoint) where it detects a point on and edge. The neckcenter.y is a constant value derived from an earlier calculation. What am i doing wrong here ?
Output of the code -
you probably want a grayscale pass before applying Canny:
Mat gray;
cvtColor(bgr,gray,CV_BGR2GRAY); // now gray is a 8bit, uchar Mat
Mat contours;
cv::Canny(gray,contours,50,150);
// now you're safe to use:
uchar value = contours.at<uchar>(i,j);
The syntax:
contours.at<uchar>(i,j)
Is correct for your case in terms of data type (i.e. a grayscale image). The problem is possibly hinted at by this line:
for(int i=0;i<img_width;i++)
When you access OpenCV pixels using at, you must specify the pixel position as (row, col), so your indexing is the wrong way round. Try this in all places where you access pixels:
contours.at<uchar>(j,i)
From the OpenCV documentation:
You have a 3 channel image of the type unsigned char. To access it you should use the cv::Vec3b type. Here is how to do it:
int channel = 0;//or 1 or 2
contours.at<cv::Vec3b>(i,j)[channel]
To check if all elements are 0:
contours.at<cv::Vec3b>(i,j)[0]==0 && contours.at<cv::Vec3b>(i,j)[1]==0 && contours.at<cv::Vec3b>(i,j)[2]==0
But where do you have the information that the image type of contours is CV_8UC3 ?