For example:
>> tmp = ones(5,5)
tmp =
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
I want a command like:
tmp(colNum - 2*rowNum > 0) = 0
that modifies entries of tmp when the column number is more than twice the row number e.g. it should produce:
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
As a second example, tmp(colNum - rowNum == 0) = 0 should set the diagonal elements of tmp to be zero.
A possibly more efficient solution is to use bsxfun like so
nRows = 5;
nCols = 5;
bsxfun(#(col,row)~(col - 2*row > 0), 1:nCols, (1:nRows)')
You can generalize this to just accept a function so it becomes
bsxfun(#(col,row)~f(col,row), 1:nCols, (1:nRows)')
And now just replace f with exactly the way you specify the equation in your question i.e.
f = #(colNum, rowNum)(colNum - 2*rowNum > 0)
or
f = #(colNum, rowNum)(colNum - rowNum == 0)
of course it might make more sense to specify your function to accept (row,col) instead of (col,row) as that's how MATLAB indexes
You can use meshgrid to generate a grid of 2D coordinates, then use this to impose any condition you wish. The variant you seek outputs 2 2D matrices where the first matrix gives you the column locations and the second matrix outputs the row locations.
For example, given your situation above:
>> [X,Y] = meshgrid(1:5, 1:5)
X =
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
Y =
1 1 1 1 1
2 2 2 2 2
3 3 3 3 3
4 4 4 4 4
5 5 5 5 5
You can see that each unique spatial location shared between X and Y give you the desired 2D location as if you were envisioning a 2D grid.
Therefore, you would do something like this for your first situation:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X > 2*Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
1 1 0 0 0
1 1 1 1 0
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
Finally for your second example:
[X,Y] = meshgrid(1:5,1:5); % Generate 2D coordinates
tmp = ones(5); % Generate desired matrix
tmp(X == Y) = 0; % Set desired locations to 0
We get:
>> tmp
tmp =
0 1 1 1 1
1 0 1 1 1
1 1 0 1 1
1 1 1 0 1
1 1 1 1 0
Simply put, generate a grid of 2D coordinates, then use those directly to index into your desired matrix using logical / Boolean conditions to set the desired locations to 0.
I have an interesting question about matrix. In Gibbs distribution, a Gibbs energy U(x) can be compute as
which is a sum of clique potentials Vc(x) over all possible cliques C (right image). A clique c is defined as a subset of sites in S (are neighborhood of x- blue color pixels are neighbor of yellow pixel in left figure) in which every pair of distinct sites are neighbors, except for single-site cliques
In which V(x) is computed as following:
My goal is how to compute U(x). In my above example, x={1,2}. However, I have some stuck at pixel at corner of image that only has less than 8 neighbors (a pixel often has 8 neighbor pixels in normal case). To solve it, I added zeros at corner of edge by convolution image with an mask. But I think it maybe affect to U(x). From above definition, could you help me find U(x=1) and U(x=2) when i consider pixels at corner of image (gray color)? I tried to solve it by below solution but I think my solution is so long and I am not sure is whether correct or not.
This is what I do
Imlabel =[ 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 2 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 2 2 2 1 1 1 1 1;
1 1 2 2 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 2 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1];
mask=[ 0 0 0;0 1 0;0 0 0];
Imlabel=conv2(Imlabel,mask); % To solve pixel in corner
num_class=2; % x={1,2}
beta=1;
for label=1:num_class
for i=2:size(Imlabel,1)-1
for j=2:size(Imlabel,2)-1
sum_V=0;
%North, south, east and west neighbors
if(label==Imlabel(i-1,j)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i,j+1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i+1,j)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i,j-1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
%% Diagonal pixels
if(label==Imlabel(i-1,j-1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i-1,j+1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i+1,j+1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i+1,j-1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
%% Save
U(i-1,j-1,label)=sum_V; %% Because Index is extended before
end
end
end
Update: Normalization
Currently, based on my understand at here. I computed normalized term as well as gibbs distribution as
P=zeros(size(U));
Z=sum(exp(-U),3);
for i=1:num_class
P(:,:,i)=exp(-U(:,:,i))./Z;
end
Your explanation makes sense as well as the code. However, you can totally get this vectorized by being smart about the kinds of functions you want to use.
For the interested readers, given your source code, what you want to do is the following (in pseudocode):
Create a matrix U that is size(Imlabel,1) x size(Imlabel,2) x num_class large. Each slice of this matrix will determine which the Gibbs energy costs for the 8 neighbouring pixels at each coordinate (i,j) in this slice.
For each class label x...
a. For each pixel in the image (i,j)...
Find those locations defined in an 8-pixel neighbourhood of (i,j) that are equal to x and set them to beta
Find those locations defined in an 8-pixel neighbourhood of (i,j) that are not equal to x and set them to -beta
Compute the sum of all of these beta and -beta values within this 8-pixel neighbourhood and set this at location U(i,j,x)
Therefore, what I would do is create a 3D matrix of costs... let's call this C, where each slice y has the same dimensions as size(Imlabel,1) x size(Imlabel,2) but each location (i,j) in a slice will be beta if Imlabel(i,j) == y and -beta otherwise. Once you do this, you can perform an N-D convolution of this matrix, but using a 2D kernel so that you can compute the summation of the 8-pixel neighbourhoods of each slice separately.
Today's magic menu consists of bsxfun, convn and a side-order of permute to sweeten the pot.
To get the cost matrix C, you can do this. This is assuming Imlabel is not padded with zeros along the boundaries:
Imlabel =[ 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 2 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 2 2 2 1 1 1 1 1;
1 1 2 2 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 2 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1];
C = double(bsxfun(#eq, Imlabel, permute(1:num_class, [1 3 2])));
C(C == 0) = -beta;
C(C == 1) = beta;
The permute function is used to create a single 3D vector that goes from 1 up to as many classes as you have defined. The reason why this is required is because bsxfun does what is known as broadcasting. What will happen in this case is given your matrix Imlabel, using the 3D vector will create a 3D cost matrix in conjunction with the eq or equals function. Each slice of this cost matrix will give you the locations of where it is either equal to the label in question, or it isn't.
You can verify that what we have for the locations matrix that is output from bsxfun is correct:
>> C = double(bsxfun(#eq, Imlabel, permute(1:num_class, [1 3 2])))
C(:,:,1) =
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 0 1 1
1 1 1 1 1 1 1 1 1 1
1 1 0 0 0 1 1 1 1 1
1 1 0 0 1 1 1 1 1 1
1 1 1 1 1 1 1 1 0 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
C(:,:,2) =
0 0 0 0 0 0 0 0 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 0 0 0
0 0 1 1 1 0 0 0 0 0
0 0 1 1 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 0 0
As you can see, for each slice, we can see which locations shared that label that is enumerated by that slice and those that don't share the same label. Once we have these locations, we need to cast this to double so that we can modify this to be a cost matrix where -beta is a location that isn't equal to a label for a particular slice and beta is. That is done by the two assignment statements after the bsxfun call.
Once you have this cost matrix, you can pad the boundaries like you did before, but make sure that the boundaries are set to a cost of -beta as you have done in your code:
mask = zeros(3,3); mask(2,2) = 1;
Cpad = convn(C, mask);
Cpad(Cpad == 0) = -beta;
The first line of code denotes the [0 0 0; 0 1 0; 0 0 0] mask that you defined in your code. Now to pad each slice independently to have a border of zeroes, we can use convn to help us do that.
Now that we have that set up, all you have to do is perform the calculation of Gibbs' energy independently per slice:
mask2 = ones(3,3); mask2(2,2) = 0;
out = convn(Cpad, mask2, 'valid');
The first line of code denotes a mask where every value is 1 except for the middle, which is 0 and it is a 3 x 3 mask. The reason why you want to do this is because this replaces the if/else statements in your double for loop logic. What you are doing is essentially a convolution where you are adding up all of the 8 neighbours except for the middle itself. This can be accomplished by using a mask that has all 1s except for the central pixel and you set that to 0.
Next, we use convn to compute the Gibbs' energy per slice independently, but using the 'valid' flag so that we can remove the zero-padded border we included at the beginning.
Now, if you take a look at out, it compares with what you have with U, but there is a slight shift because of the way you are indexing into U. Nevertheless, with the above output of out, you can verify that what we have is correct and this will handle the border cases nicely:
>> out
out(:,:,1) =
-2 2 2 2 2 2 2 2 2 -2
2 8 8 8 8 8 6 6 6 2
2 8 8 8 8 8 6 8 6 2
2 6 4 2 4 6 6 6 6 2
2 4 2 0 4 6 8 8 8 2
2 4 2 0 2 6 8 6 6 0
2 6 4 4 6 8 8 6 8 0
2 8 8 8 8 8 8 6 6 0
-2 2 2 2 2 2 2 2 2 -2
out(:,:,2) =
-8 -8 -8 -8 -8 -8 -8 -8 -8 -8
-8 -8 -8 -8 -8 -8 -6 -6 -6 -8
-8 -8 -8 -8 -8 -8 -6 -8 -6 -8
-8 -6 -4 -2 -4 -6 -6 -6 -6 -8
-8 -4 -2 0 -4 -6 -8 -8 -8 -8
-8 -4 -2 0 -2 -6 -8 -6 -6 -6
-8 -6 -4 -4 -6 -8 -8 -6 -8 -6
-8 -8 -8 -8 -8 -8 -8 -6 -6 -6
-8 -8 -8 -8 -8 -8 -8 -8 -8 -8
For example, if we took a look at the top left corner of the first slice, we see that the east, south and south east corners have the same label of 1, and so since beta = 1, our sum would be 3, but then there are the other 5 directions that we didn't consider, and in your code, you set those to -beta and so 3 - 5 = -2.
Let's also take a look at the 6th row, 4th column and let's take a look at the second label or slice. This means that any cardinal directions that are equal to label 2, we should sum these with beta / 1 while anything that isn't equal to label 2 is -beta / -1. As you can see, there are exactly 4 labels of 1 and 4 labels of 2, and so these should cancel out and give us a 0.
You can verify that what we did for all of the other locations in this matrix result in the right calculations.
The full code is simply:
Imlabel =[ 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 2 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 2 2 2 1 1 1 1 1;
1 1 2 2 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 2 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1];
C = double(bsxfun(#eq, Imlabel, permute(1:num_class, [1 3 2])));
C(C == 0) = -beta;
C(C == 1) = beta;
mask = zeros(3,3); mask(2,2) = 1;
Cpad = convn(C, mask);
Cpad(Cpad == 0) = -beta;
mask2 = ones(3,3); mask2(2,2) = 0;
out = convn(Cpad, mask2, 'valid');
In the case of timing, we can check to see how fast the above approach is compared to your original loop code. I used timeit to help facilitate that.
Here's the script I set up that sets up all of the relevant variables, and measures the time taken with your code and mine:
function test_clique_timing
% Setup
Imlabel_orig =[ 1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 2 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1;
1 1 2 2 2 1 1 1 1 1;
1 1 2 2 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 2 1;
1 1 1 1 1 1 1 1 1 1;
1 1 1 1 1 1 1 1 1 1];
num_class=2; % x={1,2}
beta = 1;
function test_orig
mask=[ 0 0 0;0 1 0;0 0 0];
Imlabel=conv2(Imlabel_orig,mask); % To solve pixel in corner
beta=1;
for label=1:num_class
for i=2:size(Imlabel,1)-1
for j=2:size(Imlabel,2)-1
sum_V=0;
%North, south, east and west neighbors
if(label==Imlabel(i-1,j)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i,j+1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i+1,j)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i,j-1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
%% Diagonal pixels
if(label==Imlabel(i-1,j-1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i-1,j+1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i+1,j+1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
if(label==Imlabel(i+1,j-1)) sum_V=sum_V+beta;
else sum_V=sum_V-beta;end
%% Save
U(i-1,j-1,label)=sum_V; %% Because Index is extended before
end
end
end
end
function test_conv
C = double(bsxfun(#eq, Imlabel_orig, permute(1:num_class, [1 3 2])));
C(C == 0) = -beta;
C(C == 1) = beta;
mask = zeros(3,3); mask(2,2) = 1;
Cpad = convn(C, mask);
Cpad(Cpad == 0) = -beta;
mask2 = ones(3,3); mask2(2,2) = 0;
out = convn(Cpad, mask2, 'valid');
end
time1 = timeit(#test_orig);
time2 = timeit(#test_conv);
fprintf('Loop code time: %f seconds\n', time1);
fprintf('Vectorized code time: %f seconds\n', time2);
fprintf('Speedup factor: %f', time1/time2);
end
Running the above script, I get these for times:
Loop code time: 0.000049 seconds
Vectorized code time: 0.000060 seconds
Speedup factor: 0.816667
This is done in seconds and I did this using MATLAB R2013a under Mac OS Mavericks 10.10.5. The speedup doesn't look that great. In fact it's a factor less than 1, which is terrible. However, what you have shown is such a small dataset. We should try with a larger dataset and see if this still holds. Let's make the matrix 2500 x 2500, with 10 class labels. I'm going to replace the Imlabel matrix with:
rng(123); %// Set seed for reproducibility
num_class = 10;
Imlabel_orig = randi(10,2500,2500);
This produces random integers from 1 to 10 in a 2500 x 2500 matrix. Doing this and running the code again gives:
Loop code time: 17.553669 seconds
Vectorized code time: 3.321950 seconds
Speedup factor: 5.284146
Yeah... that's much better. On the 2500 x 2500 matrix of 10 labels, it took roughly 17.5 seconds to compute, whereas the the vectorized code with bsxfun / convn / permute outperforms your original loop code by a factor of almost 5.2x.
As for the normalization term to factor into your Gibbs' energy calculation, you currently have this:
P=zeros(size(U));
Z=sum(exp(-U),3);
for i=1:num_class
P(:,:,i)=exp(-U(:,:,i))./Z;
end
If you take advantage of bsxfun, you can simply do this:
Z = sum(exp(-out),3)); %// out is U in my code
P = bsxfun(#rdivide, exp(-out), Z);
This achieves the same thing your code does. For each slice in P, we find the exp and negate the slice as input, and divide each term by Z. For bsxfun, the Z matrix gets replicated for as many slices as there are in out and does the element-wise division much like the loop code does.
In Matlab I've matrix where, in a previous stage of my code, an specific element was chosen. From this point of the matrix I would like to find a maximum, not just the maximum value between all its surounding neighbours for a given radius, but the maximum value at a given angle of orientation. Let me explain this with an example:
This is matrix A:
A =
0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1
The element chosen in the first stage is the 4 in A(2,4), and the next element should be the maximum value with, for example, a 315 degrees angle of orientation, that is the 7 in A(5,7).
What I've done is, depending on the angle, subdivide matrix A in different quadrants and make a new matrix (an A's submatrix) with only the values of that quadrant.
So, for this example, the submatrix will be A's 4th quadrant:
q_A =
4 3 2 8 1 0
3 3 2 2 1 0
3 2 2 2 1 0
3 3 2 7 2 1
2 3 2 3 2 1
And now, here is my question, how can I extract the 7?
The only thing I've been able to do (and it works) is to find all the values over a threshold value and then calculate how those points are orientated. Then, saving all the values that have a similar orientation to the given one (315 degrees in this example) and finally finding the maximum among them. It works but I guess there could be a much faster and "cleaner" solution.
This is my theory, but I don't have the image processing toolbox to test it. Maybe someone who does can comment?
%make (r,c) the center by padding with zeros
if r > size(A,1)/2
At = padarray(A, [size(A,1) - r], 0, 'pre');
else
At = padarray(A, [r-1], 0 'post');
if c > size(A,2)/2
At = padarray(At, [size(A,2) - c], 0, 'pre');
else
At = padarray(At, [c-1], 0 'post');
%rotate by your angle (maybe this should be -angle or else 360-angle or 2*pi-angle, I'm not sure
Ar = imrotate(At,angle, 'nearest', 'loose'); %though I think nearest and loose are defaults
%find the max
max(Ar(size(Ar,1)/2, size(Ar,2)/2:end); %Obviously you must adjust this to handle the case of odd dimension sizes.
Also depending on your array requirements, padding with -inf might be better than 0
The following is a relatively inexpensive solution to the problem, although I found wrapping my head around the matrix coordinate system a real pain, and there is probably room to tidy it up somewhat. It simply traces all matrix entries along a line around the starting point at the supplied angle (all coordinates and angles are of course based on matrix index units):
A = [ 0 1 1 1 0 0 9 1 0
0 2 2 4 3 2 8 1 0
0 2 2 3 3 2 2 1 0
0 1 1 3 2 2 2 1 0
0 8 2 3 3 2 7 2 1
0 1 1 2 3 2 3 2 1 ];
alph = 315;
r = 2;
c = 4;
% generate a line through point (r,c) with angle alph
[nr nc] = size(A);
x = [1:0.1:nc]; % overkill
m = tan(alph);
b = r-m*c;
y = m*x + b;
crd = unique(round([y(:) x(:)]),'rows');
iok = find((crd(:,1)>0) & (crd(:,1)<=nr) & (crd(:,2)>0) & (crd(:,2)<=nc));
crd = crd(iok,:);
indx=sub2ind([nr,nc],crd(:,1),crd(:,2));
% find max and position of max
[val iv]=max(A(indx)); % <-- val is the value of the max
crd(iv,:) % <-- matrix coordinates (row, column) of max value
Result:
val =
7
iv =
8
ans =
5 7
Say I have the following two matrices:
>> x = [1 4 3; 6 4 3; 6 9 3; 2 4 3; 5 4 0; 5 3 1; 6 4 7];
>> y = [0 0 1; 1 1 0; 1 1 0; 0 1 1; 0.2 0.8 0.54; 1 1 1; 0 0 0];
Where you can think of x as some image, and y as the degree of membership of each element of x to some region of interest.
Say I set those elements in x that have degree of membership = 1 to 1 (core) and the other elements to 0 as follows:
x = zeros(size(y));
x(y==1) = 1;
In which case I will have the following output:
0 0 1
1 1 0
1 1 0
0 1 1
0 0 0
1 1 1
0 0 0
Now, for the elements of 0, I substitute their values with the value of y in the corresponding location as follows:
x(x==0)=y(x==0);
Now, I select those pixels that are considered 4-neighbours of core but not in core as follows:
four_neighbourhood_pixels = imdilate(core, strel('diamond', 1)) - core;
My question is: how can we select a pixel p that belongs to four_neighbourhood_pixels that minimizes the distance between x & core?
Provided that for distance I calculate it as follows:
pdist([x,core],'minkowski');
Provided that x in the preceding command will be the matrix after substituting the zeros with the degree of membership values y i the corresponding location?
So, how can I select that pixel that belongs to four_neighbourhood_pixels that minimizes the distance between x with the zeros substituted and core?
Thanks.
If I understand correctly, core is the following matrix:
0 0 1
1 1 0
1 1 0
0 1 1
0 0 0
1 1 1
0 0 0
First find the distance between x and core.
dist=pdist([x,core],'minkowski');
dist1=squareform(dist);
[row1,row2]=find(dist1==min(dist1(:)); %interpretation: you get the minimum distance between row1 and row2 of [x core]
Veify if my understanding is correct:
You want a pixel from x which minimizes the distance dist and it should belong to four_neighbourhood_pixels. This is the matrix [x core]
1 4 3 0 0 1
6 4 3 1 1 0
6 9 3 1 1 0
2 4 3 0 1 1
5 4 0 0 0 0
5 3 1 1 1 1
6 4 7 0 0 0
Suppose you get the minimum value between 2nd row and 3rd row. Now based on this tell us what you mean by "find a pixel which minimizes..."
i'm trying to find local maxima of a vector of numbers using MATLAB. The built-in findpeaks function will work for a vector such as:
[0 1 2 3 2 1 1 2 3 2 1 0]
where the peaks (each of the 3's) only occupy one position in the vector, but if I have a vector like:
[0 1 2 3 3 2 1 1 2 3 2 1 0]
the first 'peak' occupies two positions in the vector and the findpeaks function won't pick it up.
Is there a nice way to write a maxima-finding function which will detect these sort of peaks?
You can use the REGIONALMAX function from the Image Processing Toolbox:
>> x = [0 1 2 3 3 2 1 1 2 3 2 1 0]
x =
0 1 2 3 3 2 1 1 2 3 2 1 0
>> idx = imregionalmax(x)
idx =
0 0 0 1 1 0 0 0 0 1 0 0 0
Something much easier:
a = [1 2 4 5 5 3 2];
b = find(a == max(a(:)));
output:
b = [4,5]
a = [ 0 1 2 3 3 2 1 2 3 2 1 ];
sizeA = length(a);
result = max(a);
for i=1:sizeA,
if a(i) == result(1)
result(length(result) + 1) = i;
end
end
result contains the max, followed by all the values locations that are equal to max.