Resampling of an audio signal with new time array - matlab

I am asking an upgraded and more focused version of my query as I think I remained unable to explain properly in my last query.
I want to down sample my signal based on new time array.
I have time array and sample array.
t = [0 2 3 7 8 9 10 11 12 17 18 19 20];
A = [0 0 1 2 3 5.2 0 -1.4 0 2 2.7 2 2.3];
New time array is:
Tx = 1:4:25;
I am using interpolation proposed by Andrei Davydov on stackoverflow but I think I am at fault some point.
Can someone point me out where I am at fault? Thanks a lot in advance.
If the value of A is same at Tx as w.r.t (t)then use that value and if no value is found interpolate value and assign that value to new time .
Example Code:
t = [0 2 3 7 8 9 10 11 12 17 18 19 20 ];
A = [0 0 1 2 3 5.2 0 -1.4 0 2 2.7 2 2.3 ];
plot(t,A)
Tx = 1:4:25;
B = interp1(t,A,Tx); %re-make example data to have decimal points on the x-axis
y = resample(B, 1, 2);
T = 0.05;
Ty = T / (1 / 2);
ty = (0:length(y)-1)*Ty;
figure
plot(Tx,B,'b')
hold on
plot(ty,y,'r')
plot(t,A,'g')
hold off
Link to my previous question is attached here.
Resampling of time signal in MATLAB
Note :
This is now exactly what i want so this is more clear and mature.
I am using MATLAB version 2012b so please provide me solution w.r.t that as there are few Matlab build in command which don't work in 2012b.

The main Your problem - you try to extrapolate, as original time ends at 20, but Yours ends at 25. Try this code:
clc
t = [0 2 3 7 8 9 10 11 12 17 18 19 20 ];
A = [0 0 1 2 3 5.2 0 -1.4 0 2 2.7 2 2.3 ];
Tx = 1:4:25; % Are you sure you want to extrapolate?
% max(Tx1)>max(t)
% This variant is WITHOUT extrapolation
B = interp1(t,A,Tx);
% This variant is WITH extrapolation and WHOLE time-series interpolated
% cubically
extrapBcub=interp1(t,A,Tx,'pcchip');
% If you want to have linear (default) interpolation, but cubic
% extrapolation then
extrapBlin=[B(~isnan(B)), extrapBcub(isnan(B))];
It gives the following figure:
f=figure('Position',[50 50 1500 800])
h1=subplot(1,2,1);
hold all
h(1)=plot(t,A,'-ok','LineWidth',3)
h(2)=plot(Tx,B,'-ob','LineWidth',9)
h(3)=plot(Tx,extrapBcub,':or','LineWidth',7)
h(4)=plot(Tx,extrapBlin,'-og','LineWidth',3)
xlabel('time')
ylabel('signal')
set(gca,'Box','off','Color','none','FontSize',14,'LineWidth',2)
legend({'Original data','No extrapolation','Cubic all',...
'Linear interpolation+cubic extrapolation'},'Location','SouthOutside',...
'FontSize',22)
legend boxoff
h2=subplot(1,2,2);
hold all
h3 = copyobj(h(end:-1:1), h2) % copy plots just for scaling
ylim([-2 6])
xlabel('time')
ylabel('signal')
set(gca,'Box','off','Color','none','FontSize',14,'LineWidth',2)

Related

Nonlinear Filter for image processing - looking for minimum inside a mask

I have an idea about a filter for images but I do not know how I can realize this without using a double-for-loop in MATLAB.
I have an image, and I want to use a linear filter mask on it, let's say:
[1,1,1,1,1]
This filter mask is moving over the image, pixel by pixel. For each neighbourhood, the pixel I is set to the minimum of the surrounding neighbourhood.
Here is an example:
[ … image data …]
[ … …]
[ … …]
[ … 23 68 155 20 53 …]
[ … …]
For my example, I want to filter the centering pixel with the value 155. The result would be:
[ … image data …]
[ … …]
[ … …]
[ … 23 68 20 20 53 …]
[ … …]
The pixel 155 gets replaced with the minimum value in his neighbourhood.
I can do this with a double-for-loop, but it is really slow, too slow to use it for my application.
Would be happy for a good idea how to increase the speed! Thank you
Your filter idea is called erosion. It is implemented in the Image Processing Toolbox in the function imerode. In your case, you'd apply:
result = imerode(image_data, [1,1,1,1,1]);
The neighborhood can have any shape. Set elements to 0 to exclude them from the neighborhood. For example, for a roundish neighborhood you can use
[0,1,1,1,0
1,1,1,1,1
1,1,1,1,1
1,1,1,1,1
0,1,1,1,0]
If I understand your question correctly, what you want is finding a moving minimum value with a specified window width along a specific row. This can be done with movmin function which was introduced in version 2016a.
Knowing that movmin processes columns by default (as dim = 1). So in your case, you may want to set the dim argument to 2 (move along the rows) and discard the endpoints for the value outside of the window. A sample code may look like:
k = randi(20,20,11); % make some samples
ci = 6; % sample at the center row
wd = 5; % filter window width
k(:,ci) = movmin(k(:,(ci-2:ci+2)),wd,2,'Endpoints','discard') % replace the center row samples
Take a look at the movmin documentation to learn more.
I was working on an own solution, when the answer of Y. Chang came up... I wanted to post it nevertheless. At least, the result is the same, so it seems to work.
% Test input.
A = round(rand(5) * 10)
% Dimensions.
nRows = size(A, 1);
nCols = size(A, 2);
% Kernel.
b = [1, 1, 1, 1, 1]
% Step size.
step = floor(numel(b) / 2);
% Output.
B = zeros(nRows, nCols);
for k = 1:nRows
temp = repmat(A(k, :), nCols + 2 * step, 1);
idx = double(triu(ones(size(temp)), -numel(b) + 1) & tril(ones(size(temp)), 0));
idx(idx == 0) = NaN;
temp = temp .* idx;
temp = min(temp, [], 2).';
B(k, :) = temp(step+1:end-step);
end
B
% Compare with movmin function.
BB = movmin(A, numel(b), 2)
Output:
A =
9 2 1 6 7
2 5 9 1 7
2 8 5 10 4
2 0 6 5 8
8 3 10 7 6
b =
1 1 1 1 1
B =
1 1 1 1 1
2 1 1 1 1
2 2 2 4 4
0 0 0 0 5
3 3 3 3 6
BB =
1 1 1 1 1
2 1 1 1 1
2 2 2 4 4
0 0 0 0 5
3 3 3 3 6

Aligning data arrays by closest time

I have 2 data vectors with corresponding time vectors. This data is sampled nearly simultaneously but they have slightly different timestamps (from machine precision transmission delays etc.). One or both of the data vectors experience occasional data losses & occasional double samples due to telemetry issues.
I want to match up the data arrays to where their times match to perform some math operations between them. Essentially remove points from y1 & y2 where they do not have corresponding times x1 & x2 (within about 1/2 of the sample rate to be considered a match).
Note I do not want to interpolate y1 & y2
%Sample time stamps: Real ones are much faster and not as neat.
x1 = [1 2 3 4 5 5.1 6 7 8 10 ]; %note double sample at ~5.
x2 = [.9 4.9 5.9 6.9 8.1 9.1 10.1]; %Slightly different times.
%Sample data: y is basically y1+1 if no data was missing
y1 = [1 2 3 4 5 5 6 7 8 10];
y2 = [2 6 7 8 9 10 11];
So the result should look like:
y1_m = [1 5 6 7 8 10];
y2_m = [2 6 7 8 9 11];
What I have so far: I used interp1 to find the closest time points between the 2 time arrays. Then got the time delta between them like this:
>> idx = interp1(x2,1:numel(x2),x1,'nearest','extrap')
idx =
1 1 2 2 2 2 3 4 5 7
>> xDelta = abs(x2(idx) - x1)
xDelta =
0.1000 1.1000 1.9000 0.9000 0.1000 0.2000 0.1000 0.1000 0.1000 0.1000
Now what I think I need to do is for each unique idx find the min xDelta and that should get me all the matching points. However, I haven't come up with a clever way of doing that... It seems like accumarray should be useful here but so far I failed at using it.
Here's a rough idea which you could likely improve upon, using unique and ismembertol:
function [y1_m, y2_m] = q48723002
%% Stage 0 - Setup:
%Sample time stamps: Real ones are much faster and not as neat.
x1 = [1 2 3 4 5 5.1 6 7 8 10 ]; %note double sample at ~5.
x2 = [.9 4.9 5.9 6.9 8.1 9.1 10.1]; %Slightly different times.
%Sample data: y is basically y1+1 if no data was missing
y1 = [1 2 3 4 5 5 6 7 8 10];
y2 = [2 6 7 8 9 10 11];
%% Stage 1 - Remove repeating samples:
SR = 0.5; % Sampling rate, for rounding.
[~,Loc1] = ismembertol(x1,round(x1/SR)*SR,SR/2,'DataScale',1);
[~,Loc2] = ismembertol(x2,round(x2/SR)*SR,SR/2,'DataScale',1);
u1 = unique(Loc1);
u2 = unique(Loc2);
x1u = x1(u1);
y1u = y1(u1);
x2u = x2(u2);
y2u = y2(u2);
clear Loc1 Loc2
%% Stage 2 - Get a vector of reference time steps:
ut = union(u1,u2);
%% Stage 3 - Only keep times found in both
[In1,Loc1] = ismembertol(ut,x1u,SR/2,'DataScale',1);
[In2,Loc2] = ismembertol(ut,x2u,SR/2,'DataScale',1);
valid = In1 & In2;
%% Stage 4 - Output:
y1_m = ut(Loc1(valid)); % equivalently: y1_m = ut(valid)
y2_m = y1_m + 1;
ans =
1 5 6 7 8 9
See also: uniquetol.
Here is a solution based on #Cris Luengo's comment on the original question.
It uses a sortrows & unique to get the lowest time error for each pairing of data points.
%Sample time stamps: Real ones are much faster and not as neat.
x1 = [1 2 3 4 5 5.1 6 7 8 10 ]; %note double sample at ~5.
x2 = [.9 4.9 5.9 6.9 8.1 9.1 10.1]; %Slightly different times.
%Sample data: y is basically y1+1 if no data was missing
y1 = [1 2 3 4 5 5 6 7 8 10];
y2 = [2 6 7 8 9 10 11];
%Find the nearest match
idx = interp1(x2,1:numel(x2),x1,'nearest','extrap');
xDiff = abs(x2(idx) - x1);
% Combine the matched indices & the deltas together & sort by rows.
%So lowest delta for a given index is first.
[A, idx1] = sortrows([idx(:) xDiff(:)]);
[idx2, uidx] = unique(A(:,1),'first');
idx1 = idx1(uidx); %resort idx1
%output
y1_m = y1(idx1)
y2_m = y2(idx2)
y1_m =
1 5 6 7 8 10
y2_m =
2 6 7 8 9 11

Assigning values in neighborhood of local maxima’s to the value of local maxima based on varying window width (non symmetric window width)

This question is an extension of my previous question with some new issues so I thought to make a new query. I hope it is ok.
https://stackoverflow.com/questions/46054811/changing-the-values-in-the-neighbourhood-of-local-maxima-to-the-local-maxima/46055833#46055833
Query:
Where I find local maxima. I want to make a window and assign the values depending upon window size to the neighbors the value of local maxima.
Problem: (I want my window size to change as in my original signal I have different behavior around local maxima’s.) For example in 8th window local maxima is at 34th location but I have assigned the values on the left of 34th location the value of local maxima.
In short I want to have varying and controllable window width.
Please have a look at the attached picture to get an idea of output what I want.
I hope it will give some good idea.
I want to have varying and non symmetric means not same at every local maxima window width .
I have also attached a code for nlfilter which is doing exactly what I want to do means it makes window and assign values to local maximas within that window width but I need to have flexible and changeable window width.
Is it possible to have varying window width or is there some other way possible to do that.
enter image description here
Code:
t = 1:35 ;
Y = [1 3 13 6 2 7 5 4 2 4 1 0 1 2 3 5 0 0 1 0 0 2 3 6 7 0 0 8 0 1 1 2 3 4 2];
n = 2;
[p l] = findpeaks(Y);
locations = zeros(size(Y));
locations(l) = true;
locations = conv(locations, ones(1, 2*n+1), 'same') > 0;
X = -inf(size(Y)); % create temporary
X(l) = Y(l); % copy the local maxima
X = nlfilter(X, [1 2*n+1 ], #(x) max(x)); %replace all values with it local maxima
X(l) = Y(l); % ensure local maxima are not changed
Y(locations) = X(locations); % copy filtered temporary to output
figure(1)
hold on
plot(t,Y,'r')
t = 1:35 ;
Y = [1 3 13 6 2 7 5 4 2 4 1 0 1 2 3 5 0 0 1 0 0 2 3 6 7 0 0 8 0 1 1 2 3 4 2];
plot(t,Y,'b')
hold off
I shall be grateful to you for your valuable replies.
Further Explanation:
Please have a look at the pictures attached.
2nd picture is a part of original signal with local maximas mentioned as green dots.
In 1st pictures the red lines shows the region which I want to assign the value of local maxima. Green Dot is local maxima . So you will see that if I apply window with fixed width it will not work because the points before local maxima are less than after local maxima.
The reason for placing 1 outside in example is same that there are few points before local maxima which I want to flat as compared to after local maxima.
The same is the case with other windows like last window 8 I have local maxima on 34th location but why I have chosen large values before it is only due to the values I want to assign the values of local maxima .
You can define a criterion that starting form a peak and going to the both sides we compute variance of neighbors of the peak and we increase the radius of the neighborhood until variance of neighboring elements becomes greater than a predefined threshold.
Here idx_peaks is position of peaks and peaks is value of peaks. After applying the threshold you can get number of elements before and after position of each peak n_before and n_after. Then you can find indices of neighborhood and assign values to them.
Y = [1 3 13 6 2 7 5 4 2 4 1 0 1 2 3 5 0 0 1 0 0 2 3 6 7 0 0 8 0 1 1 2 3 4 2];
idx_peaks = [3 6 10 16 19 25 28 34];
peaks = [13 7 4 5 1 7 8 4];
threshold = 2;
cumvar = #(a)cumsum(a(:).^2)./(1:numel(a)).'-(cumsum(a(:))./(1:numel(a)).').^2;
categ = zeros(numel(Y),1);
categ(idx_peaks)=1;
forward_categ = cumsum(categ(idx_peaks(1):end));
n_after = accumarray(forward_categ,Y(idx_peaks(1):end),[],#(x)sum(cumvar(x)<threshold)-1).';
backward_categ = cumsum(flipud(categ(1:idx_peaks(end))));
n_before = flipud(accumarray(backward_categ,fliplr(Y(1:idx_peaks(end))),[],#(x)sum(cumvar(x)<threshold)-1)).';
lo = idx_peaks-n_before;
up = idx_peaks+n_after;
val = repelem(peaks,up-lo+1);
index=cumsum(accumarray(cumsum([1;up(:)-lo(:)+1]),[lo(:);0]-[0;up(:)]-1)+1);
index= index(1:end-1);
Y(index) = val
Here is the result when setting the threshold to 2 :
Y=
[1 3 13 6 2 7 4 4 4 4 4 4 1 5 5 5 1 1 1 1 1 1 1 7 7 0 0 8 4 4 4 4 4 4 4]

Error in extracting values from bar chart?

d=[1 2 3 4;5 6 7 8;9 10 11 12;13 14 15 16]
bar_widh=0.2;
figure;
h = bar(d,bar_widh);
for i=1:2:4
for j=1:4
x=d(i,j)
y=d(i+1,j)
figure,plot(x,y);
end
i=i+1;
end
In this code I had plotted a bar chart and I want to extract values from bar chart. (Values are correctly extracted but only one point is plotted; not a line) But i'm getting wrong results.
My aim is to plot a line between
d(1,1) and d(2,1);d(3,1) and d(4,1);
d(1,2) and d(2,2);d(3,2) and d(4,2);
d(1,3) and d(2,3);d(3,3) and d(4,3);
d(1,4) and d(2,4);d(3,4) and d(4,4);
In first figure I need 2 lines(from 1 column); in second figure I need 2 lines(from 2 column); in third figure I need 2 lines(from 3 column) and in fourth figure I need 2 lines(from 4 column).
no.of figures=no.of columns
Version 2
I tried another version
d=[1 2 3 4;5 6 7 8;9 10 11 12;13 14 15 16]
bar_widh=0.2;
figure;
h = bar(d,bar_widh);
saveas(h,'testfigure.fig');
clear
close all
h=hgload('testfigure.fig');
ch=get(h,'Children');
l=get(ch,'Children');
x=get(l,'Xdata');
y=get(l,'Ydata');
and i'm getting error as
Error using get
Conversion to double from cell is not possible.
Error in Untitled5 (line 10)
l=get(ch,'Children');
d=[1 2 3 4;5 6 7 8;9 10 11 12;13 14 15 16];
bar_widh=0.2;
figure;
h = bar(d,bar_widh);
figure; hold on;
for i = 1:size(d,2)
x = [d(1,i) d(2,i)];
y = [d(3,i) d(4,i)];
plot(x,y);
end
To plot a line, you must make sure the parameters x and y in plot(x,y) are vectors than scalars. For example, to plot a line between P1 = [P1x, P1y] and P2 = [P2x, P2y], the paramenters should be:
x = [P1x, P2x]; y = [P1y, P2y];

How to show only the existing data points on x axis of bar graph in MATLAB?

I need to simple plot B vs. A as bar plot in MATLAB, but I don't want my x axis showing completely from 1 to 274. I only need to show the existing data point on my x axis, which can be done easily in Excel as in the image below. How can MATLAB do this?
A=[1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 20 25 27 29 37 40 42 43 48 73 204 242 274];
B=[30 15 5 9 5 6 3 3 2 1 4 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1];
You need to set both 'XTick' and 'XTickLabel' axes properties:
bar(B);
set(gca,'XTickLabel',A)
set(gca,'XTick',1:numel(A));
xlim([0 numel(A)+1]);
Here is an inelegant, but, nonetheless, working solution to your question:
x = [1,4, 6, 7]; % Your data
uni = unique(x)
yMax = length(find(x == mode(x))) + 1;
c = cell(1, length(uni));
c = strread(num2str(uni),'%s')
hist(1:length(uni));
axis([0 length(uni) 0 yMax])
set(gca, 'XTick', 1:length(uni));
set(gca, 'XTickLabel', c);
Basically, this plots the histogram as if the data were spread from 1 to the number of unique elements. Then, it sets the tick marks at each histogram value. Then, it labels each tick mark with the correct number.