I have retrieved a contour from an image and want to specifically work on the pixels in the contour. I need to find the sum (not area) of the pixel values in the contour. OpenCV only supports rectangle shaped ROI, so I have no idea how to do this. cvSum also only accepts complete images and doesn't have a mask option, so I am a bit lost on how to proceed. Does anyone have any suggestions on how to find the sum of the values of pixels in a specific contour?
First get all of your contours. Use this information to create a binary image with the white parts being the contour's outline and area. Perform an AND operation on the two images. The result will be the contours and area on a black background. Then just sum all of the pixels in this image.
If I understand right you want to sum all the pixel intensities from a gray image that are inside a contour. If so, the method that i think of is to draw that contour on a blank image and fill it , in so making yourself a mask. After that to optimize the process you can also compute the bounding rect of the contour with :
CvRect cvBoundingRect(CvArr* points, int update=0 );
After this you can make an intermediate image with :
void cvAddS(const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL);
using the value 0, the mask obtained from the contour and setting before as ROI the bounding rect.
After this, a sum on the resulting image will be a little faster.
To access the contour's point individually follow the code
vector<vector<Point> > contours;
...
printf("\n Contours pixels \n");
for(int a=0; a< contours.size(); a++)
{
printf("\nThe contour NO = %d size = %d \n",a, contours[a].size() );
for( int b = 0; b < contours[a].size(); b++ )
{
printf(" [%d, %d] ",contours[a][b].x, contours[a][b].y );
}
}
Related
I've got a Mat image which is a binary mask that I segmented and a cv::Rect that identifies an specific region. When I get the contours of the binary mask the image is like this:
Binary Mask
Contours generated
I would like to fill in the mask the region that intersects with the rectangle. How would I do that?
Thanks in advance.
There is way simpler than #ZdaR's solution: using Regions Of Interest (ROI) which directly selects the bounding rectangle region to process.
cv::Rect boundingRect(/* x, y, width, height */);
contours_image(boundingRect).setTo(255, binary_image(boundingRect));
Here, I select each region with operator parenthesis contours_image(boundingRect) and binary_image(boundingRect), and use the binary image part as a mask to set all corresponding pixels to 255.
A good choice would be to use cv::min() with the binary image and another cv::Mat() with the area under cv::Rect() painted as white. It will filter out the desired portion under the Rect as:
// Create a grayscale canvas with black background
cv::Mat canvas = cv::Mat(binary_img.size(), CV_8UC1, cv::Scalar(0));
// I created a dummy rect replace it with original rect coordinates.
cv::Rect boundingRect = cv::Rect(100, 100, 200, 200);
// Draw filled rect onto the black canvas with white color
cv::rectangle(binary_image, boundingRect, cv::Scalar(255), -1);
// Take the min of binary image and canvas to filter out the contours
cv::min(binary_image, canvas, binary_image);
EDIT:
If you want to filter the contours which intersect the cv::Rect, then you need to iterate over each contour, calculate the boundingRect and check if it intersects the given rect.
for (int i=0; i<contours.size(); i++) {
if ((cv::boundingRect(contours[i]) & boundingRect).area() > 0) {
// Your desired contours found.
}
}
I am working in C++ and opencv
I am detecting the big contour in an image because I have a black area in it.
In this case, the area is only horizontally, but it can be in any place.
Mat resultGray;
cvtColor(result,resultGray, COLOR_BGR2GRAY);
medianBlur(resultGray,resultGray,3);
Mat resultTh;
Mat canny_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
Canny( resultGray, canny_output, 100, 100*2, 3 );
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
Vector<Point> best= contours[0];
int max_area = -1;
for( int i = 0; i < contours.size(); i++ ) {
Scalar color = Scalar( 0, 0, 0 );
if(contourArea(contours[i])> max_area)
{
max_area=contourArea(contours[i]);
best=contours[i];
}
}
Mat approxCurve;
approxPolyDP(Mat(best),approxCurve,0.01*arcLength(Mat(best),true),true);
Wiht this, i have the big contour and it approximation (in approxCurve). Now, I want to obtain the corners of this approximation and get the image inside this contour, but I dont know how can I do it.
I am using this How to remove black part from the image?
But the last part I dont understad very well.
Anyone knows how can I obtain the corners? It is another way more simple that this?
Thanks for your time,
One much simpler way you could do that is to check the image pixels and find the minimum/maximum coordinates of non-black pixels.
Something like this:
int maxx,maxy,minx,miny;
maxx=maxy=-std::numeric_limits<int>::max();
minx=miny=std::numeric_limits<int>::min();
for(int y=0; y<img.rows; ++y)
{
for(int x=0; x<img.cols; ++x)
{
const cv::Vec3b &px = img.at<cv::Vec3b>(y,x);
if(px(0)==0 && px(1)==0 && px(2)==0)
continue;
if(x<minx) minx=x;
if(x>maxx) maxx=x;
if(y<miny) miny=y;
if(y>maxy) maxy=y;
}
}
cv::Mat subimg;
img(cv::Rect(cv::Point(minx,miny),cv::Point(maxx,maxy))).copyTo(subimg);
In my opinion, this approach is more reliable since you don't have to detect any contour, which could lead to false detections depending on the input image.
In a very efficient way, you can sample the original image until you find a pixel on, and from there move along a row and along a column to find the first (0,0,0) pixel. It will work, unless in the good part of the image you can have (0,0,0) pixels. If this is the case (e.g.: dead pixel), you can add a double check checking the neighbourhood of this (0,0,0) pixel (it should contain other (0,0,0) pixels.
I am trying to implement Automatic perspective correction in my iOS program and when I use the test image I found on the tutorial everything works as expected. But when I take a picture I get back a weird result.
I am using code found in this tutorial
When I give it an image that looks like this:
I get this as the result:
Here is what dst gives me that might help.
I am using this to call the method which contains the code.
quadSegmentation(Img, bw, dst, quad);
Can anyone tell me when I am getting so many green lines compared to the tutorial? And how I might be able to fix this and properly crop the image to only contain the card?
For perspective transform you need,
source points->Coordinates of quadrangle vertices in the source image.
destination points-> Coordinates of the corresponding quadrangle vertices in the destination image.
Here we will calculate these point by contour process.
Calculate Coordinates of quadrangle vertices in the source image
You will get the your card as contour by just by blurring, thresholding, then find contour, find largest contour etc..
After finding largest contour just calculate approximates a polygonal curve, here you should get 4 Point which represent corners of your card. You can adjust the parameter epsilon to make 4 co-ordinates.
Calculate Coordinates of the corresponding quadrangle vertices in the destination image
This can be easily find out by calculating bounding rectangle for largest contour.
In below image the red rectangle represent source points and green for destination points.
Adjust the co-ordinates order and Apply Perspective transform
Here I manually adjust the co-ordinates order and you can use some sorting algorithm.
Then calculate transformation matrix and apply wrapPrespective
See the final result
Code
Mat src=imread("card.jpg");
Mat thr;
cvtColor(src,thr,CV_BGR2GRAY);
threshold( thr, thr, 70, 255,CV_THRESH_BINARY );
vector< vector <Point> > contours; // Vector for storing contour
vector< Vec4i > hierarchy;
int largest_contour_index=0;
int largest_area=0;
Mat dst(src.rows,src.cols,CV_8UC1,Scalar::all(0)); //create destination image
findContours( thr.clone(), contours, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image
for( int i = 0; i< contours.size(); i++ ){
double a=contourArea( contours[i],false); // Find the area of contour
if(a>largest_area){
largest_area=a;
largest_contour_index=i; //Store the index of largest contour
}
}
drawContours( dst,contours, largest_contour_index, Scalar(255,255,255),CV_FILLED, 8, hierarchy );
vector<vector<Point> > contours_poly(1);
approxPolyDP( Mat(contours[largest_contour_index]), contours_poly[0],5, true );
Rect boundRect=boundingRect(contours[largest_contour_index]);
if(contours_poly[0].size()==4){
std::vector<Point2f> quad_pts;
std::vector<Point2f> squre_pts;
quad_pts.push_back(Point2f(contours_poly[0][0].x,contours_poly[0][0].y));
quad_pts.push_back(Point2f(contours_poly[0][1].x,contours_poly[0][1].y));
quad_pts.push_back(Point2f(contours_poly[0][3].x,contours_poly[0][3].y));
quad_pts.push_back(Point2f(contours_poly[0][2].x,contours_poly[0][2].y));
squre_pts.push_back(Point2f(boundRect.x,boundRect.y));
squre_pts.push_back(Point2f(boundRect.x,boundRect.y+boundRect.height));
squre_pts.push_back(Point2f(boundRect.x+boundRect.width,boundRect.y));
squre_pts.push_back(Point2f(boundRect.x+boundRect.width,boundRect.y+boundRect.height));
Mat transmtx = getPerspectiveTransform(quad_pts,squre_pts);
Mat transformed = Mat::zeros(src.rows, src.cols, CV_8UC3);
warpPerspective(src, transformed, transmtx, src.size());
Point P1=contours_poly[0][0];
Point P2=contours_poly[0][1];
Point P3=contours_poly[0][2];
Point P4=contours_poly[0][3];
line(src,P1,P2, Scalar(0,0,255),1,CV_AA,0);
line(src,P2,P3, Scalar(0,0,255),1,CV_AA,0);
line(src,P3,P4, Scalar(0,0,255),1,CV_AA,0);
line(src,P4,P1, Scalar(0,0,255),1,CV_AA,0);
rectangle(src,boundRect,Scalar(0,255,0),1,8,0);
rectangle(transformed,boundRect,Scalar(0,255,0),1,8,0);
imshow("quadrilateral", transformed);
imshow("thr",thr);
imshow("dst",dst);
imshow("src",src);
imwrite("result1.jpg",dst);
imwrite("result2.jpg",src);
imwrite("result3.jpg",transformed);
waitKey();
}
else
cout<<"Make sure that your are getting 4 corner using approxPolyDP..."<<endl;
teethe This typically happens when you rely on somebody else code to solve your particular problem instead of adopting the code. Look at the processing stages and also the difference between their and your image (it is a good idea by the way to start with their image and make sure the code works):
Get the edge map. - will probably work since your edges are fine
Detect lines with Hough transform. - fail since you have lines not only on the contour but also inside of your card. So expect a lot of false alarm lines
Get the corners by finding intersections between lines. - fail for the above mentioned reason
Check if the approximate polygonal curve has 4 vertices. - fail
Determine top-left, bottom-left, top-right, and bottom-right corner. - fail
Apply the perspective transformation. - fail completely
To fix your problem you have to ensure that only lines on the periphery are extracted. If you always have a dark background you can use this fact to discard the lines with other contrasts/polarities. Alternatively you can extract all the lines and then select the ones that are closest to the image boundary (if your background doesn't have lines).
I'm trying to locate some regions of a frame, the frame is in Ycbcr color space. and I have to select those regions based on their Y values.
so I wrote this code:
Mat frame. ychannel;
VideoCapture cap(1);
int key =0;
int maxV , minV;
Point max, min;
while(key != 27){
cap >> frame;
cvtColor(frame,yframe,CV_RGB_YCrCb); // converting to YCbCr color space
extractChannel(yframe, yframe, 0); // extracting the Y channel
cv::minMaxLoc(yframe,&minV,&maxV,&min,&max);
cv::threshold(outf,outf,(maxV-10),(maxV),CV_THRESH_TOZERO);
/**
Now I want to use :
cv::rectangle()
but I want to draw a rect around any pixel (see the picture bellow)that's higher than (maxV-10)
and that during the streaming
**/
key = waitKey(1);
}
I draw this picture hopping that it helps to understand what I what to do .
thanks for your help.
Once you have applied your threshold you will end up with a binary image containing a number of connected components, if you want to draw a rectangle around each component then you first need to detect those components.
The OpenCV function findContours does just that, pass it your binary image, and it will provide you with a vector of vectors of points which trace the boundary of each component in your image.
cv::Mat binaryImage;
std::vector<std::vector<cv::Point>> contours;
cv::findContours(binaryImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE)
Then all you need to do is find the bounding rectangle of each of those sets of points and draw them to your output image.
for (int i=0; i<contours.size(); ++i)
{
cv::Rect r = cv::boundingRect(contours.at(i));
cv::rectangle(outputImage, r, CV_RGB(255,0,0));
}
You have to find the each of the connected components, and draw their bounding box.
I am developing some image processing tools in iOS. Currently, I have a contour of features computed, which is of type InputArrayOfArrays.
Declared as:
std::vector<std::vector<cv::Point> > contours_final( temp_contours.size() );
Now, I would like to extract areas of the original RGB picture circled by contours and may further store sub-image as cv::Mat format. How can I do that?
Thanks in advance!
I'm guessing what you want to do is just extract the regions in the the detected contours. Here is a possible solution:
using namespace cv;
int main(void)
{
vector<Mat> subregions;
// contours_final is as given above in your code
for (int i = 0; i < contours_final.size(); i++)
{
// Get bounding box for contour
Rect roi = boundingRect(contours_final[i]); // This is a OpenCV function
// Create a mask for each contour to mask out that region from image.
Mat mask = Mat::zeros(image.size(), CV_8UC1);
drawContours(mask, contours_final, i, Scalar(255), CV_FILLED); // This is a OpenCV function
// At this point, mask has value of 255 for pixels within the contour and value of 0 for those not in contour.
// Extract region using mask for region
Mat contourRegion;
Mat imageROI;
image.copyTo(imageROI, mask); // 'image' is the image you used to compute the contours.
contourRegion = imageROI(roi);
// Mat maskROI = mask(roi); // Save this if you want a mask for pixels within the contour in contourRegion.
// Store contourRegion. contourRegion is a rectangular image the size of the bounding rect for the contour
// BUT only pixels within the contour is visible. All other pixels are set to (0,0,0).
subregions.push_back(contourRegion);
}
return 0;
}
You might also want to consider saving the individual masks to optionally use as a alpha channel in case you want to save the subregions in a format that supports transparency (e.g. png).
NOTE: I'm NOT extracting ALL the pixels in the bounding box for each contour, just those within the contour. Pixels that are not within the contour but in the bounding box are set to 0. The reason is that your Mat object is an array and that makes it rectangular.
Lastly, I don't see any reason for you to just save the pixels in the contour in a specially created data structure because you would then need to store the position for each pixel in order to recreate the image. If your concern is saving space, that would not save you much space if at all. Saving the tightest bounding box would suffice. If instead you wish to just analyze the pixels in the contour region, then save a copy of the mask for each contour so that you can use it to check which pixels are within the contour.
You are looking for the cv::approxPolyDP() function to connect the points.
I shared a similar use of the overall procedure in this post. Check the for loop after the findContours() call.
I think what you're looking for is cv::boundingRect().
Something like this:
using namespace cv;
Mat img = ...;
...
vector<Mat> roiVector;
for(vector<vector<Point> >::iterator it=contours.begin(); it<contours.end(); it++) {
if (boundingRect( (*it)).area()>minArea) {
roiVector.push_back(img(boundingRect(*it)));
}
}
cv::boundingRect() takes a vector of Points and returns a cv::Rect. Initializing a Mat myRoi = img(myRect) gives you a pointer to that part of the image (so modifying myRoi will ALSO modify img).
See more here.