change image brightness with c++ - 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 !)

Related

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.

Correctly create training matrix for SVM (OpenCV, C++)

I' m trying to create my own SVM classifier, and i stuck at the beginning. I have two sets of data for positive and negative images, and i want to create training matrix, to pass it into SVM. So i read this fundamental post, and tried to do as it says:
char* path_positive, path_negative;
int numPos, numNeg;
int imageWidth=130;
int imageHeight=160;
numPos= 176;
numNeg= 735;
path_positive= "C:\\SVM\\positive\\";
path_negative= "C:\\SVM\negative\\";
Mat classes(numPos+numNeg, 1, CV_32FC1);
Mat trainingMat(numPos+numNeg, imageWidth*imageHeight, CV_32FC1 );
vector<int> trainingLabels;
for(int file_num=0; file_num < numPos; file_num++)
{
stringstream ss(stringstream::in | stringstream::out);
ss << path_positive << file_num << ".jpg";
Mat img = imread(ss.str(), CV_LOAD_IMAGE_GRAYSCALE);
int ii = 0; // Current column in training_mat
for (int i = 0; i<img.rows; i++) {
for (int j = 0; j < img.cols; j++) {
trainingMat.at<float>(file_num,ii++) = trainingMat.at<uchar>(i,j);
}
}
trainingLabels.push_back(1);
}
But when i start this code i got Assertation failderror:
I know, that i made some stupid mistake, because i'm very new to openCV.
Thanks for any help.
EDIT: The error occurs on this :
trainingMat.at(file_num,ii++) = trainingMat.at(i,j);

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

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.

Count 'white' pixels in opencv binary image (efficiently)

I am trying to count all the white pixels in an OpenCV binary image. My current code is as follows:
whitePixels = 0;
for (int i = 0; i < height; ++i)
for (int j = 0; j < width; ++j)
if (binary.at<int>(i, j) != 0)
++whitePixels;
However, after profiling with gprof I've found that this is a very slow piece of code, and a large bottleneck in the program.
Is there a method which can compute the same value faster?
cv::CountNonZero. Usually the OpenCV implementation of a task is heavily optimized.
You can use paralell computing. You divide the image in N parts and run your code in differents threads then you get the result of each threads and after this you can add this results for obtain the finally amount.
The last pixel in a row is usually followed by the first pixel in the next row (C code):
limit=width*height;
i=0;
while (i<limit)
{
if (binary.at<int>(0,i) != 0) ++whitePixels;
++i;
}
Actually binary.at<int>(i, j) is slow access!
Here is simple code that access faster than yours.
for (int i = 0; i < height; ++i)
{
uchar * pixel = image.ptr<uchar>(i);
for (int j = 0; j < width; ++j)
{
if(pixel[j]!=0)
{
//do your job
}
}
}