Consider the 37x101 matrix below:
Each black cell has value 1, the rest of the cells value 0. I would like to fill in the "gaps" by means of cubic spline interpolation, as well as scaling the y axis from 37 to 181. The latter can be done by using the interp1 function, as in:
interp1(37,matrix,181,'pchip')
However, the outcome is now interpolated along the y-axis, but the gaps remain:
I don't want to interpolate along the x-axis, because I want the final matrix to have dimension 181 x 101. I just want to fill in the gaps using the existing cells (of the 181 x 101 matrix).
How can the original matrix (top), be scaled from 37 x 101 to 181 x 101 (without the "smoothing" in the second image), as well as filling in the gaps using some kind of spline interpolation as if this was a proper function?
It appears that your truth value grid has a single one where the true value is in each row. If the true/1 values do in fact create a line through the image, I would recommend parametrize the line with respect to t so that y = fy(t) and x = fx(t). If you're not familiar with this you can find some parametrization info on youtube tutorials or google. The main idea is that if you have , say a truth table that looks like this:
Then you could plot the the location of each pixel with respect to another variable, t and then use interp1(...) on each of these individually. In my case I defined the x and y values as follows:
n = 32;
rand('seed', 1982);
y_orig = 1:n;
x_orig = ceil(n*sin(y_orig/n*pi));
So I can plot as:
t1 = linspace(0,1, n);
plot(t1,x_orig, 'r', 'linewidth', 3);
hold all
plot(t1,y_orig, 'b', 'linewidth', 3);
legend('X', 'Y')
Note that I can get any truth value I want just by using interp1 like this (if you wanted to find the value half way between the 5th and 6th row):
desiredY = 5.5;
t= 1:n;
truthValue= interp1(t, x_orig, desiredY, 'cubic')
But we are looking to make a new image so I chose a more convenient parametrization of t between zero and one. Unfortunately, you may not have x and y off hand, so we need to pull them out of the image. Assuming you have a single true/1 value in each row we can yank out the values with max(...):
[maxVals, x1] = max(data,[],2);
x1(maxVals == 0) = [];
y1 = find(maxVals ~= 0);
Some form of find on each row would also work. If you have a truth value in each row then y1 should equal 1:n. The max function returns the index of the max in dimension 2 in the second return value. I use the next two lines to remove any entries where there truth table was empty (max is zero) and then y1 = 1:n minus those entries that were empty.
A quick and dirty way to get lots of points along this line is:
t2 = linspace(0,1,1024);
x2 = interp1(t1, x1, t2, 'cubic');
y2 = interp1(t1, y1, t2, 'cubic');
I can then plot the original points/image and this newly discovered finer line together like this:
imagesc(data);
hold all;
plot(x2,y2, 'linewidth', 2);
axis image
colormap(flipud(colormap(gray)));
To get this:
Finally, you can quickly turn this into a new image by scaling the parametrization up. My method is not particularly efficient for clarity:
y2_scaled = floor((y2(:)-1)*scaleValue + 1);
x2_scaled = floor((x2(:)-1)*scaleValue + 1);
scaleValue = 2;
data2 = zeros(n*scaleValue);
for ind = 1:length(x2_scaled)
data2(y2_scaled(ind),x2_scaled(ind)) = 1;
end
Which results in:
Note that this table has connected all the points and you now have multiple true/1's in each row. This is because I chose a very small step size for t2. You could fix this by either choosing t2 smarter, skipping multiple values in each row, or average the location of each indices in each row. Or ignoring this issue.
To fix t2 with the scaling value, you could use
t2 = linspace(0,1,n*scaleValue);
to get only one true/1 per row in the above code.
Also, if you want to only scale one dimension, you could do it like this:
y2_scaled = floor((y2(:)-1)*scaleValue + 1);
x2_scaled = floor((x2(:)-1) + 1);
scaleValue = 2;
data2 = zeros(n*scaleValue,n);
for ind = 1:length(x2_scaled)
data2(y2_scaled(ind),x2_scaled(ind)) = 1;
end
I see this as a bitmap, so why not a clamped blur?
I=yourmatrixhere
%gaussian blur
% it looks like bump-to-bump distance is 3 empties
% therefore hsize should be about 7
% it looks like bump vertical size is about 4
% therefore simga should be about 10
hsize=[3 3];
sigma = 10;
h=fspecial('gaussian',hsize,sigma)
I2=imfilter(I,h,'replicate');
At this point you have spread information to adjacent columns, but you need to "tidy up" from continuous to binary.
%threshold
th = 0.25;
I3=zeros(size(I));
ind=find(I>=th);
I3(ind)=1;
At this point, I3 is your matrix of interest to do the "erode" or interpolation.
Related
I have a numerical set X, Y, Z and I would like to reproduce a heatmap with these values. The size of the bin is 20 x 20 and the range of the X and Y axes are from -150 to 150 with Z being the color. Within that bin it should contain the average of the Z values in that range.
In Origin contains this tool to make a heatmap with the average of the values, but I would like to do it in MATLAB. The graph I made in Origin and that I would like to do in MATLAB can be seen in figure 1.
I've tried something like
load xyz.dat
x = xyz(:,1);
y = xyz(:,2);
z = xyz(:,3);
tbl = table(x,y,z);
h = heatmap(tbl,'x','y','ColorVariable','z','ColorMethod','mean');
But it printed this warning
Warning: Error updating HeatmapChart. Values in the source table variable 'x' are not grouped into discrete categories. Use the discretize function to group your values.
heatmap expects discrete x and y values. You goal is to bin your x and y's into 20 discrete bins and average the z values for each bin, but x and y themselves are continuous.
To achieve your goal take the advice of the warning message and use discretize to bin your x and y values.
% I used the following stand ins for your data:
% x = rand(540, 1);
% y = rand(540, 1);
n_bins = 20; % Number of grid cells
% If you want the width of the grid cell to be 20, use
% n_bins = (max(x) - min(x)) / 20;
x_discrete = discretize(x, n_bins);
y_discrete = discretize(y, n_bins);
tbl = table(X_discrete,y_discrete,z, 'VariableNames', {'x', 'y', 'z'});
h = heatmap(tbl,'x','y','ColorVariable','z','ColorMethod','mean');
Note: Why was this not a problem with the sample data?
Using your sample data,
x = [49.8, 14.5, -60.7, -21.6, -10.6];
y = [45.3, 7.9, 23.9, -58.5, -55.4];
z = [0.2 , -0.06, -0.35, -0.15, -0.08];
tbl = table(x',y',z', 'VariableNames', {'x', 'y', 'z'});
h = heatmap(tbl,'x','y','ColorVariable','z','ColorMethod','mean');
Does not throw an error because it treats each x and y value as a separate category. heatmap uses a call to categorical to transform the continuous values into categorical values.
Aside from not really providing the output you want (each point would be its own box instead of averaging grid cells), categorical seems to have a limit in how many categorical values it will create. I wasn't able to find any documentation about exactly what that limit is, but by experimentation, it tops out in the mid 200's. Since your vectors are 540 elements long, you get the warning about using discretize instead.
I need to plot multiple graphs in the same y-axis, but the x-axis is a bit tricky for me:
Assuming it goes from 0 to a, increasing by one, it needs to have an overall of a-1 different intervals.
Each one of them should finish at a, but it also has to have a different initialisation point. Only the first starts at 0, while each next one, starts by the previous plus one, as in the shape below. The two dashed lines, I used to visualise my thoughts as clear as I could, are not necessary.
I would appreciate any help!
Replicate your original interval 0 ... a a times (from my understanding, you'll have a intervals, not a-1), such that you get a matrix X of size [a x a+1]. Set the lower left triangle of X to NaN, so that the rows now represent your (shortening) intervals. Do your calculations on X. Pay attention, these have to support/neglect NaN values properly.
After that, you need to adjust the values in X properly, so that the intervals are plotted subsequently. Basically, we add some fixed value to each row.
Last, we need the proper xticks and xticklabels. Therefore, we extract all values from X and the modified X and get rid of the NaN values.
Here's a complete code snippet:
% Parameter
a = 7;
% Initialize intervals
X = repmat(0:a, a, 1);
X = X .* (ones(size(X)) + tril(nan(size(X)), -1));
% Calculation on these intervals; attention: there are NaN in X
Y = sin(X / a * 2 * pi);
% Modify X for plotting
X_plot = X;
X_plot(2:end, :) = X_plot(2:end, :) + cumsum(a:-1:2).';
% Get xticks
xt = X_plot.';
xt = xt(:);
xt(isnan(xt)) = [];
% Get xticklabels
xtl = X.';
xtl = xtl(:);
xtl(isnan(xtl)) = [];
% Plot
plot(X_plot.', Y.');
xticks(xt);
xticklabels(xtl);
The output (Octave 5.1.0, also tested with MATLAB Online) looks like this:
If you only want for example the start and end of each interval, you must further pre-process xt and xtl.
Hope that helps!
I'm trying to apply bare-bones image processing to images like this: My for-loop does exactly what I want it to: it allows me to find the pixels of highest intensity, and also remember the coordinates of that pixel. However, the code breaks whenever it encounters a multiple of rows – which in this case is equal to 18.
For example, the length of this image (rows * columns of image) is 414. So there are 414/18 = 23 cases where the program fails (i.e., the number of columns).
Perhaps there is a better way to accomplish my goal, but this is the only way I could think of sorting an image by pixel intensity while also knowing the coordinates of each pixel. Happy to take suggestions of alternative code, but it'd be great if someone had an idea of how to handle the cases where mod(x,18) = 0 (i.e., when the index of the vector is divisible by the total # of rows).
image = imread('test.tif'); % feed program an image
image_vector = image(:); % vectorize image
[sortMax,sortIndex] = sort(image_vector, 'descend'); % sort vector so
%that highest intensity pixels are at top
max_sort = [];
[rows,cols] = size(image);
for i=1:length(image_vector)
x = mod(sortIndex(i,1),rows); % retrieve original coordinates
% of pixels from matrix "image"
y = floor(sortIndex(i,1)/rows) +1;
if image(x,y) > 0.5 * max % filter out background noise
max_sort(i,:) = [x,y];
else
continue
end
end
You know that MATLAB indexing starts at 1, because you do +1 when you compute y. But you forgot to subtract 1 from the index first. Here is the correct computation:
index = sortIndex(i,1) - 1;
x = mod(index,rows) + 1;
y = floor(index/rows) + 1;
This computation is performed by the function ind2sub, which I recommend you use.
Edit: Actually, ind2sub does the equivalent of:
x = rem(sortIndex(i,1) - 1, rows) + 1;
y = (sortIndex(i,1) - x) / rows + 1;
(you can see this by typing edit ind2sub. rem and mod are the same for positive inputs, so x is computed identically. But for computing y they avoid the floor, I guess it is slightly more efficient.
Note also that
image(x,y)
is the same as
image(sortIndex(i,1))
That is, you can use the linear index directly to index into the two-dimensional array.
Hi i'm having a problem where I have a dataset which ranges between -10^3 to 10^3
I need to be able to plot this as with a log scale but semilogy cannot plot negative values
Say for example my data is:
x = [-3,-2,-1,0,1,2,3];
y = [-1000,-100,-10,1,10,100,1000];
(or in general y=sign(x).*10.^abs(x);)
How can I plot this in MATLAB with a log scale? If possible It would be great if the log scale ticks could be on the Y-axis too
Use your actual data as labels, but scale the plotted data with log10.
% data
x = -3:0.1:3;
y = sign(x).*10.^abs(x);
% scaling function
scale = #(x) sign(x).*log10(abs(x));
N = 7; % number of ticks desired
% picking of adequate values for the labels
TickMask = linspace(1,numel(y),N);
YTickLabels = y(TickMask);
% scale labels and plotdata, remove NaN ->inconsistency, do you really want that?
YTick = scale( YTickLabels );
Y = scale(y);
YTick(isnan(YTick)) = 0;
Y(isnan(Y)) = 0;
% plot
plot(x,Y)
set(gca,'YTick',YTick,'YTickLabels',YTickLabels)
grid on
For N = 7:
For N = 11
How to find a valid value for N?
The following function (thanks to gnovice) will return all possible values you could choose for N:
n = numel(x);
N = find(rem(n./(1:n), 1) == 0) + 1;
about the semilogy-style labels: by adding the following line before the plot:
YTickLabels = cellfun(#(x) ['10^' num2str(x)], num2cell(YTick),'UniformOutput',false)
you could at least achieve something like this:
not beautiful and not generic, but a good point to start for you.
The reason you can't make a logarithmic axis that crosses zero, is that it doesn't make sense!
Since a logarithmic scale is generally displayed as eg. 100 - 10 - 1 - 1/10 - 1/100 - ..., you would need an infinite amount of space to make the axis cross zero.
How about this:
x=logspace(-3,3);
y=sign(x).*10.^abs(x);
loglog(x,y)
#thewaywewalk has already given a beautiful solution to it. The one I'm suggesting is an epsilon improvement on it. If you make two changes
(a) Define a new MATLAB function signia that basically extracts the sign before a number.
function value = signia(x)
if(x>=0)
value = '';
else
value = '-';
end
and (b) make this little change that instead of
YTickLabels = cellfun(#(x) ['10^' num2str(x)], num2cell(YTick),'UniformOutput',false)
you use
YTickLabels = cellfun(#(x) [signia(x) '10^{' num2str(x) '}'], num2cell(YTick),'UniformOutput',false);
(notice the presence of curly braces), you'll get an improvement in the Y ticks display. I got the following.
enter image description here
I have a vector with different values.
Some of the values are zeros and sometimes they even come one after another.
I need to plot this vector against another vector with the same size but I can't have zeros in it.
What is the best way I can do some kind of interpolation to my vector and how do I do it?
I tried to read about interpolation in mat-lab but I didn't understand good enough to implement it.
If it's possible to explain it to me step by step I will be grateful since I'm new with this program.
Thanks
Starting from a dataset consisting of two equal length vectors x,y, where y values equal to zero are to be excluded, first pick the subset excluding zeros:
incld = y~=0;
Then you interpolate over that subset:
yn = interp1(x(incld),y(incld),x);
Example result, plotting x against y (green) and x against yn (red):
edit
Notice that, by the definition of interpolation, if terminal points are zero, you will have to take care of that separately, for instance by running the following before the lines above:
if y(1)==0, y(1) = y(find(y~=0,1,'first'))/2; end
if y(end)==0, y(end) = y(find(y~=0,1,'last'))/2; end
edit #2
And this is the 2D version of the above, where arrays X and Y are coordinates corresponding to the entries in 2D array Z:
[nr nc]=size(Z);
[X Y] = meshgrid([1:nc],[1:nr]);
X2 = X;
Y2 = Y;
Z2 = Z;
excld = Z==0;
X2(excld) = [];
Y2(excld) = [];
Z2(excld) = [];
ZN = griddata(X2,Y2,Z2,X,Y);
ZN contains the interpolated points.
In the figure below, zeros are shown by dark blue patches. Left is before interpolation, right is after: