OpenCv: How to convert vector<Point> to Mat (CV_8U) - c++

Hi everybody and thanks for your attention, I have the following problem:
I have a vector<Point> which stores a number of coordinates on an image.
I want to:
Create a blank white Mat of type CV_8U
For each Point in the vector of coordinates, I want to mark that Point in black over the white image,
Print the image.
Here is the function iterating over the vector:
void Frag::updateImage(vector<Point> points){
...
if(NewHeight > 0 && NewWidth > 0){
cv::Mat NewImage = cv::Mat(NewHeight, NewWidth, CV_8U, Scalar(255));
// Is this the correct way to initialize a blank Mat of type CV_8U???
for (unsigned int i = 0; i < points.size(); i++) {
uchar* PointPtr = NewImage.ptr<uchar> (points[i].x, points[i].y);
*PointPtr = 0;
}
Utility::DisplayImage(NewImage);
}
...
}
And here is my print function:
void Utility::DisplayImage(Mat& tgtImage) {
namedWindow("Draw Image", (CV_WINDOW_NORMAL | CV_WINDOW_KEEPRATIO));
imshow("Draw Image", tgtImage);
waitKey(0);
}
My problem is the following: it looks like the values are stored into the Matrix (I tried printing them), but the DisplayImage function (which works fine in all the other cases) keeps showing me just blank white images.
What am i missing? Pointer-related issues? Mat initialization issues?
<--- --- --- UPDATE --- --- --->
After the first answers, I found out that the actual issue is that I am not able to set the values in the Mat. I found that because i added a simple loop to print all the values in the Mat (since my Mats are often very small). The loop is the following ( i put it right after the iteration over the vector of Coordinates:
for(int j = 0; j< NewHeight; j++){
for(int i = 0; i< NewWidth; i++){
Logger << (int)NewImage.at<uchar> (i, j) << " ";
}
Logger << endl;
}
And its result is always this:
Creating image with W=2, H=7.
255 255
255 255
255 255
255 255
255 255
255 255
255 255
So the value are just not set, any idea?
Could it be something related to the image type (CV_8U)??

i hope this will help, not tested yet.
Mat NewImage = Mat(NewHeight, NewWidth, CV_8U, Scalar(255));
for(int j = 0; j< NewHeight; j++){
for(int i = 0; i< NewWidth; i++){
for (int k = 0; k < points.size(); k++) {
if(i==points[k].x && j ==points[k].y)
NewImage.at<uchar>(j,i) = 0;
}
}
}
imshow(NewImage);

You could instead do:
for (int i =0; i < points.size(); i++)
{
cv::circle(NewImage, points.at(i), 0, cv::Scalar(0)); //The radius of 0 indicates a single pixel
}
This dispenses with the direct data access and pointer manipulation, and is much more readable.

After a thorough analysis of my code, I detected that the coordinates stored in the vector were relative to a bigger Mat, say Mat OldImage.
The Mat NewImage was meant to store a subset of the Points of Mat OldImage (NewImage is much smaller than OldImage), but with no coordinate conversion from one coordinate system to the other, I was always writing in the wrong position.
I solved the problem by converting the Points to the correct coordinate system using a simple subtraction.

Related

change image brightness with c++

i want to change brightness of image, only accessing pixel value.
not using opencv function(ex. convertTo)
input : image , num
num means constant value for brightness
here is my code and result looks wierd.
Is there any problem?
original
result
cv::Mat function(cv::Mat img, int num){
cv::Mat output;
output = cv::Mat::zeros(img.rows, img.cols, img.type());
for (int i = 0; i < img.rows; i++)
{
for (int j = 0; j < img.cols; j++)
{
for (int c = 0; c < img.channels(); c++)
{
output.at<cv::Vec3b>(i, j)[c] = img.at<cv::Vec3b>(i, j)[c] + num;
if (output.at<cv::Vec3b>(i, j)[c] > 255){
output.at<cv::Vec3b>(i, j)[c] = 255;
}
else if (output.at<cv::Vec3b>(i, j)[c] < 0)
{
output.at<cv::Vec3b>(i, j)[c] = 0;
}
}
}
}
cv::imshow("output", output);
cv::waitKey(0);
return img;
}
not using opencv function
that's somewhat silly, since your code is already using opencv's data structures.
trying to do so, you also reinvented the wheel, albeit a slightly square one ...
Check for overflow before assigning to output.
yea that's the problem. correct way to do it would be: assign the sum to something larger than uchar, and then check
else if (output.at<cv::Vec3b>(i, j)[c] < 0)
this will never happen, try to understand why
but please note, that your whole code (triple loop, omg !!!) could be rewritten as a simple:
Mat output = img + Scalar::all(num);
(faster, safer, & this will also saturate correctly !)

Rect intersection issues

Using OpenCV 3.1: I have created a simple change detection program. I have added a feature where the user can create constraints on the locations where change will be detected. The user can select ROI's within frame view and change will only be registered within them. I am trying to do this using intersection. Contours are converted to rect's and then the rects are compared to all ROI's user stipulated (if none set program creates a single ROI size of frame). I have seen intersect used but it doesn't want to work for me.
tuple<Mat, bool, bool> checkContours(Mat frame, int minArea, vector<vector<Point>> contours, bool occupied, bool initialFrame,vector<Rect> masterROI){
int foundContourCount;
Rect boundRect, intersectingRect;
vector<Rect> boundRectList;
if (contours.size() > 1){
foundContourCount = 0;
//remove all contours with too small an area
for(int i = 0; i < contours.size(); i++){
//get the boundboxes and save the ROI as an Image
if (contourArea(contours[i]) < minArea){
continue;
}
boundRect = boundingRect( Mat(contours[i]));
boundRectList.push_back(boundRect);
}
//confirm that there are rects in the list
cout << boundRectList.size() << endl;
//check if valid bournding areas intersect the provided ROI's
for(int i = 0; i < masterROI.size();i++){
for(int ii = 0; i < boundRectList.size();ii++){
intersectingRect = boundRectList[ii] & masterROI[i];
if(intersectingRect.area() > 0){
//cout << "found one" << endl;
rectangle( frame, boundRect.tl(), boundRect.br(), Scalar(0,255,0), 1, 8, 0 );
foundContourCount++;
}
}
}

difference in get a submat in opencv

I have tested the following two snippets and they give a different result. The second one is right. I do not understand why it is and I wonder if there is a bug in opencv?
A result matrix f_sub is different in those examples.
1)
Mat f = Mat::zeros(96,112,CV_8UC1);
randu(f,0,255);
Mat f_sub = f(cv::Rect(17,14,78,68));
//mat2File("f.mm",f,1);
//mat2File("f_sub.mm",f_sub,1);
exit(0);
2)
Mat f = Mat::zeros(96,112,CV_8UC1);
randu(f,0,255);
Mat f_sub = f(cv::Rect(17,14,78,68)).clone();
//mat2File("f.mm",f,1);
//mat2File("f_sub.mm",f_sub,1);
exit(0);
The mat2File is just print a mat into a file
void mat2File(string filename, Mat M, int y)
{
ofstream fout(filename.c_str());
//fout << M.rows<<" "<<M.cols<<endl;
uchar *M_ptr = (uchar*)M.ptr();
for(size_t i=0; i<M.rows; i++)
{
fout<<endl;
for(size_t j=0; j<M.cols; j++)
{
fout<< (size_t)M_ptr[i*M.cols+j]<<" ";
}
}
}
The mat2File seems to be a culprit.
M_ptr[i*M.cols+j] is incorrect for non-continuous matrices because pitch between matrix lines is greater than M.cols. You'd better use M.at<uchar>(y,x) to access Mat pixels.

SVM + HOG, found object is always NULL

I am working on fungus spores detection. I have about 359 positive images(cropped images of fungus particles) and 171 negative images(cropped images of dust particles).
the actual size of pos/nag images is same as the size of particle which is around 8x8 but for SVM training I have resize it to 30x30 and then have used resized images to train SVM classifier. I am directly training the SVM with images not with any features.
After training the classifier successfully I used hog.setSVMDetector(myclassifier) and hog.detectMultiScale but the found objects is zero.
Now my questions is what I am doing wrong in my code?
Please click on the below link for Nagative/Positive images which I used for SVM classifier, test images and the desired particles image.
https://drive.google.com/folderview?id=0B7yRjtOGywg7fnNJTXcxR2NYb3ItWTZ0UjYwUmt6YW96R0NHLWZQbVJucEk0bnJmcFJGS0E&usp=sharing
class LinearSVM: public CvSVM {
public:
void getSupportVector(std::vector<float>& support_vector) const;
};
void LinearSVM::getSupportVector(std::vector<float>& support_vector) const {
int sv_count = this->get_support_vector_count();
// mexPrintf("size : %d\n",support_vector.size());
const CvSVMDecisionFunc* df = this->decision_func;
const double* alphas = df[0].alpha;
double rho = df[0].rho;
int var_count = this->get_var_count();
support_vector.resize(var_count, 0);
for (unsigned int r = 0; r < (unsigned)sv_count; r++) {
float myalpha = alphas[r];
const float* v = this->get_support_vector(r);
for (int j = 0; j < var_count; j++,v++) {
support_vector[j] += (-myalpha) * (*v);
}
}
support_vector.push_back(rho);
}
////////////////////////////// main.cpp ///////////////////////////
int num_files = 359*171;
int img_area = 30*30;
Mat training_mat(num_files,img_area,CV_32FC1);
vector<int> training_Labels;
Mat labels(num_files,1,CV_32FC1);
int imagenum=0;
for (int pimageNum = 0; pimageNum < 359; pimageNum++)
{
// reading Positive Images from directory and resize it to 30x30
int ii = 0; // Current column in training_mat
for (int i = 0; i<posImage.rows; i++) {
for (int j = 0; j < posImage.cols; j++) {
training_mat.at<float>(imagenum,ii++) = posImage.at<uchar>(i,j);
imagenum++;
}
}
training_Labels.push_back(1.0);
}
for (int nimageNum = 0; nimageNum < 171; nimageNum++)
{
// reading Nagative Images from directory and resize it to 30x30
int ii = 0; // Current column in training_mat
for (int i = 0; i<nagImage.rows; i++) {
for (int j = 0; j < nagImage.cols; j++) {
training_mat.at<float>(imagenum,ii++) = nagImage.at<uchar>(i,j);
imagenum++;
}
}
training_Labels.push_back(-1.0);
}
Mat(training_Labels).copyTo(labels);
labels.convertTo(labels, CV_32FC1);
CvSVMParams SVM_params;
SVM_params.svm_type = CvSVM::C_SVC;
SVM_params.kernel_type = CvSVM::LINEAR; //CvSVM::LINEAR;
SVM_params.degree = 0;
SVM_params.gamma = 3;
SVM_params.coef0 = 0;
SVM_params.C = 1;
SVM_params.nu = 0;
SVM_params.p = 0;
SVM_params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 10, 0.03);
//Train SVM
LinearSVM svmClassifier;
svmClassifier.train(training_mat, labels, Mat(), Mat(), SVM_params);
svmClassifier.save("D:\\svmClassifier.yml");
HOGDescriptor hog;
hog.winSize = Size(8, 8);
std::vector<float> support_vector;
FileStorage fs;
fs.open("D:\\svmClassifier.yml", FileStorage::READ);
fs["support_vectors"] >> support_vector;
hog.setSVMDetector(support_vector);
vector< Rect> found,found_filtered;
Size padding(Size(0, 0));
Size winStride(Size(8, 8));
cv::Mat test=cv::imread("testimage.bmp",CV_LOAD_IMAGE_GRAYSCALE);
// actual size of test image is 1024x768 which I resize to 400x 300
hog.detectMultiScale(test, found, 0.0, winStride, padding, 1.01, 0);
cout<<""found particles"<< found.size() ; // it is always zero
size_t i, j;
for (i=0; i<found.size(); i++)
{
Rect r = found[i];
for (j=0; j<found.size(); j++)
if (j!=i && (r & found[j])==r)
break;
if (j==found.size())
found_filtered.push_back(r);
}
for (i=0; i<found_filtered.size(); i++)
{
Rect r = found_filtered[i];
r.x += cvRound(r.width*0.1);
r.width = cvRound(r.width*0.8);
r.y += cvRound(r.height*0.06);
r.height = cvRound(r.height*0.9);
rectangle(test, r.tl(), r.br(), cv::Scalar(0,255,0), 2);
}
imshow("detected particles", test);
waitKey(0);
I haven't managed to get your classifier to work, but I have found the cause of your current issue.
I have put up a gist here so you can check it out. I have used my own filereading methods to read in the images, so you will have to make one or two changes so that it works for you.
The reason that you get no found objects is because your classifier vector is empty. That is due to incorrect reading of the node from the yml file. 'support_vectors' is a subnode of 'my_svm'. You were trying to read it as a top-level node, and were getting back an empty vector.
There are also a couple of other errors in the filereading loops which I have fixed (marked FIXED) - at least, they seemed incorrect to me.
This still won't correctly classify (yet). There is some issue with the format of your support vectors array, which breaks the hog descriptor.
So you'll need to put a bit more work into this, but hopefully this moves you in the right direction.
I have added a slider to the output window. When you get the classifier to work, you can change the scaleFactor with the slider which should yield interesting variation in the results.

OpenCv Implementing Gaussian Blur

I want to implement Gaussian Blur function in opencv, but when i try i get a noised image. Here is my code :
int main( int argc, char** argv )
{
src = imread( "fruits.jpg", 0 );
gauss3x3 = Mat(src.cols,src.rows,src.type()); //src.clone();
Mat kernelX = getGaussianKernel(3, 1);
Mat kernelY = getGaussianKernel(3, 1);
Mat kernelXY = kernelX * kernelY.t();
filter(src,gauss3x3,kernelXY);
namedWindow( window_name4, WINDOW_AUTOSIZE );
imshow(window_name4,gauss3x3);
}
void filter(Mat src, Mat dst, Mat kernel) {
cout << "filter" << endl;
for(int i=0; i<src.rows - 0; i++) {
for(int j=0; j<src.cols - 0; j++) {
float p = 0;
for(int k=0; k<kernel.rows; k++) {
for(int l=0; l<kernel.cols; l++) {
if(i+k < src.rows && j+l < src.cols) {
p += (src.at<uchar>(i + k,j + l) * kernel.at<uchar>(k,l));
}
}
}
if(i + kernel.rows/2 < src.rows && j + kernel.cols/2 < src.cols) {
dst.at<uchar>(i + kernel.rows/2,j + kernel.cols/2) = p / sum(kernel)[0];
}
}
}
}
I don't have any idea about the solution. Any help would be appreciated.
Change
kernel.at<uchar>(k,l)
to:
kernel.at<double>(k,l)
The point is that the default data type for the kernel is CV_64F, which corresponds to double (documentation).
Also, check that your input image is strictly in grayscale. To support color, you will have to detect the image type in the beginning and differentiate your code. To create the color version of the function, you will have to start by using .at<cv::Vec3b> in place of .at<uchar> (and this of course won't be the only necessary modificatin).
I suggest also that you change your function declaration to:
void filter(const Mat& src, Mat& dst, const Mat& kernel) {
The rest of the algorithm looks ok, apart that it might be pretty slow, it would be much faster if you avoided using the at method, and constrained the for cycles so not to have to check if you are going to touch the image border, and if you stored in some local const variable your different cv::Mat sizes and use them in your loops. Finally, sum(kernel) is constant and therefore you can also precompute that value.