OpenCV function pointPolygonTest() does not act like what I thought - c++

Please check my code, it does not work well. There is no errors occur during both build and debug session. I want to mark all the pixels with WHITE inside every contour. The contours are correct because I have drawn them separately. But the ultimate result is not right.
//Draw the Sketeches
Mat sketches(detected.size(), CV_8UC1, Scalar(0));
for (int j = ptop; j <= pbottom; ++j) {
for (int i = pleft; i <= pright; ++i) {
if (pointPolygonTest(contours[firstc], Point(j, i), false) >= 0) {
sketches.at<uchar> (i, j)= 255;
}
if (pointPolygonTest(contours[secondc], Point(j, i), false) >= 0) {
sketches.at<uchar> (i, j)= 255;
}
}
}
The variable "Mat detected" is another image used for hand detection. I have extracted two contours from it as contours[firstc] and contours[secondc]. And I also narrow down the hand part in the image to row(ptop:pbottom), and col(pleft,pright), and the two "for" loop goes correctly as well. So where exactly is the problem?.
Here is my result! Something goes wrong with it!

Related

producing "unhandled exception X at memory location Y" when checking every pixel of an grayscale image

I am trying to solve a problem where I have to check every pixel from a 640x480 cv::Mat (Greyscale Video Image -> cv::Mat MainImageSW (global)) and check if the pixel above, below, to the left and to the right is equal or below the Threshholdvalue ( GetTreshholdSW() ).
My target matrix XY_ThreshholdMat is also 640x480 and set equal to Matrix255 which is initialized with"255".
If the requirements meet I set the pixel of my new Mat "XY_ThreshholdMat" to "0" in that location.
The aim is to have every pixel which meets the req. to be black and the others white.
To do so I wrote the following code:
void GraphicsView_PP::Fill_XY_TreshholdMat()
{
cv::Mat Matrix255(GetPixmapWidth(), GetPixmapHeight(), CV_8UC1, cv::Scalar(255));
XY_ThreshholdMat = Matrix255;
for (int y = 1; y < (GetPixmapHeight()-1); y++)
{
for (int x = 1; x < (GetPixmapWidth()-1); x++)
{
if ((MainImageSW.at<uchar>(y, x)) <= GetTreshholdSW())
{
if ((MainImageSW.at<uchar>((y + 1), x) <= GetTreshholdSW()) &&
(MainImageSW.at<uchar>((y - 1), x) <= GetTreshholdSW()) &&
(MainImageSW.at<uchar>(y, (x + 1)) <= GetTreshholdSW()) &&
(MainImageSW.at<uchar>(y, (x - 1)) <= GetTreshholdSW()))
{
XY_ThreshholdMat.at<uchar>(y, x) = 0;
}
}
}
}
QImage Image((uchar*)XY_ThreshholdMat.data, XY_ThreshholdMat.cols, XY_ThreshholdMat.rows, XY_ThreshholdMat.step, QImage::Format_Grayscale8);
QPixmap Pixmap = QPixmap::fromImage(Image);
ui.graphicsView_Bild->UpdateStream(Pixmap);
cv::waitKey(5000);
}
Because I am checking the pixels above, below, to the left and to the right I start both for-loops at X/Y = 1 and let it end to (MaxWidth-1) and (MaxHeight-1).
After checking every individual pixel I want to save the new Mat where every Match is a "0" and everything else is "255" to a Pixmap and display it in my custom QGraphicsView element.
Once I run the code I get the typical "unhandled exception X at memory location Y" error but I dont know why..
Also: I am rather new at programming and I know that the way the code is designed is not the most efficient way. All that matters is that it works..
Anyone having an idea how to solve this problem?
cv::Mat Matrix255(GetPixmapWidth(), GetPixmapHeight(), ...
should be
cv::Mat Matrix255(GetPixmapHeight(), GetPixmapWidth(), ...
Because of this mistake you access the elements out of bounds and your program therefore has undefined behavior.

Opencv obatin certain pixel RGB value based on mask

My title may not be clear enough, but please look carefully on the following description.Thanks in advance.
I have a RGB image and a binary mask image:
Mat img = imread("test.jpg")
Mat mask = Mat::zeros(img.rows, img.cols, CV_8U);
Give some ones to the mask, assume the number of ones is N. Now the nonzero coordinates are known, based on these coordinates, we can surely obtain the corresponding pixel RGB value of the origin image.I know this can be accomplished by the following code:
Mat colors = Mat::zeros(N, 3, CV_8U);
int counter = 0;
for (int i = 0; i < mask.rows; i++)
{
for (int j = 0; j < mask.cols; j++)
{
if (mask.at<uchar>(i, j) == 1)
{
colors.at<uchar>(counter, 0) = img.at<Vec3b>(i, j)[0];
colors.at<uchar>(counter, 1) = img.at<Vec3b>(i, j)[1];
colors.at<uchar>(counter, 2) = img.at<Vec3b>(i, j)[2];
counter++;
}
}
}
And the coords will be as follows:
enter image description here
However, this two layer of for loop costs too much time. I was wondering if there is a faster method to obatin colors, hope you guys can understand what I was trying to convey.
PS:If I can use python, this can be done in only one sentence:
colors = img[mask == 1]
The .at() method is the slowest way to access Mat values in C++. Fastest is to use pointers, but best practice is an iterator. See the OpenCV tutorial on scanning images.
Just a note, even though Python's syntax is nice for something like this, it still has to loop through all of the elements at the end of the day---and since it has some overhead before this, it's de-facto slower than C++ loops with pointers. You necessarily need to loop through all the elements regardless of your library, you're doing comparisons with the mask for every element.
If you are flexible with using any other open source library using C++, try Armadillo. You can do all linear algebra operations with it and also, you can reduce above code to one line(similar to your Python code snippet).
Or
Try findNonZero()function and find all coordinates in image containing non-zero values. Check this: https://stackoverflow.com/a/19244484/7514664
Compile with optimization enabled, try profiling this version and tell us if it is faster:
vector<Vec3b> colors;
if (img.isContinuous() && mask.isContinuous()) {
auto pimg = img.ptr<Vec3b>();
for (auto pmask = mask.datastart; pmask < mask.dataend; ++pmask, ++pimg) {
if (*pmask)
colors.emplace_back(*pimg);
}
}
else {
for (int r = 0; r < img.rows; ++r) {
auto prowimg = img.ptr<Vec3b>(r);
auto prowmask = img.ptr(r);
for (int c = 0; c < img.cols; ++c) {
if (prowmask[c])
colors.emplace_back(prowimg[c]);
}
}
}
If you know the size of colors, reserve the space for it beforehand.

convertTo not working in opencv

I am trying to use convertTo() in opencv C++. But an error pops saying
left of:convertTo must have class/struct/union
the program is below:
for (i = 0; i < height; i += 8)
{
for (j = 0; j < width; j += 8)
{
Mat block = dctImage(Rect(j, i, 8, 8));
vector<Mat> planes;
split(block, planes);
vector<Mat> outplanes(planes.size());
for (k = 0; k < planes.size(); k++) {
planes[k].convertTo(planes[k], CV_32FC1);
dct(planes[k], outplanes[k]);
outplanes[k].convertTo(outplanes[k], CV_8UC1);
}
}
}
I'm not sure if .convertTo() can handle the case of identical source and destination. You may want to try using a pair of temporary variables to get around your error message. Here is the relevant part from your example:
// ...
for (k = 0; k < planes.size(); k++) {
Mat planes_k, outplanes_k; // <-- Added temporaries.
planes[k].convertTo(planes_k, CV_32FC1);
dct(planes_k, outplanes_k);
outplanes_k.convertTo(outplanes[k], CV_8UC1);
}
// ...
UPDATE
According to the source code of .convertTo() my suggestion isn't really required (thanks pointing this out, #boaz001).
The Left:of statement suggests that its null or undefined
i.e.
outplanes[k] does not contain what you think it should.
I did a quick search and it looks like this could be a compiler issue whereby it has difficulty understanding/interpreting the line ...
vector<Mat> outplanes(planes.size());
https://en.wikipedia.org/wiki/Most_vexing_parse
What is outplanes? Is it being instantiated correctly?
The 'Most_vexing_parse' article suggests helping the compiler with extra parenthesis
vector<Mat> outplanes((planes.size()));
Kinda depends on your code and what the outplanes method/class is actually doing, but i imagine it's not returning what you think it should be.

Combining overlapping groups in an image

I am using opencv_contrib to detect textual regions in an image.
This is the original image
This is the image after textual regions are found:
As can be seen, there are overlapping groups in the image. For example, there seem to be two groups around Hello World and two around Some more sample text
Question
In scenarios like these how can I keep the widest possible box by merging the two boxes. For these examples that would be one starting with H and ending in d so that it covers Hello World. My reason for doing is is that I would like to crop part of this image and send it to tesseract.
Here is the relevant code that draws the boxes.
void groups_draw(Mat &src, vector<Rect> &groups)
{
for (int i=(int)groups.size()-1; i>=0; i--)
{
if (src.type() == CV_8UC3)
rectangle(src,groups.at(i).tl(),groups.at(i).br(),Scalar( 0, 255, 255 ), 2, 8 );
}
}
Here is what I've tried. My ideas are in comments.
void groups_draw(Mat &src, vector<Rect> &groups)
{
int previous_tl_x = 0;
int previous_tl_y = 0;
int prevoius_br_x = 0;
int previous_br_y = 0;
//sort the groups from lowest to largest.
for (int i=(int)groups.size()-1; i>=0; i--)
{
//if previous_tl_x is smaller than current_tl_x then keep the current one.
//if previous_br_x is smaller than current_br_x then keep the current one.
if (src.type() == CV_8UC3) {
//crop the image
Mat cropedImage = src(Rect(Point(groups.at(i).tl().x, groups.at(i).tl().y),Point(groups.at(i).br().x, groups.at(i).br().y)));
imshow("cropped",cropedImage);
waitKey(-1);
}
}
}
Update
I'm trying to use [groupRectangles][4] to accomplish this:
void groups_draw(Mat &src, vector<Rect> &groups)
{
vector<Rect> rects;
for (int i=(int)groups.size()-1; i>=0; i--)
{
rects.push_back(groups.at(i));
}
groupRectangles(rects, 1, 0.2);
}
However, this is giving me an error:
textdetection.cpp:106:5: error: use of undeclared identifier 'groupRectangles'
groupRectangles(rects, 1, 0.2);
^
1 error generated.
First, the reason you get overlapping bounding boxes is that the text detector module is working on inverted channels (e.g: gray and inverted gray) and because of that the inner regions of some characters such as o's and g's are wrongly detected and grouped as characters. So if you want to detect only one mode of text (white text on dark background) just pass the inverted channels.
Replace:
for (int c = 0; c < cn-1; c++)
channels.push_back(255-channels[c]);
With:
for (int c = 0; c < cn-1; c++)
channels[c] = (255-channels[c]);
Now for your question, rectangles have defined intersection and combining operators:
rect = rect1 & rect2 (rectangle intersection)
rect = rect1 | rect2 (minimum area rectangle containing rect2 and rect3 )
rect &= rect1, rect |= rect1 (and the corresponding augmenting operations)
You can use those operators while iterating over rectangles to detect intersected rectangles and combine them, as follows:
if ((rect1 & rect2).area() != 0)
rect1 |= rect2;
Edit:
First, sort rectangle groups by area from largest to smallest:
std::sort(groups.begin(), groups.end(),
[](const cv::Rect &rect1, const cv::Rect &rect2) -> bool {return rect1.area() > rect2.area();});
Then, iterate over the rectangles, when two rectangles intersect add the smaller to the larger and then delete it:
for (int i = 0; i < groups.size(); i++)
{
for (int j = i + 1; j < groups.size(); j++)
{
if ((groups[i] & groups[j]).area() != 0)
{
groups[i] |= groups[j];
groups.erase(groups.begin() + j--);
}
}
}
One approach would be to compare every rectangle with every other rectangle to see if they overlap or intersect. If they do in a sufficient amount you can combine them into one larger rectangle.

Multi region mask OpenCV

I would like to create a mask in OpenCV containing some full rectangular regions (say 1 to 10 regions). Think of it as a mask showing the location of features of interest on an image. I know the pixel coordinates of the corners of each region.
Right now, I am first initializing a Mat to 0, then I am looping through each element. Using "if" logic, I put each pixel to 255 if they belong to the region, such as:
for (int i = 0; i<mymask.cols, i++) {
for (int j = 0; j<mymask.rows, j++) {
if ( ((i > x_lowbound1) && (i < x_highbound1) &&
(j > y_lowbound1) && (j < y_highbound1)) ||
((i > x_lowbound2) && (i < x_highbound2) &&
(j > y_lowbound2) && (j < y_highbound2))) {
mymask.at<uchar>(i,j) = 255;
}
}
}
But this is very clumsy and I think inefficient. In this case, I "fill" 2 rectangular region with 255. But there is no viable way to change the number of region I fill, beside using a switch-case and repeating the code n times.
Is there anyone thinking of something more intelligent? I would rather not use a 3rd party stuff (beside OpenCV ;) ) and I am using VisualStudio 2012.
Use cv::rectangle():
//bounds are inclusive in this code!
cv::Rect region(x_lowbound1, y_lowbound1,
x_highbound1 - x_lowbound1 + 1, y_highbound1 - y_lowbound1 + 1)
cv::rectangle(mymask, region, cv::Scalar(255), CV_FILLED);