Okay, so I'm trying openCV with c++, and I want to do a simple detection program for objects that are black colored. So I have this simple code:
int main()
{
Mat3b bgr = imread("C:/Users/sesoa/Desktop/photos/shapes.png");
Mat3b hsv;
cvtColor(bgr, hsv, COLOR_BGR2HSV);
Mat1b mask1, mask2;
inRange(hsv, Scalar(0, 0, 0, 0), Scalar(180, 255, 30, 0), mask1);
inRange(hsv, Scalar(0, 0, 0, 0), Scalar(180, 255, 40, 0), mask2);
Mat1b mask = mask1 | mask2;
imshow("Mask", mask);
waitKey();
return 0;
}
shapes.png is this:
all the shapes are rounded with color black. I would like for my program to tell me how many of connected black objects are there. Also the writing under the shapes is also black. So It shows me this as well, that's okay, cause this is a test photo anyway.
How can I modify my program to detect how many connected black objects are in the photo? (In this photo, the output should be "60" as there are 8 objects and 49 letters + 3 letters are 'i' so we have to count the dots).
EDIT:
I want the program to count black objects. I already get all black objects out like this:
If you want to count the number of objects just do the following:
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) ); // canny_output is the binary image
This will give you all the contours in the binary image (contours.size()). If you want only specific contours you can filter with contour area.
Hope it helps!
Related
I've read this post, but even after using cv::threshold to create a really binarry image, I still get ~500 contours. What am I doing wrong?
Shouldn't cv::findContours return only 13 contours since there are clear 13 blobs?
Mat img = imread("img.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat img_thresh;
threshold(img, img_thresh, 0, 255, CV_THRESH_BINARY);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
cv::findContours(img_thresh, contours, hierarchy, RetrievalModes::RETR_TREE, ContourApproximationModes::CHAIN_APPROX_SIMPLE);
RNG rng(12345);
Mat drawing = Mat::zeros(img_thresh.size(), CV_8UC3);
for (int i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());
}
imshow("drawing", drawing);
waitKey();
UPDATE1
Using cv::RETR_EXTERNAL instead of cv::RETR_TREE, but still return much more contours than should be.
If you check your binary image you will see there are a lot of independent contours:
So you first need to clean up them by eroding and dilating as below code:
And you will get this result:
Which is cleaner than the original.
It is all the code:
cv::namedWindow("result", cv::WINDOW_FREERATIO);
cv::Mat img = cv::imread(R"(rUYLL.png)");
// to gray
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::threshold(gray, gray, 0, 255, cv::THRESH_BINARY);
cv::erode(gray, gray, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
cv::dilate(gray, gray, cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)));
std::vector<std::vector<cv::Point> > contours;
cv::findContours(gray, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
cv::drawContours(img, contours, -1, cv::Scalar(0, 255, 0), 2, 8);
cv::imshow("result", img);
cv::waitKey();
And it is the output:
Hope it helps!
And one simplest way which you can also consider if it works for you, just increase the lower threshold from 0 to 80, and DONE
cv::threshold(gray, gray, 80, 255, cv::THRESH_BINARY);
JUST PLAY WITH THRESHOLD and check the result.
The same output just with changing the threshold value:
After reading some stack overflow posts on image processing, I made a program to detect and a paper in an image and remove the background. But currently the program only detects the contours. How can I crop out the biggest contour? Eventually, I will use this region and use tesseract for character recognition. I am stuck at the preprocessing part.
the image I used:
And this is what I got after running my script:
How can I approximate the big blue contour and crop it out for things like character detection?
Here is the code I used:
static void on_canny( int,void* ){
blur( dst, detected_edges, Size(3,3) );
Canny(detected_edges, detected_edges, cannyThreshold, cannyThreshold*r, kernel_size);
findContours(detected_edges, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
Mat drawing = Mat::zeros( detected_edges.size(), CV_8UC3 );
for(int i=0;i<contours.size();i++)
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );
}
imshow("Contour",drawing);
imshow("Canny", detected_edges);
}
I've just started learning OpenCv. i wanted to crop a portion of an image which is a text surrounded by the red circle. can you guys help me to find the solution like what are all the methods i should follow to crop it. I've tried few things and got the red circle cropped and stored it in a mat.
while(1)
{
capture>>img0;
imshow("original", img0);
imwrite("original.jpg", img0);
cv::inRange(img0,cv::Scalar(0,0,100),cv::Scalar(76,85,255),img1);
imshow("threshold.jpg", img1);
imwrite("threshold.jpg", img1);
// find the contours
vector< vector<Point> > contours;
findContours(img1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
Mat mask = Mat::zeros(img1.rows, img1.cols, CV_8UC1);
drawContours(mask, contours, -1, Scalar(255), CV_FILLED);
Mat crop(img0.rows, img0.cols, CV_8UC3);
crop.setTo(Scalar(255,255,255));
img0.copyTo(crop, mask);
normalize(mask.clone(), mask, 0.0, 255.0, CV_MINMAX, CV_8UC3);
imshow("mask", mask);
imshow("cropped", crop);
imwrite("mask.jpg", mask);
imwrite("cropped.jpg", crop);
if(waitKey(30)=='27')
{
break;
}
}
return 0;`[original image[cropped image][1]`
From this image i wanted to crop a text alone. do help me to find the solution by sharing me the methods or steps to follow.
Thanks in advance
If you wish to extract the text alone, you can try this:-
drawContours(mask, contours, -1, Scalar(255), CV_FILLED);
vector<Rect> boundRect( contours.size() );
for(int i=0;i<contours.size();i++)
{
boundRect[i] = boundingRect(contours[i]);//enclose in Rect
Mat ROI,ROI_txt;
if(boundRect[i].width>30 && boundRect[i].height>30)//ignore noise rects
{
ROI=img0(boundRect[i]);//extract Red circle on ROI
inRange(ROI,Scalar(0,0,0),cv::Scalar(50,50,50),ROI_txt);
//black colour threshold to extract black text
}
}
I have a video that I want to remove some specific contours on, I capture the video and then draw all the contours;
//Prepare the image for findContours
cv::cvtColor(frame, frame, CV_BGR2GRAY);
cv::threshold(frame, frame, 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 = frame.clone();
cv::findContours( contourOutput, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE );
//Draw the contours
cv::Mat contourImage(frame.size(), CV_8UC3, cv::Scalar(0,0,0));
cv::Scalar colors[3];
colors[0] = cv::Scalar(255, 0, 0);
colors[1] = cv::Scalar(0, 255, 0);
colors[2] = cv::Scalar(0, 0, 255);
for (size_t idx = 0; idx < contours.size(); idx++) {
cv::drawContours(contourImage, contours, idx, colors[idx % 3]);
}
The result looks like this:
Contours
So what I want to do now is to remove or cover objects like the trees and the tall house, basically contours that have a more complex shape than a road sign etc.
A desired result would look like this:
Removed/covered contours
It doesn't have to be exactly like on the picture as long as I can get something I can play with.
I've this image:
EDIT
Sorry but I had to remove the images!
I need to extract the contour of the non-black picture, so I used findcontour with the CV_RETR_EXTERNAL parameter, but I obtain this:
Here's the code:
static Mat canny_output, grey,draw;
vector<vector<Point>> contours;
cvtColor(final_img, grey, CV_BGR2GRAY);
Canny(grey, canny_output, 100, 200);
findContours(canny_output, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
draw = Mat::zeros(canny_output.size(), CV_8UC3);
for (size_t i = 0; i < contours.size(); i++)
{
drawContours(draw, contours, i, Scalar(255, 0, 0));
}
how can I resolve?
Simply add a binarization with minimal threshold, and remove Canny:
cvtColor(final_img, grey, CV_BGR2GRAY);
//Threshold=1: very low value, anyway the rest of the image is pure black
threshold(grey, binary, 1, 255, CV_THRESH_BINARY);
findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);