improve performance of octave plot with many lines - performance

I wonder if there is a faster approach to create a 3D plot of multiple lines. See below for a function that simulates my current approach with some dummy data. Essentially, what I'm doing, is plotting multiple 2D lines next to each other in a 3D space to quickly compare them. Each individual line is colored by its final y value.
function test_plotmany(lines = 1000, points = 100)
# this data comes from a numeric simulation, here I just fill
# it with some dummy values - ignore this
t = [1:points];
data = [];
for i = t;
data(end + 1, :) = [1:lines] .* i;
endfor
# now the actually interesting part that I want to speed up, if possible
tic;
tsize = size(t, 2);
const = ones(tsize, 1);
lines = [1:size(data, 2)];
colors = jet(size(lines, 2));
colormap(colors);
cIdx = 1;
figure
hold on;
for i = lines;
plot3(t, data(1, i) .* const, data(:, i), 'Color', colors(cIdx, :));
cIdx = cIdx + 1;
endfor
xlabel("Foo");
ylabel("Bar");
zlabel("Baz");
grid("on");
view(45, 45);
set(gca, 'XScale', 'linear', 'YScale', 'log', 'ZScale', 'log');
cb = colorbar;
caxis([min(data(tsize, :)), max(data(tsize, :))]);
set(get(cb, 'ylabel'), 'string', 'Something');
hold off;
toc;
endfunction
The outcome for test_plotmany(100, 100) is shown below, and took already 2s. The size of a real data set I have to deal with can be simulated with test_plotmany(10000, 100), which takes minutes on my machine to create.
Is there anything I can do differently to speed this up? Is there maybe a way to create a 3D matrix or such and hand that over to a 3D plot function to recreate my graph? Or could I use multiple threads to render the graph?
Any help would be appreciated, thanks!
Note: I'm using GNU Octave, version 3.8.1 on Linux 3.16.1-1-ARCH with a Intel(R) Core(TM) i3-2310M CPU # 2.10GHz. In Octave, I use the fltk graphics_toolkit - gnuplot does not work for the above at all for me (colors are wrong, cannot interact with the graph to rotate, zoom or pan it).

Related

how to make smooth and remove the white edge? Besides, why the black lines arise in my answer? How to solve it?

Based on Shai and Biguri's codes and comments, I have finished a color picture like this:
A problem arises, how to remove the white edge and make it smooth? One solution may be to build 3x3 matrix or bigger and average. But the calculations should be large for every white-edge points. Or there may be some useful functions in Matlab to deal well with this problems?
If you have a license for the image processing toolbox, you can try using for example medfilt2 to apply a median filter on the image. A 11 by 11 median filter should do the trick. It is not very difficult to reimplement the filter yourself if you don't have the toolbox.
This is just one of the possibilities, you can use many different filters that will have different impacts on sharpness ang edge removal.
Edit:
Here is a quick median filter implementation (it may contain errors and could be optimized):
function ret = imageMedianFilter(im, np)
if(size(np,2) == 1)
npx = np;
npy = np;
else
npx = np(1);
npy = np(2);
end
ret = zeros(size(im,1),size(im,2));
for xpos = 1:size(im,1)
for ypos = 1:size(im,2)
curval = double(0);
if(xpos + npx - 1) > size(im,1)
npixels_x = size(im,1) - xpos + 1;
else
npixels_x = npx;
end
if(ypos + npy - 1) > size(im,2)
npixels_y = size(im,2) - ypos + 1;
else
npixels_y = npy;
end
a = im(xpos:xpos+npixels_x-1 , ypos:ypos+npixels_y-1);
a = reshape(a,1,size(a,1)*size(a,2));
curval = median(a);
ret(xpos , ypos) = curval;
end
end
ret = uint8(ret);
end
You can use it on R,G and B components as shown by Rotem below:
RGB = cat(3, imageMedianFilter(RGB(:,:,1), [11,11]), imageMedianFilter(RGB(:,:,2), [11,11]), imageMedianFilter(RGB(:,:,3), [11,11]));
(assuming your image is named RGB).
Here is my solution. I take n*n patch to average the near RGB. But there is a problem arising. Why the right down side of processed picture showing black lines?
clc;clf;close all;clear all;
img = imread('sample2color_t1.bmp'); %// read image
bw = img(:,:,1) > 128; %// convert to binary mask
[lb,lab] = bwlabel(bw,4); %// extract distinct regions
[a,b,c]=size(img);
R=ones(a,b);
G=ones(a,b);
B=ones(a,b);
%I have omitted other colors process codes. Below it is the white edges code.
r=[];c=[];
[r,c] = find(lb ==0);
for i=1:length(r)
R(r(i),c(i))=1;
G(r(i),c(i))=1;
B(r(i),c(i))=1;
end
scale=5;%步长1,8连通
for i=1:length(r)
sumR=0;sumG=0;sumB=0;
for j=0:2*scale
for k=0:2*scale
sumR=sumR+R(r(i)-scale+j,c(i)-scale+k);
sumG=sumG+G(r(i)-scale+j,c(i)-scale+k);
sumB=sumB+B(r(i)-scale+j,c(i)-scale+k);
end
end
R(r(i),c(i))=sumR/(2*scale+1)^2;
G(r(i),c(i))=sumG/(2*scale+1)^2;
B(r(i),c(i))=sumB/(2*scale+1)^2;
end
imPaint=cat(3,R,G,B);
figure;
imshow(imPaint);

Plot over an image background in MATLAB

I'd like to plot a graph over an image. I followed this tutorial to Plot over an image background in MATLAB and it works fine:
% replace with an image of your choice
img = imread('myimage.png');
% set the range of the axes
% The image will be stretched to this.
min_x = 0;
max_x = 8;
min_y = 0;
max_y = 6;
% make data to plot - just a line.
x = min_x:max_x;
y = (6/8)*x;
imagesc([min_x max_x], [min_y max_y], img);
hold on;
plot(x,y,'b-*','linewidth',1.5);
But when I apply the procedure to my study case, it doesn't work. I'd like to do something like:
I = imread('img_png.png'); % here I load the image
DEM = GRIDobj('srtm_bigtujunga30m_utm11.tif');
FD = FLOWobj(DEM,'preprocess','c');
S = STREAMobj(FD,flowacc(FD)>1000);
% with the last 3 lines I calculated the stream network on a geographic area using the TopoToolBox
imagesc(I);
hold on
plot(S)
The aim is to plot the stream network over the satellite image of the same area.
The only difference between the two examples that doesn't let the code working is in the plot line, in the first case "plot(x,y)" works, in the other one "plot(S)" doesn't.
Thanks guys.
This is the satellite image, imagesc(I)
It is possible that the plot method of the STREAMobj performs it's own custom plotting including creating new figures, axes, toggling hold states, etc. Because you can't easily control what their plot routine does, it's likely easier to flip the order of your plotting so that you plot your stuff after the toolbox plots the STREAMobj. This way you have completely control over how your image is added.
% Plot the STREAMobj
hlines = plot(S);
% Make sure we plot on the same axes
hax = ancestor(hlines, 'axes');
% Make sure that we can add more plot objects
hold(hax, 'on')
% Plot your image data on the same axes
imagesc(I, 'Parent', hax)
Maybe I am preaching to the choir or overlooking something here but the example you used actually mapped the image to the data range of the plot, hence the lines:
% set the range of the axes
% The image will be stretched to this.
min_x = 0;
max_x = 8;
min_y = 0;
max_y = 6;
imagesc([min_x max_x], [min_y max_y], img);
where you directly plot your image
imagesc(I);
If now your data coordinates and your image coordinates are vastly different you either see one or the other.
Thanks guys, I solved in this way:
I = imread('orto.png'); % satellite image loading
DEM = GRIDobj('demF1.tif');
FD = FLOWobj(DEM,'preprocess','c');
S = STREAMobj(FD,flowacc(FD)>1000); % Stream network extraction
x = S.x; % [node attribute] x-coordinate vector
y = S.y; % [node attribute] y-coordinate vector
min_x = min(x);
max_x = max(x);
min_y = min(y);
max_y = max(y);
imagesc([min_x max_x], [min_y max_y], I);
hold on
plot(S);
Here's the resulting image: stream network over the satellite image
Actually the stream network doesn't match the satellite image just because I'm temporarily using different images and DEM.

Mid line through a set of dicom images in matlab

I have a set of Dicom images on matlab and i would like to add a midline going through all the images
I am outputting the images via imshow3d function
thanks
Edit: here's what i have, the random points are not in the middle they just run through the image
>> clc;
>>clear;
>>%imports dicom images
>>run DicomImport.m;
>>%random points for shortest distance test
>>a = [1 10 200];
>>b = [500 512 300];
>>ab = b - a;
>>n = max(abs(ab)) + 1;
>>s = repmat(linspace(0, 1, n)', 1, 3);
>>for d = 1:3
>> s(:, d) = s(:, d) * ab(d) + a(d);
>>end
>>s = round(s);
>>Z = 593;
>>N = 512;
>>X = zeros(N, N, Z);
>>X(sub2ind(size(X), s(:, 1), s(:, 2), s(:, 3))) = 1;
>>C = find(X);
>>ans.Img(C) = 5000;
>> %shows image
>>imshow3D(ans.Img);
So it looks like ans.Img contains the 3D matrix consisting of your image stack. It looks like you've got something going, but allow me to do this a bit differently. Basically, you need to generate a set of coordinates where we can access the image stack and draw a vertical line in the middle of the each image in the image stack. Do something like this. First get the dimensions of the stack, then determine the halfway point for the columns. Next, generate a set of coordinates that will draw a line down the middle for one image. After you do this, repeat this for the rest of the slices and get the column major indices for these:
%// Get dimensions
[rows,cols,slices] = size(ans.Img);
%// Get halfway point for columns
col_half = floor(cols/2);
%// Generate coordinates for vertical line for one slice
coords_middle_row = (1:rows).';
coords_middle_col = repmat(col_half, rows, 1);
%// Generate column major indices for the rest of the slices:
ind = sub2ind(size(ans.Img), repmat(coords_middle_row, slices, 1), ...
repmat(coords_middle_col, slices, 1), ...
reshape(kron(1:slices, ones(rows, 1)), [], 1));
%// Set the pixels accordingly
ans.Img(ind) = 5000;
This code is quite similar to the answer I provided to one of your earlier question; i.e. I don't use imshow3D but the framework is similar and simpler to modify in order to suit your need. In this case, upon pressing a pushbutton a line appears at the middle of the stack and you can scroll through it with the slider. I hope this can be of help.
function LineDicom(~)
clc
clear
close all
%// Load demo data
S = load('mri');
%// Get dimensions and number of slices.
ImageHeight = S.siz(1); %// Not used here
ImageWidth = S.siz(2); %// Not used here
NumSlices = S.siz(3);
S.D = squeeze(S.D);
%// Create GUI
hFig = figure('Position',[100 100 400 400],'Units','normalized');
%// create axes with handle
handles.axes1 = axes('Position', [0.2 0.2 0.6 0.6]);
%// create y slider with handle
handles.y_slider = uicontrol('style', 'Slider', 'Min', 1, 'Max', NumSlices, 'Value',1, 'Units','normalized','position', [0.08 0.2 0.08 0.6], 'callback', #(s,e) UpdateY);
handles.SlideryListener = addlistener(handles.y_slider,'Value','PostSet',#(s,e) YListenerCallBack);
%// Create pusbutton to draw line
handles.DrawLineButton= uicontrol('style', 'push','position', [40 40 100 30],'String','Draw line', 'callback', {#DrawLine,handles});
%// Flag to know whether pushbutton has been pushed
handles.LineDrawn = false;
%// Show 1st slice
imshow(S.D(:,:,1))
guidata(hFig,handles);
%// Listeners callbacks followed by sliders callbacks. Used to display each
%// slice smoothly.
function YListenerCallBack
handles = guidata(hFig);
%// Get current slice
CurrentSlice = round(get(handles.y_slider,'value'));
hold on
imshow(S.D(:,:,CurrentSlice));
%// If button was button, draw line
if handles.LineDrawn
line([round(ImageWidth/2) round(ImageWidth/2)],[1 ImageHeight],'Color','r','LineWidth',2);
end
drawnow
guidata(hFig,handles);
end
function UpdateY(~)
handles = guidata(hFig); %// Get handles.
CurrentSlice = round(get(handles.y_slider,'value'));
hold on
imshow(S.D(:,:,CurrentSlice));
if handles.LineDrawn
line([round(ImageWidth/2) round(ImageWidth/2)],[1 ImageHeight],'Color','r','LineWidth',2);
end
drawnow
guidata(hFig,handles);
end
%// Pushbutton callback to draw line.
function DrawLine(~,~,handles)
line([round(ImageWidth/2) round(ImageWidth/2)],[1 ImageHeight],'Color','r','LineWidth',2);
handles.LineDrawn = true;
guidata(hFig,handles);
end
end
Sample output:
and after moving the slider up:
Is this what you meant? If not I'll remove that answer haha and sorry.

Skipping some axis labels in a plot with imagesc

I have created a big heat map using matlab's imagesc command. It plots the error output for each combination of the values in x and y axes. As can be seen in the figure there are too many axes labels. This might become even denser as I plan to increase the number of points in both x and y axes - which means I will get more outputs on a finer grid.
I want to be flexible with the labels, and skip some of them. I want to do this for both X and Y. I also want to be flexible with the "ticks" and draw either all of them or maybe skip some of them. Keep in mind that both the X and Y values are not increasing in order, at first the increment is 0.01 for 9 points, then 0.1, then 1 or 3 or whatever. I will change these increments too.
I tried to show what I want the graph look like in the second image. I want roughly the labels shown in red boxes only. As I said these are not set values, and I will make the increments smaller which will lead to denser plot.
Thank you for your help.
OS: Windows 7, 8 (64 bit)
Matlab version: Matlab 2014 a
You can manipulate the ticks and labels like this:
ticksarray=[1 33 41 100 ...] % edit these to whatever you want
tickslabels={'1', '33', '41', '100'; ...} % match the size of both arrays
set(gca,'XTick',ticksarray)
set(gca,'XTickLabel',tickslabels)
The same thing applies to the y-axis.
Small working example:
x=1:100;
y=2*x.^2-3*x+2;
plot(x,y)
ticksarray=[1 33 41 100];
tickslabels={'1', '33', '41', '100'};
set(gca,'XTick',ticksarray)
set(gca,'XTickLabel',tickslabels)
Example:
figure(1)
load clown
subplot(211)
imagesc(X);
subplot(212)
imagesc(X);
h = gca;
Now you can either set a maximum number of labels per axis:
%// define maximum number of labels
maxLabel = 3;
h.XTick = linspace(h.xlim(1),h.xlim(2),maxLabel);
h.YTick = linspace(h.ylim(1),h.ylim(2),maxLabel);
or define how many labels should be skipped:
%// define number of labels to skip
skipLabel = 2;
h.XTick = h.XTick(1:skipLabel:end);
h.YTick = h.YTick(1:skipLabel:end)
You can also get a different number of ticks and labels, more complicated though:
maxLabel = 3;
maxTicks = 6;
h.XTick = linspace(h.xlim(1),h.xlim(2),maxTicks);
h.YTick = linspace(h.ylim(1),h.ylim(2),maxTicks);
h.XTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ) ) = repmat({''},1,maxTicks-maxLabel);
h.YTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ) ) = repmat({''},1,maxTicks-maxLabel);
If you use a prior version of Matlab 2014b, then you will need the set command to set all properties:
%// define maximum number of labels
maxLabel = 3;
Xlim = get(h,'Xlim');
Ylim = get(h,'Ylim');
set(h,'XTick', linspace(Xlim(1),Xlim(2),maxLabel));
set(h,'YTick', linspace(Ylim(1),Ylim(2),maxLabel));
%// or define number of labels to skip
skipLabel = 2;
XTick = get(h,'XTick');
YTick = get(h,'YTick');
set(h,'XTick', XTick(1:skipLabel:end));
set(h,'YTick', YTick(1:skipLabel:end));
%// or combined
maxLabel = 3;
maxTicks = 6;
Xlim = get(h,'Xlim');
Ylim = get(h,'Ylim');
set(h,'XTick', linspace(Xlim(1),Xlim(2),maxTicks));
set(h,'YTick', linspace(Ylim(1),Ylim(2),maxTicks));
XTickLabel = cellstr(get(h,'XTickLabel'));
YTickLabel = cellstr(get(h,'YTickLabel'));
XTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ),: ) = repmat({''},1,maxTicks-maxLabel);
YTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ),: ) = repmat({''},1,maxTicks-maxLabel);
set(h,'XTickLabel',XTickLabel);
set(h,'YTickLabel',YTickLabel);
After applying the second method proposed by #thewaywewalk I got the second figure below. Apparently the labels need to be structured as well, because they only take the first so many labels.
Then I tried to manipulate the labels as shown below, and got the third image.
skipLabel = 2;
XTick = get(h,'XTick');
YTick = get(h,'YTick');
set(h,'XTick', XTick(1:skipLabel:end));
set(h,'YTick', YTick(1:skipLabel:end));
XTickLabel = get(h,'XTickLabel');
labelsX = cell( length(1: skipLabel:length(XTick)) , 1);
j = 1;
for i = 1: skipLabel:length(XTick)
labelsX{j} = XTickLabel(i, :);
j = j + 1;
end
set(h,'XTickLabel', labelsX);
YTickLabel = get(h,'YTickLabel');
labelsY = cell( length(1: skipLabel:length(YTick)) , 1);
j = 1;
for i = 1: skipLabel:length(YTick)
labelsY{j} = YTickLabel(i, :);
j = j + 1;
end
set(h,'YTickLabel', labelsY);
The Y axis labels seem to be in place as before (right next to tick), however the X axis labels seem to be shifted to the left a little. How can I correct this?
Another note: How can I change the scientific values into normal numbers? Also, probably there is a better approach at manipulating the labels.

matlab: efficient computation of local histograms within circular neighboorhoods

I've an image over which I would like to compute a local histogram within a circular neighborhood. The size of the neighborhood is given by a radius. Although the code below does the job, it's computationally expensive. I run the profiler and the way I'm accessing to the pixels within the circular neighborhoods is already expensive.
Is there any sort of improvement/optimization based maybe on vectorization? Or for instance, storing the neighborhoods as columns?
I found a similar question in this post and the proposed solution is quite in the spirit of the code below, however the solution is still not appropriate to my case. Any ideas are really welcomed :-) Imagine for the moment, the image is binary, but the method should also ideally work with gray-level images :-)
[rows,cols] = size(img);
hist_img = zeros(rows, cols, 2);
[XX, YY] = meshgrid(1:cols, 1:rows);
for rr=1:rows
for cc=1:cols
distance = sqrt( (YY-rr).^2 + (XX-cc).^2 );
mask_radii = (distance <= radius);
bwresponses = img(mask_radii);
[nelems, ~] = histc(double(bwresponses),0:255);
% do some processing over the histogram
...
end
end
EDIT 1 Given the received feedback, I tried to update the solution. However, it's not yet correct
radius = sqrt(2.0);
disk = diskfilter(radius);
fun = #(x) histc( x(disk>0), min(x(:)):max(x(:)) );
output = im2col(im, size(disk), fun);
function disk = diskfilter(radius)
height = 2*ceil(radius)+1;
width = 2*ceil(radius)+1;
[XX,YY] = meshgrid(1:width,1:height);
dist = sqrt((XX-ceil(width/2)).^2+(YY-ceil(height/2)).^2);
circfilter = (dist <= radius);
end
Following on the technique I described in my answer to a similar question you could try to do the following:
compute the index offsets from a particular voxel that get you to all the neighbors within a radius
Determine which voxels have all neighbors at least radius away from the edge
Compute the neighbors for all these voxels
Generate your histograms for each neighborhood
It is not hard to vectorize this, but note that
It will be slow when the neighborhood is large
It involves generating an intermediate matrix that is NxM (N = voxels in image, M = voxels in neighborhood) which could get very large
Here is the code:
% generate histograms for neighborhood within radius r
A = rand(200,200,200);
radius = 2.5;
tic
sz=size(A);
[xx yy zz] = meshgrid(1:sz(2), 1:sz(1), 1:sz(3));
center = round(sz/2);
centerPoints = find((xx - center(1)).^2 + (yy - center(2)).^2 + (zz - center(3)).^2 < radius.^2);
centerIndex = sub2ind(sz, center(1), center(2), center(3));
% limit to just the points that are "far enough on the inside":
inside = find(xx > radius+1 & xx < sz(2) - radius & ...
yy > radius + 1 & yy < sz(1) - radius & ...
zz > radius + 1 & zz < sz(3) - radius);
offsets = centerPoints - centerIndex;
allPoints = 1:prod(sz);
insidePoints = allPoints(inside);
indices = bsxfun(#plus, offsets, insidePoints);
hh = histc(A(indices), 0:0.1:1); % <<<< modify to give you the histogram you want
toc
A 2D version of the same code (which might be all you need, and is considerably faster):
% generate histograms for neighborhood within radius r
A = rand(200,200);
radius = 2.5;
tic
sz=size(A);
[xx yy] = meshgrid(1:sz(2), 1:sz(1));
center = round(sz/2);
centerPoints = find((xx - center(1)).^2 + (yy - center(2)).^2 < radius.^2);
centerIndex = sub2ind(sz, center(1), center(2));
% limit to just the points that are "far enough on the inside":
inside = find(xx > radius+1 & xx < sz(2) - radius & ...
yy > radius + 1 & yy < sz(1) - radius);
offsets = centerPoints - centerIndex;
allPoints = 1:prod(sz);
insidePoints = allPoints(inside);
indices = bsxfun(#plus, offsets, insidePoints);
hh = histc(A(indices), 0:0.1:1); % <<<< modify to give you the histogram you want
toc
You're right, I don't think that colfilt can be used as you're not applying a filter. You'll have to check the correctness, but here's my attempt using im2col and your diskfilter function (I did remove the conversion to double so it now output logicals):
function circhist
% Example data
im = randi(256,20)-1;
% Ranges - I do this globally for the whole image rather than for each neighborhood
mini = min(im(:));
maxi = max(im(:));
edges = linspace(mini,maxi,20);
% Disk filter
radius = sqrt(2.0);
disk = diskfilter(radius); % Returns logical matrix
% Pad array with -1
im_pad = padarray(im, (size(disk)-1)/2, -1);
% Convert sliding neighborhoods to columns
B = im2col(im_pad, size(disk), 'sliding');
% Get elements from each column that correspond to disk (logical indexing)
C = B(disk(:), :);
% Apply histogram across columns to count number of elements
out = histc(C, edges)
% Display output
figure
imagesc(out)
h = colorbar;
ylabel(h,'Counts');
xlabel('Neighborhood #')
ylabel('Bins')
axis xy
function disk = diskfilter(radius)
height = 2*ceil(radius)+1;
width = 2*ceil(radius)+1;
[XX,YY] = meshgrid(1:width,1:height);
dist = sqrt((XX-ceil(width/2)).^2+(YY-ceil(height/2)).^2);
disk = (dist <= radius);
If you want to set your ranges (edges) based on each neighborhood then you'll need to make sure that the vector is always the same length if you want to build a big matrix (and then the rows of that matrix won't correspond to each other).
You should note that the shape of the disk returned by fspecial is not as circular as what you were using. It's meant to be used a smoothing/averaging filter so the edges are fuzzy (anti-aliased). Thus when you use ~=0 it will grab more pixels. It'd stick with your own function, which is faster anyways.
You could try processing with an opposite logic (as briefly explained in the comment)
hist = zeros(W+2*R, H+2*R, Q);
for i = 1:R+1;
for j = 1:R+1;
if ((i-R-1)^2+(j-R-1)^2 < R*R)
for q = 0:1:Q-1;
hist(i:i+W-1,j:j+H-1,q+1) += (image == q);
end
end
end
end

Resources