I am using iPhone5s to do black object tracking: but often meet with
Thread 6:EXC_BAD_ACCESS(code 1, dress=0x8)
and then my App quit suddenly. Could anyone tell me why this happen?
this error happens at :
template<typename _Tp> inline
_Tp Rect_<_Tp>::area() const
{
return width * height; //Thread 6:EXC_BAD_ACCESS(code 1, dress=0x8)
}
//this method is in types.hp in latest opencv framework
my colored object recognition code is as below:
#pragma mark - Protocol CvVideoCameraDelegate
#ifdef __cplusplus
- (void)processImage:(cv::Mat &)image{
Mat imageCopy,imageCopy2;
cvtColor(image, imageCopy, COLOR_BGRA2BGR);
cvtColor(imageCopy, imageCopy2, COLOR_BGR2HSV);
//smooth the image
GaussianBlur(imageCopy2, imageCopy, cv::Size(5,5),0, 0);
cv::inRange(imageCopy, cv::Scalar(0,0,0,0), cv::Scalar(180,255,30,0),
imageCopy2);
/*****************************find the contour of the detected area abd draw it***********************************/
//2-D point to store countour
std::vector< std::vector<cv::Point>> contour1;
//do opening on the binary thresholded image
int erosionSize = 3;
Mat erodeElement =
getStructuringElement(cv::MORPH_ELLIPSE,cv::Size(2*erosionSize+1,2* erosionSize+1), cv::Point(erosionSize,erosionSize));
erode(imageCopy2, imageCopy2, erodeElement);
dilate(imageCopy2, imageCopy2, erodeElement);
//Acual line to find the contour
cv::findContours(imageCopy2, contour1, RETR_EXTERNAL, CHAIN_APPROX_NONE);
//set the color used to draw the conotour
Scalar color1 = Scalar(50,50,50);
//loop the contour to draw the contour
for(int i=0; i< contour1.size(); i++){
drawContours(image, contour1, i, color1);
}
/******END*****/
/****************************find the contour of the detected area abd draw it***********************************/
/****************************Appproximate the contour to polygon && get bounded Rectangle and Circle*************/
std::vector<std::vector<cv::Point>> contours_poly(contour1.size());
std::vector<cv::Rect> boundedRect(contour1.size());
std::vector<cv::Point2f> circleCenter(contour1.size());
std::vector<float> circleRadius(contour1.size());
for (int i=0; i< contour1.size(); i++){
approxPolyDP(Mat(contour1[i]), contours_poly[i], 3, true);
boundedRect[i] = boundingRect(Mat(contours_poly[i]));
minEnclosingCircle((Mat)contours_poly[i], circleCenter[i], circleRadius[i]);
}
/******END*******/
/*****************************draw the rectangle for detected area ***********************************************/
Scalar recColor = Scalar(121,200,60);
Scalar fontColor = Scalar(0,0,225);
//find the largest contour
int largestContourIndex=0;
for (int i=0; i<contour1.size(); i++){
if(boundedRect[i].area()> boundedRect[largestContourIndex].area())
largestContourIndex=i;
}
int j=largestContourIndex;
if(boundedRect[j].area()>40){
rectangle(image, boundedRect[j].tl(), boundedRect[j].br(), recColor);
//show text at tl corner
cv::Point fontPoint = boundedRect[j].tl();
putText(image, "Black", fontPoint, FONT_HERSHEY_COMPLEX, 3, fontColor);
}
// cvtColor(imageCopy, image, COLOR_HLS2BGR);
}
#endif
Finally figure out why :
Just as #SolaWing said, there exist some null pointer. For future Viewers, just want to make it more clear:
Problem is at the following code:
if(boundedRect[j].area()>40){
rectangle(image, boundedRect[j].tl(), boundedRect[j].br(), recColor);
//show text at tl corner
cv::Point fontPoint = boundedRect[j].tl();
putText(image, "Black", fontPoint, FONT_HERSHEY_COMPLEX, 3, fontColor);
}
for this block of code, it already assume that there always exist detected areas, But Actually, when there is no target area in front of Phone Camera, the contour.size() is zero, that being said, for std::vector<cv::Rect> boundedRect(contour1.size()); boundedRect is null pointer, then there will be a problem when I use if(boundedRect[j].area()>40){}, which is using first pointer of the null pointer.
Related
I have an image and want to detect various objects at a time using opencv methods.
I have tried detecting one object using contouring and using the area to filter other counters. But I need to detect other objects too but they vary in area and length.
Can anyone help me to use any methods for detecting it.
This is the original image:
this is the code that I have tried for detection:
int main()
{
Mat msrc = imread("Task6_Resources/Scratch.jpg", 1);
Mat src = imread("Task6_Resources/Scratch.jpg", 0);
Mat imgblur;
GaussianBlur(src, imgblur, Size(13,13), 0);
cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE();
clahe->setClipLimit(8);
cv::Mat gcimg;
clahe->apply(imgblur, gcimg);
Mat thresh;
threshold(gcimg, thresh, 55, 255, THRESH_BINARY_INV);
Mat th_mina = minareafilter(thresh, 195); //function used to filter small and large blobs
Mat th_maxa = maxareafilter(th_mina, 393);
Mat imdilate;
dilate(th_maxa, imdilate, getStructuringElement(MORPH_RECT, Size(3, 1)), Point(-1, -1), 7);
int largest_area = 0;
int largest_contour_index = 0;
Rect bounding_rect;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(imdilate, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
cout << "Number of contours" << contours.size() << endl;
//Largest contour according to area
for (int i = 0; i < contours.size(); i++){
double a = contourArea(contours[i], false);
if (a > largest_area) {
largest_area = a;
largest_contour_index = i;
bounding_rect = boundingRect(contours[i]);
}
}
for (int c = 0; c < contours.size(); c++){
printf(" * Contour[%d] Area OpenCV: %.2f - Length: %.2f \n",
c,contourArea(contours[c]), arcLength(contours[c], true));
}
rectangle(msrc, bounding_rect, Scalar(0, 255, 0), 2, 8, 0);
imshow("largest contour", msrc);
waitKey(0);
destroyAllWindows();
return 0;
}
This is the image on which I am applying contouring
After the code I am able to detect the green box using largest area in contouring, but I need to detect those red boxes too. (only the region of red boxes)
The problem is here I cannot apply again area parameter to filter the contours as some other contours have same area as the resultant contour.
The image result required:
Bacisally the task is:
Implement automatic landing/takeoff of a quadcopter, which can carry one FlyCam/GoPro camera. Orientation should happen relative to altitude and position of the landing platform on 2D plane including rotation. That means the drone has a "head" and "tail" and should land in a specific position.
The landing platform looks like this
The corner shapes are for orientation on big distances, and small repetitive shapes in the center circle are for exact landing.
What approach would you take to solve this task?
Here is a pseudo code, assuming you already have full access to the motor control API; i.e. you have successfully defined what is needed for changing altitude, rotating left etc.
loop
{
if(landing board detected)
{
if(circle including the center point detected)
{
find orientation from corner circles' center
change device's orientation accordingly
}
else
{
lose altitude & move towards the center point
}
}
else
{
move around
}
}
Landing board & its center:
Assumption: It is the biggest & nearly perfect square.
1- Threshold
2- Extract contours
3- Apply shape (square) filter to contours
4- Find the biggest contour
5- Find its center
6- Crop the image with the bounding rect of this contour
Mat image = imread("~\\image.jpg");
// scale down for faster processing
pyrDown(image, image);
pyrDown(image, image);
// safe copy
Mat temp = image.clone();
// noise reduction & thresholding
GaussianBlur(image, image, Size(5,5), 3);
cvtColor(image, image, CV_BGR2GRAY);
threshold(image, image, 127, 255, CV_THRESH_OTSU);
// extract all contours
vector<vector<Point> > contours;
findContours(image, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
// define a perfect square
vector<Point> square;
square.push_back(Point(0,0));
square.push_back(Point(0,10));
square.push_back(Point(10,10));
square.push_back(Point(10,0));
// filter out contours that are not square
bool erased;
for(unsigned int i = 0; i<contours.size(); i++)
{
erased = false;
double x = matchShapes(contours[i], square, CV_CONTOURS_MATCH_I2, 0);
if(x > 0.005)
{
contours.erase(contours.begin() + i);
erased = true;
}
if(erased) i--;
}
// area filtering to find the biggest square contour
vector<double> contourAreas(contours.size());
for(unsigned int i = 0; i<contours.size(); i++)
{
contourAreas[i] = contourArea(contours[i]);
}
int ID = max_element(contourAreas.begin(), contourAreas.end()) - contourAreas.begin();
for(unsigned int i = 0; i<contours.size(); i++)
{
erased = false;
if(i != ID)
{
contours.erase(contours.begin() + i);
erased = true;
ID--;
}
if(erased) i--;
}
// find the bounding rect of this contour and crop the image within that rect
vector<Point> total;
for(unsigned int j = 0; j<contours[0].size(); j++)
{
total.push_back(contours[0][j]);
}
Rect rect = boundingRect(total);
Mat t = Mat(temp, rect);
// find the center of the landing board - to move towards it when necessary
Moments m = moments(contours[0], false);
Point center = Point(cvRound(m.m10/m.m00), cvRound(m.m01/m.m00));
Now that we have detected the board, we need to detect the corner circles for orientation.
1- Threshold
2- Extract contours
3- Apply shape (circular) filter to contours
4- Filter out circles close to the center of the board
5- Resultant circles are the corner circles, find the center of their biggest
// threshold
Mat gray;
cvtColor(t, gray, CV_BGR2GRAY);
threshold(gray, gray, 2187451321, 12186471, CV_THRESH_OTSU);
// extract contours
vector<vector<Point> > conts;
findContours(gray, conts, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
// circularity check
for(unsigned int i = 0; i<conts.size(); i++)
{
erased = false;
if(4*3.14*contourArea(conts[i]) / ((arcLength(conts[i],true) * arcLength(conts[i],true))) < 0.85)
{
conts.erase(conts.begin() + i);
erased = true;
}
if(erased) i--;
}
// position check - filtering out center circle
vector<Moments> mu(conts.size());
vector<Point2f> mc(conts.size());
for(unsigned int i = 0; i<conts.size(); i++ )
{
mu[i] = moments(conts[i], false);
}
for(unsigned int i = 0; i <conts.size(); i++ )
{
mc[i] = Point2f(mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00);
}
for(unsigned int i=0; i<conts.size(); i++)
{
erased = false;
if((((int)mc[i].x > t.cols/3) && ((int)mc[i].x < 2*t.cols/3) && ((int)mc[i].y < 2*t.rows/3) && ((int)mc[i].y > t.rows/3)))
{
mc.erase(mc.begin() + i);
conts.erase(conts.begin() + i);
erased = true;
}
if(erased) i--;
}
// selecting the biggest circle
vector<double> contAreas(conts.size());
for(unsigned int i = 0; i<conts.size(); i++)
{
contAreas[i] = contourArea(conts[i]);
}
ID = max_element(contAreas.begin(), contAreas.end()) - contAreas.begin();
for(unsigned int i = 0; i<conts.size(); i++)
{
erased = false;
if(i != ID)
{
conts.erase(conts.begin() + i);
erased = true;
ID--;
}
if(erased) i--;
}
drawContours(t, conts, -1, Scalar(0,255,255));
// finding its center - this is nothing but current orientation
Moments m2 = moments(conts[0], false);
Point c = Point(cvRound(m2.m10/m2.m00), cvRound(m2.m01/m2.m00));
input image
detected biggest-square (Mat t)
detected biggest-not close to center-circle-inside that biggest square (conts[0])
circle center and board center respectively, for orientation purposes
EDIT: Board center (center) is the position according to the image whereas circle center (c) is the position according to the board (t). Only thing left is to find the slope of line that passes through the board center and the circle center.
I need to count the number of metal balls inside a small metal cup.
I tried template matching but it showed only one result having most probability.
But i need the count of total metal balls visible.
Since background too is metallic i was unable to do color thresholding.
I tried a method of finding the first occurrence using template matching and then fill that area with RGB(0,0,0) and again did the template matching on that image, but several false detections are occurring.
My primary requirement is to find the images that have three balls filled inside the cup and any other quantities other than three should not be detected.
Please see the images of different quantities filled inside the cup
Use Hough circles - see the OpenCV documentation for how to do this. Then just count the circles that are with some empirically determined radius range.
Here are some results and code that will enable you to do what you want:
#include <iostream> // std::cout
#include <algorithm> // std::sort
#include <vector> // std::vector
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
using namespace std;
using namespace cv;
bool circle_compare (Vec3f i,Vec3f j) { return (i[2]>j[2]); }
int main(int argc, char** argv)
{
/// Read the image
Mat one = imread("one.jpg", 1 );
Mat two = imread("two.jpg", 1 );
Mat three = imread("three.jpg", 1 );
Mat four = imread("four.jpg", 1 );
if(!one.data || !two.data || !three.data || !four.data)
{
return -1;
}
// put all the images into one
Mat src(one.rows * 2, one.cols * 2, one.type());
Rect roi1(0, 0, one.cols, one.rows);
one.copyTo(src(roi1));
Rect roi2(one.cols, 0, one.cols, one.rows);
two.copyTo(src(roi2));
Rect roi3(0, one.rows, one.cols, one.rows);
three.copyTo(src(roi3));
Rect roi4(one.cols, one.rows, one.cols, one.rows);
four.copyTo(src(roi4));
// extract the blue channel because the circles show up better there
vector<cv::Mat> channels;
cv::split(src, channels);
cv::Mat blue;
GaussianBlur( channels[0], blue, Size(7, 7), 4, 4 );
vector<Vec3f> circles;
vector<Vec3f> candidate_circles;
/// Find the circles
HoughCircles( blue, candidate_circles, CV_HOUGH_GRADIENT, 1, 1, 30, 55);//, 0, 200 );
// sort candidate cirles by size, largest first
// so the accepted circles are the largest that meet other criteria
std::sort (candidate_circles.begin(), candidate_circles.end(), circle_compare);
/// Draw the circles detected
for( size_t i = 0; i < candidate_circles.size(); ++i )
{
Point center(cvRound(candidate_circles[i][0]), cvRound(candidate_circles[i][4]));
int radius = cvRound(candidate_circles[i][5]);
// skip over big circles
if(radius > 35)
continue;
// test whether centre of candidate_circle is inside of accepted circle
bool inside = false;
for( size_t j = 0; j < circles.size(); ++j )
{
Point c(cvRound(circles[j][0]), cvRound(circles[j][6]));
int r = cvRound(circles[j][7]);
int d = sqrt((center.x - c.x) * (center.x - c.x) + (center.y - c.y) * (center.y - c.y));
if(d <= r)
{
inside = true; // candidate is inside an existing circle
}
}
if(inside)
continue;
// accept the current candidate circle then draw it
circles.push_back(candidate_circles[i]);
circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );
circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );
}
// now fill the circles in the quadrant that has three balls
vector<Vec3f> tl, tr, bl, br;
for( size_t i = 0; i < circles.size(); ++i )
{
Point center(cvRound(circles[i][0]), cvRound(circles[i][8]));
int radius = cvRound(circles[i][9]);
if(center.x < one.cols)
{
if(center.y < one.rows)
{
tl.push_back(circles[i]);
}
else
{
bl.push_back(circles[i]);
}
}
else
{
if(center.y < one.rows)
{
tr.push_back(circles[i]);
}
else
{
br.push_back(circles[i]);
}
}
vector<vector<Vec3f>> all;
all.push_back(tl);
all.push_back(tr);
all.push_back(bl);
all.push_back(bl);
for( size_t k = 0; k < all.size(); ++k )
{
if(all[k].size() == 3)
{
for( size_t i = 0; i < all[k].size(); ++i )
{
Point center(cvRound(all[k][i][0]), cvRound(all[k][i][10]));
int radius = cvRound(all[k][i][11]);
circle( src, center, radius, Scalar(0,255, 255), -1, 4, 0 );
}
}
}
}
// resize for easier display
resize(src, src, one.size());
/// Save results and display them
imwrite("balls.png", src);
//namedWindow( "Balls", CV_WINDOW_AUTOSIZE );
imshow( "Balls", src );
waitKey(0);
return 0;
}
Maybe you can try the template matching algorithm, but with a twist. Don't look for circles (balls). But look for the small triangle in center of the 3 balls.
You have to take into account the rotation of the triangle, but simple contour processing should do the job.
define ROI in center of the image (center of cup)
run some edge detector and contour detection
simplify every suitable contour found
check if found contour has 3 corners with angle sharp enough to form an triangle
To distinguish case with more than 3 balls check also overall intensity of the image. Photo of 3 balls only should have quite low intensity compared to one with more balls.
EDIT:
2013-11-08 6.15PM GMT
In this case of image, might be actually helpfull to use watershed segmentation algorithm.
This algorithm is part of OpenCV, I don't now which version is the first one, but it seems it's in OCV 3.0.0: http://docs.opencv.org/trunk/modules/imgproc/doc/miscellaneous_transformations.html?highlight=watershed#cv2.watershed
Some basic for watershed on wiki: http://en.wikipedia.org/wiki/Watershed_%28image_processing%29
Hi currently i am working on an OCR reading app where i have successfully able to capture the card image by using AVFoundation framework.
For next step, i need to find out edges of the card , so that i can crop the card image from main captured image & later i can sent it to OCR engine for processing.
The main problem is now to find the edges of the card & i am using below code(taken from another open source project) which uses OpenCV for this purpose.It is working fine if the card is pure rectangular Card or Paper. But when i use a card with rounded corner (e.g Driving License), it is failed to detect . Also i dont have much expertise in OpenCV , Can any one help me in solving this issue?
- (void)detectEdges
{
cv::Mat original = [MAOpenCV cvMatFromUIImage:_adjustedImage];
CGSize targetSize = _sourceImageView.contentSize;
cv::resize(original, original, cvSize(targetSize.width, targetSize.height));
cv::vector<cv::vector<cv::Point>>squares;
cv::vector<cv::Point> largest_square;
find_squares(original, squares);
find_largest_square(squares, largest_square);
if (largest_square.size() == 4)
{
// Manually sorting points, needs major improvement. Sorry.
NSMutableArray *points = [NSMutableArray array];
NSMutableDictionary *sortedPoints = [NSMutableDictionary dictionary];
for (int i = 0; i < 4; i++)
{
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSValue valueWithCGPoint:CGPointMake(largest_square[i].x, largest_square[i].y)], #"point" , [NSNumber numberWithInt:(largest_square[i].x + largest_square[i].y)], #"value", nil];
[points addObject:dict];
}
int min = [[points valueForKeyPath:#"#min.value"] intValue];
int max = [[points valueForKeyPath:#"#max.value"] intValue];
int minIndex;
int maxIndex;
int missingIndexOne;
int missingIndexTwo;
for (int i = 0; i < 4; i++)
{
NSDictionary *dict = [points objectAtIndex:i];
if ([[dict objectForKey:#"value"] intValue] == min)
{
[sortedPoints setObject:[dict objectForKey:#"point"] forKey:#"0"];
minIndex = i;
continue;
}
if ([[dict objectForKey:#"value"] intValue] == max)
{
[sortedPoints setObject:[dict objectForKey:#"point"] forKey:#"2"];
maxIndex = i;
continue;
}
NSLog(#"MSSSING %i", i);
missingIndexOne = i;
}
for (int i = 0; i < 4; i++)
{
if (missingIndexOne != i && minIndex != i && maxIndex != i)
{
missingIndexTwo = i;
}
}
if (largest_square[missingIndexOne].x < largest_square[missingIndexTwo].x)
{
//2nd Point Found
[sortedPoints setObject:[[points objectAtIndex:missingIndexOne] objectForKey:#"point"] forKey:#"3"];
[sortedPoints setObject:[[points objectAtIndex:missingIndexTwo] objectForKey:#"point"] forKey:#"1"];
}
else
{
//4rd Point Found
[sortedPoints setObject:[[points objectAtIndex:missingIndexOne] objectForKey:#"point"] forKey:#"1"];
[sortedPoints setObject:[[points objectAtIndex:missingIndexTwo] objectForKey:#"point"] forKey:#"3"];
}
[_adjustRect topLeftCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:#"0"] CGPointValue]];
[_adjustRect topRightCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:#"1"] CGPointValue]];
[_adjustRect bottomRightCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:#"2"] CGPointValue]];
[_adjustRect bottomLeftCornerToCGPoint:[(NSValue *)[sortedPoints objectForKey:#"3"] CGPointValue]];
}
original.release();
}
This naive implementation is based on some of the techniques demonstrated in squares.cpp, available in the OpenCV sample directory. The following posts also discuss similar applications:
OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection
Square detection doesn't find squares
Find corner of papers
#John, the code below has been tested with the sample image you provided and another one I created:
The processing pipeline starts with findSquares(), a simplification of the same function implemented by OpenCV's squares.cpp demo. This function converts the input image to grayscale and applies a blur to improve the detection of the edges (Canny):
The edge detection is good, but a morphological operation (dilation) is needed to join nearby lines:
After that we try to find the contours (edges) and assemble squares out of them. If we tried to draw all the detected squares on the input images, this would be the result:
It looks good, but it's not exactly what we are looking for since there are too many detected squares. However, the largest square is actually the card, so from here on it's pretty simple and we just figure out which of the squares is the largest. That's exactly what findLargestSquare() does.
Once we know the largest square, we simply paint red dots at the corners of the square for debugging purposes:
As you can see, the detection is not perfect but it seems good enough for most uses. This is not a robust solution and I only wanted to share one approach to solve the problem. I'm sure that there are other ways to deal with this that might be more interesting to you. Good luck!
#include <iostream>
#include <cmath>
#include <vector>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgproc/imgproc_c.h>
/* angle: finds a cosine of angle between vectors, from pt0->pt1 and from pt0->pt2
*/
double angle(cv::Point pt1, cv::Point pt2, cv::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);
}
/* findSquares: returns sequence of squares detected on the image
*/
void findSquares(const cv::Mat& src, std::vector<std::vector<cv::Point> >& squares)
{
cv::Mat src_gray;
cv::cvtColor(src, src_gray, cv::COLOR_BGR2GRAY);
// Blur helps to decrease the amount of detected edges
cv::Mat filtered;
cv::blur(src_gray, filtered, cv::Size(3, 3));
cv::imwrite("out_blur.jpg", filtered);
// Detect edges
cv::Mat edges;
int thresh = 128;
cv::Canny(filtered, edges, thresh, thresh*2, 3);
cv::imwrite("out_edges.jpg", edges);
// Dilate helps to connect nearby line segments
cv::Mat dilated_edges;
cv::dilate(edges, dilated_edges, cv::Mat(), cv::Point(-1, -1), 2, 1, 1); // default 3x3 kernel
cv::imwrite("out_dilated.jpg", dilated_edges);
// Find contours and store them in a list
std::vector<std::vector<cv::Point> > contours;
cv::findContours(dilated_edges, contours, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
// Test contours and assemble squares out of them
std::vector<cv::Point> approx;
for (size_t i = 0; i < contours.size(); i++)
{
// approximate contour with accuracy proportional to the contour perimeter
cv::approxPolyDP(cv::Mat(contours[i]), approx, cv::arcLength(cv::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 && std::fabs(contourArea(cv::Mat(approx))) > 1000 &&
cv::isContourConvex(cv::Mat(approx)))
{
double maxCosine = 0;
for (int j = 2; j < 5; j++)
{
double cosine = std::fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
if (maxCosine < 0.3)
squares.push_back(approx);
}
}
}
/* findLargestSquare: find the largest square within a set of squares
*/
void findLargestSquare(const std::vector<std::vector<cv::Point> >& squares,
std::vector<cv::Point>& biggest_square)
{
if (!squares.size())
{
std::cout << "findLargestSquare !!! No squares detect, nothing to do." << std::endl;
return;
}
int max_width = 0;
int max_height = 0;
int max_square_idx = 0;
for (size_t i = 0; i < squares.size(); i++)
{
// Convert a set of 4 unordered Points into a meaningful cv::Rect structure.
cv::Rect rectangle = cv::boundingRect(cv::Mat(squares[i]));
//std::cout << "find_largest_square: #" << i << " rectangle x:" << rectangle.x << " y:" << rectangle.y << " " << rectangle.width << "x" << rectangle.height << endl;
// Store the index position of the biggest square found
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()
{
cv::Mat src = cv::imread("cc.png");
if (src.empty())
{
std::cout << "!!! Failed to open image" << std::endl;
return -1;
}
std::vector<std::vector<cv::Point> > squares;
findSquares(src, squares);
// Draw all detected squares
cv::Mat src_squares = src.clone();
for (size_t i = 0; i < squares.size(); i++)
{
const cv::Point* p = &squares[i][0];
int n = (int)squares[i].size();
cv::polylines(src_squares, &p, &n, 1, true, cv::Scalar(0, 255, 0), 2, CV_AA);
}
cv::imwrite("out_squares.jpg", src_squares);
cv::imshow("Squares", src_squares);
std::vector<cv::Point> largest_square;
findLargestSquare(squares, largest_square);
// Draw circles at the corners
for (size_t i = 0; i < largest_square.size(); i++ )
cv::circle(src, largest_square[i], 4, cv::Scalar(0, 0, 255), cv::FILLED);
cv::imwrite("out_corners.jpg", src);
cv::imshow("Corners", src);
cv::waitKey(0);
return 0;
}
instead of "pure" rectangular blobs, try to go for nearly rectangular ones.
1- gaussian blur
2- grayscale and canny edge detection
3- extract all blobs (contours) in your image and filter out small ones. you will use findcontours and contourarea functions for that purpose.
4- using moments, filter out non-rectangular ones. First you need to check out moments of rectangle-like objects. You can do it by yourself or google it. Then list those moments and find similarity between objects, create your filter as such.
Ex: After test, say you found out central moment m30's are similar for rectangle-like objects -> filter out objects having inaccurate m30.
I know maybe it's too late for this post, but I am posting this in case it might help someone else.
The iOS Core Image framework already has a good tool to detect features such as rectangles (since iOS 5), faces, QR codes and even regions containing text in a still image. If you check out the CIDetector class you'll find what you need. I am using it for an OCR app too, it's super easy and very reliable compared to what you can do with OpenCV (I am not good with OpenCV, but the CIDetector gives much better results with 3-5 lines of code).
I don't know if it is an option, but you could have the user define the edges of it rather than trying to do it programatically.
I have a binary image which contain few blobs.
I want to remove blobs which are less than a certain area.
Can any one suggest me a way?
I am using Open-CV. i did dilation and erosion to get those blobs. so i need something different to remove the reaming blobs which are less than a certain area.
You can do something like this:
// your input binary image
// assuming that blob pixels have positive values, zero otherwise
Mat binary_image;
// threashold specifying minimum area of a blob
double threshold = 100;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
vector<int> small_blobs;
double contour_area;
Mat temp_image;
// find all contours in the binary image
binary_image.copyTo(temp_image);
findContours(temp_image, contours, hierarchy, CV_RETR_CCOMP,
CV_CHAIN_APPROX_SIMPLE);
// Find indices of contours whose area is less than `threshold`
if ( !contours_all.empty()) {
for (size_t i=0; i<contours.size(); ++i) {
contour_area = contourArea(contours_all[i]) ;
if ( contour_area < threshold)
small_blobs.push_back(i);
}
}
// fill-in all small contours with zeros
for (size_t i=0; i < small_blobs.size(); ++i) {
drawContours(binary_image, contours, small_blobs[i], cv::Scalar(0),
CV_FILLED, 8);
}
//src_gray is your image that we threshold
threshold(src_gray, threshold_output, NULL, 255, THRESH_OTSU);
/// Find contours
findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
/// Approximate contours
vector<Rect> boundRect(contours.size());
for (unsigned int i = 0; i < contours.size(); i++)
{ //identify bounding box
boundRect[i] = boundingRect(contours[i]);
}
for (unsigned int i = 0; i < contours.size(); i++)
{
if ((boundRect[i].area() < //enter your area here))
{
src_gray(boundRect[i])=//set it to whatever value you want;
}
}
Well give this a try...