Magnification of high intensities using openCV - c++

I have an image which has areas of high intensities and I would like to magnify those intensities. I accomplished this in Matlab by converting a integer array in (0,255) to floating point from (0,1), then squaring each value and finally multiplying by 255 and converting back to integer.
How would something like this be done in openCV? Is there a way to access the elements piece by piece? Even so, I suppose this would be inefficient and wonder if there are openCV methods which are vectorized or otherwise optimized to accomplish this.

Given an input grayscale image:
the result of your algorithm is:
You can:
convert and scale with convertTo.
square each pixel with element-wise multiplication mul, or use pow to raise to an arbitrary number.
This is the simple code:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
Mat img = imread("path_to_image", IMREAD_GRAYSCALE);
imshow("Original", img);
// converting to float in (0,1)
img.convertTo(img, CV_32F, 1.0 / 255.0);
// power with an arbitrary number. Use 2 to square
pow(img, 2, img);
// multiplying by 255 and back to integer
img.convertTo(img, CV_8U, 255.0);
imshow("Result", img);
waitKey();
return 0;
}

Related

OpenCV, DFT function don't use in the image with IMREAD_COLOR

While reading the image with IMREAD_COLOR, 'dft' function throws the error:
DFT function works just fine when reading an image with IMREAD_GRAYSCALE. But I want to read the image with IMREAD_COLOR.
main function
const char* filename = "face.jpg";
Mat I = imread(filename, IMREAD_COLOR);
if(I.empty()) return 0;
Mat padded;
I.convertTo(padded, CV_32F);
Mat fft;
Mat planes[2];
dft(padded, fft, DFT_SCALE|DFT_COMPLEX_OUTPUT);
Mat fftBlur = fft.clone();
fftBlur *= 0.5;
split(fftBlur, planes);
Mat ph, mag;
mag.zeros(planes[0].rows, planes[0].cols, CV_32F);
ph.zeros(planes[0].rows, planes[0].cols, CV_32F);
cartToPolar(planes[0], planes[1], mag, ph);
merge(planes, 2, fftBlur);
//inverse
Mat invfft;
dft(fftBlur, invfft, DFT_INVERSE|DFT_REAL_OUTPUT);
Mat result;
invfft.convertTo(result, CV_8U);
Mat image;
cvtColor(result, image, COLOR_GRAY2RGB);
imshow("Output", result);
imshow("Image", image);
waitKey();
The message you receive is an assertion it tells you DFT function only takes single precision floating point image with one or two channels (CV_32FC1, CV_32FC2, the letter C at the end of the flag mean channel) or double precision floating point images with one or two channels (CV_64FC1, CV_64FC2).
The two channel case is actually the representation of complex image in OpenCV data storage.
If you want you can split you image to std::vector<cv::Mat> where each element does represent one channel, using cv::split apply the DFT on each channels do the processing you want on it and recreate an multichannel image thanks to cv::merge.
From Learning OpenCV (about dft function):
The input array must be of floating-point type and may be single- or double-channel. In the single-channel case, the entries are assumed to be real numbers, and the output will be packed in a special space-saving format called complex conjugate symmetrical.
The same question is mentioned here in terms of matlab image processing.
You can check out cv::split function if you want to separate channels of your initial image.

How to use the OpenCV CUDA Fourier Transform

Instead of OpenCV's normal dft, I'd like to use cuda::dft. As a start I tried performing a forward and inverse transform, but the result doesn't look anything like the input. Here's a minimal example using an OpenCV example image:
// Load 8bit test image (https://raw.githubusercontent.com/opencv/opencv/master/samples/data/basketball1.png)
Mat testImg;
testImg = imread("basketball1.png", CV_LOAD_IMAGE_GRAYSCALE);
// Convert input to complex float image
Mat_<float> imgReal;
testImg.convertTo(imgReal, CV_32F, 1.0/255.0);
Mat imgImag = Mat(imgReal.rows, imgReal.cols, CV_32F, float(0));
vector<Mat> channels;
channels.push_back(imgReal);
channels.push_back(imgImag);
Mat imgComplex;
merge(channels,imgComplex);
imshow("Img real", imgReal);
waitKey(0);
//Perform a Fourier transform
cuda::GpuMat imgGpu, fftGpu;
imgGpu.upload(imgComplex);
cuda::dft(imgGpu, fftGpu, imgGpu.size());
//Performs an inverse Fourier transform
cuda::GpuMat propGpu, convFftGpu;
cuda::dft(fftGpu, propGpu, imgGpu.size(), DFT_REAL_OUTPUT | DFT_SCALE);
Mat output(propGpu);
output.convertTo(output, CV_8U, 255, 0);
imshow("Output", output);
waitKey(0);
I played with the flags but output never looks anything like input. Using the above code I get as output:
While it should look like this:
I found the answer here. Apparently, when starting with a complex input image, it's not possible to use the flag DFT_REAL_OUTPUT.
Either you do the forward transform with a one channel float input and then you get the same as an output from the inverse transform, or you start with a two channel complex input image and get that type as output. The upside to using a complex input image is that the forward transform delivers the full sized complex field to work with, e.g. perform a convolution (see linked answer for details).
I'll add that in order to obtain an 8bit image from the inverse transform, compute the magnitude yourself like so:
Mat output(propGpu);
Mat planes[2];
split(output,planes);
Mat mag;
magnitude(planes[0],planes[1],mag);
mag.convertTo(mag, CV_8U, 255, 0);
To go into Fourier domain using OpenCV Cuda FFT and back into the spatial domain, you can simply follow the below example (to learn more, you can refer to cufft documentation, on which OpenCV Cuda FFT source code is based).
Mat test_im;
test_im = imread("frame.png", IMREAD_GRAYSCALE);
// Convert input input to real value type (CV_64F for double precision)
Mat im_real;
test_im.convertTo(im_real, CV_32F, 1.0/255.0);
imshow("Input Image", im_real);
waitKey(0);
// Perform The Fourier Transform
cuda::GpuMat in_im_gpu, fft_im;
in_im_gpu.upload(im_real);
cuda::dft(in_im_gpu, fft_im, in_im_gpu.size(), 0);
// Performs an inverse Fourier transform
cuda::GpuMat ifft_im_gpu;
//! int odd_size = imgGpu.size().width % 2;
//! cv::Size dest_size((fftGpu.size().width-1)*2 + (odd_size ? 1 : 0), fftGpu.size().height);
cv::Size dest_size = in_im_gpu.size();
int flag = (DFT_SCALE + DFT_REAL_OUTPUT) | DFT_INVERSE;
cuda::dft(fft_im, ifft_im_gpu, dest_size, flag);
Mat ifft_im(ifft_im_gpu);
ifft_im.convertTo(ifft_im, CV_8U, 255, 0);
imshow("Inverse FFT", ifft_im);
waitKey(0);

Shouldn't GaussianBlur be symmetric?

I expected a Gaussian Blur operation to be symmetric, but using the OpenCV 2.4.11 GaussianBlur I am getting differences.
Here's an example. I apply a GaussianBlur to an image, and to a flipped version of the image. I've separately verified the flip operation doesn't change the image pixel values (not shown). When I flip the blurred image back, I expected it to be the same as the blur of the original, but the diff shows a lot of small differences (between 0.0 and 6.103515625e-005). I know that's small, but it has a knock-on effect in my subsequent processing.
The Gaussian Kernel is symmetric, so the result should be the same. Is this simply a rounding error in the implementation?
int main(int, char **)
{
// e.g. 2008_005541.jpg from VOC2012 dataset
char const * const filename = "...";
float const sig_diff = 1.24899971f;
cv::Mat image = cv::imread(filename, cv::IMREAD_GRAYSCALE);
cv::Mat gray_fpt;
image.convertTo(gray_fpt, cv::DataType<float>::type, 1, 0);
GaussianBlur(gray_fpt, gray_fpt, cv::Size(), sig_diff, sig_diff);
cv::Mat mirror;
flip(image, mirror, 1);
cv::Mat mirror_gray_fpt;
mirror.convertTo(mirror_gray_fpt, cv::DataType<float>::type, 1, 0);
GaussianBlur(mirror_gray_fpt, mirror_gray_fpt, cv::Size(), sig_diff, sig_diff);
flip(mirror_gray_fpt, mirror_gray_fpt, 1);
cv::Mat diff = abs(gray_fpt - mirror_gray_fpt);
double minval, maxval;
minMaxLoc(diff, &minval, &maxval);
// minval = 0.0;
// maxval = 6.103515625e-005;
// easier to visualise the differences with this:
normalize(diff, diff, 0.0, 1.0, cv::NORM_MINMAX, CV_32FC1);
return 0;
}
EDIT: I changed the type from cv::DataType<float>::type to cv::DataType<double>::type and now the max error is 1.1368683772161603e-013, so rounding seems to be the problem.
Changing the code above to call gaussian_blur (below) instead of GaussianBlur produces no differences in the example images I've tested so far.
From this, if the working type of the Gaussian operation is double precision, then the output in floating point precision yields no error. That seems like a nice solution to my problem.
// Perform gaussian blur in double precision and convert back
void gaussian_blur(
cv::Mat const &src, cv::Mat &dst,
cv::Size ksize, double sigmaX, double sigmaY=0,
int borderType=cv::BORDER_DEFAULT)
{
cv::Mat src_dp;
src.convertTo(src_dp, cv::DataType<double>::type, SIFT_FIXPT_SCALE, 0);
cv::Mat dst_dp;
GaussianBlur(src_dp, dst_dp, ksize, sigmaX, sigmaY, borderType);
dst_dp.convertTo(dst, src.type(), SIFT_FIXPT_SCALE, 0);
}

Creating NDWI Matrix in opencv

Firstly, if you don't know, i should tell what is ndwi. Ndwi stands for normalized difference water index. It is a graphical indicator for water and the value range is [-1 1]. Ndwi is defined as follows:
(Green - NIR) / (Green + NIR)
I am middle of a simple coastline extraction tool based on opencv. I have accomplished it in MATLAB and the result is shown like this:
However, opencv version of the result is look like binarized:
When i debugged the program, i see that minimum value in the ndwi matrix is zero and this is wrong because it should be -0.8057. The code which is responsible for ndwi calculation (opencv version) as follows:
Mat ndwi = (greenRoi - nirRoi) / (greenRoi + nirRoi);
double min;
double max;
minMaxIdx(ndwi, &min, &max);
Mat adjNDWI;
convertScaleAbs(ndwi, adjNDWI, 255 / max);
What is the problem in here and how can i achieve to calculate the right ndwi values?
Note:
greenRoi and nirRoi are created in this way:
Rect rectangle = boundingRect(Mat(testCorners)); //vector<Point2f> testCorners(4);
Mat testImgGreen = imread((LPCSTR)testImgGreenPath, 0);
Mat testImgNir = imread((LPCSTR)testImgNirPath, 0);
Mat greenRoi(testImgGreen, rectangle);
Mat nirRoi(testImgNir, rectangle);
You need to explicitly create a floating point cv::Mat
cv::Mat image(cols,rows,CV_32FC1) or CV_64FC1 if you need doubles
Elements of greenRoi, nirRoi and ndwi will all be uchar's (Mat will be CV_8UC1).
Let's say greenRoi = 10, nirRoi = 40.
Your answer is not (10 - 40)/(10+40) = -0.6. The answer has to be positive (because it unsigned) and can't be a fraction. According to my calculator, this will give 0.
#Martin Beckett is correct, convert testImgGreen and testImgNir to matrices with a float type and it will work. You need:
testImgGreen.convertTo(testImgGreen, CV_32F);
testImgNir.convertTo(testImgNir , CV_32F);
Mat greenRoi(testImgGreen, rectangle);
Mat nirRoi(testImgNir, rectangle);
Mat ndwi = (greenRoi - nirRoi) / (greenRoi + nirRoi);

Adjusting Brightness, OpenCV and c++

I wanted to know if there is any function of OpenCV using C++ to adjust the brightness and contrast of a video / frame. You can convert from BGR color space to HSV color space, and discard the latter component V (luminance) to make the algorithm less sensitive to light conditions in the video, but how can I do it?
I was thinking of using something like cvAddS (frame, cvScalar (-50, -50, -50), frame) to Decrease the brightness, cvAddS and cvScalar work's well for C but how can I do that for C++, I use AddS and Scalar in my program, but don't work with C++
int main() {
VideoCapture video(1);
if(!video.isOpened()) {
cerr<<"No video input"<<endl; return -1;
}
namedWindow("Video",CV_WINDOW_AUTOSIZE);
for(;;) {
Mat frame;
video >> frame; if(!frame.data) break;
Mat frame2;
//I USE AddS AND Scalar TO DECREASE THE BRIGHTNESS
AddS(frame,Scalar(-50,-50,-50),frame2);
//BUT DON'T WORK WITH C++
imshow("Video",frame2);
int c=waitKey(20);
if(c >= 0)break;
}
}
Use matrix expression:
cv::Mat frame2 = frame + cv::Scalar(-50, -50, -50);
You might also want to adjust the contrast with histogram equalization. Convert your RGB image to HSV and apply cv::equalizeHist() to the V channel.
Brightness and contrast are usually corrected using a linear transformation of the pixel values. Brightness corresponds to the additive shift and contrast corresponds to a multiplicative factor.
In general, given a pixel value v, the new value after to correction would be v'=a*v + b.