I have to create a program that takes a video as input
and after processing it has to be composed just from BGR colors.
Were red color is predominant it has to be (0,0,255),where blue is predominate (255,0,0) and for green (0,255,0).Every area where a color is predominant should have that color, not every pixel.
I found something but it works only for one color.
Mat redFilter(const Mat& src)
{
Mat redOnly;
inRange(src, Scalar(0, 0, 1), Scalar(100, 100, 255), redOnly);
}
Can you give me some ideas for this project?
You can set the pure blue, red or green color according to the highest among B,G,R values for each pixel.
Image:
Result:
Code:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
// Load image
Mat3b img = imread("D:\\SO\\img\\barns.jpg", IMREAD_COLOR);
// Result image with only pure B,G,R values
Mat3b bgr(img.size());
for (int r = 0; r < img.rows; ++r)
{
for (int c = 0; c < img.cols; ++c)
{
// Take highest among B,G,R
Vec3b v = img(r,c);
if (v[0] > v[1] && v[0] > v[2])
{
bgr(r, c) = Vec3b(255, 0, 0);
}
else if (v[1] > v[0] && v[1] > v[2])
{
bgr(r, c) = Vec3b(0, 255, 0);
}
else
{
bgr(r, c) = Vec3b(0, 0, 255);
}
}
}
imshow("Image", img);
imshow("Result", bgr);
waitKey();
return 0;
}
Here's something that you might be looking for. http://aishack.in/tutorials/tracking-colored-objects-opencv/
This article talks about segmenting objects of a specific color. You can use the same approach and identify images in red, green and blue. The article goes a step further and figures out "where" the object is as well.
Related
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
First of all sorry if question was asked. I am working on an app that can detect the corner of a document. I am right now using openCV to detect edge. I have achieved this using openCV but I am not getting the perfect result.
I have also tried the BradLarson GPUImage but I am able how to start with this.
My code that detect the corner of the document but not a perfect result.
void find_squares(Mat& image, cv::vector<cv::vector<cv::Point>>&squares)
{
// blur will enhance edge detection
Mat blurred(image);
//cv::resize(image, image, cvSize(0.25, 0.25));
Mat gray0(blurred.size(), CV_8U), gray;
//medianBlur(image, blurred, 9); //default 9;
GaussianBlur(image, blurred, cvSize(9, 9), 2.0,2.0);
vector<vector<cv::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 = 4;
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(), cv::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<cv::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);
}
}
}
}
}
So my questions are:
1) is there any other library that can do this.
2) is there any problem in above code? Should I add some image processing before detection?
3) Can BradLarson GPUImage do this? And if it can then are there sources of sample code for edge detection?
So, I have an image cv::Mat created as an indexed 2D matrix with colors 1,2,3,... up to 255. I want to resize my image all at once but do it like I currently do - individually for each index, so as not to get mixed colors:
//...
std::map<unsigned char , cv::Mat* > clusters;
for(int i = 0; i < sy; ++i)
{
for(int j = 0; j < sx; ++j)
{
unsigned char current_k = image[i][j];
if (clusters[current_k] == NULL) {
clusters[current_k] = new cv::Mat();
(*clusters[current_k]) = cv::Mat::zeros(cv::Size(sx, sy), CV_8UC1);
}
(*clusters[current_k]).row(i).col(j) = 255;
}
}
std::vector<cv::Mat> result;
for( std::map<unsigned char, cv::Mat*>::iterator it = clusters.begin(); it != clusters.end(); ++it )
{
cv::Mat filled(cv::Size(w, h), (*it->second).type());
cv::resize((*it->second), filled, filled.size(), 0,0, CV_INTER_CUBIC);
cv::threshold( filled, filled, 1, 255, CV_THRESH_BINARY);
result.push_back(filled);
}
So, can OpenCV help me with the automation of my indexed image (so that I could not create cv::Mat per each cluster for a correct resize)?
you can use the Remap function with your own mash to interpolate the values as you'de like
take a look at this tutorial (Link)
I have an image of the background scene and an image of the same scene with objects in front. Now I want to create a mask of the object in the foreground with background substraction. Both images are RGB.
I have already created the following code:
cv::Mat diff;
diff.create(orgImage.dims, orgImage.size, CV_8UC3);
diff = abs(orgImage-refImage);
cv::Mat mask(diff.rows, diff.cols, CV_8U, cv::Scalar(0,0,0));
//mask = (diff > 10);
for (int j=0; j<diff.rows; j++) {
// get the address of row j
//uchar* dataIn= diff.ptr<uchar>(j);
//uchar* dataOut= mask.ptr<uchar>(j);
for (int i=0; i<diff.cols; i++) {
if(diff.at<cv::Vec3b>(j,i)[0] > 30 || diff.at<cv::Vec3b>(j,i)[1] > 30 || diff.at<cv::Vec3b>(j,i)[2] > 30)
mask.at<uchar>(j,i) = 255;
}
}
I dont know if I am doing this right?
Have a look at the inRange function from OpenCV. This will allow you to set multiple thresholds at the same time for a 3 channel image.
So, to create the mask you were looking for, do the following:
inRange(diff, Scalar(30, 30, 30), Scalar(255, 255, 255), mask);
This should also be faster than trying to access each pixel yourself.
EDIT : If skin detection is what you are trying to do, I would first do skin detection, and then afterwards do background subtraction to remove the background. Otherwise, your skin detector will have to take into account the intensity shift caused by the subtraction.
Check out my other answer, about good techniques for skin detection.
EDIT :
Is this any faster?
int main(int argc, char* argv[])
{
Mat fg = imread("fg.jpg");
Mat bg = imread("bg.jpg");
cvtColor(fg, fg, CV_RGB2YCrCb);
cvtColor(bg, bg, CV_RGB2YCrCb);
Mat distance = Mat::zeros(fg.size(), CV_32F);
vector<Mat> fgChannels;
split(fg, fgChannels);
vector<Mat> bgChannels;
split(bg, bgChannels);
for(size_t i = 0; i < fgChannels.size(); i++)
{
Mat temp = abs(fgChannels[i] - bgChannels[i]);
temp.convertTo(temp, CV_32F);
distance = distance + temp;
}
Mat mask;
threshold(distance, mask, 35, 255, THRESH_BINARY);
Mat kernel5x5 = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(mask, mask, MORPH_OPEN, kernel5x5);
imshow("fg", fg);
imshow("bg", bg);
imshow("mask", mask);
waitKey();
return 0;
}
This code produces this mask based on your input imagery:
Finally, here is what I get using my simple thresholding method:
Mat diff = fgYcc - bgYcc;
vector<Mat> diffChannels;
split(diff, diffChannels);
// only operating on luminance for background subtraction...
threshold(diffChannels[0], bgfgMask, 1, 255.0, THRESH_BINARY_INV);
Mat kernel5x5 = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(bgfgMask, bgfgMask, MORPH_OPEN, kernel5x5);
This produce the following mask:
I think when I'm doing it like this I get the right results: (in the YCrCb colorspace) but accessing each px is slow so I need to find another algorithm
cv::Mat mask(image.rows, image.cols, CV_8U, cv::Scalar(0,0,0));
cv::Mat_<cv::Vec3b>::const_iterator itImage= image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::const_iterator itend= image.end<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itRef= refRoi.begin<cv::Vec3b>();
cv::Mat_<uchar>::iterator itMask= mask.begin<uchar>();
for ( ; itImage!= itend; ++itImage, ++itRef, ++itMask) {
int distance = abs((*itImage)[0]-(*itRef)[0])+
abs((*itImage)[1]-(*itRef)[1])+
abs((*itImage)[2]-(*itRef)[2]);
if(distance < 30)
*itMask = 0;
else
*itMask = 255;
}
I am trying to get black and white histogram data from a color image. However the current setup I have with my histogram only shows me color data I'm sure that it's something that I have to modify in my current math setup.
// Current setup on how to render histogram data to the screen with hist being the calculated histogram
histimg = Mat::zeros(200, 320, CV_8UC3)
int binW = histimg.cols / 16;
Mat buf(1, 16, CV_8UC3);
for( int i = 0; i < 16; i++ )
{
buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./16), 255, 255);
}
cvtColor(buf, buf, CV_HSV2BGR);
for( int i = 0; i < 16; i++ )
{
int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255);
rectangle( histimg, Point(i*binW,histimg.rows),
Point((i+1)*binW,histimg.rows - val),
Scalar(buf.at<Vec3b>(i)), -1, 8 );
}
Thanks in advance for any advice.
Here are two methods:
Create whiteCount and blackCount variables. Iterate through all the pixels and increment whiteCount if the pixel is (255, 255, 255) and increment blackCount if the pixel is (0, 0, 0).
Convert the image to grayscale, create a histogram and look at the first and last bins.