How can a repeated pattern of struct assignment be condensed to a simpler expression? - matlab

I am writing code to do finite volume method operations on an NI*NJ size grid, where all the values are stored in a 1D array, defined as in comment section 1. The double loop moves through each grid element and performs calculations for the grid element, which are various functions of the surrounding elements (to the east, north, west and south).
I have an example code block here below, and my challenge is to clean up the code to eliminate some of the repitition I see. I wonder if somehow employing classes that contain properties e, w, n, s would be a beneficial approach to the problem, although I'm not well-versed in class constructions.
As can be seen, I have defined an anonymous function and then called it to calculate some value at each side (e,w,n,s). This pattern recurs numerous times throughout the entire code and it would be nice to have some way of combining such code blocks of quadruplets into one line of simple code.
arrayRe = initialize; % goes from 1:(NI*NJ)
arrayRn = initialize;
for i = 2:NI-1
for j = 2:NJ-1
% 1. coordinates (P is the center; all others are located around it (north, east, west, south))
iP = NI*(j-1) + i;
iE = iP + 1;
iW = iP - 1;
iN = iP + NI;
iS = iP - NI;
% 2. interpolation ratios
R.e = arrayRe(iP);
R.w = 1 - arrayRe(iW);
R.n = arrayRn(iP);
R.s = 1 - arrayRn(iS);
% interpolate a given value
interpolatevalue = #(array, R, i) ...
R*array(i) + (1-R)*array(iP);
% ax calc
ax_e = interpolatevalue(axArray, R.e, iE);
ax_w = interpolatevalue(axArray, R.w, iW);
ay_n = interpolatevalue(ayArray, R.n, iN);
ay_s = interpolatevalue(ayArray, R.s, iS);
% bx calc
bx_e = interpolatevalue(bxArray, R.e, iE);
bx_w = interpolatevalue(bxArray, R.w, iW);
by_n = interpolatevalue(byArray, R.n, iN);
by_s = interpolatevalue(byArray, R.s, iS);
end
end
Something I tried was to cut out the 4 lines of a particular block and put them into a function m file that returned a struct. For instance:
function a = axycalc(axArray, ayArray, R, iStruct, interpfun)
iE = iStruct.e ;
iW = iStruct.w ;
iS = iStruct.s ;
iN = iStruct.n ;
a.x_e = interpfun(axArray, R_e, iE);
a.x_w = interpfun(axArray, R_w, iW);
a.y_n = interpfun(ayArray, R_n, iN);
a.y_s = interpfun(ayArray, R_s, iS);
end
And then calling the function as follows:
a = axycalc(axArray, ayArray, R, struct('e',iE,'w',iW,'n',iN,'s',iS), #interpolatevalue)
I don't know whether this adds much in terms of readability, and creates a large amount of other m 10-line m files that I then need to keep track of. Is there a better way?

Related

UI in Matlab to get inputs to be used in a script

I am trying to create a GUI for my script in Matlab. At the moment I am opening this dialog boxes in my script to get the inputs I need.
prompt = {'Mass [kg]:','Power [kW]:','Drag Coeff:','Lift Coeff:','Area Front [m^2]:'};
dlgtitle = 'Vehicle Parameters';
dims = [1 35];
definput = {'752','650','1','3','1'};
vehicle = inputdlg(prompt,dlgtitle,dims,definput);
prompt = {'Friction Coeff:','Air Density [kg/m^3]:'};
dlgtitle = 'External Parameters';
dims = [1 35];
definput = {'1.4','1.19'};
ambient = inputdlg(prompt,dlgtitle,dims,definput);
prompt = {'Length [m]:','Corner Radius [m]:'};
dlgtitle = 'Track Parameters';
dims = [1 35];
definput = {'1000','150'};
track = inputdlg(prompt,dlgtitle,dims,definput);
Here the code continues
laptime = formula used to get the laptime;
However I would like to create something similar to what this image shows, where I can write the parameters, push the run button to run the script and get the laptime printed. Any suggestions on how I could achieve it?
Thanks a lot in advance!
steeven:
From inspecting your image, you essentially will need:
A figure object.
Ten(10) field objects.
Ten(10) Static text objects.
A pushbutton object
A callback function.
Here's a sample script:
clc
clear
close all %Very important when making GUI's in MATLAB!
%figure object that contains GUI.
f = figure('units','normalized');
%Uicontrols devoted to static text:
m_text = uicontrol('Parent',f,'units','normalized','Style','Text');
P_text = uicontrol('Parent',f,'units','normalized','Style','Text');
FS_text = uicontrol('Parent',f,'units','normalized','Style','Text');
CD_text = uicontrol('Parent',f,'units','normalized','Style','Text');
CL_text = uicontrol('Parent',f,'units','normalized','Style','Text');
CF_text = uicontrol('Parent',f,'units','normalized','Style','Text');
rho_text = uicontrol('Parent',f,'units','normalized','Style','Text');
len_text = uicontrol('Parent',f,'units','normalized','Style','Text');
rad_text = uicontrol('Parent',f,'units','normalized','Style','Text');
%Uicontrols devoted to editable fields:
m_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
P_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
FS_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
CD_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
CL_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
CF_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
rho_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
len_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
rad_edit = uicontrol('Parent',f,'units','normalized','Style','edit');
% MLG Cell array strat.
ui_texts = {m_text,P_text,FS_text,CD_text,CL_text,CF_text,rho_text,...
len_text,rad_text};
strings = {'mass','power','Front Section','Drag Coefficient',...
'Lift Coefficient','Friction Coefficient','Air Density','Length',...
'Radius'};
ui_edits = {m_edit,P_edit,FS_edit,CD_edit,CL_edit,CF_edit,rho_edit,...
len_edit,rad_edit};
defaults = {'10','100','0.5','0.05','1.2','0.0005','1.225','5','3'};
xi = 0.05; %X-coordinate of Bottom-Left corner.
yi = 0.85; %Y-coordinate of Bottom-Left corner.
width = 0.15; %Width of object.
height = 0.075; %Height of object
deltaH = 0.3; %Horizontal Spacing between objects.
deltaV = -0.25; %Vertical Spacing between objects.
offset = 0.15;
rows = 3; %Number of rows.
cols = 3; %Number of columns.
k = 0; % Counter for number of uicontrols that populate grid pattern.
for i = 1:rows
for j = 1:cols
k = k + 1;
if k <= length(ui_texts)
x = xi + deltaH*(j-1);
y = yi + deltaV*(i-1);
xo = x + offset; %horizontally offset the editable field.
set(ui_texts{k},'Position',[x, y, width, height],'String',strings{k})
set(ui_edits{k},'Position',[xo, y, width, height],'String',defaults{k})
end
end
end
%Uicontrol for output object:
out = uicontrol('Parent',f,'Style','text','units','normalized',...
'position',[xo-deltaH,y+deltaV,width,height],'String','Val = ');
%Uicontrol for button execution:
exe = uicontrol('Parent',f,'Style','pushbutton','units','normalized',...
'position',[xo-2*deltaH,y+deltaV,width,height],'String','Calculate',...
'Callback',{#computations,ui_edits,out});
%Function executed by the "exe" uicontrol.
function computations(obj,~,ui_edits,out)
no_inputs = length(ui_edits);
summation = 0;
for i = 1:no_inputs
in = get(ui_edits{i},'String');
val = str2double(in);
summation = summation +val;
end
set(out,'String',['Val = ',num2str(summation)])
end
The above script produces:
Here's the gist:
The figure is a graphical object needed to contain the editable fields and text descriptions.
The fields are objects that enable user input (These are uicontrol objects).
The static texts establish to the user the correspondence between field and input (These are also uicontrol objects).
The button is an object that executes the callback function (also a uicontrol).
The callback function is a user-defined function that collects inputs from uicontrol objects and uses them to run code.
In this example, the "calculate" button simply sums up all the numeric values of the inputs. For your applications, you must modify the programmer-defined function at the end to suit your needs.
This kind of GUI lets users input information as strings. Said information must be converted from the string type to the double type. Other GUI objects allow for direct numerical inputs (such as sliders or buttons with underlying logical structure, ie. "if this button, then this value"). When working with editable fields, the programmer must be conscious that the inputs are strings until run through a function like str2double.
Other nuances with GUI's:
Every object that the programmer intends to be interactive must have an associated programmer-defined function.
In this case, the only interactive object is the button.
Personally, my biggest challenge when making MATLAB GUI's is dealing with the positioning of the UI controls. I have used a few for loops and cell arrays to generate the grid pattern for the texts and buttons. This is quality of life on my part, more elaborate GUI's may require more sophisticated coding.
MATLAB has a feature called "GUIDE" which helps automate the uicontrol generation. This is worth a try if the programatic method I have shared is too tedious.

Average filter Matlab

I have written the 3x3 average filter. It works fine but it shows the same output image three times instead of one. How to resolve the problem?
The code is
function [filtr_image] = avgFilter(noisy_image)
[x,y] = size(noisy_image);
filtr_image = zeros(x,y);
for i = 2:x-1
for j =2:y-1
sum = 0;
for k = i-1:i+1
for l = j-1:j+1
sum = sum+noisy_image(k,l);
end
end
filtr_image(i,j) = sum/9.0;
filtr_image = uint8(filtr_image);
end
end
end
thanks in advance
What is most likely happening is the fact that you are supplying a colour image when the code is specifically meant for grayscale. The reason why you see "three" is because when you do this to allocate your output filtered image:
[x,y] = size(noisy_image)
If you have a 3D matrix, the number of columns reported by size will be y = size(noisy_image,2)*size(noisy_image,3);. As such, when you are iterating through each pixel in your image, in column major order each plane would be placed side by side each other. What you should do is either convert your image into grayscale from RGB or filter each plane separately.
Also, you have an unnecessary casting performed in the loop. Just do it once outside of the loop.
Option #1 - Filter per plane
function [filtr_image] = avgFilter(noisy_image)
[x,y,z] = size(noisy_image);
filtr_image = zeros(x,y,z,'uint8');
for a = 1 : z
for i = 2:x-1
for j =2:y-1
sum = 0;
for k = i-1:i+1
for l = j-1:j+1
sum = sum+noisy_image(k,l,a);
end
end
filtr_image(i,j,a) = sum/9.0;
end
end
end
end
Then you'd call it by:
filtr_image = avgFilter(noisy_image);
Option #2 - Convert to grayscale
filtr_image = avgFilter(rgb2gray(noisy_image));
Minor Note
You are using sum as a variable. sum is an actual function in MATLAB and you would be overshadowing this function with your variable. This will have unintended consequences if you have other functions that rely on sum later down the line.
I can't see why your code would repeat the image (unless it's a pattern cause by an integer overflow :/ ) but here are some suggestions:
if you want to use loops, at least drop the inner loops:
[x,y] = size(noisy_image);
filtr_image = zeros(x,y);
for i = 2:x-1
for j =2:y-1
% // you could do this in 1 line if you use mean2(...) instead
sub = noisy_image(i-1:i+1, j-1:j+1);
filtr_image = uint8(mean(sub(:)));
end
end
However do you know about convolution? Matlab has a built in function for this:
filter = ones(3)/9;
filtr_image = uint8(conv2(noisy_image, filter, 'same'));

Grouping of points using Triangulation

Language : MATLAB
Problem Defenition:
I have a set of 2D points in space. I would like to group the points based on their euclidean distance. My data has a property that two groups are always separated by at least R units. Hence for a given point, all points that are closer than 50 units can be considered to be its neighbors. Combining points having common neighbors would result in the groups (that is the idea at least).
Proposed Method:
Use delaunay triangulation in matlab and get list of edges of the resulting triangles. Remove all edges that are greater than R units. Each group of points left are the groups I am looking for. Remaining unconnected points can be ignored.
Attempt:
I tried to implement the above in MATLAB, but I am making a mistake in grouping the left over points. I am attaching my code.
DT = delaunayTriangulation(double(frame(:,1:2)));
edgeList = edges(DT);
edgeVertex1 = frame(edgeList(:,1),:);
edgeVertex2 = frame(edgeList(:,2),:);
dVec = edgeVertex1 - edgeVertex2;
edgeLengths = sqrt(sum(abs(dVec).^2,2));
requiredEdges = edgeLengths < NEIGH_RADIUS;
edgeLengthsFiltered = edgeLengths(requiredEdges);
edgeListFiltered = edgeList(requiredEdges,:);
% Clustering
edgeOrigins = edgeListFiltered(:,1);
edgeEndings = edgeListFiltered(:,2);
nodeList = unique(edgeOrigins);
if isempty(nodeList)
Result = struct([]);
super_struct(i).result = Result;
else
groups = cell(10,1);
groups{1} = nodeList(1);
groupLength = 2;
flag = 0;
% grouping
for j = 1:1:length(nodeList);
neighbourList = [nodeList(j); edgeEndings(edgeOrigins==nodeList(j))];
% add current node as part of neighbourList
for k = 1:1:groupLength-1
te = ismembc(groups{k}, neighbourList);
if sum(te) ~=0
temp = sort([groups{k}; neighbourList]);
groups{k} = temp([true;diff(temp(:))>0]);
flag = 1;
break;
end
end
if ~flag
groups{groupLength} = neighbourList;
groupLength = groupLength + 1;
end
flag = 0;
end
largeGroups = cell(1,1);
largeGroups_c = 1;
for j = 1:1:groupLength -1;
if ~ isempty(groups{j})
for k = j+1:1:groupLength - 1
te = ismembc(groups{j}, groups{k});
if sum(te) ~= 0
temp = sort([groups{j}; groups{k}]);
groups{j} = temp([true;diff(temp(:))>0]);
groups{k} =[];
end
end
% ignore small groups
if length(groups{j}) > MIN_PTS_IN_GROUP
largeGroups{largeGroups_c} = groups{j};
largeGroups_c = largeGroups_c+1;
end
end
end
in the above code, frame is the variable that has the list of points. The constants NEIGH_RADIUS represents R from the question. The other constant MIN_PTS_IN_GROUP is user defined to select the minimum no of points needed to consider it a cluster of interest.
When I run the above code, there are still instances where a single group of points are still represented as multiple groups.
The red lines border a single group as identified by the code above. Clearly there are intersecting groups which is wrong.
Question 1
Can someone suggest a better (and correct) way of grouping?
Question 2
Any other alternate methods of obtaining the groups faster than Triangulation would also be great!
Thank you in advance
If you know the number of group you search for, you can use kmeans function in matlab statistic toolbox or you can find other implentation on matlab exchange (kmeans clustering)

Image manipulation in matlab

I am a very new programmer (this is my second exercise), and I have been asked to shift an image (of a Galaxy) by a sinusoidal shift. I have tried to write a programme to do this as follows:
My programme:
GalaxyImage = imread('hs-2013-06-a-web.jpg');
image(GalaxyImage)
GalaxyImage=double(GalaxyImage)/255;
image(Sess2GalaxyIm(GalaxyImage));
My function:
function [GalaxySlanted] = Sess2GalaxyIm(GalaxyImage)
A = 20;
k = 3;
Y = size(GalaxyImage, 1);
X = size(GalaxyImage, 2);
max_shift = ceil(A*2);
GalaxySlanted = zeros(Y, X+max_shift, 3);
for y=1:Y
local_shift = ceil(A*sind(k*y));
local_x = 1:X;
local_x = local_x + local_shift;
GalaxySlanted(y, local_x, :)=GalaxyImage(y, :, :);
end
end
At the moment, when I run it, it seems to just be shifting my image by a constant amount and I don't see why.
Your image's shift is constant because in local_shift = ceil(A*sind(k*Y)); you used constant values for A,K and Y. may be this modification makes your code correct:(y instead of Y)
local_shift = ceil(A*sind(k*y));
There is a possible issue with how you define local_x. You start by adding some padding onto the image to compensate for the fact that the output x could be up to "A" pixels shifted on either side:
max_shift = ceil(A*2);
GalaxySlanted = zeros(Y, X+max_shift, 3);
However, in the loop, you ignore this and only start off with local_x as 1:X - so if the output from sind is negative you will be trying to index negative numbers into GalaxySlanted and get a resulting error. Changing that line to local_x = (1:X)+A; should work (with oMiD's modification of the shift).
Another quick note - there is actually a function im2double which will handle the image conversion, and scaling, in one go.

lagranges method

I found following code on internet. I am new to matlab. Now the problem whenever i copy-paste this code then it shows me error message.
function[p] = lagrange_interpolation(X,Y)
|
Error: Function definitions are not permitted in this context.
The code snippet is:
function[p] = lagrange_interpolation(X,Y)
L = zeros(n);
p = zeros(1,n);
% computing L matrice, so that each row i holds the polynom L_i
% Now we compute li(x) for i=0....n ,and we build the polynomial
for k=1:n
multiplier = 1;
outputConv = ones(1,1);
for index = 1:n
if(index ~= k && X(index) ~= X(k))
outputConv = conv(outputConv,[1,-X(index)]);
multiplier = multiplier * ((X(k) - X(index))^-1);
end
end
polynimialSize = length(outputConv);
for index = 1:polynimialSize
L(k,n - index + 1) = outputConv(polynimialSize - index + 1);
end
L(k,:) = multiplier .* L(k,:);
end
% continues
end
In all likelihood, you are probably attempting to mix random code along with your function. There are two types of M files:
scripts - have "random" code that is executed independent of anything else
functions - are the "classic" definition of functions
You cannot mix the two (that's a lie, but for now a good one). So if you are defining a function, that should be the only code in your .m file.
You should later use this function in either the command window or another function or a script by calling it via p = blahblah(bleaurgh);.
TL;DR: Make sure the function code is the only code in the script file, save it with the same name.m, call the function from somewhere else.