How do I find x and y gradient in OpenCV? - c++

I am trying to implement a custom version of Histogram of Oriented Gradients. My gradient kernel is [-1.2 0 1.2]. This kernel has to be applied in x and y directions (along rows and along columns), to find the image gradients in x and y directions Gx and Gy.
In Matlab this would be something like
hx = [-1.2 0 1.2]
hy = hx' %transpose
Gx = imfilter(double(I),hx) %Gx is the gradient along x, I is the image
Gy = imfilter(double(I),hy) %Gy is the gradient along y
How do I do this in OpenCV ? I looked at create createSeparableLinearFilter, but it seems to give some sort of sum of Gx and Gy. I need to find Gx and Gy separately.
I am looking for something like
Ptr<FilterEngine> Fx = createRowFilter(...);
Ptr<FilterEngine> Fy = createColumnFilter(...);
Fx->apply(img, Gx, ...); //Gx is x gradient, Gx and Gy are float or double
Fy->apply(img, Gy, ...); //Gy is y gradient
Of course this can be done by writing my own for loop, visiting every pixel, but I was wondering whether there is any OpenCV way to do this.

I think you are looking for
filter2D
use it each time with a different kernel.
Solution from Mathai:
float kernelY[9] = {0,-1.0,0,0,0,0,0,1.0,0};
float kernelX[9] = {0,0,0,-1.0,0,1.0,0,0,0};
Mat filterY(3, 3, CV_32F, kernelY);
Mat filterX(3, 3, CV_32F, kernelX);
filter2D(img,dsty,-1 ,filterY, Point( -1, -1 ),0, BORDER_DEFAULT );
filter2D(img,dstx,-1 ,filterX, Point( -1, -1 ),0, BORDER_DEFAULT );

Follow this tutorial to make your own custom kernels. I think you need to make an NxN kernel for OpenCV to recognize it properly (basically it will be hx; hx; hx for Gx).
HTH

Related

Opencv filter2D

I am using opencv in order to implement potential fields for a game system.
I have a cv::mat of map size and I seed it with data describing how vulnerable my units are. The matrix is using 32 bit integers and the values range from 0 to about 1200.
I then use cv::filter2D in order to find the best position for building a trurret.
int kernelSize = (turretRange * 2) + 1;
cv::Mat circleKernel = cv::Mat( kernelSize, kernelSize, __potentialDataType, cv::Scalar::all(0) );
cv::circle(circleKernel, cv::Point(turretRange + 1, turretRange + 1), turretRange, 1, -1, 8 );
cv::filter2D( vulnerabilityMap, buildMap, -1, circleKernel, cv::Point(-1,-1) );
I then calculate the min and max value positions of the buildMap, where max should give me the best position for my turret.
double min2, max2;
cv::Point min_loc2, max_loc2;
cv::minMaxLoc(buildMap, &min2, &max2, &min_loc2, &max_loc2 );
What happens is that I get the optimal position in x while the y is turretRange short.
That is, the max_loc2 is (optimal x, optimal y - turretRange)
Any hint on what I am doing wrong would be much appreciated.

Converting Cartesian image to polar, appearance differences

I'm trying to do a polar transform on the first image below and end up with the second. However my result is the third image. I have a feeling it has to do with what location I choose as my "origin" but am unsure.
radius = sqrt(width**2 + height**2)
nheight = int(ceil(radius)/2)
nwidth = int(ceil(radius/2))
for y in range(0, height):
for x in range(0, width):
t = int(atan(y/x))
r = int(sqrt(x**2+y**2)/2)
color = getColor(getPixel(pic, x, y))
setColor( getPixel(radial,r,t), color)
There are a few differences / errors:
They use the centre of the image as the origin
They scale the axis appropriately. In your example, you're plotting your angle (between 0 and in your case, pi), instead of utilising the full height of the image.
You're using the wrong atan function (atan2 works a lot better in this situation :))
Not amazingly important, but you're rounding unnecessarily quite a lot, which throws off accuracy a little and can slow things down.
This is the code combining my suggested improvements. It's not massively efficient, but it should hopefully work :)
maxradius = sqrt(width**2 + height**2)/2
rscale = width / maxradius
tscale = height / (2*math.pi)
for y in range(0, height):
dy = y - height/2
for x in range(0, width):
dx = x - width/2
t = atan2(dy,dx)%(2*math.pi)
r = sqrt(dx**2+dy**2)
color = getColor(getPixel(pic, x, y))
setColor( getPixel(radial,int(r*rscale),int(t*tscale)), color)
In particular, it fixes the above problems in the following ways:
We use dx = x - width / 2 as a measure of distance from the centre, and similarly with dy. We then use these in replace of x, y throughout the computation.
We will have our r satisfying 0 <= r <= sqrt( (width/2)^2 +(height/2)^2 ), and our t eventually satisfying 0 < t <= 2 pi so, I create the appropriate scale factors to put r and t along the x and y axes respectively.
Normal atan can only distinguish based on gradients, and is computationally unstable near vertical lines... Instead, atan2 (see http://en.wikipedia.org/wiki/Atan2) solves both problems, and accepts (y,x) pairs to give an angle. atan2 returns an angle -pi < t <= pi, so we can find the remainder modulo 2 * math.pi to it to get it in the range 0 < t <= 2pi ready for scaling.
I've only rounded at the end, when the new pixels get set.
Any questions, just ask!

calculate sphere in openGL resulted the circle does not aligned

I want to make a sphere without the gluSphere method, and I try to calculate the sphere with this code
void drawCircle(double x, double y, double r)
{
glBegin( GL_QUAD_STRIP );
for(int i=0;i<=360;i++){
glVertex3d(x+sin(i)*r,y+cos(i)*r,-5.0);
}
glEnd();
}
void drawSphere(double x,double y,double r){
glLoadIdentity();
glColor3d(1,0,0);
for(int j=0;j<180;j++){
glTranslated(0,0,r/180);
drawCircle(x,y,r*sin(j));
}
}
The result was like this
But the result was the circle that I made isn't aligned well. Is there any proper calculation so I can make the sphere right?
There is a difference between a Sphere and a Circle. a Circle is a 2 dimensional shape and Sphere is its 3D counterpart. from your code you are not generating points for a sphere but for a cylinder as the z is constant i.e. -0.5. for Sphere all 3 should change within the ranges
if the center is at (xc, yc, zc) then
x => (0-xc) < x < (0+xc)
y => (0-yc) < y < (0+yc)
z => (0-zc) < z < (0+zc)
A sphere may be defined parametrically in terms of (u,v)
x = xo + r cos(theta) cos(phi)
y = yo + r cos(theta) sin(phi)
z = zo + r sin(theta)
Your points should be a the valid combination of x, y z. that means your points can be generated using 3 loops. Only then you would have correct points for sphere.
Also since you ar eusing QuadStrip, the array you pass must have the order of points in counter clockwise or you will not be able to get the correct shape.
Instead of using sin() & cos() you should use the sphere equation: x*x + y*y + z*z = c*c
I agree with #SimpleFellow that another approach would be better. But let me show you what else is not quite how you wanted it in your code.
Your main problem is that you pass degrees to the sin and cos functions instead of radians.
The easiest remedy is to just define a function deg2rad or functions like
double sind(double val){return std::sin(val*M_PI / 180.0);}
double cosd(double val){return std::cos(val*M_PI / 180.0);}
Also, the distance between rings should be
//glTranslated(0,0,r/180); //equidistance will give you something like a pyramid
glTranslated(0, 0, r*(cosd(j)-cosd(j-1));
then the result from your code looks something like this:
When you reduce the sample points you see what's happening:
Then you'll see that you're connecting 4 successive points in a ring to a quad, which is not what you want, so if you change glBegin( GL_QUAD_STRIP ); to glBegin( GL_LINE_STRIP ) you'll see this:
If you increase the points to 360 again then you'll have this:
So now you have a sphere made of circles, but I assume that you wanted a sphere with a surface, for that have a look at this question.

Sobel Edge Detection, edge orientation

I've implemented a Sobel Edge Detector and had some questions about computing edge orientations.
I'm using this function to compute edge intensities after having done the sobel kernel convolution.
Gxy = sqrt( pow(Gx, 2) + pow(Gy,2) )
Where Gx is sum of the convolution for the sobel kernel in the X direction and Gy is sum of the convolution for the sobel kernel in the Y direction. (note the sobel kernel in the X and Y direction are different kernels)
Y kernel:
1 2 1
0 0 0
-1 -2 -1
X kernel:
-1 0 1
-2 0 2
-1 0 1
when I try to compute the edge orientation (theta is in degrees), I'm using the following rules:
if Gy == 0 and Gx == 0, then theta = 0
if Gy != 0 and Gx == 0, then theta = 90
otherwise, theta = (arctan( Gy / Gx ) * 180) / PI
all my documentation is telling me the angles should be > 0 and < 360 and I continue to get edges with negative value orientations. Is there something I'm doing incorrectly when computing theta or my convolution? Or should i just add 360 or 180 to negative theta values?
thanks in advance,
It's hard to answer your question precisely because you haven't mentioned exactly how you calculate the arctan function.
For example, if you're using the standard library's atan function, then negative angles are to be expected.
Furthermore, you'll notice that atan with a single argument can only ever return values in the first and fourth quadrants (because, for example, tan 45 == tan 225 when using degrees).
If you really want an angle in one of the four quadrants (you should really ask yourself if this matters for your application), then have a look at atan2.
If you are using OpenCV, C++ you could do the following:
Mat gray = some grayscale image;
Calculate edge gradients in x/y axis'
Mat dx(gray.rows, gray.cols, CV_16SC1);
Mat dy(gray.rows, gray.cols, CV_16SC1);
int aperture_size=3;
Sobel(gray, dx, CV_32FC1, 1, 0, aperture_size, 1, 0, BORDER_REPLICATE);
Sobel(gray, dy, CV_32FC1, 0, 1, aperture_size, 1, 0, BORDER_REPLICATE);
Calculate the angle and magnitude using OpenCV's cartToPolar
Mat Mag(gray.size(), CV_32FC1);
Mat Angle(gray.size(), CV_32FC1);
cartToPolar(dx, dy, Mag, Angle, true);

Why is iplRotate() not giving me correct results?

sigh I'm sorry to say that I'm using Intel IPL (Image Processing Library) in some image processing code I'm working on. This is the tale of my struggle with getting my images to rotate correctly.
I have a source image. It has a size (w, h) which is not necessarily square.
It is going to be rotated by angle theta.
I've calculated the output size required to fit an image of size (w, h) rotated by angle theta. This size is (dw, dh). I've allocated a destination buffer with that size.
I want to rotate the source image by angle theta about the source image's center (w/2, h/2) and have that rotated image be centered in my destination buffer.
iplRotate() takes 2 shift parameters, xShift and yShift, which indicate the distance the image should be shifted along the x and y axis after the rotate is performed.
The problem is I cannot get iplRotate to center the rotated image in the destination image. It's always off center.
My best guess for what xShift and yShift should be is the following:
xShift = dw - w
yShift = dh - h
But this doesn't work, and I'm not sure what else to do to calculate xShift and yShift. Does anyone have any suggestions for how to use iplRotate to do what I want?
One last bit of info:
I've attempted to use iplGetRotateShift() to calculate xShift and yShift, again, to no avail. I would imagine that this would work:
iplGetRotateShift(dw / 2.0, dh / 2.0, theta, &xShift, &yShift);
But it does not.
Edit:
I rewrote the code using Intel IPP 6.0 instead of IPL and I'm seeing identical wrong results. I can't imagine that Intel got rotation wrong in 2 different libraries, so I must be doing something wrong.
Edit:
I tried the following (IPP) code that Dani van der Meer suggested:
xShift = (dw - w) / 2.0;
yShift = (dh - h) / 2.0;
ippiAddRotateShift(w / 2.0, h / 2.0, angle, &xShift, &yShift);
Unfortunately, still no luck. That does not work either.
When using iplGetRotateShift you need to specify the center of rotation in the source image. This will work well if the size of the source and destination image is the same.
In your case you want an extra shift to center the image in your destination image:
xShift = (dw - w) / 2.0;
yShift = (dh - h) / 2.0;
To combine the two shift you need to use ippiAddRotateShift instead of ippiGetRotateShift.
Note: These functions refer to the IPP library version 5.3 (the version I have). I am not sure that AddRotateShift is available in IPL. But you mentioned in the question that you tried the same using IPP, so hopefully you can use IPP instead of IPL.
You get something like this
xShift = (dw - w) / 2.0;
yShift = (dh - h) / 2.0;
ippiAddRotateShift(w / 2.0, h / 2.0, angle, &xShift, &yShift);
If you use these shifts in the call to ippiRotate the image should be centered in the destination image.
I hope this helps.
EDIT:
Here is the code I used to test (the change from w to dw and h to dh and the rotation angle are just random):
//Ipp8u* dst_p; Initialized somewhere else in the code
//Ipp8u* src_p; Initialized somewhere else in the code
int w = 1032;
int h = 778;
int dw = w - 40; // -40 is just a random change
int dh = h + 200; // 200 is just a random change
int src_step = w * 3;
int dst_step = dw * 3;
IppiSize src_size = { w, h };
IppiRect src_roi = { 0, 0, w, h };
IppiRect dst_rect = { 0, 0, dw, dh };
double xShift = ((double)dw - (double)w) / 2.0;
double yShift = ((double)dh - (double)h) / 2.0;
ippiAddRotateShift((double)w / 2, (double)h / 2, 37.0, &xShift, &yShift);
ippiRotate_8u_C3R(src_p, src_size, src_step, src_roi,
dst_p, dst_step, dst_rect, 37.0, xShift, yShift, IPPI_INTER_NN);
I've never used (or heard of) IPL before, so I'm just merely guessing what the API does. But if iplRotate rotates about (0, 0) and if iplGetRotateShift does similarly, why not try rotating the other 3 corners of your original "box" (ignoring (0, 0) since that stays put): (w, 0), (0, h), and (w, h).
Your result will be a new box with some negative values. You want to "shift back" so whatever negative values you have will become zero, if you get what I mean.
Based on my reading of the documentation I think you're using iplGetRotateShift wrong. In particular, I think you need to specify the center of rotation in the source image, not the destination image, thus:
iplGetRotateShift( w / 2.0, h / 2.0, angle, &xShift, &yShift );
If it's still not working for you then can we confirm that the assumptions are valid? In particular point 3. where you calculate dw and dh.
Mathematically
dw = w * |cos(theta)| + h * |sin(theta)|
dh = h * |cos(theta)| + w * |sin(theta)|
so if theta = pi/6 say, then if w = 100 and h = 150 then presumably dw = 162?
Does the incorrect position you're getting vary with theta? Presumably it works with theta=0? What about theta=pi/2 and theta=pi/4?