find trapezoid's 4 points for wraping (rectangle ) - opencv

I need to find trapezoid's 4 points. I tried to use "ret" but only 2 points, seems just 1point is good.
find biggest rectangle result
int main(int argc, char** argv)
{
Mat src = imread("IMG_20160708_1338252.jpg");
imshow("source", src);
int largest_area = 0;
int largest_contour_index = 0;
Rect bounding_rect;
Mat thr;
cvtColor(src, thr, COLOR_BGR2GRAY); //Convert to gray
threshold(thr, thr, 125, 255, THRESH_BINARY); //Threshold the gray
vector<vector<Point> > contours; // Vector for storing contours
findContours(thr, contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE); // Find the contours in the image
for (size_t i = 0; i < contours.size(); i++) // iterate through each contour.
{
double area = contourArea(contours[i]); // Find the area of contour
//src면적이랑 area가 같으면 제외
//if (src.size().area != area) {
if (area > largest_area)
{
largest_area = area;
largest_contour_index = i; //Store the index of largest contour
bounding_rect = boundingRect(contours[i]); // Find the bounding rectangle for biggest contour
}
//}
}
printf("top%d,%d\n", bounding_rect.tl().x, bounding_rect.tl().y);
printf("bottom%d,%d\n\n", bounding_rect.br().x, bounding_rect.br().y);
printf("top%d,%d\n", bounding_rect.x, bounding_rect.y);
printf("%d,%d\n", bounding_rect.x, bounding_rect.y+ bounding_rect.height);
printf("bottom%d,%d\n", bounding_rect.x + bounding_rect.width, bounding_rect.y);
printf("%d,%d\n\n", bounding_rect.x+ bounding_rect.width, bounding_rect.y+ bounding_rect.height);
//printf("bottom%d,%d\n", bounding_rect.br().x, bounding_rect.br().y);
//contours[largest_contour_index];//가장큰 사각형
//contours[largest_contour_index][0].x; contours[largest_contour_index][0].y;
printf("1-%d,", contours[largest_contour_index]);
printf("1-%d\n", contours[largest_contour_index][0].y);
printf("2-%d,", contours[largest_contour_index][1].x);
printf("2-%d\n", contours[largest_contour_index][1].y);
printf("3-%d,", contours[largest_contour_index][2].x);
printf("3-%d\n", contours[largest_contour_index][2].y);
printf("4-%d,", contours[largest_contour_index][3].x);
printf("4-%d\n", contours[largest_contour_index][3].y);
//printf("1%d,", contours[0][largest_contour_index].x);
drawContours(src, contours, largest_contour_index, Scalar(0, 255, 0), 2); // Draw the largest contour using previously stored index.
imshow("result", src);
waitKey();
return 0;
}
How can I find biggest rectangle?

Related

object detection of various shapes in opencv

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:

OpenCV document detection FIX

I need something like here OpenCV C++/Obj-C: Detecting a sheet of paper / Square Detection
My code is working like a charm when my background and foreground is not the same, but if my background is almost the same color as the document it can't work anymore.
Here is the picture with a beige bg + almost beige document what is not working.. Can somebody help in this how can I fix this code?
https://i.imgur.com/81DrIIK.jpg
and the code is here:
vector<Point> getPoints(Mat image)
{
int width = image.size().width;
int height = image.size().height;
Mat image_proc = image.clone();
vector<vector<Point> > squares;
// blur will enhance edge detection
Mat blurred(image_proc);
medianBlur(image_proc, 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);
}
}
}
double largest_area = -1;
int largest_contour_index = 0;
for(int i=0;i<squares.size();i++)
{
double a =contourArea(squares[i],false);
if(a>largest_area)
{
largest_area = a;
largest_contour_index = i;
}
}
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "Scaning size() %d",squares.size());
vector<Point> points;
if(squares.size() > 0)
{
points = squares[largest_contour_index];
}
else
{
points.push_back(Point(0, 0));
points.push_back(Point(width, 0));
points.push_back(Point(0, height));
points.push_back(Point(width, height));
}
return points;
}
}
Thanks
You can do threshold operation in S space of HSV-color-space. https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach
I just split the channels of BGR and HSV as follow. More operations are needed.

How to improve the distance to detect objects?

I’m working on a project that should filter the red objects in an image and calculates the distance to this object with two webcams.
To detect the objects i convert the image from BGR to HSV and use the function inRange to threshold them.
Then i use findContours to get the contours in the image, which should be the contours of the red objects.
As last step i use boundingRect to get a Vector of Rect that contains one Rect per detected object.
The two images below shows my problem. The one with the pink rectangle is about 162cm away from the camera and the other about 175cm. If the Object is further then 170cm the object is not recognized, alltough the thresholded image is showing the contours of the object.
>170cm
<170cm
Is there a way to improve the distance in which the object is detected?
main.cpp
ObjectDetection obj;
StereoVision sv;
for (;;) {
cp.read(imgl);
cp2.read(imgr);
sv.calculateDisparity(imgl, imgr, dispfull, disp8, imgToDisplay);
Mat imgrt, imglt;
obj.filterColor(imgl, imgr, imglt, imgrt);
//p1 und p2 sind die gefundenen Konturen eines Bildes
vector<vector<Point> > p1 = obj.getPointOfObject(imglt);
vector<Rect> allRoisOfObjects = obj.getAllRectangles(imgl, p1);
for(int i = 0; i < allRoisOfObjects.size(); i++){
Rect pos = allRoisOfObjects.at(i);
pos.width -= 20;
pos.height -= 20;
pos.x += 10;
pos.y += 10;
disp = dispfull(pos);
float distance = sv.calculateAverageDistance(pos.tl(),pos.br(),dispfull);
stringstream ss;
ss << distance;
rectangle(imgToDisplay, allRoisOfObjects.at(i), color, 2,8, 0);
putText(imgToDisplay, ss.str(), pos.br(), 1, 1, color, 1);
ss.clear();
ss.str("");
newObjects.push_back(pos);
}
}
ObjectDetection.cpp
void ObjectDetection::filterColor(Mat& img1, Mat& img2, Mat& output1,
Mat& output2) {
Mat imgHSV, imgHSV2;
cvtColor(img1, imgHSV, COLOR_BGR2HSV); //Convert the captured frame from BGR to HSV
cvtColor(img2, imgHSV2, COLOR_BGR2HSV);
Mat imgThresholded, imgThresholded2;
inRange(imgHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV),
imgThresholded);
inRange(imgHSV2, Scalar(iLowH, iLowS, iLowV),
Scalar(iHighH, iHighS, iHighV), imgThresholded2);
output1 = imgThresholded;
output2 = imgThresholded2;
}
vector<vector<Point> > ObjectDetection::getPointOfObject(Mat img) {
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(img, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE,
Point(0, 0));
return contours;
}
vector<Rect> ObjectDetection::getAllRectangles(Mat & img, vector<vector<Point> > contours){
vector<vector<Point> > contours_poly(contours.size());
vector<Rect> boundRect(contours.size());
vector<Point2f> center(contours.size());
vector<float> radius(contours.size());
Rect rrect;
rrect.height = -1;
RNG rng(12345);
for (int i = 0; i < contours.size(); i++) {
approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
boundRect[i] = boundingRect(Mat(contours_poly[i]));
}
return boundRect;
}

Bounding a foreground object

I want to bound the foreground object by a rectangle, how shall I go about doing it?
I tried bounding the rectangle by collecting the white pixels , but it bounds the whole screen...
How shall I solve this problem?
//defined later
vector<Point> points;
RNG rng(12345);
int main(int argc, char* argv[])
{
//create GUI windows
namedWindow("Frame");
namedWindow("FG Mask MOG 2");
pMOG2 = createBackgroundSubtractorMOG2();
VideoCapture capture(0);
//update the background model
pMOG2->apply(frame, fgMaskMOG2);
frame_check = fgMaskMOG2.clone();
erode(frame_check, frame_check, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
dilate(frame_check, frame_check, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)));
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// Detect edges using Threshold
//threshold(frame_check, frame_check, 100, 255, THRESH_BINARY);
//find contours
findContours(frame_check, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
//points
for (size_t i = 0; i < contours.size(); i++) {
for (size_t j = 0; j < contours[i].size(); j++) {
if (contours[i].size() > 2000)
{
cv::Point p = contours[i][j];
points.push_back(p);
}
}
}
if (points.size() > 0){
Rect brect = cv::boundingRect(cv::Mat(points).reshape(2));
rectangle(frame, brect.tl(), brect.br(), CV_RGB(0, 255, 0), 2, CV_AA);
cout << points.size() << endl;
}
imshow("Frame", frame);
imshow("FG Mask MOG 2", fgMaskMOG2);
//get the input from the keyboard
keyboard = waitKey(1000);
}
}
Try this, it's from a project I am working on. To isolate objects I use color masks, which produce binary images, but in your case with a proper threshold on the foreground image (after filtering) you can achieve the same result.
// Morphological opening
erode(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
dilate(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
// Morphological closing
dilate(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
erode(binary, binary, getStructuringElement(MORPH_ELLIPSE, filterSize));
// Find contours
vector<vector<Point>> contours;
findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
vector<Rect> rectangles;
for(auto& contour : contours)
{
// Produce a closed polygon with object's contours
vector<Point> polygon;
approxPolyDP(contour, polygon, 3, true);
// Get polygon's bounding rectangles
Rect rect = boundingRect(polygon);
rectangles.push_back(rect);
}
This is a small example on how to draw a bounding box around the foreground objects using the points of the contour. It's with OpenCV 2.9, so you probably need to change the initialization of the BackgroundSubtractorMOG2 if you're using OpenCV 3.0.
#include <opencv2\opencv.hpp>
using namespace cv;
int main(int argc, char *argv[])
{
BackgroundSubtractorMOG2 bg = BackgroundSubtractorMOG2(30, 16.0, false);
VideoCapture cap(0);
Mat3b frame;
Mat1b fmask;
for (;;)
{
cap >> frame;
bg(frame, fmask, -1);
vector<vector<Point>> contours;
findContours(fmask.clone(), contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
for(int i=0; i<contours.size(); ++i)
{
if(contours[i].size() > 200)
{
Rect roi = boundingRect(contours[i]);
drawContours(frame, contours, i, Scalar(0,0,255));
rectangle(frame, roi, Scalar(0,255,0));
}
}
imshow("frame", frame);
imshow("mask", fmask);
if (cv::waitKey(30) >= 0) break;
}
return 0;
}

OpenCV: Retrieving color of the center of a contour

Im trying to detect the colour of a set of shapes in a black image using OpenCV, for which I use Canny detection. However the color output always comes back as black.
std::vector<std::pair<cv::Point, cv::Vec3b> > Asteroids::DetectPoints(const cv::Mat &image)
{
cv::Mat imageGray;
cv::cvtColor( image, imageGray, CV_BGR2GRAY );
cv::threshold(imageGray, imageGray, 1, 255, cv::THRESH_BINARY);
cv::Mat canny_output;
std::vector<std::vector<cv::Point> > contours;
std::vector<cv::Vec4i> hierarchy;
int thresh = 10;
// Detect edges using canny
cv::Canny( imageGray, canny_output, thresh, thresh*2, 3 );
// Find contours
cv::findContours( canny_output, contours, hierarchy, CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cv::Point(0, 0) );
std::vector<std::pair<cv::Point, cv::Vec3b> > points;
for(unsigned int i = 0; i < contours.size(); i++ )
{
cv::Rect rect = cv::boundingRect(contours[i]);
std::pair<cv::Point, cv::Vec3b> posColor;
posColor.first = cv::Point( rect.tl().x + (rect.size().width / 2), rect.tl().y + (rect.size().height / 2));
posColor.second = image.at<cv::Vec3b>( posColor.first.x, posColor.first.y );
//Dont add teh entry to the list if one with the same color and position is already pressent,
//The contour detection sometimes returns duplicates
bool isInList = false;
for(unsigned int j = 0; j < points.size(); j++)
if(points[j].first == posColor.first && points[j].second == posColor.second)
isInList = true;
if(!isInList)
points.push_back( posColor );
}
return points;
}
I know it has to be an issue with the positions or something along those lines, but I cant figure out what
I might be wrong, but off the top of my head :
Shouldn't this read
posColor.second = image.at<cv::Vec3b>(posColor.first.y, posColor.first.x);
and not the other way around like you did it ?
Matrix notation, not cartesian notation ?

Resources