I have a picture with a table in it and I need to extract the boxes with the handwriting in it then crop them,because I need to recognise the handwriting in those boxes one by one but the problem is the findSquare function gives me an output of too many squares with the same coordinates(+-5 pixels) beacuse of the several threshold levels.I already reduced the number of squares from 300 to 90 by searching in only one channel.The teacher told me to filter the extra squares by their coordinates,if the difference between the square coordinates is less then 10 pixels it means it's the same box.
The picture with the table is here https://ibb.co/Ms8YP8n
And instead of 20 boxes it crops me around 90 and most of them are the same.
So I was trying to go through the squares vector in a for loop and eliminate the squares that are seen twice.
Here is the code that I was trying with
vector<vector<Point> > same;
same.clear();
same = squares;
int sq = squares.size();
for (int i = 0; i < sq; i++) {
for (int j = 1; j < sq-1; j++) {
if (-5 < (same[i][0].x - same[j][0].x) < 5 && -5 < (same[i][0].y - same[j][0].y) < 5 &&
-5 < (same[i][1].x - same[j][1].x) < 5 && -5 < (same[i][1].y - same[j][1].y) < 5 &&
-5 < (same[i][2].x - same[j][2].x) < 5 && -5 < (same[i][2].y - same[j][2].y) < 5 &&
-5 < (same[i][3].x - same[j][3].x) < 5 && -5 < (same[i][3].y - same[j][3].y) < 5) {
squares.erase(same.begin() + j);
}
}
}
The "same" vector is just a copy of "squares".
The error that gives me is "Vector erase iterator outside range".
You can find this code in the drawSquare function bellow where you can see the full code too.
I need the pictures for future proccessing, I need to recognise the handwritten numbers.
Can anybody please help me?With another method or ideeas or some fixes in this code...
Thank you !
static void findSquares(const Mat& image, vector<vector<Point> >& squares)
{
squares.clear();
Mat pyr, timg, timb, gray0(image.size(), CV_8UC1), gray;
pyrDown(image, pyr, Size(image.cols / 2, image.rows / 2));
pyrUp(pyr, timg, image.size());
vector<vector<Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 1; c++)
{
int ch[] = { c, 0 };
mixChannels(&timg, 1, &gray0, 1, ch, 1);
// try several threshold levels
for (int l = 0; l < N; l++)
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if (l == 0)
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
Canny(gray0, gray, 0, thresh, 5);
// dilate canny output to remove potential
// holes between edge segments
dilate(gray, gray, Mat(), Point(-1, -1));
}
else
{
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l + 1) * 255 / N;
// imshow("graaay0", gray);
}
// find contours and store them all as a list
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
vector<Point> approx;
// test each contour
for (int i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
// square contours should have 4 vertices after approximation
// relatively large area (to filter out noisy contours)
//// and be convex.
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() >= 4 && approx.size() <= 6 &&
fabs(contourArea(Mat(approx))) > 5000 && fabs(contourArea(Mat(approx))) < 8000 &&
isContourConvex(Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
// find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
maxCosine = MAX(maxCosine, cosine);
}
// if cosines of all angles are small
// (all angles are ~90 degree) then write quandrange
// vertices to resultant sequence
if (maxCosine >= 0 && maxCosine < 0.2)
squares.push_back(approx);
}
}
}
}
}
// the function draws all the squares in the image
static void drawSquares(Mat& image, vector<vector<Point> > &squares)
{
vector<vector<Point> > same;
same.clear();
same = squares;
int sq = squares.size();
for (int i = 0; i < sq; i++) {
for (int j = 1; j < sq; j++) {
if (-5 < (same[i][0].x - same[j][0].x) < 5 && -5 < (same[i][0].y - same[j][0].y) < 5 &&
-5 < (same[i][1].x - same[j][1].x) < 5 && -5 < (same[i][1].y - same[j][1].y) < 5 &&
-5 < (same[i][2].x - same[j][2].x) < 5 && -5 < (same[i][2].y - same[j][2].y) < 5 &&
-5 < (same[i][3].x - same[j][3].x) < 5 && -5 < (same[i][3].y - same[j][3].y) < 5) {
squares.erase(same.begin() + j);
}
}
}
for (int i = 0; i < squares.size(); i++)
{
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
polylines(image, &p, &n, 1, true, Scalar(0, 255, 0), 3, LINE_AA);
}
imshow("Plate with green", image);
}
Mat ImageExtract(Mat& image, vector<vector<Point> >& squares)
{
char file_name[100];
int x = squares.size();
printf("squaree %d", x);
Mat roi;
for (int i = 0; i < squares.size(); i++)
{
sprintf(file_name, "cropped%d.jpg", i + 1);
Rect r = boundingRect(squares[i]);
roi = Mat(image, r);
imwrite(file_name, roi);
}
return roi;
}
void Preprocessing() {
char fname[MAX_PATH];
openFileDlg(fname);
Mat img = imread(fname, 1);
std::vector<std::vector<Point> > squares;
findSquares(img, squares);
ImageExtract(img, squares);
drawSquares(img, squares);
waitKey(0);
}
int main() {
Preprocessing();
return 0;
}
[1]: https://i.stack.imgur.com/mwvxX.png
Related
I have removed non maxima regions from the vector "bbox" . But when I plot it shows the regions which contain all i.e unsupressed & supressed.
If I measure the bbox.size() befor & after non max supression it says 4677 & 3582 respectively.
Ptr<MSER> ms = MSER::create();
vector<Rect> bbox,filtered;
vector<vector<Point> > regions;
vector <int> vec;
ms->detectRegions(gray,regions,bbox);
cout<<bbox.size()<<"Befor Delete"<<endl;
for (int i = 0; i<bbox.size(); i++){
for(int j = 0; j<bbox.size(); j++){
if( (bbox[i].x > bbox[j].x) && (bbox[i].y > bbox[j].y) && ((bbox[i].x+bbox[i].width) < (bbox[j].x+bbox[j].width)) && ((bbox[i].y+bbox[i].height) < (bbox[j].y+bbox[j].height)) && (bbox[i].area() < bbox[j].area())){
bbox.erase(bbox.begin() + i );
}
}
}
cout<<bbox.size()<<" After"<<endl;
for (int k = 0; k < bbox.size(); k++)
{
rectangle(src,bbox[k], CV_RGB(0, 255, 0));
//cout<<k<<endl;
}
imshow("mser", src);
imwrite("NoN.jpg",src);
I've been looking everywhere I can for the answer but I can't find one.
I'm making a comic balloon detection program and I need to find an ellipse that have a specific percentage of white inside the contour (percentage is to be decided later), thus why I need to count the white pixels inside the contour and I don't know how.
I have tried countNonZero() but since the parameter of that is an array it doesn't accept my minEllipse[i] or contours[i] that are declared as vector<RotatedRect>.
Below is the code:
// Modified version of thresold_callback function
// from http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/bounding_rotated_ellipses/bounding_rotated_ellipses.html
Mat fittingEllipse(int, void*, Mat inputImage)
{
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
int numberOfCaptions = 0;
// Detect edges using Threshold
threshold(inputImage, threshold_output, 224, 250, THRESH_BINARY);
findContours(inputImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<RotatedRect> minEllipse(contours.size());
Mat drawing = Mat::zeros(inputImage.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
if (contours[i].size() > 5)
minEllipse[i] = fitEllipse(Mat(contours[i]));
}
int totalContourSize = 0, whitepixels, blackpixels;
//Draw ellipse/caption
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(255, 0, 0);
if (minEllipse[i].size.height >= inputImage.rows / 8 && //IJIP-290-libre.pdf
minEllipse[i].size.width >= inputImage.cols / 10 && //IJIP-290-libre.pdf
minEllipse[i].size.height < inputImage.rows / 3 &&
minEllipse[i].size.width < inputImage.cols / 3 &&
(
(minEllipse[i].angle >= 0 && minEllipse[i].angle <= 10) ||
(minEllipse[i].angle >= 80 && minEllipse[i].angle <= 100) ||
(minEllipse[i].angle >= 170 && minEllipse[i].angle <= 190) ||
(minEllipse[i].angle >= 260 && minEllipse[i].angle <= 280) ||
(minEllipse[i].angle >= 350 && minEllipse[i].angle <= 360)
)) {
ellipse(drawing, minEllipse[i], color, -1, 8);
}
}
drawing = binarizeImage(drawing);
return drawing;
} // end of fittingEllipse
Mat CaptionDetection(Mat inputImage){
Mat outputImage, binaryImage, captionDetectImage;
binaryImage = captionDetectImage = binarizeImage(inputImage);
threshold(captionDetectImage, captionDetectImage, 224, 250, 0); //IJIP-290-libre.pdf
GaussianBlur(captionDetectImage, captionDetectImage, Size(9, 9), 0, 0);
captionDetectImage = fittingEllipse(0, 0, captionDetectImage);
//binaryImage = invertImage(binaryImage);
outputImage = inputImage;
for (int i = 0; i < inputImage.rows; i++) {
for (int j = 0; j < inputImage.cols; j++) {
if (captionDetectImage.at<uchar>(i, j) == 0) {
outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[2] = 0;
}
}
}
return outputImage;
} // end of CaptionDetection
The very bulky if statement yields me only 53% accuracy of getting the comic balloon detection (not to mention all the false detections), that's why I need to get the percentage of white pixels in the contours that are found to get a higher percentage.
EDIT:
My desired output would be the whole manga page would be black except the comic balloons and then count the number of white and black pixels there
ONLY on the CaptionDetection function should I count the number of pixels for each captions
FINAL ANSWER
I edited the code that user Kornel gave
Mat fittingEllipse(int, void*, Mat inputImage)
{
Mat outputImage;
vector<Vec4i> hierarchy;
int numberOfCaptions = 0;
// Detect edges using Threshold
threshold(inputImage, inputImage, 224, 250, THRESH_BINARY);
findContours(inputImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<RotatedRect> minEllipse(contours.size());
for (int i = 0; i < contours.size(); i++)
{
if (contours[i].size() > 5)
minEllipse[i] = fitEllipse(Mat(contours[i]));
}
//Draw ellipse/caption
outputImage = Mat::zeros(inputImage.size(), CV_8UC3);
for (int i = 0; i < contours.size(); i++)
{
Scalar color = Scalar(255, 255, 255);
Mat drawing = Mat::zeros(inputImage.size(), CV_8UC3);
ellipse(drawing, minEllipse[i], color, -1, 8);
drawing = binarizeImage(drawing);
int area = countNonZero(drawing);
if ((area >= 10000 && area <= 40000) &&
(
(minEllipse[i].angle >= 0 && minEllipse[i].angle <= 10) ||
(minEllipse[i].angle >= 80 && minEllipse[i].angle <= 100) ||
(minEllipse[i].angle >= 170 && minEllipse[i].angle <= 190) ||
(minEllipse[i].angle >= 260 && minEllipse[i].angle <= 280) ||
(minEllipse[i].angle >= 350 && minEllipse[i].angle <= 360)
)){
ellipse(outputImage, minEllipse[i], color, -1, 8);
captionMask[captionCount] = drawing;
captionCount++;
}
}
imwrite((string)SAVE_FILE_DEST + "out.jpg", outputImage);
return outputImage;
} // end of fittingEllipse
Mat replaceROIWithOrigImage(Mat inputImg, Mat mask, int k){
Mat outputImage = inputImg;
Mat maskImg = mask;
imwrite((string)SAVE_FILE_DEST + "inputbefore[" + to_string(k) + "].jpg", inputImg);
for (int i = 0; i < inputImg.rows; i++) {
for (int j = 0; j < inputImg.cols; j++) {
if (maskImg.at<uchar>(i, j) == 0) {
inputImg.at<Vec3b>(i, j)[0] = inputImg.at<Vec3b>(i, j)[1] = inputImg.at<Vec3b>(i, j)[2] = 0;
}
}
}
imwrite((string)SAVE_FILE_DEST + "maskafter[" + to_string(k) + "].jpg", inputImg);
return inputImg;
}
Mat CaptionDetection(Mat inputImage){
Mat outputImage, binaryImage, captionDetectImage;
binaryImage = captionDetectImage = binarizeImage(inputImage);
threshold(captionDetectImage, captionDetectImage, 224, 250, 0); //IJIP-290-libre.pdf
GaussianBlur(captionDetectImage, captionDetectImage, Size(9, 9), 0, 0);
captionDetectImage = fittingEllipse(0, 0, captionDetectImage);
for (int i = 0; i < captionCount; i++){
Mat replacedImg = replaceROIWithOrigImage(inputImage.clone(), captionMask[i], i);
int area = countNonZero(binarizeImage(replacedImg));
cout << area << endl;
}
return outputImage;
} // end of CaptionDetection
The if condition in fittingEllipse() is to be edited for better accuracy later.
Thank you for your help and time user a-Jays and Kornel!
Say you have a rotated rectangle rRect which defines an ellipse just like minEllipse[i] in your code.
First of all, the area of it can be estimated by the closed formula area = a * b * PI, where a and b are the semi-major and semi-minor axes (1⁄2 of the ellipse's major and minor axes), respectively, so:
cv::RotatedRect rRect(cv::Point2f(100.0f, 100.0f), cv::Size2f(100.0f, 50.0f), 30.0f);
float area = (rRect.size.width / 2.0f) * (rRect.size.height / 2.0f) * M_PI;
Or a bit shorter:
float area = (rRect.size.area() / 4.0f) * M_PI;
Alternatively, you can simply draw it over a mask by cv::ellipse(), i.e.:
cv::Mat mask = cv::Mat::zeros(200, 200, CV_8UC1);
cv::ellipse(mask, rRect, cv::Scalar::all(255), -1);
And you count the non-zero elements as the usual:
int area = cv::countNonZero(mask);
I have one function dealing with image. In that function, i am trying to find several particular ellipses. The code is working if i call it individually in a separate project, but in the whole project, it crashed when it returns.
I used many vectors in the processing to store some information during the process.
The error information:
Windows has triggered a breakpoint in KinectBridgeWithOpenCVBasics-D2D.exe.
This may be due to a corruption of the heap, which indicates a bug in KinectBridgeWithOpenCVBasics-D2D.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while KinectBridgeWithOpenCVBasics-D2D.exe has focus.
The output window may have more diagnostic information.
could any one tell me where is wrong to cause this crash. More weird is it is working in the separate project.
The code is a little long, but it is really noting, just looking for several particular ellipses with some pattern.
Thank you.
int FindNao(Mat* pImg, double* x, double* y)
{
// Fail if pointer is invalid
if (!pImg)
{
return 2;
}
// Fail if Mat contains no data
if (pImg->empty())
{
return 3;
}
//*x = 0; *y = 0;
Mat localMat = *pImg; // save a local copy of the image
cvtColor(~localMat, localMat, CV_BGR2GRAY); // Convert to gray image
threshold(localMat, localMat, 165, 255, THRESH_BINARY); // Convert into black-white image
Mat elementOpen = getStructuringElement(MORPH_ELLIPSE, Size(5,5), Point(-1,-1));
morphologyEx(localMat, localMat, MORPH_OPEN, elementOpen, Point(-1,-1), 1);
// Find all the contours in the blak-white image
vector<vector<Point>> contours;
findContours(localMat.clone(), contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
localMat.release();
// Calculate the area of each contour
vector<double> areas; int num = contours.size();
/* If no contours are found, return S_OK */
if(num < 1)
return 1;
for(int i = 0; i < num; i++)
{
areas.push_back(contourArea(contours[i]));
}
// First round of selection
// The area is small, and they are like a ellipse and around the middle in X direction and at the upper part of the image
vector<RotatedRect> selected_ellipses; // store the fitted ellipse fitted to the potential contour
vector<double> selected_areas; // store the contour area of the potential contour
int imgX = localMat.cols; int imgY = localMat.rows; // get the demension of the image
for(int i=0; i < num - 1; i++)
{
if(areas[i] < 350 && areas[i] > 10)
{
// fit an ellipse
RotatedRect ellipse1 = fitEllipse(Mat(contours[i]));
// it is a reasonable ellipse, and the area should be close to the
double length1 = ellipse1.size.height;
double length2 = ellipse1.size.width;
if( abs(1 - length1/length2) <= 0.8 &&
abs(1 - areas[i] / (CV_PI * length1 * length2 / 4) ) <= 0.2 )
{
selected_ellipses.push_back(ellipse1);
selected_areas.push_back(areas[i]);
}
}
}
/************ Second round of selection **************/
// Calculate each ellipse's dimension
vector<double> diff_dimension;
vector<double> ave_dimention;
/* If no contours are found, return S_OK */
if(selected_ellipses.size() < 1)
return 1;
for(int i = 0; i < selected_ellipses.size(); i++)
{
double difference = abs(1 - selected_ellipses[i].size.height / selected_ellipses[i].size.width);
diff_dimension.push_back(difference);
double average = (selected_ellipses[i].size.height + selected_ellipses[i].size.width) / 2;
ave_dimention.push_back(average);
}
vector<vector<int>> eyematches;
vector<vector<int>> cammatches;
// go over all the ellipses to find the matches with close area and dimension.
for(int i = 0; i < selected_ellipses.size() - 1; i++)
{
for(int j = i+1; j < selected_ellipses.size(); j++)
{
// looking for the eyes
if(diff_dimension[i] < 0.05 && diff_dimension[j] < 0.05)
{
double diff_area = abs( 1 - selected_areas[i] / selected_areas[j] );
if (diff_area < 0.05)
{
double diff_y = abs(selected_ellipses[i].center.y - selected_ellipses[j].center.y);
if(diff_y < 10)
{
vector<int> match1;
match1.push_back(i); match1.push_back(j);
eyematches.push_back(match1);
}
}
}
// looking for the cameras
double diff_x = abs(selected_ellipses[i].center.x - selected_ellipses[j].center.x);
if (diff_x < 10)
{
vector<int> match2;
match2.push_back(i); match2.push_back(j);
cammatches.push_back(match2);
}
}
}
/* Last check */
int num_eyes = eyematches.size();
int num_cams = cammatches.size();
if(num_eyes == 0 || num_cams == 0)
return 1;
// Calculate the vector between two eyes and the center
vector<Point> vector_eyes; vector<Point> center_eyes;
vector<vector<int>>::iterator ite = eyematches.begin();
while(ite < eyematches.end())
{
Point point;
point.x = selected_ellipses[(*ite)[0]].center.x - selected_ellipses[(*ite)[1]].center.x;
point.y = selected_ellipses[(*ite)[0]].center.y - selected_ellipses[(*ite)[1]].center.y;
vector_eyes.push_back(point);
point.x = (selected_ellipses[(*ite)[0]].center.x + selected_ellipses[(*ite)[1]].center.x)/2;
point.y = (selected_ellipses[(*ite)[0]].center.y + selected_ellipses[(*ite)[1]].center.y)/2;
center_eyes.push_back(point);
ite++;
}
// Calculate the vector between two cameras and the center
vector<Point> vector_cams; vector<Point> center_cams;
ite = cammatches.begin();
while(ite < cammatches.end())
{
Point point;
point.x = selected_ellipses[(*ite)[0]].center.x - selected_ellipses[(*ite)[1]].center.x;
point.y = selected_ellipses[(*ite)[0]].center.y - selected_ellipses[(*ite)[1]].center.y;
vector_cams.push_back(point);
point.x = (selected_ellipses[(*ite)[0]].center.x + selected_ellipses[(*ite)[1]].center.x)/2;
point.y = (selected_ellipses[(*ite)[0]].center.y + selected_ellipses[(*ite)[1]].center.y)/2;
center_cams.push_back(point);
ite++;
}
// Match the eyes and cameras, by calculating the center distances and intersection angle
vector<vector<int>> matches_eye_cam;
vector<vector<double>> matches_parameters;
for(int i = 0; i < num_eyes; i++)
{
for(int j = 0; j < num_cams; j++)
{
vector<int> temp1;
vector<double> temp2;
// calculate the distances
double distance = sqrt( double( (center_eyes[i].x - center_cams[j].x)^2 + (center_eyes[i].y - center_cams[j].y)^2 ) );
// calculate the cosine intersection angle
double cosAngle = vector_eyes[i].x * vector_cams[j].x + vector_eyes[i].y * vector_cams[j].y;
// store everything
temp1.push_back(i); temp1.push_back(j);
temp2.push_back(distance); temp2.push_back(cosAngle);
matches_eye_cam.push_back(temp1);
matches_parameters.push_back(temp2);
}
}
// go over to find the minimum
int min_dis = 0; int min_angle = 0;
vector<vector<double>>::iterator ite_para = matches_parameters.begin();
/* If no contours are found, return S_OK */
if(matches_parameters.size() < 1)
return 1;
for(int i = 1; i < matches_parameters.size(); i++)
{
if( (*(ite_para+min_dis))[0] > (*(ite_para+i))[0] )
min_dis = i;
if( (*(ite_para+min_angle))[1] > (*(ite_para+i))[1] )
min_angle = i;
}
// get the best match of eyes and cameras 's index
int eyes_index, cams_index;
vector<vector<int>>::iterator ite_match_eye_cam = matches_eye_cam.begin();
if(min_dis == min_angle)
{
// perfect match
eyes_index = (*(ite_match_eye_cam + min_dis))[0];
cams_index = (*(ite_match_eye_cam + min_dis))[1];
}
else
{
// tried to fuse them and find a better sulotion, but didnot work out, so
// go with the min_dis
eyes_index = (*(ite_match_eye_cam + min_dis))[0];
cams_index = (*(ite_match_eye_cam + min_dis))[1];
}
vector<vector<int>>::iterator ite_eyes = eyematches.begin();
vector<vector<int>>::iterator ite_cams = cammatches.begin();
// draw the eyes
ellipse(*pImg, selected_ellipses[(*(ite_eyes+eyes_index))[0]], Scalar(0, 255, 255), 2, 8);
ellipse(*pImg, selected_ellipses[(*(ite_eyes+eyes_index))[1]], Scalar(0, 255, 255), 2, 8);
// draw the camera
ellipse(*pImg, selected_ellipses[(*(ite_cams+cams_index))[0]], Scalar(0, 255, 0), 2, 8);
ellipse(*pImg, selected_ellipses[(*(ite_cams+cams_index))[1]], Scalar(0, 255, 0), 2, 8);
imshow("show", *pImg);
// find the upper camera
int m1 = (*(ite_cams+cams_index))[0];
int m2 = (*(ite_cams+cams_index))[1];
int upper;
if(selected_ellipses[m1].center.y < selected_ellipses[m2].center.y)
upper = m1;
else
upper = m2;
*x = selected_ellipses[upper].center.x;
*y = selected_ellipses[upper].center.y;
return 1;
}
int main()
{
Mat imO = imread("Capture.PNG");
double x, y;
FindNao(&imO, &x, &y);
cout<<x<<" "<<y<<endl;
cvWaitKey(0);
}
How can I found the circles on image using the method minEnclosingCircle but avoid the internal circles?
I'm using the opencv with c++ to detect circles on a image. I was using the method HoughCircles but sometimes it lost some circles or it detected false circles.
Therefore I'm replacing it for the minEnclosingCircle. Now the algorithm is finding all circles, but in some cases it found circles inside other circles and I want avoid this.
The image 1 is a example of input and the image 2 is it output.
The code used to process those images is this:
Start of code:
vector < Circle > houghCircles(Mat thresholdImage, float minRadius, float maxRadiuss) {
vector < vector < Point > > contours;
vector < Vec4i > hierarchy;
findContours(thresholdImage, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
vector < vector < Point > > contours_poly(contours.size());
vector < Circle > circlesTMP(contours.size());
for (int i = 0; i < contours.size(); i++) {
Point2f detectedCenter;
float detectedRadius;
approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
minEnclosingCircle((Mat) contours_poly[i], detectedCenter, detectedRadius);
if (minRadius != 0 && detectedRadius < minRadius) {
continue;
}
if (maxRadiuss != 0 && detectedRadius > maxRadiuss) {
continue;
}
sf::Circle _circle(detectedCenter, detectedRadius);
circlesTMP.push_back(_circle);
}
vector < Circle > circles;
for (int i = 0; i < circlesTMP.size(); i++) {
sf::Circle _circle = circlesTMP[i];
if (_circle.getRadius() > 0) {
circles.push_back(circlesTMP[i]);
}
}
cout << "Circles found: " << circles.size() << endl;
Mat drawing = Mat::zeros(thresholdImage.size(), CV_8UC3);
for (int i = 0; i < circles.size(); i++) {
sf::Circle _circle = circles[i];
Scalar color = Scalar(200, 187, 255);
circle(drawing, _circle.getCenter(), (int) _circle.getRadius(), color, 2, 8, 0);
}
imshow(drawing, "OpenCVUtil");
waitkey();
return circles;
}
you can check this link out and change your mode parameter and test your code:
http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=findcontours#findcontours
check out the results you obtain for CV_RETR_EXTERNAL instead of CV_RETR_TREE as the mode.
I asked a previous question here and following the advice from the answer I built the below program which I thought would detect large rectangle but it doesn't detect the rectangle at all. It does work on this image though.
Original Image
Desired Image
I want the solution to work on not only this image but different images of this kind. Major part of the code below is from different answers on SO
My full program:
#include <cv.h>
#include <highgui.h>
using namespace cv;
using namespace std;
double angle( Point pt1, Point pt2, Point pt0 ) {
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
}
void find_squares( Mat& image, vector< vector< Point> >& squares)
{
// blur will enhance edge detection
Mat blurred(image);
medianBlur(image, blurred, 9);
Mat gray0(blurred.size(), CV_8U), gray;
vector< vector< Point> > contours;
// find squares in every color plane of the image
for (int c = 0; c < 3; c++)
{
int ch[] = {c, 0};
mixChannels(&blurred, 1, &gray0, 1, ch, 1);
// try several threshold levels
const int threshold_level = 2;
for (int l = 0; l < threshold_level; l++)
{
// Use Canny instead of zero threshold level!
// Canny helps to catch squares with gradient shading
if (l == 0)
{
Canny(gray0, gray, 10, 20, 3); //
// Dilate helps to remove potential holes between edge segments
dilate(gray, gray, Mat(), Point(-1,-1));
}
else
{
gray = gray0 >= (l+1) * 255 / threshold_level;
}
// Find contours and store them in a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
// Test contours
vector< Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional
// to the contour perimeter
approxPolyDP( Mat(contours[i]), approx, arcLength( Mat(contours[i]), true)*0.02, true);
// Note: absolute value of an area is used because
// area may be positive or negative - in accordance with the
// contour orientation
if (approx.size() == 4 &&
fabs(contourArea( Mat(approx))) > 1000 &&
isContourConvex( Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
}
}
void find_largest_square(const vector<vector <Point> >& squares, vector<Point>& biggest_square) {
if (!squares.size()) {
return;
}
int max_width = 0;
int max_height = 0;
int max_square_idx = 0;
const int n_points = 4;
for (size_t i = 0; i < squares.size(); i++) {
Rect rectangle = boundingRect(Mat(squares[i]));
if ((rectangle.width >= max_width) && (rectangle.height >= max_height)) {
max_width = rectangle.width;
max_height = rectangle.height;
max_square_idx = i;
}
}
biggest_square = squares[max_square_idx];
}
int main(int argc, char* argv[])
{
Mat img = imread(argv[1]);
if (img.empty())
{
cout << "!!! imread() failed to open target image" << endl;
return -1;
}
vector< vector< Point> > squares;
find_squares(img, squares);
vector<Point> largest_square;
find_largest_square(squares, largest_square);
for (int i = 0; i < 4; ++i) {
line(img, largest_square[i], largest_square[(i+1)%4], Scalar(0, 255, 0), 1, CV_AA);
}
imwrite("squares.png", img);
imshow("squares", img);
waitKey(0);
return 0;
}
I think you can do it easily using findContours function - http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html The biggest contour (or eventually second biggest) should be contour of black rectangle. Then just find the smallest rectangle which will surround this contour (just find points with the biggest/smallest x/y coordinates).