I am trying to use EM on OpenCV 2.4.5 for background and foreground image separation. However, unlike the previous version of C class, the c++ is very confusing to me and several routines are rather confusing due to lack of documentation (from my point..)
I wrote the following code, but it seems not to work. It gives error and I tried very hard to debug but still not working.
Mat image;
image = imread("rose.jpg",1);
Mat _m(image.rows, image.cols, CV_32FC3);
Mat _f(image.rows, image.cols, CV_8UC3);
Mat _b(image.rows, image.cols, CV_8UC3);
Mat sample(image.rows * image.cols, 3, CV_32FC1);
Mat float_image;
image.convertTo(float_image,CV_64F);
Mat background_ = Mat(image.rows * image.cols, 3, CV_64F);
int counter = 0;
//Converting from Float image to Column vector
for (int j = 0; j < image.rows; j++)
{
Vec3f* row = float_image.ptr<Vec3f > (j);
for (int i = 0; i < image.cols; i++)
{
sample.at<Vec3f> (counter++, 0) = row[i];
}
}
//sample.reshape(1,image.rows * image.cols);
cout<<"Training"<<endl;
EM params = EM(2);
params.train(sample);
Mat _means = params.get<Mat>("means");
Mat _weights = params.get<Mat> ("weights");
cout<<"Finished Training"<<endl;
Basically, I am converting the image to float of type CV_64F and passing it into the training routine. Perhaps I think i am wrong, can i get help on my error. Thank you
You are mixing your float types.
If you need double precision, change Vec3f to Vec3d.
Otherwise
image.convertTo(float_image,CV_64F);
Mat background_ = Mat(image.rows * image.cols, 3, CV_64F);
should be
image.convertTo(float_image,CV_32F);
Mat background_ = Mat(image.rows * image.cols, 3, CV_32F);
Related
float k[]={1531.49,0,1267.78,0,1521.439,952.078,0,0,1};
float d[]={-0.27149,0.15384,0.0046,-0.0026};
CvMat camera1=cvMat( 3, 3, CV_32FC2, k );
CvMat distCoeffs1=cvMat(1,4,CV_32FC2,d);
const int npoints = 4; // number of point specified
// Points initialization.
// Only 2 ponts in this example, in real code they are read from file.
float input_points[npoints][4] = {{0,0}, {2560, 1920}}; // the rest will be set to 0
CvMat * src = cvCreateMat(1, npoints, CV_32FC2);
CvMat * dst = cvCreateMat(1, npoints, CV_32FC2);
// fill src matrix
float * src_ptr = (float*)src->data.ptr;
for (int pi = 0; pi < npoints; ++pi) {
for (int ci = 0; ci < 2; ++ci) {
*(src_ptr + pi * 2 + ci) = input_points[pi][ci];
}
}
cvUndistortPoints(src, dst, &camera1, &distCoeffs1);
I hope to use the cvUndistortPoints function .And used the example code to test.When I used the VS2012 to run,it dosen't work.It says“src.size dosen't match the dst.size".For I am a rookie in OpenCV.Can someone help me?
Thank you.
the result of runing by vs20121
again, please use opencv's c++ api, not the deprecated c one:
Mat_<float> cam(3,3); cam << 1531.49,0,1267.78,0,1521.439,952.078,0,0,1;
Mat_<float> dist(1,5); dist <<-0.27149,0.15384,0.0046,-0.0026;
const int npoints = 4; // number of point specified
// Points initialization.
// Only 2 ponts in this example, in real code they are read from file.
Mat_<Point2f> points(1,npoints);
points(0) = Point2f(0,0);
points(1) = Point2f(2560, 1920);
Mat dst; // leave empty, opencv will fill it.
undistortPoints(points, dst, cam, dist);
cerr << dst;
[-0.90952414, -0.69702172, 0.92829341, 0.69035494, -0.90952414, -0.69702172, -0.90952414, -0.69702172]
int sizeOfChannel = (_width / 2) * (_height / 2);
double* channel_gr = new double[sizeOfChannel];
// filling the data into channel_gr....
cv::Mat my( _width/2, _height/2, CV_32F,channel_gr);
cv::Mat src(_width/2, _height/2, CV_32F);
for (int i = 0; i < (_width/2) * (_height/2); ++i)
{
src.at<float>(i) = channel_gr[i];
}
cv::imshow("src",src);
cv::imshow("my",my);
cv::waitKey(0);
I'm wondering why i'm not getting the same image in my and src imshow
update:
I have changed my array into double* still same result;
I think it is something to do with steps?
my image output
src image output
this one works for me:
int halfWidth = _width/2;
int halfHeight = _height/2;
int sizeOfChannel = halfHeight*halfWidth;
// ******************************* //
// you use CV_321FC1 later so it is single precision float
float* channel_gr = new float[sizeOfChannel];
// filling the data into channel_gr....
for(int i=0; i<sizeOfChannel; ++i) channel_gr[i] = i/(float)sizeOfChannel;
// ******************************* //
// changed row/col ordering, but this shouldnt be important
cv::Mat my( halfHeight , halfWidth , CV_32FC1,channel_gr);
cv::Mat src(halfHeight , halfWidth, CV_32FC1);
// ******************************* //
// changed from 1D indexing to 2D indexing
for(int y=0; y<src.rows; ++y)
for(int x=0; x<src.cols; ++x)
{
int arrayPos = y*halfWidth + x;
// you have a 2D mat so access it in 2D
src.at<float>(y,x) = channel_gr[arrayPos ];
}
cv::imshow("src",src);
cv::imshow("my",my);
// check for differences
cv::imshow("diff1 > 0",src-my > 0);
cv::imshow("diff2 > 0",my-src > 0);
cv::waitKey(0);
'my' is array of floats but you give it pointer to arrays of double. There no way it can get data from this array properly.
It seems that the constructor version that you are using is
Mat::Mat(int rows, int cols, int type, const Scalar& s)
This is from OpenCV docs. Seems like you are using float for src and assigning from channel_gr (declared as double). Isn't that some form of precision loss?
I am using older version of C because the book I am using is outdated :( Currently, I am working on a project to detect an object in an image. First I do Gaussian smoothing on the gray scale image, then erode it. After that, I apply threshold. Now I am trying to obtain how many black pixels there are for every width so that I can compare it with other row to determine the center. I am trying this in 'for' loop, however, I am keep getting the error:
term does not evaluate to a function taking 1 arguments
#include <highgui.h>
#include <cv.h>
#include <cxcore.h>
int main()
{
int total,
zero,
width,
blackpixel;
IplImage* in = cvLoadImage("Wallet.jpg", CV_LOAD_IMAGE_GRAYSCALE);
IplImage* gsmooth = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
IplImage* erode = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
IplImage* Iat = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
IplImage* bpixel = cvCreateImage(cvGetSize(in), IPL_DEPTH_8U, 1);
cvSmooth(in, gsmooth, CV_GAUSSIAN, 3, 0, 0, 0);
cvErode(gsmooth, erode, NULL, 2);
cvThreshold(erode, Iat, 100, 255, CV_THRESH_BINARY);
total = (Iat->height)*(Iat->width);
zero = total - cvCountNonZero(Iat);
printf("Total pixels: %d\nWhite pixels: %d\nBlack pixels: %d\n", total, cvCountNonZero(Iat), zero);
for(int i = 0; i < Iat->width; i++)
{
blackpixel = Iat->width(i);
}
cvNamedWindow("Original", 1);
cvNamedWindow("Gaussian Smoothing", 1);
cvNamedWindow("Erode", 1);
cvNamedWindow("Adaptive Threshold", 1);
cvShowImage("Original", in);
cvShowImage("Gaussian Smoothing", gsmooth);
cvShowImage("Erode", erode);
cvShowImage("Adaptive Threshold", Iat);
cvWaitKey(0);
cvReleaseImage(&in);
cvReleaseImage(&gsmooth);
cvReleaseImage(&erode);
cvReleaseImage(&Iat);
cvDestroyWindow("Original");
cvDestroyWindow("Gaussian Smoothing");
cvDestroyWindow("Erode");
cvDestroyWindow("Adaptive Threshold");
}
First of all, don't be afraid to use C++ API when using an outdated book like "Learining OpenCV", because the concepts are still relevant. Translating to C++ API is not hard if You understand the idea, and is a great exercise because You can't just copy-paste the code. I learned OpenCV this way, and I think it worked :).
With C++ API it would be as simple as
cv::Mat zeros = cv::Mat::zeros(Iat.size());
cv::Mat blackPixels = (Iat == zeros);
int blackPixelsCount = blackPixels.total();
The problem in the line
blackpixel = Iat->width(i);
is the wrong syntax.
Iat->width will give you the width of the image, an integer property.
I don't thing that the loop
for(int i = 0; i < Iat->height; i++)
{
blackpixel = Iat->width(i);
}
can calculate the number of black pixels in a given row. You might need something like
for(int i = 0; i < Iat->height; i++) // // every row
{
for(int j = 0; j < Iat->width; j++) // pixels in each row
{
// get count pixels here
}
// do things with the count for the current row
}
If you are using a cvMat data structure instead of IplImage, this should be faster.
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'm messing around with OpenCV, and am trying to do some of the same stuff signal processing stuff I've done in MatLab. I'm looking to mask out some frequencies, so I have constructed a matrix which will do this. The problem is that there seem to be a few more steps in OpenCV than in Matlab to accomplish this.
In Matlab, it's simple enough:
F = fft2(image);
smoothF = F .* mask; // multiply FT by mask
smooth = ifft2(smoothF); // do inverse FT
But I'm having trouble doing the same in OpenCV. The DFT leaves me with a 2 channel image, so I've split the image, multiplied by the mask, merged it back, and then perform the inverse DFT. However, I got a weird result in my final image. I'm pretty sure I'm missing something...
CvMat* maskImage(CvMat* im, int maskWidth, int maskHeight)
{
CvMat* mask = cvCreateMat(im->rows, im->cols, CV_64FC1);
cvZero(mask);
int cx, cy;
cx = mask->cols/2;
cy = mask->rows/2;
int left_x = cx - maskWidth;
int right_x = cx + maskWidth;
int top_y = cy + maskHeight;
int bottom_y = cy - maskHeight;
//create mask
for(int i = bottom_y; i < top_y; i++)
{
for(int j = left_x; j < right_x; j++)
{
cvmSet(mask,i,j,1.0f); // Set M(i,j)
}
}
cvShiftDFT(mask, mask);
IplImage* maskImage, stub;
maskImage = cvGetImage(mask, &stub);
cvNamedWindow("mask", 0);
cvShowImage("mask", maskImage);
CvMat* real = cvCreateMat(im->rows, im->cols, CV_64FC1);
CvMat* imag = cvCreateMat(im->rows, im->cols, CV_64FC1);
cvSplit(im, imag, real, NULL, NULL);
cvMul(real, mask, real);
cvMul(imag, mask, imag);
cvMerge(real, imag, NULL, NULL, im);
IplImage* maskedImage;
maskedImage = cvGetImage(imag, &stub);
cvNamedWindow("masked", 0);
cvShowImage("masked", maskedImage);
return im;
}
Any reason you are merging the real and imaginary components in the reverse order?