Illumination normalization in OpenCV - opencv

I am working on a face recognition project. I have pictures with different lighting so I need to do illumination normalization. I read a paper which which claims to do illumination normalization. The paper describe the following function and values.
1- gamma correction with gamma = 0.2
2- Difference of Gaussian (DOG) filtering with (sigma0 = 1, sigma1 =2)
3- contrast equalization (truncation threshold of 10 and compressive component 0.1 is used in the paper)
I use CvPow for gamma correction, CvSmooth for DoG and Threshold() with truncate (I don't know how to specify the compression component) but I didn't get the exact image. I used histogram equalization for contrast equalization.
If someone has done it before or has any idea??
Link to the paper: http://lear.inrialpes.fr/pubs/2007/TT07/Tan-amfg07a.pdf
The code is below: (Python code of Peb Aryan converted to JAVACV)
public static IplImage preprocessImg(IplImage img)
{
IplImage gf = cvCreateImage(cvSize(img.width(),img.height()),IPL_DEPTH_32F, 1 );
IplImage gr = IplImage.create(img.width(),img.height(), IPL_DEPTH_8U, 1);
IplImage tr = IplImage.create(img.width(),img.height(), IPL_DEPTH_8U, 1);
IplImage b1 = IplImage.create(img.width(),img.height(),IPL_DEPTH_32F, 1 );
IplImage b2 = IplImage.create(img.width(),img.height(),IPL_DEPTH_32F, 1 );
IplImage b3 = IplImage.create(img.width(),img.height(),IPL_DEPTH_32F, 1 );
CvArr mask = IplImage.create(0,0,IPL_DEPTH_8U, 1 );
cvCvtColor(img, gr, CV_BGR2GRAY);
gamma(gr,gr,gf);
cvSmooth(gf,b1,CV_GAUSSIAN, 1);
cvSmooth(gf,b2,CV_GAUSSIAN,23);
cvSub(b1,b2,b2,mask);
cvConvertScale(b2,gr,127,127);
cvEqualizeHist(gr, gr);
//cvThreshold(gr,tr,255,0,CV_THRESH_TRUNC);
return gr;
}
public static void gamma(IplImage src,IplImage dst, IplImage temp)
{
cvConvertScale(src,temp, 1.0/255,0);
cvPow(temp, temp, 0.2);
cvConvertScale(temp, dst, 255,0);
}
Here is the result of my attempt:
And the reference from the paper:

Don't know if it's too late for you.
In the original paper, DoG was performed by a given sigma, here your radius(23) it too big. Try radius = 7 and radius = 1. About the equalization step, it's different from the paper. you need implement one by yourself.
BTW: some basic functions like cvSmooth was not implemented right for your application. You probably need to implement by yourself to get a better result.

Related

How to add Noise to Color Image - Opencv

I'm trying to to add noise to an Image & then Denoise it to test my DeNoising algorithm! So for benchmark i'm referring this Online Test samples. I'm trying to replicate the Noise model.
With reference to this threads 1 , 2 I'm adding noise to image like this!
Mat mSource_Bgr;
mSource_Bgr= imread(FileName_S,1);
double m_NoiseStdDev=10;
Mat mNoise_Bgr = mSource_Bgr.clone();
Mat mGaussian_noise = Mat(mSource_Bgr.size(),CV_8UC3);
randn(mGaussian_noise,0,m_NoiseStdDev);
mNoise_Bgr += mGaussian_noise;
normalize(mNoise_Bgr,mNoise_Bgr,0, 255, CV_MINMAX, CV_8UC3);
imshow("Output Window",mNoise_Bgr);
//imshow("Gaussian Noise",mGaussian_noise);
My Input Image
Output Image with Noise
Problem:
Adding Noise to the image alters overall brightness of the Image which in turn alters my final results PSNR!
I want to get the results as much as closer to this one!
What i have tried so far!
I have tried to add the noise only in the color channel.
Convert the Input image into YUV Color space
Add the Noise only in the UV Color Channels & Keep the Y channel unaltered.
Results are very bad & the overall color of the image is getting altered! Will add the code if needed!
So any advice regarding this is much appreciated! May be give me some formulas for adding Noise to the image!
Thank you #Andrey Smorodov For your insights!
I got it working! Here is my updated code for adding Noise in a Color Image. Hope this will be useful for someone!
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
inline BYTE Clamp(int n)
{
n = n>255 ? 255 : n;
return n<0 ? 0 : n;
}
bool AddGaussianNoise(const Mat mSrc, Mat &mDst,double Mean=0.0, double StdDev=10.0)
{
if(mSrc.empty())
{
cout<<"[Error]! Input Image Empty!";
return 0;
}
Mat mGaussian_noise = Mat(mSrc.size(),CV_16SC3);
randn(mGaussian_noise,Scalar::all(Mean),Scalar::all(StdDev));
for (int Rows = 0; Rows < mSrc.rows; Rows++)
{
for (int Cols = 0; Cols < mSrc.cols; Cols++)
{
Vec3b Source_Pixel= mSrc.at<Vec3b>(Rows,Cols);
Vec3b &Des_Pixel= mDst.at<Vec3b>(Rows,Cols);
Vec3s Noise_Pixel= mGaussian_noise.at<Vec3s>(Rows,Cols);
for (int i = 0; i < 3; i++)
{
int Dest_Pixel= Source_Pixel.val[i] + Noise_Pixel.val[i];
Des_Pixel.val[i]= Clamp(Dest_Pixel);
}
}
}
return true;
}
bool AddGaussianNoise_Opencv(const Mat mSrc, Mat &mDst,double Mean=0.0, double StdDev=10.0)
{
if(mSrc.empty())
{
cout<<"[Error]! Input Image Empty!";
return 0;
}
Mat mSrc_16SC;
Mat mGaussian_noise = Mat(mSrc.size(),CV_16SC3);
randn(mGaussian_noise,Scalar::all(Mean), Scalar::all(StdDev));
mSrc.convertTo(mSrc_16SC,CV_16SC3);
addWeighted(mSrc_16SC, 1.0, mGaussian_noise, 1.0, 0.0, mSrc_16SC);
mSrc_16SC.convertTo(mDst,mSrc.type());
return true;
}
int main(int argc, const char* argv[])
{
Mat mSource= imread("input.png",1);
imshow("Source Image",mSource);
Mat mColorNoise(mSource.size(),mSource.type());
AddGaussianNoise(mSource,mColorNoise,0,10.0);
imshow("Source + Color Noise",mColorNoise);
AddGaussianNoise_Opencv(mSource,mColorNoise,0,10.0);//I recommend to use this way!
imshow("Source + Color Noise OpenCV",mColorNoise);
waitKey();
return 0;
}
Looks like your noise matrix can't get negative values as it have unsigned char element type. Try operate with real valued matrices, it should help.
There are mainly two methods to add say awgn noise (mean = 0, standard deviation = 30) to a colored image.
First: You can add the awgn noise of mean = 0, standard deviation = 30 to each of Red, Green, and Blue channels independently (or any other color model-HSI, YUV, Lab); and then combine the noisy channels to form the colored noisy image.
Second: To use the in-built function to add noise to the colored image directly. eg. imnoise() in Matlab.
I tried with both the methods (imnoise and independently), I got the same result.
You mentioned "I have tried to add the noise only in the color channel.
Convert the Input image into YUV Color space
Add the Noise only in the UV Color Channels & Keep the Y channel unaltered."
If you are using the YUV color model, I would suggest you do the opposite. Keep U, and V channel unaltered and add noise only to the Y channel only.

opencv split vs mixChannels

To separate hue channel from HSV image, here is the code using the mixChannels function:
/// Transform it to HSV
cvtColor( src, hsv, CV_BGR2HSV );
/// Use only the Hue value
hue.create( hsv.size(), hsv.depth() );
int ch[] = { 0, 0 };
mixChannels( &hsv, 1, &hue, 1, ch, 1 );
But I know split function can also do this:
vector<Mat> chs;
split(hsv, chs);
Mat hue = chs[0];
Is that OK?
If these are the same, I think split method is more clean. Am I right?
You are pretty much right, split() is used to split all the channels of an multi-channel matrix into single channel matrices. On the other hand if you are interested in only one channel you can use mixChannels(). So you don't have to allocate memory for other channels as we do with split().
Keep things simple and use extractChannel, which wraps mixChannels for you.
cv::Mat hue;
int cn = 0; // hue
cv::extractChannel(hsv, hue, cn);

palm veins enhancement with OpenCV

I'm trying to implement in OpenCV an algorithm to bring out the details of a palm vein pattern. I've based myself on a paper called "A Contactless Biometric System Using Palm Print and Palm Vein Features" that I've found on the Internet. The part I'm interested in is the chapter 3.2 Pre-processing. The steps involved are shown there.
I'd like to do the implementation using OpenCV but until now I'm stuck hard. Especially they use a Laplacian filter on the response of a low-pass filter to isolate the principal veins but my result gets very noisy, no matter the parameters I try!
Any help would be greatly appreciated!
Ok finally I've figured out by myself how to do it. Here is my code :
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#define THRESHOLD 150
#define BRIGHT 0.7
#define DARK 0.2
using namespace std;
using namespace cv;
int main()
{
// Read source image in grayscale mode
Mat img = imread("roi.png", CV_LOAD_IMAGE_GRAYSCALE);
// Apply ??? algorithm from https://stackoverflow.com/a/14874992/2501769
Mat enhanced, float_gray, blur, num, den;
img.convertTo(float_gray, CV_32F, 1.0/255.0);
cv::GaussianBlur(float_gray, blur, Size(0,0), 10);
num = float_gray - blur;
cv::GaussianBlur(num.mul(num), blur, Size(0,0), 20);
cv::pow(blur, 0.5, den);
enhanced = num / den;
cv::normalize(enhanced, enhanced, 0.0, 255.0, NORM_MINMAX, -1);
enhanced.convertTo(enhanced, CV_8UC1);
// Low-pass filter
Mat gaussian;
cv::GaussianBlur(enhanced, gaussian, Size(0,0), 3);
// High-pass filter on computed low-pass image
Mat laplace;
Laplacian(gaussian, laplace, CV_32F, 19);
double lapmin, lapmax;
minMaxLoc(laplace, &lapmin, &lapmax);
double scale = 127/ max(-lapmin, lapmax);
laplace.convertTo(laplace, CV_8U, scale, 128);
// Thresholding using empirical value of 150 to create a vein mask
Mat mask;
cv::threshold(laplace, mask, THRESHOLD, 255, CV_THRESH_BINARY);
// Clean-up the mask using open morphological operation
morphologyEx(mask,mask,cv::MORPH_OPEN,
getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5,5)));
// Connect the neighboring areas using close morphological operation
Mat connected;
morphologyEx(mask,mask,cv::MORPH_CLOSE,
getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(11,11)));
// Blurry the mask for a smoother enhancement
cv::GaussianBlur(mask, mask, Size(15,15), 0);
// Blurry a little bit the image as well to remove noise
cv::GaussianBlur(enhanced, enhanced, Size(3,3), 0);
// The mask is used to amplify the veins
Mat result(enhanced);
ushort new_pixel;
double coeff;
for(int i=0;i<mask.rows;i++){
for(int j=0;j<mask.cols;j++){
coeff = (1.0-(mask.at<uchar>(i,j)/255.0))*BRIGHT + (1-DARK);
new_pixel = coeff * enhanced.at<uchar>(i,j);
result.at<uchar>(i,j) = (new_pixel>255) ? 255 : new_pixel;
}
}
// Show results
imshow("frame", img);
waitKey();
imshow("frame", result);
waitKey();
return 0;
}
So the main steps of the paper are followed here. For some parts I've inspired myself on code I've found. It's the case for the first processing I apply that I've found here. Also for the High-pass filter (laplacian) I've inspired myself on the code given in OpenCV 2 Computer Vision Application Programming Cookbook.
Finally I've done some little improvements by allowing to modify the brightness of the background and the darkness of the veins (see defines BRIGHT and DARK). I've also decided to blur a bit the mask to have a more "natural" enhancement.
Here the results (Source / Paper result / My result) :

how to separate BGR components of a pixel in color image using openCV

Since each pixel memory contains 8 bit for each component Blue,Green and Red. So how can I separate these components from Image or Image Matrix. As
int Blue = f(Image(X,y));// (x,y) = Coordinate of a pixel of Image
similarly, for red and green.
So what should be function f and 2D matrix Image;
Thanks in advance
First off, you must go through the basics of OpenCV and turn your attention towards other parts of image processing. What you ask for is pretty basic and assuming you will be using OpenCV 2.1 and higher,
cv::Mat img = Read the image off the disk or do something to fill the image.
To access the RGB values
img.at<cv::Vec3b>(x,y);
But would give the values in reverse that is BGR. So make sure you note this.
Basically a cv::Vec3b type that is accessed.
img.at<cv::Vec3b>(x,y)[0];//B
img.at<cv::Vec3b>(x,y)[1];//G
img.at<cv::Vec3b>(x,y)[2];//R
or
Vec3f pixel = img.at<Vec3f>(x, y);
int b = pixel[0];
int g = pixel[1];
int r = pixel[2];
Now onto splitting the image into RGB channels you can use the following
Now down to primitive C style of OpenCV (There C and C++ style supported)
You can use the cvSplit function
IplImage* rgb = cvLoatImage("C://MyImage.bmp");
//now create three single channel images for the channel separation
IplImage* r = cvCreateImage( cvGetSize(rgb), rgb->depth,1 );
IplImage* g = cvCreateImage( cvGetSize(rgb), rgb->depth,1 );
IplImage* b = cvCreateImage( cvGetSize(rgb), rgb->depth,1 );
cvSplit(rgb,b,g,r,NULL);
OpenCV 2 CookBook Is one of the best books on OpenCV. Will help you alot.

OpenCV - Image Stitching

I am using following code to stitch to input images. For an unknown
reason the output result is crap!
It seems that the homography matrix is wrong (or is affected wrongly)
because the transformed image is like an "exploited star"!
I have commented the part that I guess is the source of the problem
but I cannot realize it.
Any help or point is appriciated!
Have a nice day,
Ali
void Stitch2Image(IplImage *mImage1, IplImage *mImage2)
{
// Convert input images to gray
IplImage* gray1 = cvCreateImage(cvSize(mImage1->width, mImage1->height), 8, 1);
cvCvtColor(mImage1, gray1, CV_BGR2GRAY);
IplImage* gray2 = cvCreateImage(cvSize(mImage2->width, mImage2->height), 8, 1);
cvCvtColor(mImage2, gray2, CV_BGR2GRAY);
// Convert gray images to Mat
Mat img1(gray1);
Mat img2(gray2);
// Detect FAST keypoints and BRIEF features in the first image
FastFeatureDetector detector(50);
BriefDescriptorExtractor descriptorExtractor;
BruteForceMatcher<L1<uchar> > descriptorMatcher;
vector<KeyPoint> keypoints1;
detector.detect( img1, keypoints1 );
Mat descriptors1;
descriptorExtractor.compute( img1, keypoints1, descriptors1 );
/* Detect FAST keypoints and BRIEF features in the second image*/
vector<KeyPoint> keypoints2;
detector.detect( img1, keypoints2 );
Mat descriptors2;
descriptorExtractor.compute( img2, keypoints2, descriptors2 );
vector<DMatch> matches;
descriptorMatcher.match(descriptors1, descriptors2, matches);
if (matches.size()==0)
return;
vector<Point2f> points1, points2;
for(size_t q = 0; q < matches.size(); q++)
{
points1.push_back(keypoints1[matches[q].queryIdx].pt);
points2.push_back(keypoints2[matches[q].trainIdx].pt);
}
// Create the result image
result = cvCreateImage(cvSize(mImage2->width * 2, mImage2->height), 8, 3);
cvZero(result);
// Copy the second image in the result image
cvSetImageROI(result, cvRect(mImage2->width, 0, mImage2->width, mImage2->height));
cvCopy(mImage2, result);
cvResetImageROI(result);
// Create warp image
IplImage* warpImage = cvCloneImage(result);
cvZero(warpImage);
/************************** Is there anything wrong here!? *******************/
// Find homography matrix
Mat H = findHomography(Mat(points1), Mat(points2), 8, 3.0);
CvMat HH = H; // Is this line converted correctly?
// Transform warp image
cvWarpPerspective(mImage1, warpImage, &HH);
// Blend
blend(result, warpImage);
/*******************************************************************************/
cvReleaseImage(&gray1);
cvReleaseImage(&gray2);
cvReleaseImage(&warpImage);
}
This is what I would suggest you to try, in this order:
1) Use CV_RANSAC option for homography. Refer http://opencv.willowgarage.com/documentation/cpp/calib3d_camera_calibration_and_3d_reconstruction.html
2) Try other descriptors, particularly SIFT or SURF which ship with OpenCV. For some images FAST or BRIEF descriptors are not discriminating enough. EDIT (Aug '12): The ORB descriptors, which are based on BRIEF, are quite good and fast!
3) Try to look at the Homography matrix (step through in debug mode or print it) and see if it is consistent.
4) If above does not give you a clue, try to look at the matches that are formed. Is it matching one point in one image with a number of points in the other image? If so the problem again should be with the descriptors or the detector.
My hunch is that it is the descriptors (so 1) or 2) should fix it).
Also switch to Hamming distance instead of L1 distance in BruteForceMatcher. BRIEF descriptors are supposed to be compared using Hamming distance.
Your homography, might calculated based on wrong matches and thus represent bad allignment.
I suggest to path the matrix through additional check of interdependancy between its rows.
You can use the following code:
bool cvExtCheckTransformValid(const Mat& T){
// Check the shape of the matrix
if (T.empty())
return false;
if (T.rows != 3)
return false;
if (T.cols != 3)
return false;
// Check for linear dependency.
Mat tmp;
T.row(0).copyTo(tmp);
tmp /= T.row(1);
Scalar mean;
Scalar stddev;
meanStdDev(tmp,mean,stddev);
double X = abs(stddev[0]/mean[0]);
printf("std of H:%g\n",X);
if (X < 0.8)
return false;
return true;
}

Resources