I want to change the diagonal values if equal to 1.
is it possible to look for diagonals and change the values in this particular diagonal to another value.
For example:
X =
1 1 1 0
1 1 0 1
1 0 1 1
0 1 1 1
I want to change this diagonal :
1
0
1
to
2.2
0
2.2
I tried:
XX(logical(eye(size(XX)))) = 2
but this will change all the values not only ones.
Could you please explain how to do that for other diagonals?
The function diag is useful to manipulate diagonals. It only extracts a diagonal from a matrix, or forms a new matrix given a vector of diagonal elements. But with appropriate arithmetic this is sufficient:
X = [1 1 1 0
1 1 0 1
1 0 1 1
0 1 1 1];
k = 2; % which diagonal to change
d = diag(X,k); % the old diagonal
n = d;
n(n==1) = 2.2; % the new diagonal values
X = X - diag(d,k) + diag(n,k); % subtract old values from diagonal, add new ones
Output:
X =
1.00000 1.00000 2.20000 0.00000
1.00000 1.00000 0.00000 2.20000
1.00000 0.00000 1.00000 1.00000
0.00000 1.00000 1.00000 1.00000
Here comes a solution only using linear indexing. I wanted to avoid generating additional matrices. Nothing's wrong with Cris Luengo's answer, that was just for fun.
% Input.
X = [1 1 1 0
1 1 0 1
1 0 1 1
0 1 1 1]
% Which diagonal to change.
k = 2;
% Determine dimension.
dim = size(X, 1);
% Calculate indices of diagonal elements.
idx = (max(abs(k), k * dim) + 1):(dim + 1):numel(X);
idx = idx(1:end+min(k+1, 0));
% Replace diagonal elements with new value.
X(idx(X(idx) == 1)) = 2.2
Output:
X =
1.00000 1.00000 2.20000 0.00000
1.00000 1.00000 0.00000 2.20000
1.00000 0.00000 1.00000 1.00000
0.00000 1.00000 1.00000 1.00000
Lets assume I have a logical matrix A (about 1000x1000 size) and want to find for each element the euclidean distance to the nearest TRUE value. How can that be done fast in Matlab?
E.g if i have matrix A:
A = [1 0 0 0
0 1 1 1
0 0 0 0
0 0 1 0]
Then what i want is:
B = [0 1 1 1
1 0 0 0
1.41 1 1 1
2 1 0 1]
One possibility would be imdilate(), but then i would have to dilate a MxN Matrix with a 2Mx2N Matrix, which would take way too long.
I tried calculating the distances from each element to each element==1 using pdist2() and then taking the minimum but that turned out to use way too much memory.
Any suggestions? I would also settle for a solution that just approximates it.
The bwdist function in the Image Processing Toolbox does precisely this
A = [1 0 0 0
0 1 1 1
0 0 0 0
0 0 1 0];
B = bwdist(A);
% 0.00000 1.00000 1.00000 1.00000
% 1.00000 0.00000 0.00000 0.00000
% 1.41421 1.00000 1.00000 1.00000
% 2.00000 1.00000 0.00000 1.00000
Say I have two m by n matrices A and B. B is a zero\one binary matrix. for each cell (i,j) in B containing 1 I want to do the following calculation:
(A(i,j)sign(A(i+1,j))-A(i+1,j)sign(A(i,j)))/(sign(A(i+1,j))-sign(A(i,j)))
Can this be done with no loops?
Is there a convolution filter that can make me do this?
If not what would be the best way to implement this?
You can directly use B in order to index the positions in your matrix A where you want to do the calculation. The harder part comes from the fact that you need to fetch the values at i+1 as well.
The fact that you go fecth the value that's one row below might be problematic if you have some of your 1 values in the last row of B, I'll assume in the following that you don't. If you do, just handle that case separfately and put 0's in the last row of B.
% Get the indexes from your matrix B :
Indexes=find(B(:));
% With how indexing works in MATLAB, you can fetch the value in the row below the fetched index by just adding 1 to the indexes :
% Note : This is where it will mess up if you have 1's in the last row of `B`
Indexes_Next=Indexes+1;
% Do your calculation and fill them in a matrix `C`, using the indexes :
C=zeros(size(A));
C(Indexes)=A(Indexes).*sign(A(Indexes_Next))-A(Indexes_Next).*sign(A(Indexes))./sign(A(Indexes_Next))-sign(A(Indexes));
Toy example :
m=20;
n=10;
B=randi(2,m,n)-1;
B(end,:)=0;
B =
1 1 1 0 1 0 0 0 1 1
1 1 1 0 0 0 1 1 1 0
1 0 1 0 1 0 0 1 1 1
1 0 1 1 1 0 0 1 1 0
0 1 0 0 1 1 0 0 0 0
1 1 0 1 0 1 0 1 1 0
1 1 1 1 1 0 0 0 1 1
1 0 1 1 0 0 1 0 1 0
1 0 0 0 1 1 0 0 0 0
1 0 0 0 0 1 1 0 1 1
1 0 0 1 1 1 0 0 1 1
1 0 0 1 0 1 1 0 0 0
0 0 0 0 0 1 0 0 1 1
1 0 1 0 0 0 0 1 0 0
0 1 1 1 1 0 1 0 0 1
1 0 1 1 1 0 0 0 0 1
1 0 1 0 0 1 1 1 1 0
0 0 0 0 0 1 0 1 1 0
0 1 0 1 1 0 1 1 0 0
0 0 0 0 0 0 0 0 0 0
A=randn(m,n);
A =
Columns 1 through 6:
-1.7288795 -0.5695908 0.4039973 0.4178222 1.9367721 -0.6211859
-0.4945766 -0.2536017 1.0269974 0.5980587 0.1747083 0.8528545
0.9100027 0.9414117 1.6132723 0.2620914 -1.1753700 0.1701842
-0.5094579 -1.5272232 -0.0270926 1.1561264 -0.8968151 0.5069460
-0.4602732 -0.2735907 -0.0827892 0.1235966 0.2566247 -0.6536810
0.0021322 0.2238454 -1.9115733 1.0635302 -0.9725437 0.9375332
1.1415043 0.7147173 1.1052473 -1.0797606 -1.0508984 -0.8286737
-1.1394488 -0.9882800 0.8274943 0.5247194 0.1164014 1.6475021
-0.6300175 -0.2726597 1.9213140 -0.2248069 0.8301544 1.2854954
0.9846066 -0.0560177 -0.1916319 0.8563492 0.1493325 -1.1080141
0.8041186 0.3818663 -0.6250196 0.4691516 1.6304691 1.9712590
0.3986143 0.2072083 0.5478786 0.8156810 0.7733391 0.1741200
2.1892863 -0.6791961 -0.5672075 -1.2172510 -2.1611237 0.2908826
-0.0317070 0.7387466 -0.8868863 -1.6897724 0.0445329 -0.7162208
0.7670334 0.1991324 -1.8576145 -0.9314224 0.3317805 2.3690729
-0.2091770 2.1657798 -1.0850389 -0.5931122 -1.6465039 -1.8521626
-0.7298015 0.5714788 -1.9456336 2.5631989 0.8980204 0.7283266
0.0126946 -2.0152983 0.0837190 0.0824572 -1.9949677 -0.7532233
0.6209000 -1.5505561 -0.0273443 -0.0888351 0.3024363 0.0104319
0.1721166 -0.9743276 2.1976957 -0.5196333 0.4917723 0.5235348
Columns 7 through 10:
-1.3386919 1.5511076 -0.0823747 0.6598207
0.1000738 1.3927819 1.2658383 0.3986191
-0.3424243 -1.3382283 -0.9360141 1.3003693
-0.6647429 -0.4520907 -0.6012909 0.7016295
-1.2671007 1.0972997 -0.6613468 0.2677242
-0.7083392 0.8622831 1.1582403 0.7597853
0.3218546 1.7128733 0.6578481 -1.7975630
-0.1816331 0.1582915 0.0125391 -1.6381652
1.1860973 -0.2897986 0.9418221 0.5233429
0.9335806 0.2088674 -1.1177073 -0.3280461
0.8333193 0.7273941 -0.6683300 -0.6137806
0.0360114 -0.3457279 -0.9115280 -0.6171204
-1.2861883 -0.7169084 -0.4313750 -0.4534017
-0.3267513 -0.3327733 1.0649930 0.9503430
0.3513518 0.7668540 0.8704655 0.3367261
0.6942558 -0.0811757 -0.0930538 0.4074495
0.0882976 -0.2911365 -3.0432331 -0.3264093
0.4319805 -0.3641444 0.3984263 -0.0481398
0.8404379 -0.4123483 -0.0465423 -0.1831455
-0.8230964 0.3130749 0.6854873 -0.2411392
Output :
--------
C
C =
Columns 1 through 8:
3.22346 1.82319 -1.62300 0.00000 0.76206 0.00000 0.00000 0.00000
1.41543 1.68781 -1.58627 0.00000 0.00000 0.00000 -1.44250 -3.73101
-2.41946 0.00000 -2.64036 0.00000 3.07219 0.00000 0.00000 2.79032
1.96973 0.00000 1.10988 0.03253 0.35981 0.00000 0.00000 1.64521
0.00000 0.95025 0.00000 0.00000 -2.22917 1.28385 0.00000 0.00000
-2.13937 -1.49087 0.00000 -3.14329 0.00000 -2.76621 0.00000 -1.85059
-3.28095 -2.70300 -0.72225 0.44496 0.06550 0.00000 0.00000 0.00000
2.76947 0.00000 -2.09382 -1.74953 0.00000 0.00000 2.00446 0.00000
1.35459 0.00000 0.00000 0.00000 -0.31918 -3.39351 0.00000 0.00000
-0.81951 0.00000 0.00000 0.00000 0.00000 1.86324 -0.89974 0.00000
-0.59450 0.00000 0.00000 -1.34653 -0.14287 0.79714 0.00000 0.00000
-2.79067 0.00000 0.00000 -3.03293 0.00000 -1.11676 -2.32220 0.00000
0.00000 0.00000 0.00000 0.00000 0.00000 -2.00710 0.00000 0.00000
1.73533 0.00000 3.74450 0.00000 0.00000 0.00000 0.00000 1.43408
0.00000 -2.96665 3.94265 2.52453 -2.97828 0.00000 -1.34290 0.00000
1.93898 0.00000 4.03067 2.97009 0.25152 0.00000 0.00000 0.00000
0.28289 0.00000 -0.86191 0.00000 0.00000 -2.48155 -1.34368 1.65528
0.00000 0.00000 0.00000 0.00000 0.00000 0.25721 0.00000 1.77649
0.00000 3.52488 0.00000 1.60847 -1.18934 0.00000 -2.66353 0.90073
0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000 0.00000
Columns 9 and 10:
2.18346 -0.73880
-3.20185 0.00000
2.53731 -0.40126
2.26264 0.00000
0.00000 0.00000
-0.49961 0.00000
-0.35469 4.43573
-1.92928 0.00000
0.00000 0.00000
2.78604 1.94183
2.57986 2.23090
0.00000 0.00000
1.63362 1.49694
0.00000 0.00000
0.00000 -1.07072
0.00000 -1.73386
-1.64481 0.00000
-1.44497 0.00000
0.00000 0.00000
0.00000 0.00000
I have rotation matrix which is not orthogonal. Whats wrong. I can't get it.
Exterior=[-6.6861,12.6118,-8.0660,[-0.4467,-0.3168,0.2380]*pi/180];%# deg 2 rad
%#data
ax=Exterior(4);
by=Exterior(5);
cz=Exterior(6);
%#Rotation in X
Rx = [1 0 0
0 cos(ax) -sin(ax)
0 sin(ax) cos(ax)];
%#Rotation in Y
Ry = [cos(by) 0 sin(by)
0 1 0
-sin(by) 0 cos(by)];
%#Rotation in Z
Rz = [cos(cz) -sin(cz) 0
sin(cz) cos(cz) 0
0 0 1];
R=Rx*Ry*Rz;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
R =
0.99998 -0.0041538 -0.0055292
0.0041969 0.99996 0.0077962
0.0054966 -0.0078192 0.99995
Orthogonality check
Inv(R)-R'=
2.2204e-016 2.6021e-018 8.6736e-019
0 1.1102e-016 -1.7347e-018
-2.6021e-018 3.4694e-018 2.2204e-016
R*R'=
2.2204e-016 2.6021e-018 8.6736e-019
0 1.1102e-016 -1.7347e-018
-2.6021e-018 3.4694e-018 2.2204e-016
Why there is different signs.???????
Any mistake??
Looks like the numbers in your orthogonality check are just due to rounding errors... They're really quite tiny.
There is an error in the question, pointed out by #ChisA. The OP pasted the same matrix for inv(R)-R' and R*R'
If we reconstruct the input file:
Exterior = [-6.681,12.6118,-8.0660,[-0.4467,-03168,0.2380]*pi/180]
ax = Exterior(4)
by = Exterior(5)
cz = Exterior(6)
Rx = [1 0 0 ; 0 cos(ax) -sin(ax) ; 0 sin(ax) cos(ax)]
Ry = [cos(by) 0 sin(by) ; 0 1 0 ; -sin(by) 0 cos(by)]
Rz = [cos(cz) -sin(cz) 0 ; sin(cz) cos(cz) 0 ; 0 0 1]
R = Rx*Ry*Rz
inv(R)-R'
R*R'
And run in through Octave (I don't have MATLAB):
Exterior =
-6.6810e+00 1.2612e+01 -8.0660e+00 -7.7964e-03 -5.5292e+01 4.1539e-03
ax = -0.0077964
by = -55.292
cz = 0.0041539
Rx =
1.00000 0.00000 0.00000
0.00000 0.99997 0.00780
0.00000 -0.00780 0.99997
Ry =
0.30902 0.00000 0.95106
0.00000 1.00000 0.00000
-0.95106 0.00000 0.30902
Rz =
0.99999 -0.00415 0.00000
0.00415 0.99999 0.00000
0.00000 0.00000 1.00000
R =
0.3090143 -0.0012836 0.9510565
-0.0032609 0.9999918 0.0024092
-0.9510518 -0.0038458 0.3090076
ans =
-5.5511e-17 1.3010e-18 1.1102e-16
2.1684e-19 0.0000e+00 -4.3368e-19
-1.1102e-16 -4.3368e-19 -5.5511e-17
ans =
1.0000e+00 -1.9651e-19 -4.6621e-18
-1.9651e-19 1.0000e+00 8.4296e-19
-4.6621e-18 8.4296e-19 1.0000e+00
Notice the R*R' is very close to I and inv(R)-R' is very close to 0.
Notice also that I get different small values than the OP. Because I am using a different piece of software the rounding errors will be different. So you should never rely on an exact comparison between two floating point numbers. You always should include some tolerance.
I hope this makes things a little clearer. See the comment by #gnovice below for links to more detailed information about rounding errors.
I don't understand why R*R' should be nearly zero. It should be the 3x3 identity matrix.
You might have a copy and paste error on your original question.
This is the following part of the below:
2) Additional question:
After getting the average of the non-zero neighbors, I also want to test if the neighbor elements are equal, lesser, or greater than the average of the nonzeros. If it is greater or equal then '1' or else '0'.
Note: if the neighbors are with in the radius of the two or more centres, take the smallest centre average to test.
0 12 9
4 **9** 15
11 19 0
The '9' in the middle is within the radius of 12, 15, and 19 centres, so take the minimum average of those min[9.000, 9.000, 8.000]=8.000
For example, when radius = 1 m or 1 element away.
new_x =
0 0 0 0 0
0 0 **9.0000** 9.0000 0
0 4.0000 9.0000 **9.0000** 0
0 **8.3333** **8.0000** 0 0
0 2.0000 4.0000 8.0000 0
0 4.0000 5.0000 8.0000 0
0 0 0 0 0
Test_x =
0 0 0 0 0
0 0 **9.0000** 1 0
0 0 1 **9.0000** 0
0 **8.3333** **8.0000** 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
=================================================================================
1) Say if I have a matrix, shown as below,
X =
0 0 0 0 0
0 0 12 9 0
0 4 9 15 0
0 11 19 0 0
0 2 4 8 0
0 4 5 8 0
0 0 0 0 0
and I want to find the average of the surrounding non-zero elements that is greater than 10. The rest of the elements still remain the same i.e. elements < 10.
So I want my solution to look something like,
new_x =
0 0 0 0 0
0 0 9.0000 9.0000 0
0 4.0000 9.0000 9.0000 0
0 8.3333 8.0000 0 0
0 2.0000 4.0000 8.0000 0
0 4.0000 5.0000 8.0000 0
0 0 0 0 0
Not: that I am NOT only looking at the neighbors of the element thats greather than some value (i.e. 10 in this case).
Lets say any elements thats are greater than 10 are the 'centre' and we want to find the avearge of the non-zeros with the radius of say 1 m. where 1 metre = 1 element away from the centre.
Note: It might not always be 1 meter away in radius i.e. can be 2 or more. In this case it wont be just top, bottom, left and right of the centre.
****Also Be aware of the matrix boundary. For example, when radius = 2 or more, some of the average of nonzero neighbors are out side the boundary.**
For example,
For radius =1 m = 1 element away,
new_x = average of [(i+1,j) , (i-1,j) , (i,j+1) and (i,j-1)] - top, bottom, right, and left of the centre.
For radius =2 m = 2 elements away,
new_x = average of [(i+1,j), (i+2,j) , (i-1,j) , (i-2,j), (i,j+1), (i,j+2), (i,j-1), (i,j-2), (i+1,j+1), (i+1,j-1), (i-1,j-1), and (i-1,j+1)].
==================================================================
I have tried a few things before, however I am not familiar with the functions.
So please help me to solve the problem.
Thank you in advance.
EDIT:
Note this requires functions from the Image Processing Toolbox, namely: COLFILT and STREL
r = 1; %# radius
t = 10; %# threshold value
mid = round((2*r+1)^2/2); %# mid point
nhood = getnhood(strel('diamond', r));
nhood(mid) = false;
fcn = #(M)sum(M(nhood(:),:),1)./(sum(M(nhood(:),:)~=0)+all(M(nhood(:),:)==0)).*(M(mid,:)>=t)+M(mid,:).*(M(mid,:)<t);
new_x = colfilt(x, 2*[r r]+1, 'sliding',fcn)
For r=1:
new_x =
0 0 0 0 0
0 0 9 9 0
0 4 9 9 0
0 8.3333 8 0 0
0 2 4 8 0
0 4 5 8 0
0 0 0 0 0
For r=2:
new_x =
0 0 0 0 0
0 0 11.2 9 0
0 4 9 10.167 0
0 7 7.7778 0 0
0 2 4 8 0
0 4 5 8 0
0 0 0 0 0
In fact, it should work for any radius >= 1
Notice how the diamond shape structuring element represents the neighborhood:
nhood =
0 1 0
1 0 1
0 1 0
nhood =
0 0 1 0 0
0 1 1 1 0
1 1 0 1 1
0 1 1 1 0
0 0 1 0 0
and so on..
Explanation:
We use the COLFILT function which traverse the matrix using a sliding neighborhood of NxN, and places each block as a column in a temporary matrix.
We process each column of this temp matrix (blocks) using the function fcn, and the result will be placed in the correct location once finished (COLFILT uses IM2COL and COL2IM underneath).
We check for two cases depending of the value of the center of the block:
If its less than 10, it returns that value unchanged: M(mid,:)
if its >=10, we compute the mean of the non-zero elements of its neighborhood
sum(M(nhood(:),:),1) ./ (sum(M(nhood(:),:)~=0) + all(M(nhood(:),:)==0)).
The last term in there is necessary to avoid dividing by zero
Notice how the result of 1 & 2 above are combined using R1.*(M(mid,:)<t) + R2.*(M(mid,:)>=t) to emulate an if/else choice.
Here is the algorithm I think you are describing in your question. For each pixel:
If the pixel value is less than 10, do nothing.
If the pixel value is greater than or equal to 10, replace the pixel value by the average of the non-zero 4-connected nearest neighbors.
If this is correct (as it appears to be from the sample matrices you gave), then you could use the function NLFILTER from the Image Processing Toolbox (if you have access to it) to perform this operation:
fcn = #(x) [x(5) sum(x(2:2:8))/max(sum(x(2:2:8) > 0),1)]*[x(5) < 10; x(5) >= 10];
new_x = nlfilter(X,[3 3],fcn);
EDIT: If you don't have access to the Image Processing Toolbox, you can also do this using the built-in CONV2 function, like so:
kernel = [0 1 0; ... %# Convolution kernel
1 0 1; ...
0 1 0];
sumX = conv2(X,kernel,'same'); %# Compute the sum of neighbors
%# for each pixel
nX = conv2(double(X > 0),kernel,'same'); %# Compute the number of non-zero
%# neighbors for each pixel
index = (X >= 10); %# Find logical index of pixels >= 10
new_x = X; %# Initialize new_x
new_x(index) = sumX(index)./max(nX(index),1); %# Replace the pixels in index
%# with the average of their
%# non-zero neighbors
The above handles your radius = 1 case. To address your radius = 2 case, you just have to change the convolution kernel to the following and rerun the above code:
kernel = [0 0 1 0 0; ...
0 1 1 1 0; ...
1 1 0 1 1; ...
0 1 1 1 0; ...
0 0 1 0 0];
You could do something like this: (tested in Octave, should work in matlab)
octave-3.2.3:17> toohigh = (x>=10)
toohigh =
0 0 0 0 0
0 0 1 0 0
0 0 0 1 0
0 1 1 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
octave-3.2.3:18> nbr_avg = filter2(ones(3,3)/9,x)
nbr_avg =
0.00000 1.33333 2.33333 2.33333 1.00000
0.44444 2.77778 5.44444 5.00000 2.66667
1.66667 6.11111 8.77778 7.11111 2.66667
1.88889 5.44444 8.00000 6.11111 2.55556
1.88889 5.00000 6.77778 4.88889 1.77778
0.66667 1.66667 3.44444 2.77778 1.77778
0.44444 1.00000 1.88889 1.44444 0.88889
octave-3.2.3:19> y=x; y(toohigh) = nbr_avg(toohigh)
y =
0.00000 0.00000 0.00000 0.00000 0.00000
0.00000 0.00000 5.44444 9.00000 0.00000
0.00000 4.00000 9.00000 7.11111 0.00000
0.00000 5.44444 8.00000 0.00000 0.00000
0.00000 2.00000 4.00000 8.00000 0.00000
0.00000 4.00000 5.00000 8.00000 0.00000
0.00000 0.00000 0.00000 0.00000 0.00000
The filter2 function allows you to filter on neighbors (not sure what function you want...), and if you use a boolean index matrix (toohigh in this case) to select those members of the original matrix that are too high, you can replace them with the ones you want.
More specifically, filter2 allows you to convolve with an arbitrary matrix. The matrix of all ones does a spatial low pass filter.
note: my math doesn't match yours. I'm not quite sure why you want to average only the nonzero neighbors (that gives higher weight to nonzero neighbors when there are zeros), but if you wanted to do that, you could do filter2(ones(3,3),x) ./ M where M = filter2(ones(3,3),(x ~= 0)) is the count of nonzero neighbors.
EDIT: Here is a solution that does not require the Image Processing Toolbox. It does, however, use conv2nan.m which is part of the free NaN toolbox.
This approach relies on doing two different filtering/convolution operations: one that gets the sum of surrounders for each element, and one that gets the count of nonzero surrounders. Then, you are ready to combine them to get the average of nonzero surrounders only. Like this:
% set up starting point matrix with some zeros
X = magic(4);
X(X < 5) = 0;
X(X == 0) = NaN; % convert zeros to NaNs to work with conv2nan
countmat = double(X > 0);
cmat = [0 1 0;
1 0 1;
0 1 0]; % consider surrounding elements only
[m1,c] = conv2nan(X,cmat); % sum of surrounding elements
[m2,c] = conv2nan(countmat,cmat); % number of surrounding elements > 0
x_new = m1./m2; % compute average we want
x_new = x_new(2:end-1,2:end-1); % trim edges created by conv2
x_new(~countmat) = 0; % restore zero elements
x_new(X < 10) = X(X < 10) % restore < 10 elements
It does some extra work in that the convolutions are done for all elements and not just those that are >= 10. But it's more general than the manual looping approach.