Let A be an n by 3 matrix, such that the first two columns are all ordered pairs of the form (5*i,5*i) for i from 1 to 200. The third column contains values from 0 to 1, which I will call intensities. I want to make a 1000 by 1000 plot so that the rectangle at (5*i,5*i) is shaded with intensity described by the third column entry.
I'm familiar with the heatmap function and imshow, but I don't see a way to include this "scaling by 5" to make a nice plot. And of course in general the x and y coordinates may not be scaled by the same amount.
Is there a nice way to do this in Matlab?
With imagesc it's actually pretty simple:
First some example data:
%// generate example data
ii = 1:200;
[xx,yy] = meshgrid(ii);
A(:,1) = 5*xx(:);
A(:,2) = 5*yy(:);
A(:,3) = randi([0,1],1,40000);
Actual answer
n = 200;
%// reshape data
D = reshape( A(:,3),n,n );
%// heatmap
imagesc(A(:,1),A(:,2),D)
colormap(gray)
caxis([0,1])
gives:
Important notice
If your coordinates are not sorted as required for imagesc you can sort them with:
A = sortrows(A,[2,1]);
Clown Example
%// original image
load clown
I = reshape(1:numel(X),size(X));
[R,C] = ind2sub(size(X),I);
A(:,1) = R(:);
A(:,2) = C(:);
A(:,3) = X(:);
D = reshape( A(:,3),200,320 );
figure(1)
subplot(1,3,1)
imagesc(A(:,1),A(:,2),D)
%// shuffled image -> shuffled data
shuffle = randperm(320*200);
A = A(shuffle,:);
D = reshape( A(:,3),200,320 );
subplot(1,3,2)
imagesc(A(:,1),A(:,2),D)
%// sorted image
A = sortrows(A,[2,1]);
D = reshape( A(:,3),200,320 );
subplot(1,3,3)
imagesc(A(:,1),A(:,2),D)
You see, even if your coordinates are sorted like a mess, you can rebuild the image with sortrows.
See this
function DrawHeatmap(X,Y,Z)
%DRAWHEATMAP Draw a 2D heatmap for (X,Y) coordinates whose values are in Z
% X, Y , Z must be columns
% By: Eng. Osama Talaat Abdel-Hafiz - PhD Student
% Egypt - Sept 2017
if size(X,2)==1 && size(Y,2)==1 && size(Z,2)==1
F = scatteredInterpolant(X,Y,Z); % create a function from interpolation
[X,Y] = meshgrid(min(X):0.1:max(X),min(Y):0.1:max(Y));
Z = F(X,Y);
contourf(X, Y, Z, linspace(floor(min(min(Z))),ceil(max(max(Z))),400), 'LineColor','none')
colorbar;
else
error('X, Y , Z must be columns')
end
end
Related
I have a large XYZ file (300276x3, this file includes x and y coordinates (not lat/lon, but polar stereographic) and elevation z) and I'm wondering if it would be possible to convert this into a gridded dataset (n x m matrix). The xyz file can be downloaded from:
https://wetransfer.com/downloads/4ae4ce51072dceef93486314d161509920191021213532/48e4ee68c17269bd6f7a72c1384b3c9a20191021213532/60b04d
and imported in matlab by:
AIS_SEC = importdata('AIS_SEC.xyz');
I tried:
X= XYZ(:,1);
Y= XYZ(:,2);
Z= XYZ(:,3);
xr = sort(unique(X));
yr = sort(unique(Y));
gRho = zeros(length(yr),length(xr));
gRho = griddata(X,Y,Z,xr,yr')
imagesc(gRho)
Requested 300276x300276 (671.8GB) array exceeds maximum array size preference. Creation of arrays
greater than this limit may take a long time and cause MATLAB to become unresponsive. See array size
limit or preference panel for more information.
I tried:
% Get coordinate vectors
x = unique(XYZ(:,1)) ;
y = unique(XYZ(:,2)) ;
% dimensions of the data
nx = length(x) ;
ny = length(y) ;
% Frame matrix of grid
D = reshape(XYZ(:,3),[ny,nx]) ;
% flip matrix to adjust for plot
H = flipud(H) ;
% Transpose the matrix
H = H' ; % Check if is required
surf(x,y,H) ;
Error using reshape
To RESHAPE the number of elements must not change.
I can now plot the nx3 file with scatter3 (see image)
scatter3(XYZ(:,1),XYZ(:,2),XYZ(:,3),2,XYZ(:,3)) ;
colorbar
But I'd like to do it with imagesc. Hence, I would like to convert the nx3 file into a nxm matrix (in raster/gridded format) and as en extra I would like it as a geotiff file for use in QGIS.
Thanks!
You were almost there... Looking at the message about array size you got, it seems likely that the result of unique(X) results in 300276 unique values, probably due to some noisy data.
So instead of using griddata with these large X and Y vectors, you can define some new ones on the domain you need:
% make some sample data
N = 1000;
xv = linspace(-10,10,N);
yv = linspace(-10,10,N);
[XV,YV] = meshgrid(xv,yv);
ZV = XV.^2 + YV.^2;
% make into long vectors:
X = XV(:);
Y = YV(:);
Z = ZV(:);
% make x and y vector to interpolate z
N = 50; % size of new grid
xv = linspace(min(X), max(X), N);
yv = linspace(min(Y), max(Y), N);
[XV,YV] = meshgrid(xv,yv);
% use griddata to find right Z for each x,y pair
ZV_grid = griddata(X,Y,Z,XV,YV);
% look at result
figure();
subplot(211)
imagesc(ZV);
subplot(212);
imagesc(ZV_grid)
I have written a 2D histogram algorithm for 2 matlab vectors. Unfortunately, I cannot figure out how to vectorize it, and it is about an order of magnitude too slow for my needs. Here is what I have:
function [ result ] = Hist2D( vec0, vec1 )
%Hist2D takes two vectors, and computes the two dimensional histogram
% of those images. It assumes vectors are non-negative, and bins
% are the integers.
%
% OUTPUTS
% result -
% size(result) = 1 + [max(vec0) max(vec1)]
% result(i,j) = number of pixels that have value
% i-1 in vec0 and value j-1 in vec1.
result = zeros(max(vec0)+1, max(vec1)+1);
fvec0 = floor(vec1)+1;
fvec1 = floor(vec0)+1;
% UGH, This is gross, there has to be a better way...
for i = 1 : size(fvec0);
result(fvec0(i), fvec1(i)) = 1 + result(fvec0(i), fvec1(i));
end
end
Thoughts?
Thanks!!
John
Here is my version for a 2D histogram:
%# some random data
X = randn(2500,1);
Y = randn(2500,1)*2;
%# bin centers (integers)
xbins = floor(min(X)):1:ceil(max(X));
ybins = floor(min(Y)):1:ceil(max(Y));
xNumBins = numel(xbins); yNumBins = numel(ybins);
%# map X/Y values to bin indices
Xi = round( interp1(xbins, 1:xNumBins, X, 'linear', 'extrap') );
Yi = round( interp1(ybins, 1:yNumBins, Y, 'linear', 'extrap') );
%# limit indices to the range [1,numBins]
Xi = max( min(Xi,xNumBins), 1);
Yi = max( min(Yi,yNumBins), 1);
%# count number of elements in each bin
H = accumarray([Yi(:) Xi(:)], 1, [yNumBins xNumBins]);
%# plot 2D histogram
imagesc(xbins, ybins, H), axis on %# axis image
colormap hot; colorbar
hold on, plot(X, Y, 'b.', 'MarkerSize',1), hold off
Note that I removed the "non-negative" restriction, but kept integer bin centers (this could be easily changed into dividing range into equally-sized specified number of bins instead "fractions").
This was mainly inspired by #SteveEddins blog post.
You could do something like:
max0 = max(fvec0) + 1;
max1 = max(fvec1) + 1;
% Combine the vectors
combined = fvec0 + fvec1 * max0;
% Generate a 1D histogram
hist_1d = hist(combined, max0*max1);
% Convert back to a 2D histogram
hist_2d = reshape(hist, [max0 max1]);
(Note: untested)
I was trying to plot a regression curve:
coef_fit = polyfit(norm_dist,norm_time,7);
y_fit = polyval(coef_fit,xlim);
plot(xlim,y_fit,'r');
But it is always plotting a line respective of the order I pass.
The problem is that the x values you are using are the output ot xlim, which is a length-2 vector. You need to define an x vector with more values:
norm_dist = sort(5*randn(1,50) + (1:50)); %// example x values
norm_time = 5*randn(1,50) + (1:50).^2; %// example y values
x = linspace(min(norm_dist), max(norm_dist), 200); %// define x values for plot
coef_fit = polyfit(norm_dist,norm_time,7);
y_fit = polyval(coef_fit,x);
plot(x,y_fit,'r');
hold on
plot(norm_dist, norm_time, 'b.') %// plot original points for comparison
Using the imfindcircles function in MATLAB to track circles in two images. I start with approximately a grid of circles which deforms. I am trying to sort the two column vector from imfindcircles into matrices so that neighbouring circles are neighbouring elements in the matrices. The first image the circles conform to a grid and the following code works:
[centXsort,IX] = sortrows(centres1,1); %sort by x
centYsort =zeros(289,2); %preallocate
for i = 1:17:289
[sortedY,IY] = sortrows(centXsort(i:i+16,:),2); %sort by y within individual column
centYsort(i:i+16,:) = sortedY;
end
cent1mat = reshape(centYsort,17,17,2); %reshape into centre matrices
This doesn't work for the second image as some of the circles overlap in the x or y direction, but neighbouring circles never overlap. This means that in the second set of matrices the neighbouring circles aren't neighbouring elements after sorting.
Is there a way to approximate a scatter of points into a matrix?
This answer doesn't work in every single case, but it seems good enough for situations where the points don't vary too wildly.
My idea is to start at the grid corners and work our way along the outside diagonals of the matrix, trying to "grab" the nearest points that seem like they fit into the grid-points based any surrounding points we've already captured.
You will need to provide:
The number of rows (rows) and columns (cols) in the grid.
Your data points P arranged in a N x 2 array, rescaled to the unit square on [0,1] x [0,1]. (I assume the you can do this through visual inspection of the corner points of your original data.)
A weight parameter edge_weight to tell the algorithm how much the border points should be attracted to the grid border. Some tests show that 3-5 or so are good values.
The code, with a test case included:
%// input parameters
rows = 11;
cols = 11;
edge_weight = 4;
%// function for getting squared errors between the points list P and a specific point pt
getErr =#(P,pt) sqrt( sum( bsxfun(#minus,P,pt(:)').^2, 2 ) ); %'
output_grid = zeros(rows,cols,2); %// output grid matrix
check_grid = zeros(rows,cols); %// matrix flagging the gridpoints we have covered
[ROW,COL] = meshgrid(... %// coordinate points of an "ideal grid"
linspace(0,1,rows),...
linspace(0,1,cols));
%// create a test case
G = [ROW(:),COL(:)]; %// the actual grid-points
noise_factor = 0.35; %// noise radius allowed
rn = noise_factor/rows;
cn = noise_factor/cols;
row_noise = -rn + 2*rn*rand(numel(ROW),1);
col_noise = -cn + 2*cn*rand(numel(ROW),1);
P = G + [row_noise,col_noise]; %// add noise to get points
%// MAIN LOOP
d = 0;
while ~isempty(P) %// while points remain...
d = d+1; %// increase diagonal depth (d=1 are the outer corners)
for ii = max(d-rows+1,1):min(d,rows)%// for every row number i...
i = ii;
j = d-i+1; %// on the dth diagonal, we have d=i+j-1
for c = 1:4 %// repeat for all 4 corners
if i<rows & j<cols & ~check_grid(i,j) %// check for out-of-bounds/repetitions
check_grid(i,j) = true; %// flag gridpoint
current_gridpoint = [ROW(i,j),COL(i,j)];
%// get error between all remaining points and the next gridpoint's neighbours
if i>1
errI = getErr(P,output_grid(i-1,j,:));
else
errI = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i+1,j)
errI = errI + edge_weight*getErr(P,current_gridpoint);
end
if j>1
errJ = getErr(P,output_grid(i,j-1,:));
else
errJ = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i,j+1)
errJ = errJ + edge_weight*getErr(P,current_gridpoint);
end
err = errI.^2 + errJ.^2;
%// find the point with minimal error, add it to the grid,
%// and delete it from the points list
[~,idx] = min(err);
output_grid(i,j,:) = permute( P(idx,:), [1 3 2] );
P(idx,:) = [];
end
%// rotate the grid 90 degrees and repeat for next corner
output_grid = cat(3, rot90(output_grid(:,:,1)), rot90(output_grid(:,:,2)));
check_grid = rot90(check_grid);
ROW = rot90(ROW);
COL = rot90(COL);
end
end
end
Code for plotting the resulting points with edges:
%// plotting code
figure(1); clf; hold on;
axis([-0.1 1.1 -0.1 1.1])
for i = 1:size(output_grid,1)
for j = 1:size(output_grid,2)
scatter(output_grid(i,j,1),output_grid(i,j,2),'b')
if i < size(output_grid,1)
plot( [output_grid(i,j,1),output_grid(i+1,j,1)],...
[output_grid(i,j,2),output_grid(i+1,j,2)],...
'r');
end
if j < size(output_grid,2)
plot( [output_grid(i,j,1),output_grid(i,j+1,1)],...
[output_grid(i,j,2),output_grid(i,j+1,2)],...
'r');
end
end
end
I've developed a solution, which works for my case but might not be as robust as required for some. It requires a known number of dots in a 'square' grid and a rough idea of the spacing between the dots. I find the 'AlphaShape' of the dots and all the points that lie along the edge. The edge vector is shifted to start at the minimum and then wrapped around a matrix with the corresponding points are discarded from the list of vertices. Probably not the best idea for large point clouds but good enough for me.
R = 50; % search radius
xy = centres2;
x = centres2(:,1);
y = centres2(:,2);
for i = 1:8
T = delaunay(xy); % delaunay
[~,r] = circumcenter(triangulation(T,x,y)); % circumcenters
T = T(r < R,:); % points within radius
B = freeBoundary(triangulation(T,x,y)); % find edge vertices
A = B(:,1);
EdgeList = [x(A) y(A) x(A)+y(A)]; % find point closest to origin and rotate vector
[~,I] = min(EdgeList);
EdgeList = circshift(EdgeList,-I(3)+1);
n = sqrt(length(xy)); % define zeros matrix
matX = zeros(n); % wrap x vector around zeros matrix
matX(1,1:n) = EdgeList(1:n,1);
matX(2:n-1,n) = EdgeList(n+1:(2*n)-2,1);
matX(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,1);
matX(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,1);
matY = zeros(n); % wrap y vector around zeros matrix
matY(1,1:n) = EdgeList(1:n,2);
matY(2:n-1,n) = EdgeList(n+1:(2*n)-2,2);
matY(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,2);
matY(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,2);
centreMatX(i:n+i-1,i:n+i-1) = matX; % paste into main matrix
centreMatY(i:n+i-1,i:n+i-1) = matY;
xy(B(:,1),:) = 0; % discard values
xy = xy(all(xy,2),:);
x = xy(:,1);
y = xy(:,2);
end
centreMatX(centreMatX == 0) = x;
centreMatY(centreMatY == 0) = y;
Question: is it possible to illustrate an image on non-uniform axis?
Details:
I need to illustrate a multidimensional timeseries as an image. But the time grid of this timeseries is very non-uniform. Here is an example:
m = 10;
n = 3;
t = sort(rand(m, 1)); % non-uniform time
values = randn(m, n); % some random values
The figure, plot(t, values); handles it well.
But imagesc() converts t into uniform time between t(1) and t(end) according to documentation:
imagesc(x,y,C) displays C as an image and specifies the bounds of the
x- and y-axis with vectors x and y.
Therefore, the command:
figure, imagesc(t, 1 : n, values'); colorbar;
illustrates the image on uniform time grid.
Edit: It's possible to re-sample the timeseries with higher uniform resolution. But my timeseries is already very large.
There is pcolor function in MATLAB. This function does exactly what you're asking.
m = 10;
n = 3;
t = sort(rand(m, 1)); % non-uniform time
values = randn(m, n); % some random values
figure
plot(t, values);
figure
pcolor(t, 1 : n, values');
colorbar;
try uimagesc from the file exchange.
Solution
Try using surface for non-uniform spacing.
First, create a 3D xyz surface of the same size as your input data:
m = 10;
n = 3;
t = sort(rand(m, 1)); % non-uniform time
values = randn(m, n); % some random values
x = repmat(t,1,n);
y = repmat(1:n,m,1);
z = zeros(size(y));
Then, colormap your values. There is a nice tool posted to the mathworks file exchange, real2rgb, that can do this for you:
cdata = real2rgb(values); % Where size(cdata) = [m n 3]
Lastly, plot the surface. You can even get fancy and set the transparency.
surface(x,y,z,cdata,'EdgeColor','none','FaceColor','texturemap',...
'CDataMapping','direct');
alpha(0.3)