Can't I use graphshortestpath function at here? - matlab

I want to calculate Diameter of graph, which means greatest distance between any two vertices of G.
cm is connectivity matrix of graph, and diameter of graph should be in variable a.
But MATLAB gave me some error message 'Input argument should be a sparse array.'
Can't I use graphshortestpath function to calculate diameter? Then what should I do instead?
cm = [0,1,1,1,0;1,0,0,1,0;0,1,0,0,0;1,0,0,0,0;0,0,0,0,0];
bg = biograph(cm);
a = 1;
for i = 1:4
for j = (i+1):5
[dist,path,pred] = graphshortestpath(bg,i,j)
if a<=dist
a = dist
end
end
end

I haven't tested this (I don't have MATLAB here), but how about making cm sparse, and use that as input to graphshortestpath?
According to the documentation, "[The first argument must be an] N-by-N sparse matrix that represents a graph. Nonzero entries in matrix G represent the weights of the edges." Thus, you should not use the biograph as input.
Check our the first example in the documentation, it explains it very well!
cm_full = [0,1,1,1,0;1,0,0,1,0;0,1,0,0,0;1,0,0,0,0;0,0,0,0,0];
cm = sparse(cm_full);
bg = biograph(cm);
a = 1;
for i = 1:4
for j = (i+1):5
[dist,path,pred] = graphshortestpath(cm,i,j)
if a<=dist
a = dist
end
end
end
end

Related

K-nearest neighbourhood in a spcific range in MATLAB

I am dealing with k-neighbour problem in MATLAB. There is an image with row r and column c. And divide it into r*c blocks - each blcok represents a patch centered in each pixel.
And I want to find the k-nearest neighbourbood of each blcok within a specific search range. At first I use knnsearch with kdTree:
ns = createns(Block','nsmethod','kdtree');
[Index_nearest,dist] = knnsearch(ns,Block','k',k+1);
However, I find that it would find k-nearest neighbourhood in all blocks, instead of the specific range. Therefore, is there any other method to achieve the goal? Could anyone give me some hints? Thanks in advance!
Edit: the code for knnsearch:
function [Index_nearest, Weight] = Compute_Weight(Input, Options)
% Input the data and pre-processing
windowsize = Options.winsize;
k = Options.directionsize;
deviation = Options.deviation; % Deviation for Gaussian kernel
h = Options.h; % This parameter is for controling the weights
[r,c] = size(Input);
In_pad = padarray(Input, [windowsize windowsize], 'symmetric');
window_size = (2*windowsize+1)*(2*windowsize+1);
Block = zeros(window_size,r*c);
%% Split the input data into blocks
for i = 1:r
for j = 1:c
block = In_pad(i:i+2*windowsize,j:j+2*windowsize);
Block(:,r*(i-1)+j) = block(:); % expand from column to column
end
end
%% Find k-nearest neighbour blocks
% Create a KDtree with all local patches
ns = createns(Block','nsmethod','kdtree');
% Find the patches closest by in intensity in relation to the local patch itself
[Index_nearest,ddd] = knnsearch(ns,Block','k',k+1);
Index_nearest = Index_nearest';
Index_nearest = Index_nearest(2:k+1,:);
end

Best method of removing flattish areas of a signal in MatLab?

Say I have a signal that looks a bit like this:
that I am processing in MatLab, what functions would I have to use to get rid of the flattish area in the middle? is there any functions that can do that, or do I need to program it in myself? Currently I just have a blank function as I don't know where to start:
function removala = removal(a, b)
end
Is there any quick functions that can remove it or do I just have to search for all values below a threshold and remove them myself? For reference a and b are vectors of amplitude points.
use findpeaks:
% generating signal
x = 1:0.1:10;
y = rand(size(x))*0.5;
y([5,25,84]) = [6,-5.5,7.5];
z = y;
thresh = 0.75; % height threshold
% find peaks
[pks,locs] = findpeaks(z,'MinPeakProminence',thresh);
% remove signal noise between peaks
for ii = 1:length(locs)-1
zz = z(locs(ii)+1:locs(ii+1)-1);
zz(abs(zz) < thresh) = 0;
z(locs(ii)+1:locs(ii+1)-1) = zz;
end
% plot
plot(x,y);
hold on
plot(x,z);
plot(x(locs),pks,'og');
legend('original signal','modified signal','peaks')
You probably want to remove the signal whose absolute power is less than a certain threshold.
So the two input of your function would be the raw signal, and the threshold. The function will output a variable "cleanSignal"
function cleanSignal = removal(rawSignal,threshold)
simplest implementation. remove the data below threshold. If rawSignal is a matrix the resulting variable will be a vector concatenating all the epochs above threshold.
ind = abs(rawSignal)<threshold;
rawSignal(ind) = [];
cleanSignal = rawSignal;
This might not be the behavior that you want, since you want to preserve the original shape of your rawSignal matrix. So you can just "nan" the values below threshold.
ind = abs(rawSignal)<threshold;
rawSignal(ind) = nan;
cleanSignal = rawSignal;
end
Notice that this does not really removes flat signal, but signal which is close to 0.
If you really want to remove flat signal you should use
ind = abs(diff(rawSignal))<threshold;

Area Calculation of Surf plot in MATLAB

I have irregular 3D cartesian coordinates that make up the one eighth of the surface of a sphere. Thanks to Benoit_11 Answer to a previously posed question the surface can now be plotted outside of the MATLAB cftool in a normal command line script
Since this point I have been attempting to calculate the area of the surface using the following code patched together from other answers within the area. The code basically calculates the area from the vertices that produce the surface and then sums them all to produce an area.
surface = [ansx1,ansy1,ansz1];
[m,n] = size(zdata1);
area = 0;
for i = 1:m-1
for j = 1:n-1
v0_1 = [xdata1(i,j) ydata1(i,j) zdata1(i,j) ];
v1_1 = [xdata1(i,j+1) ydata1(i,j+1) zdata1(i,j+1) ];
v2_1 = [xdata1(i+1,j) ydata1(i+1,j) zdata1(i+1,j) ];
v3_1 = [xdata1(i+1,j+1) ydata1(i+1,j+1) zdata1(i+1,j+1)];
a_1= v1_1 - v0_1;
b_1 = v2_1 - v0_1;
c_1 = v3_1 - v0_1;
A_1 = 1/2*(norm(cross(a_1, c_1)) + norm(cross(b_1, c_1)));
area = area + A_1;
end
end
fprintf('\nTotal area is: %f\n\n', area);`
However the issue I am having is that the calculated surface over estimates the possible surface. This is due to the removal of NaN from the original matrix and their replacement with 0 this results in the figure 1. Figure 2 provides the only area I would like to calculate
Does anybody have a way of ignoring the zeros within the code provided to calculate the surface area of the data that generates Figure 1?
Thanks in advance
I think you only have to check if one of the four points of an field equal zero.
What about this:
% example surface
[X,Y,Z] = peaks(30);
% manipulate it
[lza, lzb] = size(Z);
for nza = 1:lza
for nzb = 1:lzb
if Z(nza,nzb) < 0
Z(nza,nzb) = Z(nza,nzb)-1;
else
Z(nza,nzb) = 0;
end
end
end
surfc(X,Y,Z)
% start calculating the surface area
A = 0;
lX = length(X);
lY = length(Y);
for nx = 1:lX-1
for ny = 1:lY-1
eX = [X(ny,nx) X(ny,nx+1)
X(ny+1,nx) X(ny+1,nx+1)];
eY = [Y(ny,nx) Y(ny,nx+1)
Y(ny+1,nx) Y(ny+1,nx+1)];
eZ = [Z(ny,nx) Z(ny,nx+1)
Z(ny+1,nx) Z(ny+1,nx+1)];
% check the field
if eZ(1,1)==0 || eZ(1,2)==0 || eZ(2,1)==0 || eZ(2,2)==0
continue
end
% take two triangles, calculate the cross product to get the surface area
% and sum them.
v1 = [eX(1,1) eY(1,1) eZ(1,1)];
v2 = [eX(1,2) eY(1,2) eZ(1,2)];
v3 = [eX(2,1) eY(2,1) eZ(2,1)];
v4 = [eX(2,2) eY(2,2) eZ(2,2)];
A = A + norm(cross(v2-v1,v3-v1))/2;
A = A + norm(cross(v2-v4,v3-v4))/2;
end
end

Image histogram implementation with Matlab

I'm tyring to implement (I know there's a custom function for achieving it) the grayscale image histogram in Matlab, so far I've tried:
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = [0:255];
for i = 1:rows
for j = 1:cols
p = openImage(i,j);
histogram_values(p) = histogram_values(p) + 1;
end
end
histogram(histogram_values)
However when I call the function, for example: histogram_matlab('Harris.png')
I obtain some graph like:
which is obviously not what I expect, x axis should go from 0 to 255 and y axis from 0 to whatever max value is stored in histogram_values.
I need to obtain something like what imhist offers:
How should I set it up? Am I doing a bad implementation?
Edit
I've changed my code to improvements and corrections suggested by #rayryeng:
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = zeros(256,1)
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) +1;
histogram_values(p) = histogram_values(p) + 1;
end
end
histogram(histogram_values, 0:255)
However the histogram plot is not that expected:
Here it's noticeable that there's some issue or error on y axis as it definitely would reach MORE than 2.
In terms of calculating the histogram, the computation of the frequency per intensity is correct though there is a slight error... more on that later. Also, I would personally avoid using loops here. See my small note at the end of this post.
Nevertheless, there are three problems with your code:
Problem #1 - Histogram is not initialized properly
histogram_values should contain your histogram, yet you are initializing the histogram by a vector of 0:255. Each intensity value should start with a count of 0, and so you actually need to do this:
histogram_values = zeros(256,1);
Problem #2 - Slight error in for loop
Your intensities range from 0 to 255, yet MATLAB starts indexing at 1. If you ever get intensities that are 0, you will get an out-of-bounds error. As such, the proper thing to do is to take p and add it with 1 so that you start indexing at 1. However, one intricacy I need to point out is that if you have a uint8 precision image, adding 1 to an intensity of 255 will simply saturate the value to 255. It won't go to 256.... so it's also prudent that you cast to something like double to ensure that 256 will be reached.
Therefore:
histogram_values = zeros(256,1);
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) + 1;
histogram_values(p) = histogram_values(p) + 1;
end
end
Problem #3 - Not calling histogram right
You should override the behaviour of histogram and include the edges. Basically, do this:
histogram(histogram_values, 0:255);
The second vector specifies where we should place bars on the x-axis.
Small note
You can totally implement the histogram computation yourself without any for loops. You can try this with a combination of bsxfun, permute, reshape and two sum calls:
mat = bsxfun(#eq, permute(0:255, [1 3 2]), im);
h = reshape(sum(sum(mat, 2), 1), 256, 1);
If you'd like a more detailed explanation of how this code works under the hood, see this conversation between kkuilla and myself: https://chat.stackoverflow.com/rooms/81987/conversation/explanation-of-computing-an-images-histogram-vectorized
However, the gist of it as below.
The first line of code creates a 3D vector of 1 column that ranges from 0 to 255 by permute, and then using bsxfun with the eq (equals) function, we use broadcasting so that we get a 3D matrix where each slice is the same size as the grayscale image and gives us locations that are equal to an intensity of interest. Specifically, the first slice tells you where elements are equal to 0, the second slice tells you where elements are equal to 1 up until the last slice where it tells you where elements are equal to 255.
For the second line of code, once we compute this 3D matrix, we compute two sums - first summing each row independently, then summing each column of this intermediate result. We then get the total sum per slice which tells us how many values there were for each intensity. This is consequently a 3D vector, and so we reshape this back into a single 1D vector to finish the computation.
In order to display a histogram, I would use bar with the histc flag. Here's a reproducible example if we use the cameraman.tif image:
%// Read in grayscale image
openImage = imread('cameraman.tif');
[rows,cols] = size(openImage);
%// Your code corrected
histogram_values = zeros(256,1);
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) + 1;
histogram_values(p) = histogram_values(p) + 1;
end
end
%// Show histogram
bar(0:255, histogram_values, 'histc');
We get this:
Your code looks correct. The problem is with the call to histogram. You need to supply the number of bins in the call to histogram, otherwise they will be computed automatically.
Try this simple modification which calls stem to get the right plot, instead of relying on histogram
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = [0:255];
for i = 1:rows
for j = 1:cols
p = openImage(i,j);
histogram_values(p) = histogram_values(p) + 1;
end
end
stem(histogram_values); axis tight;
EDIT: After some inspection of the code you have a 0/1 error. If you have a pixel of value zero then histogram_value(p) will give you an index error
Try this instead. No need for vectorization for this simple case:
function hv = histogram_matlab_vec(I)
assert(isa(I,'uint8')); % for now we assume uint8 with range [0, 255]
hv = zeros(1,256);
for i = 1 : numel(I)
p = I(i);
hv(p + 1) = hv(p + 1) + 1;
end
stem(hv); axis tight;
end

1D gaussian filter over non equidistant data

I have a data distributed in non-equidistant 1D space and I need to convolve this with a Gaussian filter,
gaussFilter = sqrt(6.0/pi*delta**2)*exp(-6.0*x**2 /delta**2);
where delta is a constant and x corresponds to space.
Can anyone hint how to perform a good integration (2nd order) as the data is not equally spaced taking care of the finite end? I intend to write the code in Fortran, but a Matlab example is also welcome.
use this:
function yy = smooth1D(x,y,delta)
n = length(y);
yy = zeros(n,1);
for i=1:n;
ker = sqrt(6.0/pi*delta^2)*exp(-6.0*(x-x(i)).^2 /delta^2);
%the gaussian should be normalized (don't forget dx), but if you don't want to lose (signal) energy, uncomment the next line
%ker = ker/sum(ker);
yy(i) = y'*ker;
end
end
Found something which works.
Though not sure if this is very accurate way as the integration (trapz) is of first order.
function [fbar] = gaussf(f,x,delta )
n = length(f);
fbar = zeros(n,1);
for i=1:n;
kernel = sqrt(6/(pi*delta^2))*exp(-6*((x - x(k))/delta).^2);
kernel = kernel/trapz(x,kernel);
fbar(i) = trapz(x,f.*kernel);
end
end