How is it possible to calculate the black / white ratio of the pixels inside the outline of a contour (not the bounding box)?
The image is pre-processed with cv::threshold(src, img, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU); and then inverted img = 255 - img;
I look for the retangular outline of the table (contour) via cv::RETR_EXTERNAL.. I want to calculate the black pixels inside the contour
There can be other components in the image so I can't just count all non-zero pixels
This is the original image before binarized and inverted
I think there's some confusion about terminology. A contour is simply a sequence of points. If you draw them as a closed polygon (e.g. with cv::drawContours), all the points inside the polygon will be white.
You can however use this mask to count the white or black pixels on your thresholded image:
cv::Mat1b bw_image = ...
std::vector<std::vector<cv::Point>> contours;
cv::findContours(bw_image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
for(size_t i=0; i<contours.size(); ++i)
{
cv::Mat1b contour_mask(bw_image.rows, bw_image.cols, uchar(0));
cv::drawContours(contour_mask, contours, i, Scalar(255), cv::FILLED);
int total_white_inside_contour = cv::countNonZero(mask);
int white_on_image_inside_contour = cv::countNonZero(bw_image & mask);
int black_on_image_inside_contour = total_white_inside_contour - white_on_image_inside_contour;
}
You cannot calculate the white and black ratio of a contour, because what is a contour? A group of white pixels which are connected which each other calls contour, so a contour does not contain any black pixel if it does, it calls hole inside the contour.
And also a contour does not have a specific shape.
So you can do it by Bounding Rectangle the rectangle around the contour then you will be to calculate the black and white ratio inside the rectangle.
Related
I have jpg image and polygon which I want to use as mask in this way: image inside this polygon should be displayed, and all outside this polygon should be 100% transparent. Now I achieved only first goal - I can display image inside polygon, but all outside of it is black:
cv::Mat image;
//load image
image.convertTo(image, CV_8UC3, 255.0);
std::vector<cv::Point> contour;
//load polygon
const cv::Point* elementPoints[1] = { &contour[0] };
int numberOfPoints = (int)contour.size();
cv::Mat mask = cv::Mat::zeros(image.size(), image.type());
cv::fillPoly(mask, elementPoints, &numberOfPoints, 1, cv::Scalar( 255, 255, 255), 8);
cv::Mat dstImage = cv::Mat::zeros(image.size(), image.type());
cv::bitwise_and(image, mask, dstImage);
imwrite("test.jpg", dstImage);
I know that I need to use alpha channel, but it's unclear what I need to do next and how to implement this.
How can I get transparent background outside the mask?
First, create your image with four channels as described in this answer. Use negative source for fourth channel to get it zeroed out already. You now have a totally transparent image.
Create your mask just as you did before, just using different RGBA values (be aware that Scalar has a fourth constructor parameter for alpha values):
cv::fillPoly(mask, elementPoints, &numberOfPoints, 1, cv::Scalar(0, 0, 0, 255), 8);
Finally, apply the mask to set the region in question to totally opaque:
cv::bitwise_or(image, mask, dstImage);
You might want to retain the RGB values of the original image (so you can operate on later) or you might clear them out (which will result in higher compression and thus smaller file size). If the latter, use an inverted mask with RGBA set to 0, 0, 0, 0 and apply that with bitwise_and...
I have an image in which pixels above a certain value I'd like to turn red, and pixels below a certain value I'd like to turn blue.
So far, I can get a matrix of red pixels, and a matrix of blue pixels by using thresholding, and bitwise operators to set the pixel value:
cvtColor(displayImage, displayImage, COLOR_GRAY2BGR);
threshold(displayImage, highThresh, highThreshVal, 255, 0);
highThresh = highThresh & Scalar(0, 0, 255); // Turn it red
threshold(displayImage, lowThresh, lowThreshVal, 255, 1);
lowThresh = lowThresh & Scalar(255, 0, 0); // Turn it blue
displayImage = lowThresh + highThresh;
When I display the displayImage, I see almost exactly what I want. It's an image where all the pixels below lowThreshVal are blue, and all pixels above highThreshVal are red. However, the pixels that are in between these values are all set to 0. Whereas, I would like to show the original image overlayed with the blue and red images. I'm not sure how to do this, or if I'm taking the best approach.
I know I can't add the images because I want to make sure every pixel above the threshold is pure red, not a mix of red and the original image, this yields pink-ish pixels instead of bright red pixels, which defeats the purpose of what I'm trying to build. But as of right now, I'm kind of stuck on what to do.
This worked.
cvtColor(displayImage, displayImage, COLOR_GRAY2BGR);
origImage = displayImage.clone();
threshold(origImage, highThresh, highThreshVal, 255, 0); // Binary thresholding
cvtColor(highThresh, highThresh, CV_BGR2GRAY);
displayImage.setTo(Scalar(0, 0, 255), highThresh);
threshold(origImage, lowThresh, lowThreshVal, 255, 1); // Binary thresholding
cvtColor(lowThresh, lowThresh, CV_BGR2GRAY);
displayImage.setTo(Scalar(255, 0, 0), lowThresh);
I'm new to image processing and development. I have used opencv, There I need to extract circle from a given image. That circle given x, y coordinates are (radius) in Oder to do that I used following code. But my problem is I have to take black rectangle. So the image patch having unwanted black pixels. How do I save just only circle?
my code
double save_key_points(Mat3b img, double x, double y, double radius, string
filename, string foldername)
{
// print image height and width first and check.
Vec3f circ(x, y, radius);
// Draw the mask: white circle on black background
Mat1b mask(img.size(), uchar(0));
circle(mask, Point(circ[0], circ[1]), circ[2], Scalar(255), CV_FILLED);
// Compute the bounding box
Rect bbox(circ[0] - circ[2], circ[1] - circ[2], 2 * circ[2], 2 * circ[2]);
// Create a black image
Mat3b res(img.size(), Vec3b(0, 0, 0));
// Copy only the image under the white circle to black image
img.copyTo(res, mask);
// Crop according to the roi
res = res(bbox);
//remove black but doesn't work.
Mat tmp, alpha;
threshold(res, alpha, 100, 255, THRESH_BINARY);
// Save the image
string path = "C:\\Users\\bb\\Desktop\\test_results\\test_case8\\" + foldername + filename + ".png";
imwrite(path, res);
Mat keypointimg = imread(path, CV_LOAD_IMAGE_GRAYSCALE);
//print the cordinate of one patch.
cordinate_print(keypointimg, radius);
}
(Here i want without black background)
If I understand what you are asking correctly you could remove the black from an image you can use a mask. The mask can highlight anything that is of a certain colour or in your case the shade of black. Check out the link for this implementation and see if it is what you are looknig for. It is in python but can be easily adapted.
Image Filtering
I have the center, the angle of inclination (direction) and the measures of the sides of a sub-image that I want to crop from another image, for example:
to:
I managed to put it with the right inclination using:
Mat img;
Point center;
float angle;
Mat rotation = getRotationMatrix2D(center, angle, 1.0);
warpAffine(img, img, rotation, img.size());
But I dont know how to cut that area with the given sides... How can I do it?
If you can rotate the image, after that you just need one of the corners position (for instance, bottom-right corner) and the width and height of the image you want to crop.
With that said, you can set a ROI (region of interest) and crop with
cv::Rect RegionOfInterest(top_left_x, top_left_y, rectangle_width, rectangle_height);
cv::Mat outputImage;
outputImage = originalImage(RegionOfInterest).clone();
where top_left_x and top_left_y are your top left corner coordinates, and rectangle_width and rectangle_height are the width and height of the rectangle you are interested in extract.
I would like to know how to draw semi-transparent shapes in OpenCV, similar to those in the image below (from http://tellthattomycamera.wordpress.com/)
I don't need those fancy circles, but I would like to be able to draw a rectangle, e.g, on a 3 channel color image and specify the transparency of the rectangle, something like
rectangle (img, Point (100,100), Point (300,300), Scalar (0,125,125,0.4), CV_FILLED);
where 0,125,125 is the color of the rectangle and 0.4 specifies the transparency.
However OpenCV doesn't have this functionality built into its drawing functions. How can I draw shapes in OpenCV so that the original image being drawn on is partially visible through the shape?
The image below illustrates transparency using OpenCV. You need to do an alpha blend between the image and the rectangle. Below is the code for one way to do this.
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
int main( int argc, char** argv )
{
cv::Mat image = cv::imread("IMG_2083s.png");
cv::Mat roi = image(cv::Rect(100, 100, 300, 300));
cv::Mat color(roi.size(), CV_8UC3, cv::Scalar(0, 125, 125));
double alpha = 0.3;
cv::addWeighted(color, alpha, roi, 1.0 - alpha , 0.0, roi);
cv::imshow("image",image);
cv::waitKey(0);
}
In OpenCV 3 this code worked for me:
cv::Mat source = cv::imread("IMG_2083s.png");
cv::Mat overlay;
double alpha = 0.3;
// copy the source image to an overlay
source.copyTo(overlay);
// draw a filled, yellow rectangle on the overlay copy
cv::rectangle(overlay, cv::Rect(100, 100, 300, 300), cv::Scalar(0, 125, 125), -1);
// blend the overlay with the source image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source);
Source/Inspired by: http://bistr-o-mathik.org/2012/06/13/simple-transparency-in-opencv/
Adding to Alexander Taubenkorb's answer, you can draw random (semi-transparent) shapes by replacing the cv::rectangle line with the shape you want to draw.
For example, if you want to draw a series of semi-transparent circles, you can do it as follows:
cv::Mat source = cv::imread("IMG_2083s.png"); // loading the source image
cv::Mat overlay; // declaring overlay matrix, we'll copy source image to this matrix
double alpha = 0.3; // defining opacity value, 0 means fully transparent, 1 means fully opaque
source.copyTo(overlay); // copying the source image to overlay matrix, we'll be drawing shapes on overlay matrix and we'll blend it with original image
// change this section to draw the shapes you want to draw
vector<Point>::const_iterator points_it; // declaring points iterator
for( points_it = circles.begin(); points_it != circles.end(); ++points_it ) // circles is a vector of points, containing center of each circle
circle(overlay, *points_it, 1, (0, 255, 255), -1); // drawing circles on overlay image
cv::addWeighted(overlay, alpha, source, 1 - alpha, 0, source); // blending the overlay (with alpha opacity) with the source image (with 1-alpha opacity)
For C++, I personally like the readability of overloaded operators for scalar multiplication and matrix addition:
... same initial lines as other answers above ...
// blend the overlay with the source image
source = source * (1.0 - alpha) + overlay * alpha;