How to find nonzero pixels in specific territory in Mat - opencv

I want to find nonzero pixels with findNonZero function in specific place, not the whole Mat.
In the picture posted below, I find the teritory of white patches with findContours function. Later, I invert the image posted below with bitwise_not function and I need to find the places of pixels of those black patterns seperately for each white patch. How can I do that? For each white patch there should be a Mat or Array with black pixel coordinates.
My current approach is to find the contours of the white patches and draw them all each into seperate Mats. Then, find the coordinates of white patch white pixels with findNonZero, mix the image with black patterns and with for loop check whether the pixel, which was white, now is black. Put the coordinates of those pixels in a List and later do other things... But this method is neither smart and simple, nor efficient.
Is there a possibility to do it much simplier and more efficiently? Like being able to find nonZero pixels inside the contours?

Hi Here is a sample implementation! Please use this!
void findNonZero( InputArray _src, InputArray _Mask, OutputArray _idx )
{
Mat src = _src.getMat();
Mat msk = _Mask.getMat();
CV_Assert( src.type() == CV_8UC1 );
CV_Assert( src.size() == msk.size());
int n = countNonZero(src);
if( n == 0 )
{
_idx.release();
return;
}
if( _idx.kind() == _InputArray::MAT && !_idx.getMatRef().isContinuous() )
_idx.release();
_idx.create(n, 1, CV_32SC2);
Mat idx = _idx.getMat();
CV_Assert(idx.isContinuous());
Point* idx_ptr = idx.ptr<Point>();
for( int i = 0; i < src.rows; i++ )
{
const uchar* bin_ptr = src.ptr(i);
const uchar* msk_ptr = msk .ptr(i);
for( int j = 0; j < src.cols; j++ )
if( bin_ptr[j] && msk_ptr[j])
*idx_ptr++ = Point(j, i);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
string sFileNameEx="F:\\Balaji\\Image Samples\\Test.jpg";
size_t lastindex = sFileNameEx.find_last_of(".");
String sFileName = sFileNameEx.substr(0, lastindex);
bool bSaveImages=true;
Mat mSrc_Gray,mSrc_Mask,mResult_Bgr;
mSrc_Gray= imread(sFileNameEx,0);
mSrc_Mask= Mat(mSrc_Gray.size(),CV_8UC1,Scalar(0));
cvtColor(mSrc_Gray,mResult_Bgr,COLOR_GRAY2BGR);
if(mSrc_Gray.empty())
{
cout<<"[Error]! Invalid Input Image";
return 0;
}
threshold(mSrc_Gray,mSrc_Gray,10,255,THRESH_BINARY);
imshow("mSrc_Gray",mSrc_Gray);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
/// Find contours
findContours( mSrc_Gray.clone(), contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0) );
for( size_t i = 0; i < contours.size(); i++ )
{
if(contourArea(contours[i])>500)
{
drawContours(mSrc_Mask,contours,i,Scalar(255),-1);
}
}
//May be further Filtering like erode needed?
imshow("mSrc_Mask",mSrc_Mask);
vector<Point> locations; // output, locations of non-zero pixels
findNonZero(mSrc_Gray,mSrc_Mask,locations);
for( size_t i = 0; i < locations.size(); i++ )
{
circle(mResult_Bgr,locations[i],1,Scalar(0,255,0),1);
}
imshow("mResult_Bgr",mResult_Bgr);
waitKey(0);
return 0;
}

Related

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.

find trapezoid's 4 points for wraping (rectangle )

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?

Histogram for to classify five colors is not giving proper results in opencv

I have samples of five types: Red, Orange, Green, Yellow, Blue. I have written a code to create histogram. But in the output, blue is perceived as red and actually all the values doesn't seem to be correct.
I want to remove the affect of white and black pixels so, i am eliminating the pixel which are white or black through a "if condition" i.e. if(saturation>50 && V>50) then only use that pixel for histogram.
Example of my Images:
My code for a normalized histogram with 5 bins is following:
void getHistogram(Mat input, MatND& hist_input)
{
float totalNumPixels = 0;
int h_bins = 5;
int h_range = 179;
hist_input = Mat::zeros(1, h_bins, CV_32FC1);
Mat hsv_input;
hsv_input.create( input.rows, input.cols, CV_8UC3);
cvtColor(input, hsv_input, CV_RGB2HSV);
cout<<"done upto here \n";
Mat channels[3];
split( hsv_input, channels);
for(int i=0; i<channels[0].rows; i++)
{
for(int j=0; j<channels[0].cols; j++)
{
if ( (int)channels[1].at<uchar>(i,j)>50 && (int)channels[2].at<uchar>(i,j)>50 )
{
totalNumPixels++;
int pixelValue = (int)channels[0].at<uchar>(i,j);
int correspondingBin = (pixelValue*h_bins)/h_range;
hist_input.at<float>(0, correspondingBin) = hist_input.at<float>(0, correspondingBin) + 1;
}
}
}
for(int j=0; j<hist_input.cols; j++)
{
float pixelValue = hist_input.at<float>(0,j);
hist_input.at<float>(0, j) = hist_input.at<float>(0, j) / totalNumPixels;
}
}

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 ?

OpenCV squares: filtering output

Here is the out put of square-detection example my problem is filter this squares
first problem is its drawing one than more lines for same area;
second one is i just need to detect object not all image.
The other problem is i have to take just biggest object except all image.
Here is a code for detection:
static void findSquares( const Mat& image, vector >& squares ){
squares.clear();
Mat pyr, timg, gray0(image.size(), CV_8U), gray;
// down-scale and upscale the image to filter out the noise
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 < 3; 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:
gray = gray0 >= (l+1)*255/N;
}
// find contours and store them all as a list
findContours(gray, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
vector<Point> approx;
// test each contour
for( size_t i = 0; i < contours.size(); i++ )
{
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
if( approx.size() == 4 &&
fabs(contourArea(Mat(approx))) > 1000 &&
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( maxCosine < 0.3 )
squares.push_back(approx);
}
}
}
}
}
You need to take a look at the flags for findContours(). You can set a flag called CV_RETR_EXTERNAL which will return only the outer-most contour (all contours inside of it are thrown away). This will probably return the entire frame, so you'll need to narrow down the search so that it doesnt check your frame boundaries. Use the function copyMakeBorder() to accomplish this. I would also recommend removing your dilate function as it is probably causing duplicate contours on either side of a line (you might not even need the border if you remove the dilate). Here is my output:

Resources