Convert matlab line plot to stl - matlab

I'm trying to output Voronoi cells to a format which can be used for 3D printing.
MATLAB generates voronoi cells from lists of X and Y coordinates. My script generates such a list of points, but getting to a format I can export is seeming problematic.
My main hopes lie with stlwrite, http://www.mathworks.com/matlabcentral/fileexchange/20922-stlwrite-write-binary-or-ascii-stl-file
This function/script requires a surface to export.
function [lines, LineData, pOut] = makeSurfaceFromVorVerts(Lx, Ly, varargin)
p = inputParser;
addRequired(p,'Lx',...
#(x) (isequal(class(x),'double') && isequal(size(x,1),2)));
addRequired(p,'Ly',...
#(x) (isequal(class(x),'double') && isequal(size(x,1),2)));
defaultResolution = 100;
addOptional(p,'Resolution',defaultResolution,...
#(x) (isequal(class(x),'double') && isequal(size(x),[1 1])));
defaultBoundary = [0 110; 0 110];
addOptional(p,'Boundaries',defaultBoundary,...
#(x) (isequal(class(x),'double') && isequal(size(x),[2 2])));
parse(p,Lx,Ly,varargin{:});
pOut = p;
LX = p.Results.Lx;
LY = p.Results.Ly;
Bounds = p.Results.Boundaries;
% Strip high values
reducedXdat = [];
reducedYdat = [];
for i=1:size(LX,2)
if LX(1,i) > Bounds(1,1) && LX(1,i) < Bounds(1,2) && ... % X value of start of line
LX(2,i) > Bounds(2,1) && LX(2,i) < Bounds(2,2) && ... % Y value of start of line
LY(1,i) > Bounds(1,1) && LY(1,i) < Bounds(1,2) && ... % X value of end of line
LY(2,i) > Bounds(2,1) && LY(2,i) < Bounds(2,2), % Y value of end of line
reducedXdat = [reducedXdat, LX(:,i)];
reducedYdat = [reducedYdat, LY(:,i)];
end
end
% Initialise a grid of points
%sXnew = (Bounds(1,2) - Bounds(1,1)) * p.Results.Resolution;
%sYnew = (Bounds(2,2) - Bounds(2,1)) * p.Results.Resolution;
%Z = zeros(sXnew, sYnew,'uint8');
%for x=1:size(X,1)
% for y=1:size(Y,1)
% nX = floor(X(x)*p.Results.Resolution);
% nY = floor(Y(y)*p.Results.Resolution);
% Z(nX,nY) = 1;
% end
%end
%surface = Z;
%coords = [X,Y];
lines = line(reducedXdat,reducedYdat);
LineData = [reducedXdat; reducedYdat];
end
My processing script, above, takes the points generated by the command
[Lx, Ly] = voronoi(xValuesOfCellCentres, yValuesOfCellCentres);
along with an optional 'boundary' matrix (there's also a check for resolution, for the commented section) and then outputs lines.
I would like these lines to form the surface. I considered creating a mesh using a binary Z value (1 for points, 0 for everywhere else), but I don't know how I could also include the positions between points, ie those covered by the lines.
I expect that there is some relevant middle step I can take, to create a frame based on extrusion of the lines drawn (either by this script, which has cut away the extra lines to infinity, or by voronoi(X,Y), but I can't work it out.

Found a way to do this.
Changed the script, new script is pasted at the bottom.
Workflow goes:
Matlab (create line plot, then for i=1:size(lineHandles,1), set(lineHandles(i),'lineWidth',4),end). Change the line width as required.)
Save as .png file.
Gimp (crop file to a nice rectangle)
InkScape (open png file, click image, menu Path -> Trace Bitmap -> Color Quantization (2 colors) -> Drag the path aside (it shows path in the status bar at the bottom when you have this selected). Click image and delete. Place path (with nodes) back onto the page (there's an x/y coordinates setting in a toolbar at the top, set these both to 0.
Save as a .dxf file. To get these to input more nicely, use the extension provided at http://www.thingiverse.com/thing:14221. This should be installed by copying the .py and .inx files to /usr/share/inkscape/extensions (or similar)
Open in OpenSCAD using the command import("/path/to/filename.dxf");
This object can be extruded using the linear_extrude(height = x) where x is a height in mm (other lengths probably configurable)
Render using CGAL (F6 is the shortcut for this in OpenSCAD.)
Export to .stl file (menu Design > Export as STL...)
MATLAB script (edit as needed and take outputs as required, if you want the line handles you'll need to put in almost all of the output arguments (or reorder them):
%voronoiScriptHex generates voronoi cells in a hexagonal tesselation grid
% [X, Y, Fig, Axis, Lx, Ly, lH, lD] = voronoiScript(Bnd, Spc, D, ...)
%
% Output variables
% X is the x coordinate of the voronoi cell centres
% Y is the y coordinate of the voronoi cell centres
% Fig is the handle of the figure generated
% Axis is the handle of the axes used
% Lx gives the start and end x coordinates of the voronoi lines
% Ly gives the start and end y coordinates of the voronoi lines
% lH is the set of handles for the voronoi lines
% lD is constructed from [Lx; Ly], it is a [4 by Length] array
%
% Bnd specifies the boundaries for the region to be covered. It should be
% either one number, or a pair of numbers in a [1x2] vector, specifying
% [maxX, maxY]. 0 is taken as the minimum value.
%
% Spc specifies the average spacing. For a hex grid, it only accepts one
% value.
%
% D specifies the variation from a uniform grid. It is multiplied by a
% random number between -0.5 and 0.5 and added to the [x,y] coordinates
% of a point. If size(D) = [1x2], then the values are for [Dx, Dy].
%
% Optional arguments can be used to place some points exactly on the grid
% they would lie on with D[x/y] = 0. The first should be 'PartFixed' -
% this is a boolean and if true, some points are fixed to the grid.
%
% The second argument is 'FractionFixed'. This is an integer value
% (double class variables are accepted, but floor(arg) must be equal to
% (arg)). It specifies inversely how often points should be fixed, eg a
% value of 1 fixes every point, whilst a value of 5 fixes 1/5 of the
% points.
%
% PlotScatter is another boolean value, which sets if a scatter plot of
% the X,Y values corresponding to the cell centres should be included in
% the figure.
function [X, Y, Figure, Axis, Lx, Ly, lineHandles, lineData] = ...
voronoiScriptHex(Boundary, Spacing, Delta, varargin)
p = inputParser;
p.FunctionName = 'voronoiScript';
addRequired(p, 'Boundary', #checkTypes);
addRequired(p, 'Spacing', #isnumeric);
addRequired(p, 'Delta', #checkTypes);
defaultPart = false;
addOptional(p, 'PartFixed', defaultPart, #islogical);
defaultFraction = 2;
addOptional(p, 'FractionFixed', defaultFraction, #isAnInt);
defaultScatter = false;
addOptional(p, 'PlotScatter', defaultScatter, #islogical);
parse(p, Boundary, Spacing, Delta, varargin{:});
% Get values for boundaries and delta
% (Can be vectors or scalars)
if isequal(size(p.Results.Boundary),[1,2])
% Boundary is a vector [maxX, maxY]
BoundaryY = p.Results.Boundary(1,2);
else
BoundaryY = p.Results.Boundary(1,1);
end
if isequal(size(p.Results.Delta),[1,2])
% Delta is a vector [dX, dY]
DeltaY = p.Results.Delta(1,2);
else
DeltaY = p.Results.Delta(1,1);
end
Spacing = p.Results.Spacing;
BoundaryX = p.Results.Boundary(1,1);
DeltaX = p.Results.Delta(1,1);
D1 = [2*Spacing*cosd(30), Spacing];
numP = [ceil(BoundaryX/D1(1,1)) ceil(BoundaryY/D1(1,2))];
D2 = D1 ./ 2;
% Create the values
counter = 1;
xList(numP(1,1)*numP(1,2)) = 0;
yList(numP(1,1)*numP(1,2)) = 0;
for x=1:numP(1,1)
for y = 1:numP(1,2)
xList(counter) = (getPointValue(x, D1(1,1), DeltaX)-D2(1,1));
xList(counter+1) = getPointValue(x, D1(1,1), DeltaX);
yList(counter) = (getPointValue(y, D1(1,2), DeltaY)-D2(1,2));
yList(counter+1) = getPointValue(y, D1(1,2), DeltaY);
counter = counter + 2;
end
end
% Set some of the points to be without random change
if (p.Results.PartFixed),
for counter=1:p.Results.FractionFixed:size(xList,2),
[x, y] = getXYfromC(counter, numP(1,2));
xList(counter) = x*Spacing;
yList(counter) = y*Spacing;
end
end
X = xList;
Y = yList;
% Set manual ticks for the figure axes
ticksX = zeros(1,numP(1,1)+1);
for i=1:numP(1,1)+1,
ticksX(i) = i*D1(1,1);
end
ticksY = zeros(1,numP(1,2)+1);
for i=1:numP(1,2)+1,
ticksY(i) = i*D1(1,2);
end
BoundCoeff = 1.08;
Bounds = [0 BoundCoeff*BoundaryX; 0 BoundCoeff*BoundaryY];
% Give the figure a handle that is returned, and set axes values
Figure = figure;
Axis = axes;
axis equal;
minor = 'off';
gridtoggle = 'off';
set(Axis,'XTickMode','manual','YTickMode','manual', ...
'XGrid',gridtoggle,'YGrid',gridtoggle, ...
'XMinorGrid',minor,'YMinorGrid',minor, ...
'XTick',ticksX,'YTick',ticksY, ...
'XMinorTick',minor,'YMinorTick',minor, ...
'XLim',[0 Bounds(1,2)],'YLim',[0 Bounds(2,2)]);
%set(Axis,'XLim',[0 Bounds(1,2)],'YLim',[0 Bounds(2,2)]);
% Create the voronoi cells, returning the line points
[Lx, Ly] = voronoi(X,Y);
% Strip high values
counter = 1;
reducedXdat = zeros(2,size(Lx,2));
reducedYdat = zeros(2,size(Lx,2));
for i=1:size(Lx,2)
if Lx(1,i) > Bounds(1,1) && Lx(1,i) < Bounds(1,2) && ... % X value of start of line
Lx(2,i) > Bounds(2,1) && Lx(2,i) < Bounds(2,2) && ... % Y value of start of line
Ly(1,i) > Bounds(1,1) && Ly(1,i) < Bounds(1,2) && ... % X value of end of line
Ly(2,i) > Bounds(2,1) && Ly(2,i) < Bounds(2,2), % Y value of end of line
reducedXdat(:,counter) = Lx(:,i);
reducedYdat(:,counter) = Ly(:,i);
counter = counter + 1;
end
end
Lx = reducedXdat(:,1:counter-1);
Ly = reducedYdat(:,1:counter-1);
% Plot the voronoi lines
lineHandles = line(Lx, Ly);
% Set colours to black
if (1)
for i=1:size(lineHandles,1)
set(lineHandles(i),...
'LineWidth',3, ...
'LineSmoothing','on', ...
'Color',[0 0 0]);
end
end
lineData = [Lx; Ly];
if (p.Results.PlotScatter)
hold on;
scatter(X,Y);
end
end
function bool = checkTypes(arg)
bool = (isequal(class(arg),'double') && ...
(isequal(size(arg),[1,1]) || isequal(size(arg),[1,2])));
end
function bool = isAnInt(arg)
bool = (isequal(floor(arg),arg) && ...
isequal(size(arg),[1,1]) && ...
isnumeric(arg));
end
function val = getPointValue(intV, spacing, delta)
val = (((rand(1)-0.5)*delta)+(intV*spacing));
end
function [x,y] = getXYfromC(counter, sizeY)
x = floor(counter/sizeY)+1;
y = counter - ((x-1)*sizeY);
end
SCAD script file to place the voronoi cells within a pair of cylinders for 3D printing a grid. Edit as needed with path, or changes to shapes etc:
$fn = 360;
inkFile = "/path/to/my/file";
scl = 1.05; // Scale variable
transVec = [-100, -98, 0]; // Move the imported image pattern so that it's centred.
union(){
makeRing([0,0,3],inR=96, outR=99, height=6);
makeRing([0,0,1.5], inR=99, outR=102, height=3);
intersection() {
#linear_extrude(height = 6.05, convexity=40, center=false) // Removing the # will get rid of the overlay, which helps see where the grid is.
scale([scl, scl, 1])
translate(transVec)
import(inkFile);
makeCylinder([0,0,3], 96, 6, $fn=360);
}
}
module makeCylinder(centre, radius, height) {
translate(centre){
cylinder(h = height, r = radius, center=true);
}
}
module makeRing(centre,inR, outR, height) {
difference() {
makeCylinder(centre, outR, height);
makeCylinder(centre, inR, height+0.1);
}
}

Related

How to remove lines that are on obstacles PRM example

I have the following code to generate a PRM map that will be used for A* application. There exist 2 problems with the code
It keeps the blue lines representing the original PRM where lines can cross over obstacles. I don't want to keep the blue lines but I couldn't find the way to remove them.
The green lines are going over obstacles even though they shouldn't
The code is as follows
clc;
clear all;
close all;
seed = 123512;
rng(seed);
xaxis = 100;
yaxis = 100;
obstacles = false(xaxis,yaxis);
[X,Y] = meshgrid(1:xaxis,1:yaxis);
obstacles(50:75,50:75) = true;
obstacles(25:35,30:40) = true;
obstacles(25:35,60:80) = true;
figure;
imshow(~obstacles,"InitialMagnification",1000);
axis([0 xaxis 0 yaxis]);
axis xy;
axis on;
%PRM PARAMETERS
max_nodes_connect = 4;
max_connect_len = 40;
segments = 1;
max_nodes_grid = 30;
skipped = 0;
%PRM ALGO
nodes = 0; %Counter
map = zeros(size(obstacles)); %generate map
map(obstacles) = 1; %put the obstacles
Graph_connections = Inf(max_nodes_grid,max_nodes_connect + 1); %rows = # nodes cols = ID and neighbors
while (nodes < max_nodes_grid)
node_x = randi(xaxis);
node_y = randi(yaxis);
if(map(node_y,node_x)==1 || map(node_y,node_x)==2)
continue;
end
nodes = nodes + 1; %a valid node generated
map(node_y,node_x) = 2; %2 means there exists a node at that location
hold on
scatter(node_x,node_y,"red","filled")
%NODES TO CONNECT
nodes_to_connect = [];
distances = [];
for i= 1:numel(Graph_connections(:,1))
if(Graph_connections(i,1)==Inf)
break
end
[row,col] = ind2sub(size(map),Graph_connections(i,1));
%Check if within range
if(norm([node_y,node_x]-[row,col])>max_connect_len)
continue;
end
line_on_obstacle = check_obstacle(map,node_x,node_y,row,col);
%Check if obstacle thru line HAS TO BE WRITTEN
if(line_on_obstacle)
disp("Check Obstacle: " + line_on_obstacle);
skipped = skipped + 1;
continue;
end
nodes_to_connect = [nodes_to_connect, Graph_connections(i,1)];
distances = [distances; [Graph_connections(i,1),norm([node_y,node_x]-[row,col])]];
end
Graph_connections(nodes,1) = sub2ind(size(map),node_y,node_x);
if(size(distances)>0)
sorted_distances = sortrows(distances,2);
for i = 1:min(max_nodes_connect,size(sorted_distances,1))
Graph_connections(nodes,i+1) = sorted_distances(i,1);
[row,col] = ind2sub(size(map),sorted_distances(i,1));
if(line_on_obstacle==false)
disp("Line is not on obstacle")
hold on
plot([node_x,col],[node_y,row],"green","LineWidth",1.5);
continue;
else
disp("Line is on obstacle: " + [node_x,col] + " " + [node_y,row]);
break;
end
end
disp("==========================")
end
end
function on_line = check_obstacle(map,node_x,node_y,row,col)
on_line = 0;
my_line = line([node_x,col],[node_y,row]);
line_spacing = max(abs(my_line.XData(1) - my_line.XData(2))+1,abs(my_line.XData(1) - my_line.XData(2))+1);
x_coordinates_line = round(linspace(my_line.XData(1),my_line.XData(2),line_spacing));
y_coordinates_line = round(linspace(my_line.YData(1),my_line.YData(2),line_spacing));
for i = 1:line_spacing
if(map(x_coordinates_line(i),y_coordinates_line(i))==1)
disp("ON OBSTACLE: " + x_coordinates_line(i) + " " + y_coordinates_line(i));
on_line = true;
break;
end
end
end
The check_obstacle function is used to check if the points on the line are in the boundaries of obstacles. What am I missing here?
close all;clear all;clc
format short
nx=100;ny=100; % grid size
[X,Y]=meshgrid(1:nx,1:ny);
Nobst=3 % amount obstacles
Nnet=30 % amount net points
Dmax=100 % net segment max length
% OBSTACLES
% define obstacles.
% Following are as defined in question
P1=[50 50; 75 50; 75 75; 50 75; 50 50];
P2=[25 30; 25 40; 35 40; 35 30; 25 30];
P3=[25 60; 25 80; 35 80;35 60;25 60];
% obstacle points all in one array
Pobst=[P1(:);P2(:);P3(:)];
Pobst=reshape(Pobst,[size(P1,1),size(P1,2),Nobst]);
% plot obstacles
hp=[]
figure(1)
ax=gca
hp=patch(squeeze(Pobst(:,1,:)),squeeze(Pobst(:,2,:)),[.5 .5 .5])
axis([1 nx 1 ny]);grid on
hp.EdgeAlpha=0;
ax.DataAspectRatio=[1 1 1]
hold(ax,'on')
% obstacle segments list : [x1 y1 x2 y2 d(X1,Y1)]
Lobst1=[]
for k=2:1:size(P1,1)
Lobst1=[Lobst1; [P1(k-1,:) P1(k,:) ((P1(k-1,1)-P1(k,1))^2+(P1(k-1,2)-P1(k,2))^2)^.5]];
end
Lobst2=[]
for k=2:1:size(P2,1)
Lobst2=[Lobst2; [P2(k-1,:) P2(k,:) ((P2(k-1,1)-P2(k,1))^2+(P2(k-1,2)-P2(k,2))^2)^.5]];
end
Lobst3=[]
for k=2:1:size(P3,1)
Lobst3=[Lobst3; [P3(k-1,:) P3(k,:) ((P3(k-1,1)-P3(k,1))^2+(P3(k-1,2)-P3(k,2))^2)^.5]];
end
Lobst=[Lobst1;Lobst2;Lobst3]
%% NETWORK
% all grid points outside obstacles
[in1,on1]=inpolygon(X(:),Y(:),P1(:,1),P1(:,2));
[in2,on2]=inpolygon(X(:),Y(:),P2(:,1),P2(:,2));
[in3,on3]=inpolygon(X(:),Y(:),P3(:,1),P3(:,2));
Xout=X(~in1 & ~in2 & ~in3);Yout=Y(~in1 & ~in2 & ~in3);
% plot(ax,Xout,Yout,'og','LineStyle','none') % check
% choose Nnet points outside obstacles
nP2=randi([1 numel(Xout)],Nnet,1);
Pnet=[Xout(nP2) Yout(nP2)];
plot(ax,Pnet(:,1),Pnet(:,2),'or','LineStyle','none')
% net segments list [x1 y1 x2 y2 d(X2,Y2) 0/1] 6th column [0 1] 1: draw 0: do not draw
nLnet=nchoosek([1:size(Pnet,1)],2);
Lnet=[Pnet(nLnet(:,1),1) Pnet(nLnet(:,1),2) ... % segment 1st point
Pnet(nLnet(:,2),1) Pnet(nLnet(:,2),2) ... % segment 2nd point
((Pnet(nLnet(:,1),1)-Pnet(nLnet(:,1),2)).^2+(Pnet(nLnet(:,2),1)+Pnet(nLnet(:,2),2)).^2).^.5 ... % segment length
ones(size(nLnet,1),1)];
% check all net links are plotted
for k=1:1:size(Lnet,1)
plot(ax,[Lnet(k,1) Lnet(k,3)],[Lnet(k,2) Lnet(k,4)],'b');
hold(ax,'on')
end
% remove segments longer than Dmax
Lnet=sortrows(Lnet,5,'descend');
[~,n1]=max(Lnet(:,5)<=Dmax);
Lnet(1:n1-1,:)=[];
for k=1:1:size(Lnet,1)
plot(ax,[Lnet(k,1) Lnet(k,3)],[Lnet(k,2) Lnet(k,4)],'r');
hold(ax,'on')
end
%%
Redrawing and NOT removing net segments longer than Dmax
close all
hp=[]
figure(1)
ax=gca
hp=patch(squeeze(Pobst(:,1,:)),squeeze(Pobst(:,2,:)),[.5 .5 .5])
axis([1 nx 1 ny]);grid on
hp.EdgeAlpha=0;
ax.DataAspectRatio=[1 1 1]
hold(ax,'on')
plot(ax,Pnet(:,1),Pnet(:,2),'or','LineStyle','none')
Lnet=[Pnet(nLnet(:,1),1) Pnet(nLnet(:,1),2) ... % segment 1st point
Pnet(nLnet(:,2),1) Pnet(nLnet(:,2),2) ... % segment 2nd point
((Pnet(nLnet(:,1),1)-Pnet(nLnet(:,1),2)).^2+(Pnet(nLnet(:,2),1)+Pnet(nLnet(:,2),2)).^2).^.5 ... % segment length
ones(size(nLnet,1),1)];
% check what pair segments intersect
for k2=1:1:size(Lnet,1)
allclear=ones(1,size(Lobst,1));
% allclear=zeros(1,size(Lobst,1));
for k1=1:1:size(Lobst,1)
% segments are contained in lines : check lines intersect
x1o=Lobst(k1,1);y1o=Lobst(k1,2);x2o=Lobst(k1,3);y2o=Lobst(k1,4);
x1n=Lnet(k2,1);y1n=Lnet(k2,2);x2n=Lnet(k2,3);y2n=Lnet(k2,4);
x1=x1o;x2=x2o;y1=y1o;y2=y2o;
x3=x1n;x4=x2n;y3=y1n;y4=y2n;
% t1 : x parameter
t1=((x1-x3)*(y3-y4)-(y1-y3)*(x3-x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
xp=x1+t1*(x2-x1); % xp : crossing x coordinage
% u1 : y parameter
u1=-((x1-x2)*(y1-y3)-(y1-y2)*(x1-x3))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4));
yp=y1+t1*(y2-y1); % yp : crossing y coordinate
% checks
plot(ax,x1o,y1o,'c*');plot(ax,x2o,y2o,'c*');plot(ax,[x1o x2o],[y1o y2o],'c')
plot(ax,x1n,y1n,'m*');plot(ax,x2n,y2n,'m*'); % plot(ax2,[x1n x2n],[y1n y2n],'m')
m1o=(y2o-y1o)/(x2o-x1o); % slopes
m2n=(y2n-y1n)/(x2n-x1n) ;
if (t1>=0 && t1<=1 && u1>=0 && u1<=1) && ...
(xp>=0 || xp<=nx || yp>=0 || yp<=ny)
allclear(k1)=0; % then do not plot this segment
end
end
if sum(allclear)==size(Lobst,1) %k2-th net segment hits no obstacles
plot(ax,x1n,y1n,'m*');plot(ax,x2n,y2n,'m*');plot(ax,[x1n x2n],[y1n y2n],'m')
elseif sum(allclear)~=size(Lobst,1)
Lnet(k2,end)=0;
end
end
Comments
1.- Note I have added format short because with format long there is decimal accretion right at the bottom of a few intersection points, that believe it or not, cause some false results.
2.- I like Torsen's explanation to find line segment intersections available here.
3.- There are faster ways to implement the main loop, but I make it go through all net-segment vs obstacle-element in case you may need it this way, to for instance count hits.
4.- There's also room for improvement in the way Lobst is generated.
5.- These lines
x1=x1o;x2=x2o;y1=y1o;y2=y2o;
x3=x1n;x4=x2n;y3=y1n;y4=y2n;
it's just an easy way to plug in formulation to already written lines, reducing amount variables is also left for next version.

How do I find local threshold for coefficients in image compression using DWT in MATLAB

I'm trying to write an image compression script in MATLAB using multilayer 3D DWT(color image). along the way, I want to apply thresholding on coefficient matrices, both global and local thresholds.
I like to use the formula below to calculate my local threshold:
where sigma is variance and N is the number of elements.
Global thresholding works fine; but my problem is that the calculated local threshold is (most often!) greater than the maximum band coefficient, therefore no thresholding is applied.
Everything else works fine and I get a result too, but I suspect the local threshold is miscalculated. Also, the resulting image is larger than the original!
I'd appreciate any help on the correct way to calculate the local threshold, or if there's a pre-set MATLAB function.
here's an example output:
here's my code:
clear;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%% COMPRESSION %%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% read base image
% dwt 3/5-L on base images
% quantize coeffs (local/global)
% count zero value-ed coeffs
% calculate mse/psnr
% save and show result
% read images
base = imread('circ.jpg');
fam = 'haar'; % wavelet family
lvl = 3; % wavelet depth
% set to 1 to apply global thr
thr_type = 0;
% global threshold value
gthr = 180;
% convert base to grayscale
%base = rgb2gray(base);
% apply dwt on base image
dc = wavedec3(base, lvl, fam);
% extract coeffs
ll_base = dc.dec{1};
lh_base = dc.dec{2};
hl_base = dc.dec{3};
hh_base = dc.dec{4};
ll_var = var(ll_base, 0);
lh_var = var(lh_base, 0);
hl_var = var(hl_base, 0);
hh_var = var(hh_base, 0);
% count number of elements
ll_n = numel(ll_base);
lh_n = numel(lh_base);
hl_n = numel(hl_base);
hh_n = numel(hh_base);
% find local threshold
ll_t = ll_var * (sqrt(2 * log2(ll_n)));
lh_t = lh_var * (sqrt(2 * log2(lh_n)));
hl_t = hl_var * (sqrt(2 * log2(hl_n)));
hh_t = hh_var * (sqrt(2 * log2(hh_n)));
% global
if thr_type == 1
ll_t = gthr; lh_t = gthr; hl_t = gthr; hh_t = gthr;
end
% count zero values in bands
ll_size = size(ll_base);
lh_size = size(lh_base);
hl_size = size(hl_base);
hh_size = size(hh_base);
% count zero values in new band matrices
ll_zeros = sum(ll_base==0,'all');
lh_zeros = sum(lh_base==0,'all');
hl_zeros = sum(hl_base==0,'all');
hh_zeros = sum(hh_base==0,'all');
% initiate new matrices
ll_new = zeros(ll_size);
lh_new = zeros(lh_size);
hl_new = zeros(lh_size);
hh_new = zeros(lh_size);
% apply thresholding on bands
% if new value < thr => 0
% otherwise, keep the previous value
for id=1:ll_size(1)
for idx=1:ll_size(2)
if ll_base(id,idx) < ll_t
ll_new(id,idx) = 0;
else
ll_new(id,idx) = ll_base(id,idx);
end
end
end
for id=1:lh_size(1)
for idx=1:lh_size(2)
if lh_base(id,idx) < lh_t
lh_new(id,idx) = 0;
else
lh_new(id,idx) = lh_base(id,idx);
end
end
end
for id=1:hl_size(1)
for idx=1:hl_size(2)
if hl_base(id,idx) < hl_t
hl_new(id,idx) = 0;
else
hl_new(id,idx) = hl_base(id,idx);
end
end
end
for id=1:hh_size(1)
for idx=1:hh_size(2)
if hh_base(id,idx) < hh_t
hh_new(id,idx) = 0;
else
hh_new(id,idx) = hh_base(id,idx);
end
end
end
% count zeros of the new matrices
ll_new_size = size(ll_new);
lh_new_size = size(lh_new);
hl_new_size = size(hl_new);
hh_new_size = size(hh_new);
% count number of zeros among new values
ll_new_zeros = sum(ll_new==0,'all');
lh_new_zeros = sum(lh_new==0,'all');
hl_new_zeros = sum(hl_new==0,'all');
hh_new_zeros = sum(hh_new==0,'all');
% set new band matrices
dc.dec{1} = ll_new;
dc.dec{2} = lh_new;
dc.dec{3} = hl_new;
dc.dec{4} = hh_new;
% count how many coeff. were thresholded
ll_zeros_diff = ll_new_zeros - ll_zeros;
lh_zeros_diff = lh_zeros - lh_new_zeros;
hl_zeros_diff = hl_zeros - hl_new_zeros;
hh_zeros_diff = hh_zeros - hh_new_zeros;
% show coeff. matrices vs. thresholded version
figure
colormap(gray);
subplot(2,4,1); imagesc(ll_base); title('LL');
subplot(2,4,2); imagesc(lh_base); title('LH');
subplot(2,4,3); imagesc(hl_base); title('HL');
subplot(2,4,4); imagesc(hh_base); title('HH');
subplot(2,4,5); imagesc(ll_new); title({'LL thr';ll_zeros_diff});
subplot(2,4,6); imagesc(lh_new); title({'LH thr';lh_zeros_diff});
subplot(2,4,7); imagesc(hl_new); title({'HL thr';hl_zeros_diff});
subplot(2,4,8); imagesc(hh_new); title({'HH thr';hh_zeros_diff});
% idwt to reconstruct compressed image
cmp = waverec3(dc);
cmp = uint8(cmp);
% calculate mse/psnr
D = abs(cmp - base) .^2;
mse = sum(D(:))/numel(base);
psnr = 10*log10(255*255/mse);
% show images and mse/psnr
figure
subplot(1,2,1);
imshow(base); title("Original"); axis square;
subplot(1,2,2);
imshow(cmp); colormap(gray); axis square;
msg = strcat("MSE: ", num2str(mse), " | PSNR: ", num2str(psnr));
title({"Compressed";msg});
% save image locally
imwrite(cmp, 'compressed.png');
I solved the question.
the sigma in the local threshold formula is not variance, it's the standard deviation. I applied these steps:
used stdfilt() std2() to find standard deviation of my coeff. matrices (thanks to #Rotem for pointing this out)
used numel() to count the number of elements in coeff. matrices
this is a summary of the process. it's the same for other bands (LH, HL, HH))
[c, s] = wavedec2(image, wname, level); %apply dwt
ll = appcoeff2(c, s, wname); %find LL
ll_std = std2(ll); %find standard deviation
ll_n = numel(ll); %find number of coeffs in LL
ll_t = ll_std * (sqrt(2 * log2(ll_n))); %local the formula
ll_new = ll .* double(ll > ll_t); %thresholding
replace the LL values in c in a for loop
reconstruct by applying IDWT using waverec2
this is a sample output:

Simulating random walkers which can not collide into each other in Matlab

I have written a code to simulate the motion of circular particles in a 2d box. Whenever they move out of the box, I put them inside the box and near the wall. I want to add the diameter (2R) of particles in the code, which means when the distance between the center of two circles become less than 2R, they separate along the line connecting their centers so that the distance between the centers of the circles becomes equal to 2R.
Could anyone suggest a code to perevent the overlapping of particles?
This is my code in which overlap is not considered:
clear all
close all
l = 224; nn = 800; %number of particles
time = 1000; dd = 1;
x= l*rand(1,nn);
y= l*rand(1,nn);
for t = 1:time;
x= x + rand(1,nn)-0.5* ones(1,nn);
y=y+rand(1,nn)-0.5* ones (1,nn);
index = (x < 0); x(index) = abs(normrnd(0,1,1,nnz(index)));
index = (y < 0); y(index) = abs(normrnd(0,1,1,nnz(index)));
index = (x > l); x(index) = l-abs(normrnd(0,1,1,nnz(index)));
index = (y > l); y(index) = l-abs(normrnd(0,1,1,nnz(index)));
end
Here is some commented code which does what you want. Notably:
psize is some defined particle size for interaction.
point-to-point distances found using pdist2.
points that are too close are moved away from each other by some amount (dp times their current distances, if dp=1/2 then their x and y distances double) until there are no clashes.
See comments for details.
clear; close all;
l = 224; nn = 800; % number of particles
time = 100;
x = l*rand(1,nn); y = l*rand(1,nn);
psize = 2; % Particle size for interaction
dp = 0.1;
figure; hold on; axis([0 l 0 l]);
for t = 1:time;
% Random movement
movement = 2*rand(2,nn)-1;
x = x + movement(1,:);
y = y + movement(2,:);
index = (x < 0); x(index) = abs(normrnd(0,1,1,nnz(index)));
index = (y < 0); y(index) = abs(normrnd(0,1,1,nnz(index)));
index = (x > l); x(index) = l-abs(normrnd(0,1,1,nnz(index)));
index = (y > l); y(index) = l-abs(normrnd(0,1,1,nnz(index)));
% Particle interaction. Loop until there are no clashes. For
% robustness, some max iteration counter should be added!
numclash = 1;
while numclash > 0
dists = pdist2([x;y]', [x;y]'); % Distances between all particles
dists(dists < psize) = NaN; % Those too close are assigned NaN
tooclose = isnan(tril(dists,-1)); % All NaNs identified by logical
[clash1,clash2] = find(tooclose); % Get particles which are clashing
numclash = numel(clash1); % Get number of clashes
% All points where there was a clash, move away from each other
x(clash1) = x(clash1) + (x(clash1)-x(clash2))*dp;
x(clash2) = x(clash2) - (x(clash1)-x(clash2))*dp;
y(clash1) = y(clash1) + (y(clash1)-y(clash2))*dp;
y(clash2) = y(clash2) - (y(clash1)-y(clash2))*dp;
end
% Plot to visualise results. Colour fade from dark to bright green over time
scatter(x,y,'.','markeredgecolor',[0.1,t/time,0.4]);
drawnow;
end
hold off
Result:
Edit:
For a clearer diagram, you could initialise some colour matrix C = rand(nn,3); and plot using
scatter(x,y,[],C*(t/time),'.'); % the (t/time) factor makes it fade from dark to light
This would give each particle a different colour, which also fade from dark to light, rather than just fading from dark to light as before. The result would be something like this:

Changing color of plot lines in a loop

I have a question about changing the color of lines in a plot in MATLAB. I have written a code where I want to change the color of lines in a plot that is embedded in several for loops (the code is shown below). In the code, a new plot is created (with its own figure) when the "if loop" condition is satisfied. I want each plot to have lines with different colors (from other plots), so I created a variable = "NewColor" to increment and change the line colors. However, the following problem has been occurring:
Suppose I am in debug mode and I have stopped at the plot command. I run the next step and a plot is created with a blue line. I check the value of NewColor and find NewColor = 0.1. Next, I use the "run to cursor" command to step to the next time the plot command is activated. When I do this I am still within the "i for loop", so NewColor has not changed. I check the editor to find that NewColor = 0.1. Therefore, when I run the next step a blue line should show up on the current plot. To the contrary and my disbelief an orange line shows up (in addition to the blue line). I don't understand since in both steps of the debugger NewColor = 0.1, and the code is written so the color of the lines = [0,NewColor,0]. If anyone can find the error of my ways it would be greatly appreciated. Thanks
ThetaPlot = [40,50]; % Put incident angle as input
Count1 = 0;
Count2 = 0;
NewColor = 0;
for m = 1:length(ThetaPlot)
NewColor = 0.1;
Title = sprintf('Angle(%d)',ThetaPlot(m));
figure('name',Title)
Count1 = 0;
for i = 1:length(xrange)-1 % X Coordinate of Start Node
for j = 1:length(yrange)-1 % Y Coordinate of Start Node
Count1 = Count1+1;
for k = 2:length(xrange) % X Coordinate of End Node
for l = 2:length(yrange) % Y Coordinate of End Node
Count2 = Count2+1;
if ReflRayData(Count2,ThetaPlot(m) - ThetaIncident(1) + 1,Count1) == 1
x = [xrange(i),xrange(k)];
y = [yrange(j),yrange(l)];
plot(x,y,['-',[0,NewColor,0],'o']);
hold on;
end
end
Count2 = 0;
end
end
end
NewColor = NewColor + 0.02;
end
Full Code:
%% Calculating Angles of Reflection
run = 1; % Set run = 1 for calculations
if run == 1
xrange = [0:1:14.5]'; % Coordinates to try for Panel Geometry (in)
yrange = [0:1:36]'; % Coordinates to try for Panel Geometry (in)
ThetaIncident = [-90:1:90]'; % Incident Angle of Ray (measured relative to normal direction with clockwise postive)
OvenGlassXrange = [14.5:0.1:36.5]; %Range of X coordinates for Oven Glass
ReflRayData = zeros((length(xrange)-1)*(length(yrange)-1),length(ThetaIncident),(length(xrange)-1)*(length(yrange)-1)); % Matrix containing Reflected Ray Data
Count1 = 0;
Count2 = 0;
for i = 1:length(xrange)-1 % X Coordinate of Start Node
for j = 1:length(yrange)-1 % Y Coordinate of Start Node
Count1 = Count1+1;
for k = 2:length(xrange) % X Coordinate of End Node
for l = 2:length(yrange) % Y Coordinate of End Node
Count2 = Count2+1;
for m = 1:length(ThetaIncident)
xStart = xrange(i);
yStart = yrange(j);
xEnd = xrange(k);
yEnd = yrange(l);
m1 = (yEnd - yStart)/(xEnd - xStart); % Slope between Start and End Nodes
b1 = yStart - m1*xStart;
m2 = 1/m1; % Slope of normal direction
b2 = (yEnd - 0.5*(yEnd - yStart)) - m2*(xEnd - 0.5*(xEnd - xStart));
ArbXCoor = 1; % Arbitary Point X Coordinate on Normal Line
ArbYCoor = m2*ArbXCoor+b2; % Arbitary Point Y Coordinate on Normal Line
ThetaReflected = -ThetaIncident(m); % Reflected Angle
ArbXCoorRot = ArbXCoor*cosd(ThetaReflected) - ArbYCoor*sind(ThetaReflected); % Arbitary Point X Coordinate on Reflected Line
ArbYCoorRot = ArbYCoor*cosd(ThetaReflected) + ArbXCoor*sind(ThetaReflected); % Arbitary Point Y Coordinate on Reflected Line
m3 = (ArbYCoorRot - (yEnd - 0.5*(yEnd - yStart)))/(ArbXCoorRot - (xEnd - 0.5*(xEnd - xStart))); % Slope of Reflected Line
b3 = (yEnd - 0.5*(yEnd - yStart)) - m3*(xEnd - 0.5*(xEnd - xStart));
ElemLength = sqrt((yEnd - yStart)^2 + (xEnd - xStart)^2);
if min(OvenGlassXrange) < -b3/m3 && -b3/m3 < max(OvenGlassXrange) && -1 < m1 && m1 < 0 && m1 ~= -Inf && m1 ~= Inf && ElemLength < 3
ReflRayData(Count2,m,Count1) = 1;
end
end
end
end
Count2 = 0;
end
end
%% Plotting
ThetaPlot = [40,50]; % Put incident angle as input
Count1 = 0;
Count2 = 0;
NewColor = 0;
for m = 1:length(ThetaPlot)
NewColor = 0.1;
Title = sprintf('Angle(%d)',ThetaPlot(m));
figure('name',Title)
Count1 = 0;
for i = 1:length(xrange)-1 % X Coordinate of Start Node
for j = 1:length(yrange)-1 % Y Coordinate of Start Node
Count1 = Count1+1;
for k = 2:length(xrange) % X Coordinate of End Node
hold on;
for l = 2:length(yrange) % Y Coordinate of End Node
Count2 = Count2+1;
if ReflRayData(Count2,ThetaPlot(m) - ThetaIncident(1) + 1,Count1) == 1
x = [xrange(i),xrange(k)];
y = [yrange(j),yrange(l)];
plot(x,y,['-',[0,NewColor,0],'o']);
hold on;
end
end
Count2 = 0;
end
end
end
NewColor = NewColor + 0.02;
end
instead of plot(x,y,['-',[0,NewColor,0],'o']); try:
plot(x,y,'linestyle','-','marker','o','color',[0,NewColor,0])
According to the Matlab documentation, by calling hold on Matlab uses the next color.
hold on retains plots in the current axes so that new plots added to the axes do not delete existing plots. New plots use the next colors and line styles based on the ColorOrder and LineStyleOrder properties of the axes.
Therefore, in your code when you call hold on inside the for, it just uses the next color.
My solution is to put figure; hold on; before for loop and remove that one in you loop

Matlab figure keeps the history of the previous images

I am working on rotating image manually in Matlab. Each time I run my code with a different image the previous images which are rotated are shown in the Figure. I couldn't figure it out. Any help would be appreciable.
The code is here:
[screenshot]
im1 = imread('gradient.jpg');
[h, w, p] = size(im1);
theta = pi/12;
hh = round( h*cos(theta) + w*abs(sin(theta))); %Round to nearest integer
ww = round( w*cos(theta) + h*abs(sin(theta))); %Round to nearest integer
R = [cos(theta) -sin(theta); sin(theta) cos(theta)];
T = [w/2; h/2];
RT = [inv(R) T; 0 0 1];
for z = 1:p
for x = 1:ww
for y = 1:hh
% Using matrix multiplication
i = zeros(3,1);
i = RT*[x-ww/2; y-hh/2; 1];
%% Nearest Neighbour
i = round(i);
if i(1)>0 && i(2)>0 && i(1)<=w && i(2)<=h
im2(y,x,z) = im1(i(2),i(1),z);
end
end
end
end
x=1:ww;
y=1:hh;
[X, Y] = meshgrid(x,y); % Generate X and Y arrays for 3-D plots
orig_pos = [X(:)' ; Y(:)' ; ones(1,numel(X))]; % Number of elements in array or subscripted array expression
orig_pos_2 = [X(:)'-(ww/2) ; Y(:)'-(hh/2) ; ones(1,numel(X))];
new_pos = round(RT*orig_pos_2); % Round to nearest neighbour
% Check if new positions fall from map:
valid_pos = new_pos(1,:)>=1 & new_pos(1,:)<=w & new_pos(2,:)>=1 & new_pos(2,:)<=h;
orig_pos = orig_pos(:,valid_pos);
new_pos = new_pos(:,valid_pos);
siz = size(im1);
siz2 = size(im2);
% Expand the 2D indices to include the third dimension.
ind_orig_pos = sub2ind(siz2,orig_pos(2*ones(p,1),:),orig_pos(ones(p,1),:), (1:p)'*ones(1,length(orig_pos)));
ind_new_pos = sub2ind(siz, new_pos(2*ones(p,1),:), new_pos(ones(p,1),:), (1:p)'*ones(1,length(new_pos)));
im2(ind_orig_pos) = im1(ind_new_pos);
imshow(im2);
There is a problem with the initialization of im2, or rather, the lack of it. im2 is created in the section shown below:
if i(1)>0 && i(2)>0 && i(1)<=w && i(2)<=h
im2(y,x,z) = im1(i(2),i(1),z);
end
If im2 exists before this code is run and its width or height is larger than the image you are generating the new image will only overwrite the top left corner of your existing im2. Try initializing im2 by adding adding
im2 = zeros(hh, ww, p);
before
for z = 1:p
for x = 1:ww
for y = 1:hh
...
As a bonus it might make your code a little faster since Matlab won't have to resize im2 as it grows in the loop.