What is the imfilter() equivalent of a nlfilter() function? - image

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 );

Related

Interp2 of image with transformed coordinates

I have 2 greyscale images that i am trying to align using scalar scaling 1 , rotation matrix [2,2] and translation vector [2,1]. I can calculate image1's transformed coordinates as
y = s*R*x + t;
Below the resulting images are shown.
The first image is image1 before transformation,
the second image is image1 (red) with attempted interpolation using interp2 shown on top of image2 (green)
The third image is when i manually insert the pixel values from image1 into an empty array (that has the same size as image2) using the transformed coordinates.
From this we can see that the coordinate transformation must have been successful, as the images are aligned although not perfectly (which is to be expected since only 2 coordinates were used in calculating s, R and t) .
How come interp2 is not producing a result more similar to when i manually insert pixel values?
Below the code for doing this is included:
Interpolation code
function [transformed_image] = interpolate_image(im_r,im_t,s,R,t)
[m,n] = size(im_t);
% doesn't help if i use get_grid that the other function is using here
[~, grid_xr, grid_yr] = get_ipgrid(im_r);
[x_t, grid_xt, grid_yt] = get_ipgrid(im_t);
y = s*R*x_t + t;
yx = reshape(y(1,:), m,n);
yy = reshape(y(2,:), m,n);
transformed_image = interp2(grid_xr, grid_yr, im_r, yx, yy, 'nearest');
end
function [x, grid_x, grid_y] = get_ipgrid(image)
[m,n] = size(image);
[grid_x,grid_y] = meshgrid(1:n,1:m);
x = [reshape(grid_x, 1, []); reshape(grid_y, 1, [])]; % X is [2xM*N] coordinate pairs
end
The manual code
function [transformed_image] = transform_image(im_r,im_t,s,R,t)
[m,n] = size(im_t);
[x_t, grid_xt, grid_yt] = get_grid(im_t);
y = s*R*x_t + t;
ymat = reshape(y',m,n,2);
yx = ymat(:,:,1);
yy = ymat(:,:,2);
transformed_image = zeros(m,n);
for i = 1:m
for j = 1:n
% make sure coordinates are inside
if (yx(i,j) < m & yy(i,j) < n & yx(i,j) > 0.5 & yy(i,j) > 0.5)
transformed_image(round(yx(i,j)),round(yy(i,j))) = im_r(i,j);
end
end
end
end
function [x, grid_x, grid_y] = get_grid(image)
[m,n] = size(image);
[grid_y,grid_x] = meshgrid(1:n,1:m);
x = [grid_x(:) grid_y(:)]'; % X is [2xM*N] coordinate pairs
end
Can anyone see what i'm doing wrong with interp2? I feel like i have tried everything
Turns out i got interpolation all wrong.
In my question i calculate the coordinates of im1 in im2.
However the way interpolation works is that i need to calculate the coordinates of im2 in im1 such that i can map the image as shown below.
This means that i also calculated the wrong s,R and t since they were used to transform im1 -> im2, where as i needed im2 -> im1. (this is also called the inverse transform). Below is the manual code, that is basically the same as interp2 with nearest neighbour interpolation
function [transformed_image] = transform_image(im_r,im_t,s,R,t)
[m,n] = size(im_t);
[x_t, grid_xt, grid_yt] = get_grid(im_t);
y = s*R*x_t + t;
ymat = reshape(y',m,n,2);
yx = ymat(:,:,1);
yy = ymat(:,:,2);
transformed_image = zeros(m,n);
for i = 1:m
for j = 1:n
% make sure coordinates are inside
if (yx(i,j) < m & yy(i,j) < n & yx(i,j) > 0.5 & yy(i,j) > 0.5)
transformed_image(i,j) = im_r(round(yx(i,j)),round(yy(i,j)));
end
end
end
end

How to rotate a 2D theoretical path to fit or be overlaid on a path from videoprocessing

I track the motion of an object from a video file in MATLAB and save the locations from each frame in a numberOfFrames x 2 array.
I know the theoretical path or intended path. When recording the movie the camera is at some unknown angle in space. Therefore, the path is skewed. The only information I have is the scaling between the pixels and millimeters by using the object diameter.
Now I would like to rotate the intended path, and move it around until it is overlaid on the tracked motion path.
I start with my theoretical path (Pth) then rotate it in 3-dimensions to "Pthr". After that I loop over each point in "M". And for each point in "M", I look for the closest point from "Pthr". Then, I repeat for the next point in "M". This probably has a problem of choosing the same point in "Pthr" for multiple points in "M".
I noticed this is sensitive to my initial guess and it gives terrible results.
Also, M is not a perfect path, since it is experimental measurements it is no where near perfect. measured vs. theoretical unrotated path
% M = [Mx,My], is location in x and y, from motion tracking.
% scale = 20; % pixels/mm, using the size of object
% I build the theoretical path (Pth) goes from to (0,0,0) to (0,3,0) to
% (3,3,0) to (3,0,0) to be approximately the same length as M
Pthup = linspace(0,3,num)';
Pthdwn = linspace(3,0,num)';
Pth0 = zeros(size(Pthup));
Pth3 = 3*ones(size(Pthup));
% Pth is approximately same length as M
Pth = scale*[Pth0 Pthup Pth0;Pthup Pth3 Pth0;Pth3 Pthdwn Pth0];
% using fmincon in matlab to minimize the sum of the square
lb = [145 0 -45 min(min(M)) min(min(M))]; %upper bound
ub = [180 90 45 max(max(M)) max(max(M))]; %lower bound
coro = [180 0 0 mean(Mx) mean(My)]; %initial guess
% initial guess (theta(x),theta(y),theta(z), shift in x, shift in y)
cnt = 0; er = 1;
while (abs(er)>0.1)
[const,fval] = fmincon(#(cor)findOrientation(cor,Pth,M),coro,[],[],[],[],lb,ub);
er = sum(const-coro);
coro = const;
cnt = 1+cnt;
if (cnt>50)
cnt = cnt;
break
end
end
%% function findOrientation keeps rotating Pth until it is closest to M
function [Eo] = findOrientation(cor,Pth,M)
% cor = [angle of rotations, center coordinate];(degrees, non-dimensiolaized in pixels)
% M = measured coordinates from movie in pixel
% coor: is output of the form [x-coordiante,y-coordinate, absolute distance from Center(i,:)]
% F = sum of least square, sum(coor(:,3))
%% Rotation of theoretical path about z,y,x and shifting in it in xy
thx = cor(1);
thy = cor(2);
thz = cor(3);
xy = cor([4:5]);
% T = [cosd(thn) -sind(thn);
% sind(thn) cosd(thn)]; %rotation matrix in 3D
Tz = [cosd(thz) -sind(thz) 0;
sind(thz) cosd(thz) 0;0 0 1]; %rotation matrix
Ty = [cosd(thy) 0 -sind(thy);0 1 0;
sind(thy) 0 cosd(thy)]; %rotation matrix
Tx = [1 0 0;0 cosd(thx) -sind(thx);
0 sind(thx) cosd(thx)]; %rotation matrix
Pthr = zeros(size(Pth));
for i = 1:size(Pth,1)
xp = Tz*Pth(i,:)';
xp = Ty*xp;
xp = Tx*xp;
Pthr(i,:) = xp.';
end
Pthr = Pthr(:,[1,2]); % omit third value because it is 2D
Pthr = Pthr + [cor([4:5])];
rin = sqrt(Pthr(:,1).^2+Pthr(:,2).^2); %theoretical radius
Centern = sqrt(M(:,1).^2 + M(:,2).^2);%measured radius
for i = 1:size(M,1) %loop over each point in tracked motion
sub = Pthr-M(i,:); %subtracting M(i,:) from all Pthr
for j = 1:length(sub)
dist(j,1) = norm(sub(j,:));% distance from M(i,:) to all ri
end
%index is based on the min absolute distance between Pthr and M(i,:). It chooses the closest Pthr to a specific M(i,:)
[mn, index] = min(dist);
erri = abs(rin(index)-Centern(i))./rin(index);
coor(i,:) = erri;
end
Eo = sum(coor);

Sampling pixels from an Image - increasing performance

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,:);

MATLAB finding average RGB value across all pixels in image

Code is below. I'm looping through an input image 1 pixel at a time and determining its RGB value. Afterwards i'm trying to find the average RGB value for the image overall. For some reason the averaging portion of my code isnt working though.
im = imread(filename);
[width, height, depth] = size(im);
count = 0;
r=0;
g=0;
b=0;
for x = 1 : width
for y = 1: height
r = r + im(x,y,1);
g = g + im(x,y,2);
b = b + im(x,y,3);
count = count + 1;
end
end
%find averages of each RGB value.
r2 = r/count;
g2 = g/count;
b2 = b/count;
Why not vectorizing and using mean?
mean( reshape( im, [], 3 ), 1 )
The following code would work as well;
pep = imread('peppers.png');
mean(mean(pep))
This will return a 1x1x3 vector which will be the mean values of R, G, and B respectively.

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