opencv: How to fill the area/edge outside a region - c++

The image below only contains the black and white pixels after thresholding. I draw a rotated rectangle in grey on top of this image. Now I would like to count the number of black pixels within this rotated rectangle, but not including the black pixels outside the white rectangle-ish rectangle (i.e. number of the pixels within the white rectangle).
What is the best approach to do that? Shall I fill the area outside the white rectangle with white pixel? Any suggestions are welcomed.

If you know the angle and size of the area you want to search, you can cut it out of the image using this technique:
How to straighten a rotated rectangle area of an image using opencv in python?
(I know its Python, but there's a c++ equivalent)
EDIT
You can use this code to return and print the value of each pixel so you can get an idea on the kind of values you are getting back (should just be 0 and 1/255 if binary image)
for(int i=0; i<img.rows; i++)
for(int j=0; j<img.cols; j++)
std::cout<<"Value: "<<static_cast<int>(gray_image.at<uchar>(i,j));
Once you are getting these values maybe make a counter that will increment every time a pixel has a value over a certain threshold

Related

cv::detail::MultiBandBlender strange white streaks at the end of the photo

I'm working with OpenCV 3.4.8 with C++11 and I'm trying to blend images together.
In this example I have 2 images (thiers mask shown in the screen belowe). I have georeference, so I can easy calculate corners of this images in the final image.
The data outside the masks are black.
My code looks like something like that:
std::vector<cv::UMat> inputImages;
std::vector<cv::UMat> masks;
std::vector<cv::Point> corners;
std::vector<cv::Size> imgSizes;
/*
here is code where I load images, create thier masks
(like in the screen above) and calculate corners.
*/
cv::Ptr<cv::detail::SeamFinder> seamFinder = new cv::detail::DpSeamFinder();
seamFinder->find(inputImages, corners, masks);
cv::Ptr<cv::detail::Blender> blender = new cv::detail:: MultiBandBlender(false);
blender->prepare(corners, imgSizes);
for(size_t i = 0; i < inputImages.size(); i++)
{
blender->feed(inputImages[i], masks[i], corners[i]);
}
cv::UMat blendedImg, outMask;
blender->blend(blendedImg, outMask);
SeamFinder gives me result like in the screen above. Finded seam lines looks good and Im very satisied form them. But the other problem occurs in the next step. The MultiBandBlender is making strange white streaks when the seam line goes on the end of the data.
This is an example:
When I don't use blender, but just use masks to cut the oryginal images and just add (cv::add()) images together with additional alpha channel (made from masks) I get very good results without any holes and strange colors, but I need to have more smoothed transition :/
Can anyone help me? When I create MultiBand Blender with smaller num_bands the white streaks are smaller, and with the num_bands = 0 the results looks like with just adding images.
I looked at feed() and blend() methods in the MultiBandBlender and I think that it is connected with Gaussian or Laplacian pyramid and the final restoring images from Laplacian pyramid in the blend() method.
EDIT1:
When Gaussian and Laplacian pyramids are created the copyMakeBorder(), which prevents the MultiBandBlender from making this white streaks when images are fully filled with the data. So in my case I think that I need to create my blender almost the same like MultiBandBlender, but copyMakeBorder() method in the feed() method change to the something that will "extend" my image inside the mask, like #AlexanderKondratskiy suggested.
Now I don't know how to achive correct "extend" similar to BORDER_REFLECT or BORDER_REFLECT_101.
I suspect your input images contain white pixels outside those masks. The white banding occurs around the areas where the seam follows the mask exactly. For Laplacian for example, pixels outside the mask do influence the final result, as each layer of a pyramid is essentially some blurring kernel on the image.
If you have some kind of good data outside the mask, keep it. If you do not, I suggest "extending" your image beyond the mask to maintain a smooth transition.
Edit:
Here's two things you could try, unless someone with more experience with OpenCV comes along.
To prove/disprove my hypothesis, fill the black region with just the average or median color within the mask. This should make the transition to the outside region less sharp, and hopefully reduce the artefacts. If that does not happen, my answer is wrong.
In terms of what is probably a good generalization of "BORDER_REFLECT" when the edge is arbitrary, you could try something like this:
Find the centroid c of the mask polygon
For each pixel p outside the mask, think of the line between it and c
Calculate point p' along this line that is the same distance inside the mask area, as p is from the mask edge. (i.e. you're reflecting along the mask edge)
Linearly interpolate the color of from the neighbors of p' (as it's position may not fall exactly in the middle of a pixel). That's the color of pixel p

Any better way to locate whitespaces on binary images?

This is a picture of a corridor after masking.
I'm trying to follow the "free path" in an indoor environment using opencv, the way I'm trying to locate the free area or whitespace in the image is by traversing the whole array and checking the pixel values, but this seems too slow. I also tried using findContours and edge detection methods from opencv but the largest contour area is pointing at the far left corner of the white area. Any other way I can do this ?
import cv2
im = cv2.imread('YourImagePAth\\image.png')
gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
contours, hierarchy =
cv2.findContours(gray,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[-2:]
idx =0
for cnt in contours:
idx += 1
x,y,w,h = cv2.boundingRect(cnt)
roi=im[y:y+h,x:x+w]
cv2.rectangle(im,(x,y),(x+w,y+h),(0,0,255),2)
cv2.drawContours(im,contours,-1,(0,255,0),3)
cv2.imshow('img',im)
cv2.waitKey(0)
now if you want the white pixels area use : area = cv.contourArea(cnt)
and if you want the red rectangle area use : w*h (bounding box dimensions).
This is what you should get if you draw the bounding box using the code above :

extract the shapes made a particular colour in opencv

take this image below
I would like to extract the shapes of the red outline into a separate image. I want to do this because I want to check the convexity of theses shapes for my work. Any advice? I tried split channels but that just removes the red colour from the image.
Since you have drawn the red border by yourself, there is no need to analyze the red component at all. By doing that, you are exactly like someone who take a print screen of txt file and trying to OCR it!
The solution:
cv::BoundingBox around the point of the first red contour.
Get ROI of the rectangle and store it in a separate cv::Mat.
Create a new black(0) cv::Mat with the same header of the previous cv::Mat.
Draw the contour with White(255) using cv::fillPoly.
cv::bitwise_and between the two cv::Mats.
You could try making an image that comprises pixels where red is the dominant colour, for example you would examine every pixel and make a B/W image like this
#define MIN_RED 192
#define MAX_OTHER 64
// each pixel
if (r >= MIN_RED && g <= MAX_OTHER && b <= MAX_OTHER)
c = 1;
else
c = 0;
This would filter out the blues and greens and grays and preserve the bright reds.

Create mask to select the black area

I have a black area around my image and I want to create a mask using OpenCV C++ that selects just this black area so that I can paint it later. How can i do that without affecting the image itself?
I tried to convert the image to grayscale and then using threshold to convert it to binary, but it affects my image since the result contains black pixels from inside the image.
Another Question : if i want to crop the image instead of paint it, how can i do it??
Thanks in advance,
I would solve the problem like this:
Inverse-binarize the image with a threshold of 1 (i.e. all pixels with the value 0 are set to 1, all others to 0)
use cv::findContours to find white segments
remove segments that don't touch image borders
use cv::drawContours to draw the remaining segments to a mask.
There is probably a more efficient solution in terms of runtime efficiency, but you should be able to prototype my solution quite quickly.

OpenCV measure rectangular image size

I have an app that finds an object in a frame and uses warpPerspective to correct the image to be square. In the course of doing so you specify an output image size. However, I want to know how to do so without harming its apparent size. How can I unwarp the 4-corners of the image without changing the size of the image? I don't need the image itself, I just want to measure its height and width in pixels within the original image.
Get a transform matrix that will square up the corners.
std::vector<cv::Point2f> transformedPoints;
cv::Mat M = cv::getPerspectiveTransform(points, objectCorners);
cv::perspectiveTransform(points, transformedPoints, M);
This will square up the image, but in terms of the objectCorners coordinate system. Which is -0.5f to 0.5f not the original image plane.
BoundingRect almost does what I want.
cv::Rect boundingRectangle = cv::boundingRect(points);
But as the documentation states
The function calculates and returns the minimal up-right bounding rectangle for the specified point set.
And what I want is the bounding rectangle after it has been squared-up, not without squaring it up.
According to my understanding to your post, here is something which should help you.
OpenCV perspective transform example.
Update if it still doesn't help you out in finding the height and width within the image
Minimum bounding rect of the points
cv::RotatedRect box = cv::minAreaRect(cv::Mat(points));
As the minAreaRect reference on OpenCV's website states
Finds a rotated rectangle of the minimum area enclosing the input 2D point set.
You can call box.size and get the width and height.