What is the exact mask for Sobel (Gx and Gy)? What I saw is there are two types on how people wrote it such as below,
Style 1
Gx = [-1 -2 -1
0 0 0
1 2 1]
Gy = [-1 0 1
-2 0 2
-1 0 1]
Style 2
Gx = [-1 0 1
-2 0 2
-1 0 1]
Gy = [-1 -2 -1
0 0 0
1 2 1]
Edited
#Aurelis
In Matlab --> (row x col)
In OpenCV --> (col x row)
However, the diagram below is correct for both
-->column
^
|row
|
Probably in Matlab will output Gx == horizontal edge, Gy == vertical edge if Style 1 is used and Gx == horizontal edge, Gy ==vertical edge if Style 2 is used. Both will produce same output (internal operation might be different due to the col-row major order).
#Abhishek You are using style 1 to compute the horizontal and vertical edge? and Gx correspond to horizontal edge while Gy correspond to vertical edge?
Does that mean style 2 is complement of that? For eg. computing Gx will gives vertical edge and Gy gives horizontal edge?
Style 2 is correct. However, using both the styles we will get the same result, as the kernels are convolved with the image
Gx = [-1 -2 -1
0 0 0 <--- will extract features in Y direction and not in X direction.
1 2 1]
Gy = [-1 0 1
-2 0 2 <--- will extract features in X direction and not in Y direction.
-1 0 1]
This can be verified by using a simple 2-D convolution.
original image:
using Style1,Gx:
using style1, Gy:
If you are using mathematical notation, the proper mask is Style 2 (see here).
Your confusion may be arising from the difference between matrices in MATLAB and OpenCV. MATLAB matrices are specified in column-major order, while OpenCV matrices are specified in row-major order.
Style 1 is representing the Sobel mask in a column-major fashion, and Style 2 represents the same mask in row-major order.
Related
I would like to ask that what's the origin pixel in the image coordinate system used in poly2mask function in MATLAB.
It's been asked by others in OpenCV and wonder if it's the same in MATLAB. Let me repeat the person's question:
In general there may be two kinds of such systems, for the first (0,0) is defined as the center of the upper left pixel, which means the upper left corner of the upper left pixel is (-0,5, -0.5) and the center of the image is ((cols - 1) / 2, (rows - 1) / 2).
The other one is that (0,0) is the upper left corner of the upper left pixel, so the center of the upper left pixel is (0.5, 0.5) and the center of the image is (cols / 2, rows / 2).
My question is which system is adopted in poly2mask function in MATLAB?
MATLAB uses 1-based indexing, not 0-based indexing. Consequently, the top-left pixel has the center at (1,1). poly2mask follows the same convention, they're pretty consistent at the MathWorks.
This experiment shows this:
>> poly2mask([0.8,1.2,1.2,0.8],[0.8,0.8,1.2,1.2],3,5)
ans =
3×5 logical array
1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
I drew a polygon closely around the (1,1) point, and the mask shows the top-left pixel marked. If you draw a polygon around the (0.5,0.5) point, no pixel is marked:
>> poly2mask([0.8,1.2,1.2,0.8]-0.5,[0.8,0.8,1.2,1.2]-0.5,3,5)
ans =
3×5 logical array
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
The one thing that is easy to make mistakes with is the order of the axes. The first index is y the second is x. But many functions in the Image Processing Toolbox (and elsewhere) take coordinates as (x,y), rather than as (y,x). For example:
>> poly2mask([0.8,1.2,1.2,0.8]+1,[0.8,0.8,1.2,1.2],3,5)
ans =
3×5 logical array
0 1 0 0 0
0 0 0 0 0
0 0 0 0 0
Here, x = 2 and y = 1, and the pixel ans(1,2) is set!
Consider the following MATLAB code:
C = [ 0 0 0 0 0
0 1 2 1 0
0 2 4 2 0
0 1 2 1 0
0 0 0 0 0 ];
pcolor( C );
shading interp;
axis square
Note that C is invariant under 90 degree rotations. Also note this sentence from the help for pcolor:
With shading interp, each cell is colored by bilinear interpolation of the colors at its four vertices, using all elements of C.
However, the plotted image is as follows:
Note that the image is not invariant under 90 degree rotations (consider e.g. the four corners). Now, unless I horribly misunderstand bilinear interpolation, this must be wrong. MATLAB seems to be interpolating on triangles, which is not the same as bilinear interpolation.
Is there any way of working round this MATLAB bug, and getting correct bilinear interpolation? (Other than manually interpolating additional points myself, which still would not cure the issue if one zoomed in far enough.)
I remember having read a few threads concerning this weird behavior in the official Matlab forums, in the past. Unfortunately, I found none right now with a quick search. Anyway... you aren't the first used pointing out that shading interp, used in combination with with pcolor, behaves in a weird manner, creating shapes that don't reflect the underlying data.
The main problem is that shading interp interpolates between data points without caring about how sensible your grid is to smoothing. If you want a result that doesn't look jagged, you have to provide data sampled at a higher resolution:
C = [
0 0 0 0 0
0 1 2 1 0
0 2 4 2 0
0 1 2 1 0
0 0 0 0 0
];
C = interp2(C,5,'cubic');
pcolor(C);
shading interp;
axis square;
This produces an amazing result, and the output doesn't show any artifact or asymmetry:
I Create a plot using imagesc. The X/Y axis are longitude and latitude respectively. The Z values are the intensity of the images for the image shown below. What I'd like to be able to do is calculate the area in each of the polygons shown. Can anybody recommend a straightforward (or any) method in accomplishing this?
EDIT
Forgot to include image.
Below is a toy example. It hinges on the assumption that the Z values are different inside the objects from outside (here: not 0). Also here I assume a straight divider at column 4, but the same principle (applying a mask) can be applied with other boundaries. This also assumes that the values are equidistant along x and y axes, but the question does not state the opposite. If that is not the case, a little more work using bsxfun is needed.
A = [0 2 0 0 0 2 0
3 5 3 0 1 4 0
1 4 0 0 3 2 3
2 3 0 0 0 4 2
0 2 6 0 1 6 1
0 3 0 0 2 3 0
0 0 0 0 0 0 0];
area_per_pix = 0.5; % or whatever
% plot it
cm = parula(10);
cm(1, :) = [1 1 1];
figure(1);
clf
imagesc(A);
colormap(cm);
% divider
dv_idx = 4;
left_object = A(:, 1:(dv_idx-1));
left_mask = left_object > 0; % threshold object
num_pix_left = sum(left_mask(:));
% right object, different method
right_mask = repmat((1:size(A, 2)) > dv_idx, size(A, 1), 1);
right_mask = (A > 0) & right_mask;
num_pix_right = sum(right_mask(:));
fprintf('The left object is %.2f units large, the right one %.2f units.\n', ...
num_pix_left * area_per_pix, num_pix_right * area_per_pix);
This might be helpful: http://se.mathworks.com/matlabcentral/answers/35501-surface-area-from-a-z-matrix
He has not used imagesc, but it's a similar problem.
How can I calculate in MatLab similarity transformation between 4 points in 3D?
I can calculate transform matrix from
T*X = Xp,
but it will give me affine matrix due to small errors in points coordinates. How can I fit that matrix to similarity one? I need something like fitgeotrans, but in 3D
Thanks
If I am interpreting your question correctly, you seek to find all coefficients in a 3D transformation matrix that will best warp one point to another. All you really have to do is put this problem into a linear system and solve. Recall that warping one point to another in 3D is simply:
A*s = t
s = (x,y,z) is the source point, t = (x',y',z') is the target point and A would be the 3 x 3 transformation matrix that is formatted such that:
A = [a00 a01 a02]
[a10 a11 a12]
[a20 a21 a22]
Writing out the actual system of equations of A*s = t, we get:
a00*x + a01*y + a02*z = x'
a10*x + a11*y + a12*z = y'
a20*x + a21*y + a22*z = z'
The coefficients in A are what we need to solve for. Re-writing this in matrix form, we get:
[x y z 0 0 0 0 0 0] [a00] [x']
[0 0 0 x y z 0 0 0] * [a01] = [y']
[0 0 0 0 0 0 x y z] [a02] [z']
[a10]
[a11]
[a12]
[a20]
[a21]
[a22]
Given that you have four points, you would simply concatenate rows of the matrix on the left side and the vector on the right
[x1 y1 z1 0 0 0 0 0 0] [a00] [x1']
[0 0 0 x1 y1 z1 0 0 0] [a01] [y1']
[0 0 0 0 0 0 x1 y1 z1] [a02] [z1']
[x2 y2 z2 0 0 0 0 0 0] [a10] [x2']
[0 0 0 x2 y2 z2 0 0 0] [a11] [y2']
[0 0 0 0 0 0 x2 y2 z2] [a12] [z2']
[x3 y3 z3 0 0 0 0 0 0] * [a20] = [x3']
[0 0 0 x3 y3 z3 0 0 0] [a21] [y3']
[0 0 0 0 0 0 x3 y3 z3] [a22] [z3']
[x4 y4 z4 0 0 0 0 0 0] [x4']
[0 0 0 x4 y4 z4 0 0 0] [y4']
[0 0 0 0 0 0 x4 y4 z4] [z4']
S * a = T
S would now be a matrix that contains your four source points in the format shown above, a is now a vector of the transformation coefficients in the matrix you want to solve (ordered in row-major format), and T would be a vector of target points in the format shown above.
To solve for the parameters, you simply have to use the mldivide operator or \ in MATLAB, which will compute the least squares estimate for you. Therefore:
a = S^{-1} * T
As such, simply build your matrix like above, then use the \ operator to solve for your transformation parameters in your matrix. When you're done, reshape T into a 3 x 3 matrix. Therefore:
S = ... ; %// Enter in your source points here like above
T = ... ; %// Enter in your target points in a right hand side vector like above
a = S \ T;
similarity_matrix = reshape(a, 3, 3).';
With regards to your error in small perturbations of each of the co-ordinates, the more points you have the better. Using 4 will certainly give you a solution, but it isn't enough to mitigate any errors in my opinion.
Minor Note: This (more or less) is what fitgeotrans does under the hood. It computes the best homography given a bunch of source and target points, and determines this using least squares.
Hope this answered your question!
The answer by #rayryeng is correct, given that you have a set of up to 3 points in a 3-dimensional space. If you need to transform m points in n-dimensional space (m>n), then you first need to add m-n coordinates to these m points such that they exist in m-dimensional space (i.e. the a matrix in #rayryeng becomes a square matrix)... Then the procedure described by #rayryeng will give you the exact transformation of points, you then just need to select only the coordinates of the transformed points in the original n-dimensional space.
As an example, say you want to transform the points:
(2 -2 2) -> (-3 5 -4)
(2 3 0) -> (3 4 4)
(-4 -2 5) -> (-4 -1 -2)
(-3 4 1) -> (4 0 5)
(5 -4 0) -> (-3 -2 -3)
Notice that you have m=5 points which are n=3-dimensional. So you need to add coordinates to these points such that they are n=m=5-dimensional, and then apply the procedure described by #rayryeng.
I have implemented a function that does that (find it below). You just need to organize the points such that each of the source-points is a column in a matrix u, and each of the target points is a column in a matrix v. The matrices u and v are going to be, thus, 3 by 5 each.
WARNING:
the matrix A in the function may require A LOT of memory for moderately many points nP, because it has nP^4 elements.
To overcome this, for square matrices u and v, you can simply use T=v*inv(u) or T=v/u in MATLAB notation.
The code may run very slowly...
In MATLAB:
u = [2 2 -4 -3 5;-2 3 -2 4 -4;2 0 5 1 0]; % setting the set of source points
v = [-3 3 -4 4 -3;5 4 -1 0 -2;-4 4 -2 5 -3]; % setting the set of target points
T = findLinearTransformation(u,v); % calculating the transformation
You can verify that T is correct by:
I = eye(5);
uu = [u;I((3+1):5,1:5)]; % filling-up the matrix of source points so that you have 5-d points
w = T*uu; % calculating target points
w = w(1:3,1:5); % recovering the 3-d points
w - v % w should match v ... notice that the error between w and v is really small
The function that calculates the transformation matrix:
function [T,A] = findLinearTransformation(u,v)
% finds a matrix T (nP X nP) such that T * u(:,i) = v(:,i)
% u(:,i) and v(:,i) are n-dim col vectors; the amount of col vectors in u and v must match (and are equal to nP)
%
if any(size(u) ~= size(v))
error('findLinearTransform:u','u and v must be the same shape and size n-dim vectors');
end
[n,nP] = size(u); % n -> dimensionality; nP -> number of points to be transformed
if nP > n % if the number of points to be transform exceeds the dimensionality of points
I = eye(nP);
u = [u;I((n+1):nP,1:nP)]; % then fill up the points to be transformed with the identity matrix
v = [v;I((n+1):nP,1:nP)]; % as well as the transformed points
[n,nP] = size(u);
end
A = zeros(nP*n,n*n);
for k = 1:nP
for i = ((k-1)*n+1):(k*n)
A(i,mod((((i-1)*n+1):(i*n))-1,n*n) + 1) = u(:,k)';
end
end
v = v(:);
T = reshape(A\v, n, n).';
end
I have an image in MATLAB:
y = rgb2gray(imread('some_image_file.jpg'));
and I want to do some processing on it:
pic = some_processing(y);
and find the local maxima of the output. That is, all the points in y that are greater than all of their neighbors.
I can't seem to find a MATLAB function to do that nicely. The best I can come up with is:
[dim_y,dim_x]=size(pic);
enlarged_pic=[zeros(1,dim_x+2);
zeros(dim_y,1),pic,zeros(dim_y,1);
zeros(1,dim_x+2)];
% now build a 3D array
% each plane will be the enlarged picture
% moved up,down,left or right,
% to all the diagonals, or not at all
[en_dim_y,en_dim_x]=size(enlarged_pic);
three_d(:,:,1)=enlarged_pic;
three_d(:,:,2)=[enlarged_pic(2:end,:);zeros(1,en_dim_x)];
three_d(:,:,3)=[zeros(1,en_dim_x);enlarged_pic(1:end-1,:)];
three_d(:,:,4)=[zeros(en_dim_y,1),enlarged_pic(:,1:end-1)];
three_d(:,:,5)=[enlarged_pic(:,2:end),zeros(en_dim_y,1)];
three_d(:,:,6)=[pic,zeros(dim_y,2);zeros(2,en_dim_x)];
three_d(:,:,7)=[zeros(2,en_dim_x);pic,zeros(dim_y,2)];
three_d(:,:,8)=[zeros(dim_y,2),pic;zeros(2,en_dim_x)];
three_d(:,:,9)=[zeros(2,en_dim_x);zeros(dim_y,2),pic];
And then see if the maximum along the 3rd dimension appears in the 1st layer (that is: three_d(:,:,1)):
(max_val, max_i) = max(three_d, 3);
result = find(max_i == 1);
Is there any more elegant way to do this? This seems like a bit of a kludge.
bw = pic > imdilate(pic, [1 1 1; 1 0 1; 1 1 1]);
If you have the Image Processing Toolbox, you could use the IMREGIONALMAX function:
BW = imregionalmax(y);
The variable BW will be a logical matrix the same size as y with ones indicating the local maxima and zeroes otherwise.
NOTE: As you point out, IMREGIONALMAX will find maxima that are greater than or equal to their neighbors. If you want to exclude neighboring maxima with the same value (i.e. find maxima that are single pixels), you could use the BWCONNCOMP function. The following should remove points in BW that have any neighbors, leaving only single pixels:
CC = bwconncomp(BW);
for i = 1:CC.NumObjects,
index = CC.PixelIdxList{i};
if (numel(index) > 1),
BW(index) = false;
end
end
Alternatively, you can use nlfilter and supply your own function to be applied to each neighborhood.
This "find strict max" function would simply check if the center of the neighborhood is strictly greater than all the other elements in that neighborhood, which is always 3x3 for this purpose. Therefore:
I = imread('tire.tif');
BW = nlfilter(I, [3 3], #(x) all(x(5) > x([1:4 6:9])) );
imshow(BW)
In addition to imdilate, which is in the Image Processing Toolbox, you can also use ordfilt2.
ordfilt2 sorts values in local neighborhoods and picks the n-th value. (The MathWorks example demonstrates how to implemented a max filter.) You can also implement a 3x3 peak finder with ordfilt2 with the following logic:
Define a 3x3 domain that does not include the center pixel (8 pixels).
>> mask = ones(3); mask(5) = 0 % 3x3 max
mask =
1 1 1
1 0 1
1 1 1
Select the largest (8th) value with ordfilt2.
>> B = ordfilt2(A,8,mask)
B =
3 3 3 3 3 4 4 4
3 5 5 5 4 4 4 4
3 5 3 5 4 4 4 4
3 5 5 5 4 6 6 6
3 3 3 3 4 6 4 6
1 1 1 1 4 6 6 6
Compare this output to the center value of each neighborhood (just A):
>> peaks = A > B
peaks =
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 0 0 0 0
or, just use the excellent: extrema2.m