How to apply Gaussian filter to DFT output in OpenCV - image

I want to create a Gaussian high-pass filter after determining the correct padding size (e.g., if image width and height is 10X10, then should be 20X20).
I have Matlab code that I am trying to port in OpenCV, but I am having difficulty properly porting it. My Matlab code is show below:
f1_seg = imread('thumb1-small-test.jpg');
Iori = f1_seg;
% Iori = imresize(Iori, 0.2);
%Convert to grayscale
I = Iori;
if length(size(I)) == 3
I = rgb2gray(Iori);
end
%
%Determine good padding for Fourier transform
PQ = paddedsize(size(I));
I = double(I);
%Create a Gaussian Highpass filter 5% the width of the Fourier transform
D0 = 0.05*PQ(1);
H = hpfilter('gaussian', PQ(1), PQ(2), D0);
% Calculate the discrete Fourier transform of the image.
F=fft2(double(I),size(H,1),size(H,2));
% Apply the highpass filter to the Fourier spectrum of the image
HPFS_I = H.*F;
I know how to use the DFT in OpenCV, and I am able to generate its image, but I am not sure how to create the Gaussian filter. Please guide me to how I can create a high-pass Gaussian filter as is shown above?

I believe where you are stuck is that the Gaussian filter supplied by OpenCV is created in the spatial (time) domain, but you want the filter in the frequency domain. Here is a nice article on the difference between high and low-pass filtering in the frequency domain.
Once you have a good understanding of how frequency domain filtering works, then you are ready to try to create a Gaussian Filter in the frequency domain. Here is a good lecture on creating a few different (including Gaussian) filters in the frequency domain.
If you are still having difficulty, I will try to update my post with an example a bit later!
EDIT :
Here is a short example that I wrote on implementing a Gaussian high-pass filter (based on the lecture I pointed you to):
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using namespace std;
double pixelDistance(double u, double v)
{
return cv::sqrt(u*u + v*v);
}
double gaussianCoeff(double u, double v, double d0)
{
double d = pixelDistance(u, v);
return 1.0 - cv::exp((-d*d) / (2*d0*d0));
}
cv::Mat createGaussianHighPassFilter(cv::Size size, double cutoffInPixels)
{
Mat ghpf(size, CV_64F);
cv::Point center(size.width / 2, size.height / 2);
for(int u = 0; u < ghpf.rows; u++)
{
for(int v = 0; v < ghpf.cols; v++)
{
ghpf.at<double>(u, v) = gaussianCoeff(u - center.y, v - center.x, cutoffInPixels);
}
}
return ghpf;
}
int main(int /*argc*/, char** /*argv*/)
{
Mat ghpf = createGaussianHighPassFilter(Size(128, 128), 16.0);
imshow("ghpf", ghpf);
waitKey();
return 0;
}
This is definitely not an optimized filter generator by any means, but I tried to keep it simple and straight forward to ease understanding :) Anyway, this code displays the following filter:
NOTE : This filter is not FFT shifted (i.e., this filter works when the DC is placed in the center instead of the upper-left corner). See the OpenCV dft.cpp sample (lines 62 - 74 in particular) on how to perform FFT shifting in OpenCV.
Enjoy!

Related

Efficiently Transforming from Spherical Coordinates to Cartesian Coordinates using Eigen

I need to transform the coordinates from spherical to Cartesian space using the Eigen C++ Library. The following code serves the purpose.
const int size = 1000;
Eigen::Array<std::pair<float, float>, Eigen::Dynamic, 1> direction(size);
for(int i=0; i<direction.size();i++)
{
direction(i).first = (i+10)%360; // some value for this example (denoting the azimuth angle)
direction(i).second = (i+20)%360; // some value for this example (denoting the elevation angle)
}
SSPL::MatrixX<T1> transformedMatrix(3, direction.size());
for(int i=0; i<transformedMatrix.cols(); i++)
{
const T1 azimuthAngle = direction(i).first*M_PI/180; //converting to radians
const T1 elevationAngle = direction(i).second*M_PI/180; //converting to radians
transformedMatrix(0,i) = std::cos(azimuthAngle)*std::cos(elevationAngle);
transformedMatrix(1,i) = std::sin(azimuthAngle)*std::cos(elevationAngle);
transformedMatrix(2,i) = std::sin(elevationAngle);
}
I would like to know a better implementation is possible to improve the speed.
I know that Eigen has supporting functions for Geometrical transformations. But I am yet to see a clear example to implement the same.
Is it also possible to vectorize the code to improve the performance?
You could at least use the vectorized versions of sine/cosine:
void dir2vector2(Eigen::Matrix3Xf& out, const Eigen::Array2Xf& in){
Eigen::Array2Xf sine = sin(in * (M_PI/180));
Eigen::Array2Xf cosi = cos(in * (M_PI/180));
out.resize(3, in.cols());
out << cosi.row(0) * cosi.row(1),
sine.row(0) * cosi.row(1),
sine.row(1);
}
There would still be a lot of optimization potential, e.g., calculating both sine and cosine of the same angle could share a lot of computation. And it is technically not necessary to store sine and cosi explicitly into temporaries (but Eigen is currently not able to automatically re-use common-sub expressions).
Also, the multiplication at the end could be vectorized better, if you store your input and output in row-major format (though the Eigen comma-initializer currently does not well with vectorization, it seems).

How to improve intensity contrast in image?

I have an image with very low intensity contrast from its background.
The first line between the two arrows is the line with low contrast.
The second line is ok. Please see in the below image.
The original image is as shown below.
I used the following method to enhance the contrast in Gray scale.
First the image is changed to Gray color and used the following method.
cv::Mat temp;
for (int i = 0; i < 1; i++) // number of iterations has to be adjusted
{
cv::threshold(image, temp, 0, 255, CV_THRESH_BINARY| CV_THRESH_OTSU);//
cv::bitwise_and(image, temp, image);
cv::normalize(image, image, 0, 255, cv::NORM_MINMAX, -1, temp);
}
I have image with a little bit higher in contrast in Gray scale, but is there any method better than this in Gray scale or Color?
I would look at histogram equalization, that might serve your needs. Basic (global) equalization or even adaptive can yield great results. Parameters will likely need to be tuned for the adaptive method (using the one from the docs example for now).
I get (global equalization - left; adaptive equalization - right):
Once the equalization is done, you might have better luck with thresholding (though your example is very low contrast):
From there, you can use standard contour/shape matching etc to try to find the location of your 1st black line.
Gotten from
import cv2
import matplotlib.pyplot as plt
import numpy as np
raw_img_load = cv2.imread('H1o8X.png')
imgr = cv2.cvtColor(raw_img_load,cv2.COLOR_BGR2GRAY)
clahe = cv2.createCLAHE(clipLimit=30.0, tileGridSize=(8,8))
imgray_ad = clahe.apply(imgr)#adaptive
imgray = cv2.equalizeHist(imgr)#global
res = np.hstack((imgray,imgray_ad))#so we can plot together
plt.imshow(res,cmap='gray')
plt.show()
ret,thresh = cv2.threshold(imgray_ad,150,255,type=cv2.THRESH_BINARY+cv2.THRESH_OTSU)
plt.imshow(thresh,cmap='gray')
plt.show()
EDIT: based on #Doleron's answer, for this particular problem I would recommend using fastNlMeansDenoising (applied before any histogram equalization). Note, however, that it can be a slow function for high-res images/time-sensitive image processing.
The #Antoine Zambelli answer is awsome and it is the correct one. Anyway, I dug some here and and tried to remove the noise previously with fastNlMeansDenoising to improve the final result:
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/photo.hpp"
using namespace cv;
using cv::CLAHE;
int main(int argc, char** argv) {
Mat srcImage = imread("H1o8X.png", CV_LOAD_IMAGE_GRAYSCALE);
imshow("src", srcImage);
Mat denoised;
fastNlMeansDenoising(srcImage, denoised, 10);
Mat image = denoised;
Ptr<CLAHE> clahe = createCLAHE();
clahe->setClipLimit(30.0);
clahe->setTilesGridSize(Size(8, 8));
Mat imgray_ad;
clahe->apply(image, imgray_ad);
Mat imgray;
cv::equalizeHist(image, imgray);
imshow("imgray_ad", imgray_ad);
imshow("imgray", imgray);
Mat thresh;
threshold(imgray_ad, thresh, 150, 255, THRESH_BINARY | THRESH_OTSU);
imshow("thresh", thresh);
Mat result;
Mat kernel = Mat::ones(8, 8, CV_8UC1);
erode(thresh, result, kernel);
imshow("result", result);
waitKey();
return 0;
}

C++ Eigen AlignedBox Transformations

I am trying to make my first steps with the C++ Eigen library. The Matrix functionality was very intuitive but I have some problems using the AlignedBox type from the Geometry module.
For an exercise I have to rotate an AlignedBox around a specific point and be able to translate it within a 2D plane using Eigen::Transform.
I have tried around for quite a while.
#include <iostream>
#include <eigen3/Eigen/Dense>
int main()
{
// create 1D AlignedBox
Eigen::MatrixXf sd1(1,1);
Eigen::MatrixXf sd2(1,1);
sd1 << 0;
sd2 << 3;
Eigen::AlignedBox1f box1(sd1, sd2);
// rotation of 45 deg
typedef Eigen::Rotation2D<float> R2D;
R2D r(M_PI/4.0);
// create transformation matrix with rotation of 45 deg
typedef Eigen::Transform< float, 2, Eigen::AffineCompact > SE2;
SE2 t;
t = r;
// how to apply transformation t to box1???
return 0;
}
I thought I have to multiply the AlignedBox with t.matrix() but since the Box is no matrix type and I did not find any useful build in function I have no idea how to apply the transformation. Any help would be appreciated
Note that result will be a 2D box. You can compute it by applying the affine transformation to the two 2D extremities, and updating the 2D box with the extend method, e.g.:
AlignedBox2f box2;
box2.extend(t * Vector2f(box1.min()(0), 0));
box2.extend(t * Vector2f(box1.max()(0), 0));
To apply another transformation to box2, you can use the same principle on the 4 corners of the box that you can get using the AlignedBox::corner method.

How to blur the outcome of a fragment shader?

I'm working on a shader that generates little clouds based on some mask images. Right now it works well, but i feel the result is missing something, and i thought a blur would be nice. I remember a basic blur algorithm where you have to apply a convolution with a matrix of norm 1 (the bigger the matrix the greater the result) and an image. The thing is, I don't know how to treat the current outcome of the shader as an image. So basically I want to keep the shader as is, but getting it blurry. Any ideas?, how can I integrate the convolution algorithm to the shader? Or does anyone know of other algorithm?
Cg code:
float Luminance( float4 Color ){
return 0.6 * Color.r + 0.3 * Color.g + 0.1 * Color.b;
}
struct v2f {
float4 pos : SV_POSITION;
float2 uv_MainTex : TEXCOORD0;
};
float4 _MainTex_ST;
v2f vert(appdata_base v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
sampler2D _MainTex;
sampler2D _Gradient;
sampler2D _NoiseO;
sampler2D _NoiseT;
float4 frag(v2f IN) : COLOR {
half4 nO = tex2D (_NoiseO, IN.uv_MainTex);
half4 nT = tex2D (_NoiseT, IN.uv_MainTex);
float4 turbulence = nO + nT;
float lum = Luminance(turbulence);
half4 c = tex2D (_MainTex, IN.uv_MainTex);
if (lum >= 1.0f){
float pos = lum - 1.0f;
if( pos > 0.98f ) pos = 0.98f;
if( pos < 0.02f ) pos = 0.02f;
float2 texCord = (pos, pos);
half4 turb = tex2D (_Gradient, texCord);
//turb.a = 0.0f;
return turb;
}
else return c;
}
It appears to me that this shader is emulating alpha testing between a backbuffer-like texture (passed via the sampler2D _MainTex) and a generated cloud luminance (represented by float lum) mapped onto a gradient. This makes things trickier because you can't just fake a blur and let alpha blending take care of the rest. You'll also need to change your alpha testing routine to emulate an alpha blend instead or restructure your rendering pipeline accordingly. We'll deal with blurring the clouds first.
The first question you need to ask yourself is if you need a screen-space blur. Seeing the mechanics of this fragment shader, I would think not -- you want to blur the clouds on the actual model. Given this, it should be sufficient to blur the underlying textures and result in a blurred result -- except you're emulating alpha clipping, so you'll get rough edges. The question is what to do about those rough edges. That's where alpha blending comes in.
You can emulate alpha blending by using a lerp (linear interpolation) between the turb color and c color with lerp() function (depending on which shader language you're using). You'll probably want something that looks like return lerp(c, turb, 1 - pos); instead of return turb; ... I'd expect you'll want to tweak this continually until you understand and start getting the results you want. (For example, you may prefer lerp(c, turb, 1 - pow(pos,4)))
In fact, you can try this last step (just adding the lerp) before modifying your textures to get an idea of what the alpha blending will do for you.
Edit: I hadn't considered the case where the _NoiseO and _NoiseT samplers were changing continually, so simply telling you to blur them was minimally useful advice. You can emulate blurring by using a multi-tap filter. The most simple way is to take uniformly spaced samples, weight them, and sum them together resulting in your final color. (Typically you'll want the weights themselves to sum to 1.)
This being said, you may or may not way to do this on the _NoiseO and _NoiseT textures themselves -- you may want to create a screen-space blur instead which may look more interesting to a viewer. In this case, the same concept applies, but you need to do the calculations for the offset coordinates for each tap and then perform a weighted summation.
For example if we were going with the first case and we wanted to sample from the _Noise0 sampler and blur it slightly, we could use this box filter (where all the weights are the same and sum to 1, thus performing an average):
// Untested code.
half4 nO = 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2( 0, 0))
+ 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2( 0, g_offset.y))
+ 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(g_offset.x, 0))
+ 0.25 * tex2D(_Noise0, IN.uv_MainTex + float2(g_offset.x, g_offset.y))
Alternatively, if we wanted the entire cloud output to appear blurry we'd wrap the cloud generation portion in a function and call it instead of tex2D() for the taps.
// More untested code.
half4 genCloud(float2 tc) {
half4 nO = tex2D (_NoiseO, IN.uv_MainTex);
half4 nT = tex2D (_NoiseT, IN.uv_MainTex);
float4 turbulence = nO + nT;
float lum = Luminance(turbulence);
float pos = lum - 1.0;
if( pos > 0.98f ) pos = 0.98f;
if( pos < 0.02f ) pos = 0.02f;
float2 texCord = (pos, pos);
half4 turb = tex2D (_Gradient, texCord);
// Figure out how you'd generate your alpha blending constant here for your lerp
turb.a = ACTUAL_ALPHA;
return turb;
}
And the multi-tap filtering would look like:
// And even more untested code.
half4 cloudcolor = 0.25 * genCloud(IN.uv_MainTex + float2( 0, 0))
+ 0.25 * genCloud(IN.uv_MainTex + float2( 0, g_offset.y))
+ 0.25 * genCloud(IN.uv_MainTex + float2(g_offset.x, 0))
+ 0.25 * genCloud(IN.uv_MainTex + float2(g_offset.x, g_offset.y))
return lerp(c, cloudcolor, cloudcolor.a);
However doing this is going to be relatively slow for calculations if you make the cloud function too complex. If you're bound by raster operations and texture reads (transferring texture/buffer data to and from memory) chances are this won't matter much unless you use a much more advanced blurring technique (such successful downsampling through ping-ponged buffers, useful for blurs/filters that are expensive because they have lots of taps). But performance is another entire consideration from just getting the look you want.

what 2 & 3 mean in this and how can i change them CvMat* rot = cvCreateMat(2,3,CV_32FC1)

What do 2 & 3 mean in this and how can I change them?
CvMat* rot = cvCreateMat(2,3,CV_32FC1)
When I change these two values I get an openCV GUI error handler.
size of input arguments do not match()
in function cvConvertScale.\cxconvert.cpp(1601)
I want to understand what that means
Update:
The code is:
#include <cv.h>
#include <highgui.h>
int main()
{
CvMat* rot = cvCreateMat(2,3,CV_32FC1);
IplImage *src, *dst;
src=cvLoadImage("doda.jpg");
// make acopy of gray image(src)
dst = cvCloneImage( src );
dst->origin = src->origin;
// make dstof zeros
cvZero( dst );
// Compute rotation matrix
double x=0.0;
// loop to get rotation from 0 to 360 by 4 press on anykey
for(int i=1;i<=5;i++)
{
CvPoint2D32f center = cvPoint2D32f(src->width/2,src->height/2);
double angle = 0+x;
double scale = 0.6;
cv2DRotationMatrix( center, angle, scale, rot );
// Do the transformation
cvWarpAffine( src, dst, rot);
cvNamedWindow( "Affine_Transform", 1 );
cvShowImage( "Affine_Transform", dst );
if (i<=4)
x=x+90.0;
else
x=0.0;
cvWaitKey();
}
cvReleaseImage( &dst );
cvReleaseMat( &rot );
return 0;
}
2 and 3 are the row and column counts of the matrix you're creating.
From Introduction to programming with OpenCV:
Allocate a matrix:
CvMat* cvCreateMat(int rows, int cols, int type);
type: Type of the matrix elements. Specified in form
CV_<bit_depth>(S|U|F)C<number_of_channels>. E.g.: CV_8UC1 means an
8-bit unsigned single-channel matrix, CV_32SC2 means a 32-bit signed
matrix with two channels.
Example:
CvMat* M = cvCreateMat(4,4,CV_32FC1);
Changing them is as simple as substituting different values. But I guess you should already know that.
2 = number of rows and 3 = number of columns in your matrix, rot.
Can you post the entire code? Or maybe tell us what you want to achieve? Are you trying to rotate an image?
Also, I'd recommend upgrading to OpenCV 2.0 which has a C++ interface. With the new version, you can extensively use the Mat class which handles everything (matrices,images,etc.) and makes things much simpler.
You get an error using any other shape than 2x3 because it is then meaningless for opencv when you use rot for rotation.
Take a look at Jacob's answer.
He describes the rotation matrix components in details.

Resources