I am sampling some pixels from a reference image Ir and then moving them on a secondary image In. The first function I have written is as follows:
[r,c,d] = size(Ir);
rSample = fix(r * 0.4); % sample 40 percent of pixels
cSample = fix(c * 0.4); % sample 40 percent of pixels
rIdx = randi(r,rSample,1); % uniformly sample indices for rows
cIdx = randi(c,cSample,1); % uniformly sample indices for columns
kk = 1;
for ii = 1:length(rIdx)
for jj=1:length(cIdx)
In(rIdx(ii),cIdx(jj),:) = Ir(rIdx(ii),cIdx(jj),:) * fcn(rIdx(ii),cIdx(jj));
kk = kk + 1;
end
end
Another method to increase the performance (speed) of the code, that I came around is as follows:
nSample = fix(r*c*0.4);
Idx = randi(r*c,nSample,1);
for ii = 1:nSample
[I,J] = ind2sub([r,c],Idx(ii,1));
In(I,J,:) = Ir(I,J,:) * fcn(I,J);
end
In both codes, fcn(I,J) is a function that performs some computation on the pixel at [I,J] and the process can be different depending on the indices of the pixel.
Although I have removed one for-loop, I guess there is a better technique to increase the performance of the code even more.
Update:
As suggested by #Daniel the following line of the code does the job.
In(rIdx,cIdx,:)=Ir(rIdx,cIdx,:);
But the point is, I prefer to have only the sampled pixels to be able to process them faster. For instance having the samples in a vector format wit 3 layers for RGB.
Io = Ir(rIdx,cIdx,:);
Io1 = Io(:,:,1);
Io1v = Io1(:);
Ir=ones(30,30,3);
In=Ir*.5;
[r,c,d] = size(Ir);
rSamples = fix(r * 0.4); % sample 40 percent of pixels
cSamples = fix(c * 0.4); % sample 40 percent of pixels
rIdx = randi(r,rSamples,1); % uniformly sample indices for rows
cIdx = randi(c,cSamples,1); % uniformly sample indices for columns
In(rIdx,cIdx,:)=Ir(rIdx,cIdx,:);
Related
I have an image of arbitrary dimensions ROWS and COLS. I want to tile this image into patches of arbitrary, but constant size blockSize = [blockSizeR, blockSizeC], given an arbitrary, but constant stride stride = [strideR, strideC]. When the number of patches in row or column direction times the respective block size doesn't equal the number of rows or columns, respectively (i.e. if there were spare rows or columns), I don't care about them (i.e. they can be ignored). It's sufficient if the image is tiled into all possible patches that fit completely into the image starting from the top left pixel.
There is a bunch of possible solutions floating around the web, but some don't allow overlap, some don't allow outputs if there are spare rows or columns, some are making inefficient use of for loops.
The closest thing to what I need is probably the solution posted on https://de.mathworks.com/matlabcentral/answers/330357-how-do-i-store-a-series-of-rgb-images-in-a-2d-array:
%img: source image
stride = [5, 5]; %height, width
blocksize = [11, 11]; %height, width
tilescount = (size(img(:, :, 1)) - blocksize - 1) / stride + 1;
assert(all(mod(tilescount, 1) == 0), 'cannot divide image into tile evenly')
tiles = cell(tilescount);
tileidx = 1;
for col = 1 : stride(2) : size(img, 2 ) - blocksize(2)
for row = 1 : stride(1) : size(img, 1) - blocksize(1)
tiles{tileidx} = img(row:row+stride(1)-1, col:col+stride(2)-1, :);
tileidx = tileidx + 1;
end
end
However, it also seems to work only if there are no spare rows or columns. How can I adapt that to an efficient solution for images with an arbitrary number of channels (I seek to apply it on both single-channel images and RGB images)?
The code above did not fully work, so I came up with the following solution based on it. Variable names are chosen such that they are self-explanatory.
tilesCountR = floor((ROWS - rowBlockSize - 1) / rowStride + 1);
tilesCountC = floor((COLS - colBlockSize - 1) / colStride + 1);
tiles = cell(tilesCountR * tilesCountC,1);
tileidx = 1;
for col = 1 : colStride : COLS - colBlockSize
for row = 1 : rowStride : ROWS - rowBlockSize
tiles{tileidx} = img(row:row+rowBlockSize-1, col:col+colBlockSize-1, :);
tileidx = tileidx + 1;
end
end
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.
v = videoinput('winvideo', 1, 'YUY2_320x240');
s = serial('COM1', 'BaudRate', 9600);
fopen(s);
while(1)
h = getsnapshot(v);
rgb = ycbcr2rgb(h);
for i = 1:240
for j = 1:320
if rgb(i,j,1) > 140 && rgb(i,j,2) < 100 % use ur own conditions
bm(i, j) = 1;
else
bm(i, j) = 0;
end
end
end
This is the code i got from my senior regarding image processing using MATLAB. The above code is to convert the image to binary image, But in the code rgb(i, j, 1) > 140 I didn't understand that command. How to select that 140 and what does that rgb(i, j, 1) mean?
You have an RGB image rgb where the third dimension are the RGB color planes. Thus, rgb(i,j,1) is the red value at row i, column j.
By doing rgb(i,j,1)>140 it tests if this red value is greater than 140. The value 140 appears to be ad hoc, picked for a specific task.
The code is extremely inefficient as there is no need for a loop:
bm = rgb(:,:,1)>140 & rgb(:,:,2)<100;
Note the change from && to the element-wise operator &. Here I'm assuming that the size of rgb is 240x320x3.
Edit: The threshold values you choose completely depend on the task, but a common approach to automatic thresholding is is Otsu's method, graythresh. You can apply it to a single color plane to get a threshold:
redThresh = graythresh(rgb(:,:,1)) * 255;
Note that graythresh returns a value on [0,1], so you have to scale that by the data range.
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
I have a section of code that calculates the percent of pixels in a binary grid with value == 1 using a 50x50 sliding window:
f = #(x) numel(x(x==1))/numel(x);
I2 = nlfilter(buffer,[50 50],f);
I have heard that imfilter is a more efficient way to make focal calculations and, as such, hope to do some benchmarking. What is the imfilter() equivalent of the above nlfilter() function?
The complete code with sample data is attached
% Generate a grid of 0's to begin with.
m = zeros(400, 400, 'uint8');
% Generate 100 random "trees".
numRandom = 100;
linearIndices = randi(numel(m), 1, numRandom);
% Assign a radius value of 1-12 to each tree
m(linearIndices) = randi(12, [numel(linearIndices) 1]);
buffer = false(size(m));
for radius =1:12 % update to actual range
im_r = m==radius;
se = strel('disk',radius);
im_rb = imfilter(im_r, double(se.getnhood()));
buffer = buffer | im_rb;
end
% The imfilter approach
% The nlfilter approach
f = #(x) numel(x(x==1))/numel(x);
I2 = nlfilter(buffer,[50 50],f);
imshowpair(buffer,I2, 'montage')
For binary images (only 0s and 1s), what you have done is simple summation in a sliding window. Thus, the average filter of imfilter can be adopted here as:
h = fspecial( 'average', 50 );
I2 = imfilter( double( buffer ), h );