Partitions Image into boxes containing object - c++

I have encountered a problem to partition binarized image with boxes/ subimage containing object (Note: the boxes can be irregular while the object is in circle of any other primitive shapes). This could be explained with images as below:
Figure 1: Image with circle as objects of interest
Figure 2: Image with boxes of arbitrary size containing objects of interest
Thus, any opinion that this can be done?

Since you mentioned that:
the boxes can be irregular
you can use the Voronoi diagram (computed by distanceTransform):
Code:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
Mat1f dist;
Mat1i labels;
distanceTransform(img, dist, labels, CV_DIST_L2, 3, DIST_LABEL_CCOMP);
// Show result
Mat1b labels1b;
labels.convertTo(labels1b, CV_8U);
normalize(labels1b, labels1b, 0, 255, NORM_MINMAX);
Mat3b res;
applyColorMap(labels1b, res, COLORMAP_JET);
res.setTo(Scalar(0,0,0), ~img);
imshow("Result", res);
waitKey();
return 0;
}
Update
If you need the boxes to be rectangles, you can look at recursive XY Cut algorithm. Here is a modified version of XY Cut algorithm, that makes the rectangles not touching the foreground objects, so that the sum of all rectangles covers the whole image area. Here I inverted the image since usually black is background, and white is foreground.
Code:
#include <opencv2\opencv.hpp>
#include <vector>
using namespace std;
using namespace cv;
vector<Rect> XYCut_projH(const Mat1b& src, Rect roi)
{
Mat1b projH;
reduce(src(roi), projH, 1, CV_REDUCE_MAX);
vector<Rect> rects;
bool bOut = true;
vector<int> coords;
coords.push_back(0);
for (int i = 0; i < projH.rows; ++i)
{
if (bOut && projH(i) > 0)
{
coords.back() = (coords.back() + i) / 2;
bOut = false;
}
else if (!bOut && projH(i) == 0)
{
coords.push_back(i);
bOut = true;
}
}
coords.front() = 0;
coords.back() = projH.rows;
if (coords.size() <= 1) return rects;
for (int i = 0; i < coords.size() - 1; ++i)
{
Rect r(0, coords[i], src.cols, coords[i + 1] - coords[i]);
r = (r + roi.tl()) & roi;
rects.push_back(r);
}
return rects;
}
vector<Rect> XYCut_projV(const Mat1b& src, Rect roi)
{
Mat1b projV;
reduce(src(roi), projV, 0, CV_REDUCE_MAX);
vector<Rect> rects;
bool bOut = true;
vector<int> coords;
coords.push_back(0);
for (int i = 0; i < projV.cols; ++i)
{
if (bOut && projV(i) > 0)
{
coords.back() = (coords.back() + i) / 2;
bOut = false;
}
else if (!bOut && projV(i) == 0)
{
coords.push_back(i);
bOut = true;
}
}
coords.front() = 0;
coords.back() = projV.cols;
if (coords.size() <= 1) return rects;
for (int i = 0; i < coords.size() - 1; ++i)
{
Rect r(coords[i], 0, coords[i + 1] - coords[i], src.rows);
r = (r + roi.tl()) & roi;
rects.push_back(r);
}
return rects;
}
void XYCut_step(const Mat1b& src, Rect roi, vector<Rect>& rects, bool bAlternate)
{
vector<Rect> step;
if (bAlternate)
{
step = XYCut_projH(src, roi);
if ((step.size() == 1) && (step[0] == roi) && (XYCut_projV(src, roi).size() == 1))
{
rects.push_back(roi);
return;
}
}
else
{
step = XYCut_projV(src, roi);
if ((step.size() == 1) && (step[0] == roi) && (XYCut_projH(src, roi).size() == 1))
{
rects.push_back(roi);
return;
}
}
for (int i = 0; i < step.size(); ++i)
{
XYCut_step(src, step[i], rects, !bAlternate);
}
}
void XYCut(const Mat1b& src, vector<Rect>& rects)
{
bool bAlternate = true;
Rect roi(0, 0, src.cols, src.rows);
XYCut_step(src, roi, rects, bAlternate);
}
int main()
{
Mat1b img = imread("path_to_image", IMREAD_GRAYSCALE);
// invert image, if needed
img = ~img;
// Apply (modified) XY Cut
vector<Rect> rects;
XYCut(img, rects);
// Show results
Mat3b res;
cvtColor(img, res, COLOR_GRAY2BGR);
for (int i = 0; i < rects.size(); ++i)
{
rectangle(res, rects[i], Scalar(0,255,0));
}
imshow("Result", res);
waitKey();
return 0;
}
Note that this algorithm works only if it's possible to make a cut along X or Y dimension, i.e. there is a horizontal or vertical line with all background pixels. This means that this won't work in a very cluttered image.

Related

Wrong pose estimate when using Charuco on image with higher resolution

I'm working on a project where a high quality pose estimate is needed. I am therefore trying to get this pose estimate using OpenCV charuco board. Previously I have been using a Aruco board of size 2x2, but the pose estimate was not sufficient.
I have made the charuco estimate work using a realSense D415 camera with resolution 640x480. However, when I change the resolution to 1280x720 the coordinate system which I draw on the board, starts jumping around completely random.
The code for estimating the charuco board is here:
void ReconstructionSystem::detect_charuco_markers(cv::Mat& image, cv::Matx33f& matrix, cv::Vec<float, 5>& coef, int& centerPix_x, int& centerPix_y, cv::Vec3d& rotation, bool& arucoFound)
{
cv::Ptr<cv::aruco::Dictionary> dictionary = cv::aruco::getPredefinedDictionary(cv::aruco::DICT_4X4_50);
cv::Ptr<cv::aruco::CharucoBoard> board = cv::aruco::CharucoBoard::create(3, 3, 0.04f, 0.02f, dictionary);
cv::Ptr<cv::aruco::DetectorParameters> params = cv::aruco::DetectorParameters::create();
//params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_NONE;
std::vector<int> markerIds;
std::vector<std::vector<cv::Point2f>> markerCorners;
cv::Mat copyImage;
image.copyTo(copyImage);
cv::Mat gray;
cv::cvtColor(copyImage, gray, cv::COLOR_RGB2GRAY);
cv::aruco::detectMarkers(gray, board->getDictionary(), markerCorners, markerIds, params);
// if at least one marker detected
if (markerIds.size() > 3) {
cv::aruco::drawDetectedMarkers(image, markerCorners, markerIds);
std::vector<cv::Point2f> charucoCorners;
std::vector<int> charucoIds;
cv::aruco::interpolateCornersCharuco(markerCorners, markerIds, gray, board, charucoCorners, charucoIds, matrix, coef);
// if at least one charuco corner detected
if (charucoIds.size() > 3) {
cv::Scalar color = cv::Scalar(255, 0, 0);
cv::aruco::drawDetectedCornersCharuco(image, charucoCorners, charucoIds, color);
cv::Vec3d rvec, tvec;
bool valid = cv::aruco::estimatePoseCharucoBoard(charucoCorners, charucoIds, board, matrix, coef, rvec, tvec);
// if charuco pose is valid
if (valid){
cv::drawFrameAxes(image, matrix, coef, rvec, tvec, 0.1f);
arucoFound = true;
}
else
{
arucoFound = false;
}
}
else
{
arucoFound = false;
}
}
else
{
arucoFound = false;
}
board = NULL;
dictionary = NULL;
copyImage.release();
gray.release();
}
The function above is called within this while loop:
//Variables for transformation matrices
int centerPix_x = 0, centerPix_y = 0;
cv::Vec3d rotationVec;
cv::Matx33f rotation;
bool arucoWasFound = false;
std::vector<float> final_x, final_y, final_z;
std::vector<float> rotation_x, rotation_y, rotation_z;
cv::Matx33f matrix = get_cameraMatrix(path);
cv::Vec<float, 5> coef = get_distCoeffs(path);
const auto window_name = "Validation image";
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
// TODO Also add here that if we have iterated through X frames and not found Aruco, exit with failure
while (cv::waitKey(1) < 0 && cv::getWindowProperty(window_name, cv::WND_PROP_AUTOSIZE) >= 0 && counter < 60) {
rs2::frame f = sensorPtr->color_data.wait_for_frame();
// Query frame size (width and height)
const int w = f.as<rs2::video_frame>().get_width();
const int h = f.as<rs2::video_frame>().get_height();
cv::Mat image(cv::Size(w, h), CV_8UC3, (void*)f.get_data(), cv::Mat::AUTO_STEP);
cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
//detect_aruco_markers(image, matrix, coef, centerPix_x, centerPix_y, rotationVec, arucoWasFound);
detect_charuco_markers(image, matrix, coef, centerPix_x, centerPix_y, rotationVec, arucoWasFound);
if (arucoWasFound)
{
rs2::depth_frame depth = sensorPtr->depth_data.wait_for_frame();
rs2_intrinsics intrinsic = rs2::video_stream_profile(depth.get_profile()).get_intrinsics();
float pixel_distance_in_meters = depth.get_distance(centerPix_x, centerPix_y);
float InputPixelAsFloat[2];
InputPixelAsFloat[0] = centerPix_x;
InputPixelAsFloat[1] = centerPix_y;
float finalDepthPoint[3];
rs2_deproject_pixel_to_point(finalDepthPoint, &intrinsic, InputPixelAsFloat, pixel_distance_in_meters);
// Postion //
final_x.push_back(finalDepthPoint[0]);
final_y.push_back(finalDepthPoint[1]);
final_z.push_back(finalDepthPoint[2]);
// Rotation //
rotation_x.push_back(rotationVec[0]);
rotation_y.push_back(rotationVec[1]);
rotation_z.push_back(rotationVec[2]);
counter++;
}
cv::imshow(window_name, image);
}
cv::destroyWindow(window_name);
Furthermore, here is an image of the detection using resolution of 1270x720.
And here is an image of the detection with resolution 640x480.
If anybody knows why this is happening please let me know :D
As pointed out the problem was that the calibration of the cameras had been made with a wrong resolution, in my case 640x480 instead of 1280x720. Below is the code I used to calculate the calibration matrix and coefficients. The two values that were wrong were: cv::Size frameSize(_imageWidth, _imageWidth);
void ReconstructionSystem::camera_calibration()
{
std::string folder_501("\\Users\\Mikke\\Desktop\\Calibration\\501_images\\*.png");
std::string folder_309("\\Users\\Mikke\\Desktop\\Calibration\\309_images\\*.png");
for (int x = 0; x < 2; x++)
{
std::vector<cv::String> filenames;
std::string currentCam;
if (x == 0) currentCam = folder_501;
if (x == 1) currentCam = folder_309;
cv::glob(currentCam, filenames, false);
for each (std::string var in filenames)
{
printf("file: %s\n", var.c_str());
}
cv::Size patterSize(9, 6);
std::vector<std::vector<cv::Point2f>> q(filenames.size());
std::vector<std::vector<cv::Point3f>> Q;
int checkerboard[2] = { 10, 7 }; //size of checkerboard
int square_size = 27; //2.7 cm == 27mm
std::vector<cv::Point3f> objp;
for (int i = 1; i < checkerboard[1]; i++) {
for (int j = 1; j < checkerboard[0]; j++) {
objp.push_back(cv::Point3f(j * square_size, i * square_size, 0));
}
}
std::vector<cv::Point2f> imgPoint;
std::size_t i = 0;
for (auto const& f : filenames) {
std::cout << std::string(f) << std::endl;
cv::Mat img = cv::imread(filenames[i]);
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_RGB2GRAY);
bool patternFound = cv::findChessboardCorners(gray, patterSize, q[i], cv::CALIB_CB_ADAPTIVE_THRESH + cv::CALIB_CB_NORMALIZE_IMAGE + cv::CALIB_CB_FAST_CHECK);
if (patternFound) {
cv::cornerSubPix(gray, q[i], cv::Size(11, 11), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::EPS + cv::TermCriteria::MAX_ITER, 30, 0.001));
Q.push_back(objp);
}
// Display
cv::drawChessboardCorners(img, patterSize, q[i], patternFound);
cv::imshow("chessboard detection", img);
cv::waitKey(0);
i++;
}
cv::Matx33f K(cv::Matx33f::eye());
cv::Vec<float, 5> k(0, 0, 0, 0, 0);
std::vector<cv::Mat> rvecs, tvecs;
std::vector<double> stdIntrinsics, stdExtrinsics, perViewErrors;
int flags = cv::CALIB_FIX_ASPECT_RATIO + cv::CALIB_FIX_K3 + cv::CALIB_ZERO_TANGENT_DIST + cv::CALIB_FIX_PRINCIPAL_POINT;
cv::Size frameSize(_imageWidth, _imageWidth);
std::cout << "calibrating..." << std::endl;
float error = cv::calibrateCamera(Q, q, frameSize, K, k, rvecs, tvecs, flags);
std::cout << "reprojection error = " << error << "\nK = \n" << K << "\nk=\n" << k << std::endl;
if (x == 0) {
std::string path_mat = "\\Users\\Mikke\\Desktop\\Calibration\\104122061501\\calibration_Mat_new.yml";
std::string path_coe = ("\\Users\\Mikke\\Desktop\\Calibration\\104122061501\\calibration_coef_new.yml");
saveData_mat(path_mat, K);
saveData_coef(path_coe, k);
}
if (x == 1) {
std::string path_mat = "\\Users\\Mikke\\Desktop\\Calibration\\102122061309\\calibration_Mat_new.yml";
std::string path_coe = ("\\Users\\Mikke\\Desktop\\Calibration\\102122061309\\calibration_coef_new.yml");
saveData_mat(path_mat, K);
saveData_coef(path_coe, k);
}
}
}

Debug assertion failure, "__acrt_first_block == header" when using openCV for live webcam? [duplicate]

This question already has answers here:
why opencv imshow() create a new window has the same name as namedWindow() does in Debug Mode?
(1 answer)
Debug Assertion Failed! Expression: __acrt_first_block == header
(6 answers)
Closed 4 years ago.
I'm trying to do finger recognition using opencv, which has been working correctly when simply processing one image from the capture, however after adding the while loop to have it go from single image capture to live processing, there seems to be a heap error showing. I'm currently running it on Visual Studio 2017 w/ OpenCV 3.41.
The error from the Microsoft Visual C++ Runtime Library is
Debug Assertion Failed!
File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 996
Expression: __acrt_first_block == header
The code I'm using is:
#include "stdafx.h"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
int myMax(int a, int b, int c);
int myMin(int a, int b, int c);
void mySkinDetect(Mat& src, Mat& dst);
Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
void thresh_callback(int, void*);
int main(){
VideoCapture cap(0);
while (1) {
cap >> src;
Mat frameDest;
frameDest = Mat::zeros(src.rows, src.cols, CV_8UC1);
mySkinDetect(src, frameDest);
int erosion_size = 1;
Mat element = getStructuringElement(MORPH_RECT,
Size(2 * erosion_size + 1, 2 * erosion_size + 1),
Point(erosion_size, erosion_size));
erode(frameDest, frameDest, element);
erode(frameDest, frameDest, element);
namedWindow("Skin", WINDOW_AUTOSIZE);
imshow("Skin", frameDest);
blur(frameDest, src, Size(3, 3));
createTrackbar(" Threshold:", "Source", &thresh, max_thresh, thresh_callback);
thresh_callback(0, 0);
if (waitKey(30) == 27) { break; }
}
return(0);
}
void thresh_callback(int, void*)
{
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
threshold(src, threshold_output, thresh, 255, THRESH_BINARY);
findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<vector<Point>>hull(contours.size());
vector<vector<int> > hullsI(contours.size());
vector<vector<Vec4i>>defects(contours.size());
int index = 0;
int area = 0;
for (int i = 0; i < contours.size(); i++)
{
double a = contourArea(contours[i]);
if (a>area)
{
area = a;
index = i;
}
}
for (int i = 0; i < contours.size(); i++)
{
convexHull(contours[i], hull[i], false);
convexHull(contours[i], hullsI[i], false);
if (hullsI[i].size() > 3)
{
convexityDefects(contours[i], hullsI[i], defects[i]);
}
}
Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);
for (size_t i = 0; i< contours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(drawing, contours, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point());
drawContours(drawing, hull, (int)i, color, 1, 8, vector<Vec4i>(), 0, Point());
}
int fingers = 0;
if (area>50)
{
for (int j = 0; j<defects[index].size(); ++j)
{
const Vec4i& def = defects[index][j];
float depth = def[3] / 256;
if (depth > 5) // filter defects by depth
{
int start = def[0];
Point ptStart(contours[index][start]);
int end = def[1];
Point ptEnd(contours[index][end]);
int min = def[2];
Point ptFar(contours[index][min]);
line(drawing, ptStart, ptEnd, Scalar(0, 255, 0), 1);
line(drawing, ptStart, ptFar, Scalar(0, 255, 0), 1);
line(drawing, ptEnd, ptFar, Scalar(0, 255, 0), 1);
circle(drawing, ptFar, 4, Scalar(0, 255, 0), 2);
fingers += 1;
}
}
}
std::string s = std::to_string(fingers-1);
namedWindow("Hull demo", WINDOW_AUTOSIZE);
putText(drawing, "Number Fingers = "+s, Point(drawing.cols/1.5, drawing.rows / 10), FONT_HERSHEY_PLAIN, 1.2f, Scalar(200, 0, 0), 2);
imshow("Hull demo", drawing);
}
int myMax(int a, int b, int c) {
int m = a;
(void)((m < b) && (m = b));
(void)((m < c) && (m = c));
return m;
}
//Function that returns the minimum of 3 integers
int myMin(int a, int b, int c) {
int m = a;
(void)((m > b) && (m = b));
(void)((m > c) && (m = c));
return m;
}
//Function that detects whether a pixel belongs to the skin based on RGB values
void mySkinDetect(Mat& src, Mat& dst) {
//Surveys of skin color modeling and detection techniques:
//Vezhnevets, Vladimir, Vassili Sazonov, and Alla Andreeva. "A survey on pixel-based skin color detection techniques." Proc. Graphicon. Vol. 3. 2003.
//Kakumanu, Praveen, Sokratis Makrogiannis, and Nikolaos Bourbakis. "A survey of skin-color modeling and detection methods." Pattern recognition 40.3 (2007): 1106-1122.
for (int i = 0; i < src.rows; i++) {
for (int j = 0; j < src.cols; j++) {
//For each pixel, compute the average intensity of the 3 color channels
Vec3b intensity = src.at<Vec3b>(i, j); //Vec3b is a vector of 3 uchar (unsigned character)
int B = intensity[0]; int G = intensity[1]; int R = intensity[2];
if ((R > 95 && G > 40 && B > 20) && (myMax(R, G, B) - myMin(R, G, B) > 15) && (abs(R - G) > 15) && (R > G) && (R > B)) {
dst.at<uchar>(i, j) = 255;
}
}
}
}

OpenCV : How to find the pixels inside a contour in c++

Suppose if we are working on an image, is there any way to access the pixels inside the contour?
I have already found the contour using the function findContours() and even found the moments but I couldn't find the pixels inside the contour.
Any suggestions are Welcome!!
Thank you!
As #Miki already mentioned you can use connectedComponents to perform a labeling. Then you iterate through the bounding box of your object like #Amitay Nachmani suggested. But instead of using pointPolygonTest you can check if the value at your current positions matches your current label Here is a small example:
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <vector>
using namespace cv;
using namespace std;
Mat binary, labels, stats, centroids;
int main()
{
Mat src = imread("C:\\Users\\phili\\Pictures\\t06-4.png",0);
threshold(src, binary, 0, 255, CV_THRESH_OTSU);
int nLabels = connectedComponentsWithStats(binary, labels, stats, centroids);
vector<vector<Point>> blobs(nLabels-1);
for (int i = 1; i < nLabels; i++) //0 is background
{
//get bounding rect
int left = stats.at<int>(i, CC_STAT_LEFT) ;
int top = stats.at<int>(i, CC_STAT_TOP);
int width = stats.at<int>(i, CC_STAT_WIDTH);
int height = stats.at<int>(i, CC_STAT_HEIGHT);
blobs[i - 1].reserve(width*height);
int x_end = left + width;
int y_end = top + height;
for (int x = left; x < x_end; x++)
{
for (int y = top; y < y_end; y++)
{
Point p(x, y);
if (i == labels.at<int>(p))
{
blobs[i-1].push_back(p);
}
}
}
}
}
EDIT:
Since youre using OpenCV 2.4 there are two ways to achieve the same results.
First you could use findContours to detect the blobs, then draw them (filled) into a new image with a specific color as label (be aware that your blobs could contain holes) Then iterate through the image inside the bounding rectangle of each contour and get all points with the label of your current contour. If you just iterate through the bounding rectangle inside your binary image, you have problems with objects overlapping the bounding rectangle.
Here is the code:
int getBlobs(Mat binary, vector<vector<Point>> & blobs)
{
Mat labels(src.size(), CV_32S);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binary, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
blobs.clear();
blobs.reserve(contours.size());
int count = 1; //0 is background
for (int i = 0; i < contours.size(); i++) // iterate through each contour.
{
//if contour[i] is not a hole
if (hierarchy[i][3] == -1)
{
//draw contour without holes
drawContours(labels, contours, i, Scalar(count),CV_FILLED, 0, hierarchy, 2, Point());
Rect rect = boundingRect(contours[i]);
int left = rect.x;
int top = rect.y;
int width = rect.width;
int height = rect.height;
int x_end = left + width;
int y_end = top + height;
vector<Point> blob;
blob.reserve(width*height);
for (size_t x = left; x < x_end; x++)
{
for (size_t y = top; y < y_end; y++)
{
Point p(x, y);
if (count == labels.at<int>(p))
{
blob.push_back(p);
}
}
}
blobs.push_back(blob);
count++;
}
}
count--;
return count;
}
Second you can perform your own labling with floodfill. Therefore you iterate through your image and start floodfill for every white pixel, iterate through the bounding rectangle and get all points that have the same seedColor.
Here is the code:
int labeling(Mat binary, vector<vector<Point>> &blobs)
{
FindBlobs(binary, blobs);
return blobs.size();
}
with
void FindBlobs(const Mat &binary, vector<vector<Point>> &blobs)
{
blobs.clear();
// Fill the label_image with the blobs
// 0 - background
// 1 - unlabelled foreground
// 2+ - labelled foreground
cv::Mat label_image;
binary.convertTo(label_image, CV_32FC1);
float label_count = 2; // starts at 2 because 0,1 are used already
for (int y = 0; y < label_image.rows; y++) {
float *row = (float*)label_image.ptr(y);
for (int x = 0; x < label_image.cols; x++) {
if (row[x] != 255) {
continue;
}
cv::Rect rect;
cv::floodFill(label_image, Point(x, y), Scalar(label_count), &rect, Scalar(0), Scalar(0), 4 );
vector<Point> blob;
blob.reserve(rect.width*rect.height);
for (int i = rect.y; i < (rect.y + rect.height); i++) {
float *row2 = (float*)label_image.ptr(i);
for (int j = rect.x; j < (rect.x + rect.width); j++) {
if (row2[j] != label_count)
{
continue;
}
blob.push_back(Point(j, i));
}
}
blobs.push_back(blob);
label_count++;
}
}
}
I used this image:
And here are the bounding boxes and the points inside the contour for visualization:
Create a new image with filled contours using fillPoly.
fillPoly(filledImage, contours, Scalar(255, 255, 255));
Then find the non-zero pixels within that image using findNonZero.
vector<Point> indices;
findNonZero(filledImage, indices);
The "indices" result refer to pixels inside the contour
Use the pointPolygonTest http://docs.opencv.org/2.4/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html?highlight=pointpolygontest#pointpolygontest on the all the pixels inside the bounding box of the contour contour.

watershed of IRM images

I want to apply an algorithm of segmentation of ( watershed by markers ). I have a problem in this code. It's applied only with one image (lena.jpg)
I want to use it with irm images but it's not working.
I think that the problem is in image RGB and gray.
#include <opencv\highgui.h>
#include <opencv2\imgproc\imgproc.hpp>
#include <cstdio>
#include <iostream>
using namespace cv;
using namespace std;
static void help()
{
cout << "\nThis program demonstrates the famous watershed segmentation algorithm in OpenCV: watershed()\n"
"Usage:\n"
"./watershed [image_name -- default is ../data/fruits.jpg]\n" << endl;
cout << "Hot keys: \n"
"\tESC - quit the program\n"
"\tr - restore the original image\n"
"\tw or SPACE - run watershed segmentation algorithm\n"
"\t\t(before running it, *roughly* mark the areas to segment on the image)\n"
"\t (before that, roughly outline several markers on the image)\n";
}
Mat markerMask, img;
Point prevPt(-1, -1);
static void onMouse(int event, int x, int y, int flags, void*)
{
if (x < 0 || x >= img.cols || y < 0 || y >= img.rows)
return;
if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
prevPt = Point(-1, -1);
else if (event == EVENT_LBUTTONDOWN)
prevPt = Point(x, y);
else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
{
Point pt(x, y);
if (prevPt.x < 0)
prevPt = pt;
line(markerMask, prevPt, pt, Scalar::all(255), 5, 8, 0);
line(img, prevPt, pt, Scalar::all(255), 5, 8, 0);
prevPt = pt;
imshow("image", img);
}
}
int main(int argc, char** argv)
{
char* filename = argc >= 2 ? argv[1] : (char*)"samah.png";
Mat img0 = imread(filename, 1), imgGray;
if (img0.empty())
{
cout << "Couldn'g open image " << filename << ". Usage: watershed <image_name>\n";
//system("wait");
return 1;
}
help();
namedWindow("image", 1);
img0.copyTo(img);
cvtColor(img, markerMask, COLOR_BGR2GRAY);
cvtColor(markerMask, imgGray, COLOR_GRAY2BGR);
markerMask = Scalar::all(0);
imshow("image", img);
setMouseCallback("image", onMouse, 0);
for (;;)
{
int c = waitKey(0);
if ((char)c == 27)
break;
if ((char)c == 'r')
{
markerMask = Scalar::all(0);
img0.copyTo(img);
imshow("image", img);
}
if ((char)c == 'w' || (char)c == ' ')
{
int i, j, compCount = 0;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(markerMask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
if (contours.empty())
continue;
Mat markers(markerMask.size(), CV_32S);
markers = Scalar::all(0);
int idx = 0;
for (; idx >= 0; idx = hierarchy[idx][0], compCount++)
drawContours(markers, contours, idx, Scalar::all(compCount + 1), -1, 8, hierarchy, INT_MAX);
if (compCount == 0)
continue;
vector<Vec3b> colorTab;
for (i = 0; i < compCount; i++)
{
int b = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int r = theRNG().uniform(0, 255);
colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
double t = (double)getTickCount();
watershed(img0, markers);
t = (double)getTickCount() - t;
printf("execution time = %gms\n", t*1000. / getTickFrequency());
Mat wshed(markers.size(), CV_8UC3);
// paint the watershed image
for (i = 0; i < markers.rows; i++)
for (j = 0; j < markers.cols; j++)
{
int index = markers.at<int>(i, j);
if (index == -1)
wshed.at<Vec3b>(i, j) = Vec3b(255, 255, 255);
else if (index <= 0 || index > compCount)
wshed.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
else
wshed.at<Vec3b>(i, j) = colorTab[index - 1];
}
wshed = wshed*0.5 + imgGray*0.5;
imshow("watershed transform", wshed);
}
}
system("wait");
return 0;
}

Detect largest rectangle from an image using OpenCV

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).