Wrong dimensions when normalizing an image - matlab

I am getting some error in this code section
X=imread ('Lighthouse.jpg'); %reads picture as int8 matrix
figure, imagesc(X), colormap gray, title('original picture'), % display picture
filter=[-1 0 1; -2 0 2; -1 0 1]; % builds Sobel filter matrix
filter=single(filter); %convert double to single
x=single(X); % convert int8 to single
x=x/max(max(x)); %normalisation to [0,1]
The error I get:
Error using /
Inputs must be 2-D, or at least one input must be scalar.
To compute elementwise RDIVIDE, use RDIVIDE (./) instead.
Error in sobel (line 10)
x=x/max(max(x)); %normalisation to [0,1]
Also when I am using ./ as suggested, I get new error:
Array dimensions must match for binary array op.
Error in sobel (line 10)
x=x./max(max(x)); %normalisation to [0,1]
I am doing something wrong in the normalization step.
How do I resolve this issue?

Why do you call max twice. If I run the code with
x=x/max(x(:))
I do not get an error. This runs the matrix in 1D.

Whilst Caduceus' answer is correct; it normalises over all three colours in one go. What's probably better for your case is rgb2gray, to get a single colour channel and then normalise that instead (using x/max(x(:))).
X=imread ('lighthouse.png'); %reads picture as int8 matrix
filter=[-1 0 1; -2 0 2; -1 0 1]; % builds Sobel filter matrix
filter=single(filter); %convert double to single
x = single(rgb2gray(X)); % rgb2gray gives a uint8, you want single
% x=x/max(x(:)); %normalisation to [0,1] , not needed here as x can directly be used
% for Sobel purposes as it's a grey scale image.
figure;
subplot(1,2,1)
imagesc(X)
colormap(gray)
title('original picture'), % display picture
subplot(1,2,2)
imagesc(x)
colormap(gray)
title 'Grey scale'
The reason for the first error is is that max gives a column-wise maximum, and that this is a 3D matrix. max(max()) thus gives a 1D one, instead of the desired scalar.
Then the second error occurs because max(max()) gives an array, which doesn't have the same amount of entries as the full matrix (obviously).
Basically if size(x) = [row, column channels], size(max(x)) = [row channels]
and size(max(max(x)) = [row]. Using the colon operator actually makes the entire 3D matrix a single column vector, and max(x(:)) thus gives a single value, which is the maximum across all rows, columns and channels.

When I run your code the error message says "Use RDIVIDE (./)".
implement it like this:
x=x./max(max(x));
This divides each RGB layer by its maximum. You may have to replicate the max values (I guess this depends on matlab version), use this line instead
x=x./repmat(max(max(x)),size(X,1),size(X,2),1);

Related

MATLAB convert graythresh output into actual intensity

I am trying to find the threshold of a 3D image that is 258 x 318 x 801 double. I first reshaped the image into 1D array and then used graythresh
ROI = reshape(postImg,[],1);
thresh = graythresh(ROI);
But I was trying to find the actually intensity threshold instead of a value between 0 and 1. Is there a way to convert this other than using multithresh?
From MATLAB documentation:
The graythresh function converts multidimensional arrays to 2-D
arrays, using reshape, and ignores any nonzero imaginary part of I.
So, your reshape is probably redundant. I think this would do:
thresh = graythresh(postImg); % postIm can be 3D
BinIm = imbinarize(postIm,thresh); % creates a binary mask of your image

MATLAB 3D matrix, max in different directions + rotation using MIP

I have a 3D image called img, let's say it is a 291x287x801 int16 array. I am using the MIP (Maximum intensity projection) to find the image with the maximum intensity in different directions. I know that I could use max to get the MIP:
MIPimg=max(img,[],3);
imagesc(MIPimg);
However, this is not giving me the right direction. I think it is along the z-direction, but what should I do if I want to find the MIP along the y or x direction?
I did try to change that 3 which indicates dimension to 1 or 2, but MATLAB tells me
Error using image
Color data must be an m-by-n-by-3 or m-by-n matrix.
when calling imagesc(MIPimg).
I also tried MIPimg=max(img,[ ],[2 3]); but that didn't help.
Your problem is that imagesc expects either a 2D array as input, or a 3D array where the 3rd dimension has exactly 3 values (this is the way MATLAB represents an RGB image). When you do max(img,[],1), you get an 1x287x801 array back, which has 801 elements along the 3rd dimension, not 3 as MATLAB expects.
What you need to do for display is to convert this 1x287x801 array into an 287x801 array. The function squeeze does this (it removes all dimensions with size 1):
MIPimg = squeeze(max(img,[],1));
I cannot reproduce your problem:
% create random 3D-unit8-matrix (to mimic an image)
img = uint8(randi(255,10,12,3)); % 10x12x3 matrix
% maximum over all rows (direction 1) of each of the 3 10x12 matrices => returns 3 1x12 arrays
[val,idx] = max(img,[],1); % default direction
% maximum over all columns (direction 2) of each of the 3 10x12 matrices => returns 3 10x1 vectors
[val,idx] = max(img,[],2);
% maximum over all slices (direction 3) of each of the 10x12 1x3 "depth" arrays => returns a 10x12 matrix
[val,idx] = max(img,[],3);
overall maximum
max(max(max(img))) % no useful information about the position
position of maximum:
[val_slc,idx_slc] = max(img,[],3); % I can better think in 2D
[val_row,idx_row] = max(val_slc);
[val_col,idx_col] = max(val_row);
idx_max = [idx_row(idx_col),idx_col,idx_slc(idx_row(idx_col),idx_col)];
check
assert( max(max(max(img))) == img(idx_max(1),idx_max(2),idx_max(3)) )

Matlab: trouble with taking derivative of image?

So I need to take the derivative of an image in the x-direction for this assignment, with the goal of getting some form of gradient. My thought is to use the diff(command) on each row of the image and then apply a Gaussian filter. I haven't started the second part because the first is giving me trouble. In attempting to get the x-derivative I have:
origImage = imread('TightRope.png');
for h = 1:3 %%h represents color channel
for i = size(origImage,1)
newImage(i,:,h) = diff(origImage(i,:,h)); %%take derivative of row and translate to new row
end
end
The issue is somewhere along the way I get the error 'Subscripted assignment dimension mismatch.'.
Error in Untitled2 (line 14)
newImage(i,:,h) = diff(origImage(i,:,h));
Does anyone have any ideas on why that might be happening and if my approach is correct for getting the gradient/gaussian derivative?
Why not use fspecial along with imfilter instead?
figure;
I = imread('cameraman.tif');
subplot 131; imshow(I); title('original')
h = fspecial('prewitt');
derivative = imfilter(I,h','replicate'); %'
subplot 132; imshow(derivative); title('derivative')
hsize = 5;
sigma = 1;
h = fspecial('gaussian', hsize, sigma) ;
gaussian = imfilter(derivative,h','replicate'); %'
subplot 133; imshow(gaussian); title('derivative + gaussian')
The result is the following one:
If your goal is to use diff to generate the derivative rather than to create a loop, you can just tell diff to give you the derivative in the x-direction (along dimension 2):
newImage = diff(double(origImage), 1, 2);
The 1 is for the first derivative and 2 is for the derivative along the second dimension. See diff.
As #rayryeng mentions in his answer, it's important to cast the image as double.
Given a N element vector, diff returns a N-1 length vector, so the reason why you are getting an alignment mismatch is because you are trying to assign the output of diff into an incorrect number of slots. Concretely, supposing that N is the total number of columns, you are using diff on a 1 X N vector which thus returns a 1 x (N - 1) vector and you are trying to assign this output as a single row into the output image which is expected to be 1 x N. The missing element is causing the alignment mismatch. diff works by taking pairs of elements in the vector and subtracting them to produce new elements, thus the reason why there is one element missing in the final output.
If you want to get your code working, one way is to pad each row of the image or signal vector with an additional zero (for example) as input into diff. Something like this could work. Take note that I'll be converting your image to double to allow the derivative to take on negative values:
origImage = imread('...'); %// Place path to image here and read in
origImage = im2double(origImage); %// Change - Convert to double precision
newImage = zeros(size(origImage)); %// Change - Create blank new image and populate each row per channel manually
for h = 1:3 %%h represents color channel
for ii = 1:size(origImage,1) %// Change - fixed for loop iteration
newImage(ii,:,h) = diff([0 origImage(ii,:,h)]); %// Change
end
end
Take note that your for loop was incorrect since it didn't go over every row... just the last row.
When I use the onion.png image that's part of the image processing toolbox:
...and when I run this code, I get this image using imshow(newImage,[]);:
Take note that the difference filter was applied to each channel individually and I changed the intensities per channel so that the smallest value gets mapped to 0 and the largest value gets mapped to 1. How you can interpret this image is that any areasthat have a non-black colour have some non-zero differences and hence there is some activity going on in those areas and any areas that have a dark / black colour means that there is no activity going on in those areas. Take note that we applied a horizontal filter, so if you wanted to do this vertically, you'd simply repeat the behaviour but apply this column-wise instead of row-wise as you did above.

Filter image that contains NaNs in Matlab?

I have a 2d array (doubles) representing some data, and it has a bunch of NaNs in it. The contour plot of the data looks like this:
All of the white spaces are NaNs, the gray diamond is there for reference, and the filled contour shows the shape of my data. When I filter the data with imfilt, the NaNs significantly chew into the data, so we end up with something like this:
You can see that the support set is significantly contracted. I can't use this, as it has chewed into some of the more interesting variations on the edges (for reasons specific to my experiments, those edges are important).
Is there a function to filter within an island of NaNs that treats edges similar to edges of rectangular filtering windows, instead of just killing the edges? Sort of like an nanmean function, except for convolving images?
Here is my filter code:
filtWidth = 7;
imageFilter=fspecial('gaussian',filtWidth,filtSigma);
%convolve them
dataFiltered = imfilter(rfVals,imageFilter,'symmetric','conv');
and the code for plotting the contour plot:
figure
contourf(dataFiltered); hold on
plot([-850 0 850 0 -850], [0 850 0 -850 0], 'Color', [.7 .7 .7],'LineWidth', 1); %the square (limits are data-specific)
axis equal
There is some code at the Mathworks file exchange (ndanfilter.m) that comes close to what I want, but I believe it only interpolates NaNs that are sprinkled on the interior of an image, not data showing this island-type effect.
Note: I just found nanconv.m, which does exactly what I want, with a very intuitive usage (convolve an image, ignoring NaN, much like nanmean works). I've made this part of my accepted answer, and include a comparison to the performance of the other answers.
Related questions
Gaussian filtering a image with Nan in Python
The technique I ended up using was the function nanconv.m at Matlab's File Exchange. It does exactly what I was looking for: it runs the filter in a way that ignores the NaNs just the way that Matlab's built-in function nanmean does. This is a hard to decipher from the documentation of the function, which is a tad cryptic.
Here's how I use it:
filtWidth = 7;
filtSigma = 5;
imageFilter=fspecial('gaussian',filtWidth,filtSigma);
dataFiltered = nanconv(data,imageFilter, 'nanout');
I'm pasting the nanconv function below (it is covered by the BSD license). I will post images etc when I get a chance, just wanted to post what I ended up doing for anyone curious about what I did.
Comparison to other answers
Using gnovice's solution the results look intuitively very nice, but there are some quantitative blips on the edges that were a concern. In practice, the extrapolation of the image beyond the edges led to many spuriously high values at the edges of my data.
Using krisdestruction's suggestion of replacing the missing bits with the original data, also looks pretty decent (especially for very small filters), but (by design) you end up with unfiltered data at the edges, which is a problem for my application.
nanconv
function c = nanconv(a, k, varargin)
% NANCONV Convolution in 1D or 2D ignoring NaNs.
% C = NANCONV(A, K) convolves A and K, correcting for any NaN values
% in the input vector A. The result is the same size as A (as though you
% called 'conv' or 'conv2' with the 'same' shape).
%
% C = NANCONV(A, K, 'param1', 'param2', ...) specifies one or more of the following:
% 'edge' - Apply edge correction to the output.
% 'noedge' - Do not apply edge correction to the output (default).
% 'nanout' - The result C should have NaNs in the same places as A.
% 'nonanout' - The result C should have ignored NaNs removed (default).
% Even with this option, C will have NaN values where the
% number of consecutive NaNs is too large to ignore.
% '2d' - Treat the input vectors as 2D matrices (default).
% '1d' - Treat the input vectors as 1D vectors.
% This option only matters if 'a' or 'k' is a row vector,
% and the other is a column vector. Otherwise, this
% option has no effect.
%
% NANCONV works by running 'conv2' either two or three times. The first
% time is run on the original input signals A and K, except all the
% NaN values in A are replaced with zeros. The 'same' input argument is
% used so the output is the same size as A. The second convolution is
% done between a matrix the same size as A, except with zeros wherever
% there is a NaN value in A, and ones everywhere else. The output from
% the first convolution is normalized by the output from the second
% convolution. This corrects for missing (NaN) values in A, but it has
% the side effect of correcting for edge effects due to the assumption of
% zero padding during convolution. When the optional 'noedge' parameter
% is included, the convolution is run a third time, this time on a matrix
% of all ones the same size as A. The output from this third convolution
% is used to restore the edge effects. The 'noedge' parameter is enabled
% by default so that the output from 'nanconv' is identical to the output
% from 'conv2' when the input argument A has no NaN values.
%
% See also conv, conv2
%
% AUTHOR: Benjamin Kraus (bkraus#bu.edu, ben#benkraus.com)
% Copyright (c) 2013, Benjamin Kraus
% $Id: nanconv.m 4861 2013-05-27 03:16:22Z bkraus $
% Process input arguments
for arg = 1:nargin-2
switch lower(varargin{arg})
case 'edge'; edge = true; % Apply edge correction
case 'noedge'; edge = false; % Do not apply edge correction
case {'same','full','valid'}; shape = varargin{arg}; % Specify shape
case 'nanout'; nanout = true; % Include original NaNs in the output.
case 'nonanout'; nanout = false; % Do not include NaNs in the output.
case {'2d','is2d'}; is1D = false; % Treat the input as 2D
case {'1d','is1d'}; is1D = true; % Treat the input as 1D
end
end
% Apply default options when necessary.
if(exist('edge','var')~=1); edge = false; end
if(exist('nanout','var')~=1); nanout = false; end
if(exist('is1D','var')~=1); is1D = false; end
if(exist('shape','var')~=1); shape = 'same';
elseif(~strcmp(shape,'same'))
error([mfilename ':NotImplemented'],'Shape ''%s'' not implemented',shape);
end
% Get the size of 'a' for use later.
sza = size(a);
% If 1D, then convert them both to columns.
% This modification only matters if 'a' or 'k' is a row vector, and the
% other is a column vector. Otherwise, this argument has no effect.
if(is1D);
if(~isvector(a) || ~isvector(k))
error('MATLAB:conv:AorBNotVector','A and B must be vectors.');
end
a = a(:); k = k(:);
end
% Flat function for comparison.
o = ones(size(a));
% Flat function with NaNs for comparison.
on = ones(size(a));
% Find all the NaNs in the input.
n = isnan(a);
% Replace NaNs with zero, both in 'a' and 'on'.
a(n) = 0;
on(n) = 0;
% Check that the filter does not have NaNs.
if(any(isnan(k)));
error([mfilename ':NaNinFilter'],'Filter (k) contains NaN values.');
end
% Calculate what a 'flat' function looks like after convolution.
if(any(n(:)) || edge)
flat = conv2(on,k,shape);
else flat = o;
end
% The line above will automatically include a correction for edge effects,
% so remove that correction if the user does not want it.
if(any(n(:)) && ~edge); flat = flat./conv2(o,k,shape); end
% Do the actual convolution
c = conv2(a,k,shape)./flat;
% If requested, replace output values with NaNs corresponding to input.
if(nanout); c(n) = NaN; end
% If 1D, convert back to the original shape.
if(is1D && sza(1) == 1); c = c.'; end
end
One approach would be to replace the NaN values with nearest-neighbor interpolates using scatteredInterpolant (or TriScatteredInterp in older MATLAB versions) before performing the filtering, then replacing those points again with NaN values afterward. This would be akin to filtering a full 2-D array using the 'replicate' argument as opposed to the 'symmetric' argument as a boundary option for imfilter (i.e. you're replicating as opposed to reflecting values at the jagged NaN boundary).
Here's what the code would look like:
% Make your filter:
filtWidth = 7;
imageFilter = fspecial('gaussian', filtWidth, filtWidth);
% Interpolate new values for Nans:
nanMask = isnan(rfVals);
[r, c] = find(~nanMask);
[rNan, cNan] = find(nanMask);
F = scatteredInterpolant(c, r, rfVals(~nanMask), 'nearest');
interpVals = F(cNan, rNan);
data = rfVals;
data(nanMask) = interpVals;
% Filter the data, replacing Nans afterward:
dataFiltered = imfilter(data, imageFilter, 'replicate', 'conv');
dataFiltered(nanMask) = nan;
Okay without using your plot function, I can still give you a solution. What you want to do is find all the new NaN's and replace it with the original unfiltered data (assuming it is correct). While it's not filtered, it's better than reducing the domain of your contour image.
% Toy Example Data
rfVals= rand(100,100);
rfVals(1:2,:) = nan;
rfVals(:,1:2) = nan;
% Create and Apply Filter
filtWidth = 3;
imageFilter=fspecial('gaussian',filtWidth,filtWidth);
dataFiltered = imfilter(rfVals,imageFilter,'symmetric','conv');
sum(sum(isnan( dataFiltered ) ) )
% Replace New NaN with Unfiltered Data
newnan = ~isnan( rfVals) & isnan( dataFiltered );
dataFiltered( newnan ) = rfVals( newnan );
sum(sum(isnan( rfVals) ) )
sum(sum(isnan( dataFiltered ) ) )
Detect new NaN using the following code. You can also probably use the xor function.
newnan = ~isnan( rfVals) & isnan( dataFiltered );
Then this line sets the indices in dataFiltered to the values in rfVals
dataFiltered( newnan ) = rfVals( newnan );
Results
From the lines printed in the console and my code, you can see that the number of NaN in dataFiltered is reduced from 688 to 396 as was the number of NaN in rfVals.
ans =
688
ans =
396
ans =
396
Alternate Solution 1
You can also use a smaller filter near the edges by specifying a smaller kernel and merging it after, but if you just want valid data with minimal code, my main solution will work.
Alternate Solution 2
An alternate approach is to pad/replace the NaN values with zero or some constant you want so that it will work, then truncate it. However for signal processing/filtering, you will probably want my main solution.
Want to improve this post? Add citations from reputable sources by editing the post. Posts with unsourced content may be edited or deleted.
nanfilter does exactly the same thing with nanconv when filtering as long as the filter is the same. If you get the nan values before you use nanfilter and then add the back to the after-filtered matrix, you will get the same result with what you get from nanconv with the option 'nanout', as long as you use the same filter.

How do I create an image matrix with a line drawn in it in MATLAB?

I want to plot a line from one well-defined point to another and then turn it into an image matrix to use a Gaussian filter on it for smoothing. For this I use the functions line and getframe to plot a line and capture the figure window in an image, but getframe is very slow and not very reliable. I noticed that it does not capture anything when the computer is locked and I got an out of memory error after 170 executions.
My questions are:
Is there a substitute to getframe that I can use?
Is there a way to create the image matrix and draw the line directly in it?
Here is a minimal code sample:
figure1=line([30 35] ,[200 60]);
F= getframe;
hsize=40; sigma=20;
h = fspecial('gaussian',hsize,sigma);
filteredImg = imfilter(double(F.cdata), h,256);
imshow(uint8(filteredImg));
[update]
High-Performance Mark's idea with linspace looks very promising, but how do I access the matrix coordinates calculated with linspace? I tried the following code, but it does not work as I think it should. I assume it is a very simple and basic MATLAB thing, but I just can not wrap my head around it:
matrix=zeros(200,60);
diagonal=round([linspace(30,200,numSteps); linspace(35,60,numSteps)]);
matrix(diagonal(1,:), diagonal(2,:))=1;
imshow(matrix);
Here's one example of drawing a line directly into a matrix. First, we'll create a matrix of zeros for an empty image:
mat = zeros(250, 250, 'uint8'); % A 250-by-250 matrix of type uint8
Then, let's say we want to draw a line running from (30, 35) to (200, 60). We'll first compute how many pixels long the line will have to be:
x = [30 200]; % x coordinates (running along matrix columns)
y = [35 60]; % y coordinates (running along matrix rows)
nPoints = max(abs(diff(x)), abs(diff(y)))+1; % Number of points in line
Next, we compute row and column indices for the line pixels using linspace, convert them from subscripted indices to linear indices using sub2ind, then use them to modify mat:
rIndex = round(linspace(y(1), y(2), nPoints)); % Row indices
cIndex = round(linspace(x(1), x(2), nPoints)); % Column indices
index = sub2ind(size(mat), rIndex, cIndex); % Linear indices
mat(index) = 255; % Set the line pixels to the max value of 255 for uint8 types
You can then visualize the line and the filtered version with the following:
subplot(1, 2, 1);
image(mat); % Show original line image
colormap(gray); % Change colormap
title('Line');
subplot(1, 2, 2);
h = fspecial('gaussian', 20, 10); % Create filter
filteredImg = imfilter(mat, h); % Filter image
image(filteredImg); % Show filtered line image
title('Filtered line');
If you have Computer Vision System toolbox there is a ShapeInserter object available. This can be used to draw lines, circles, rectangles and polygons on the image.
mat = zeros(250,250,'uint8');
shapeInserter = vision.ShapeInserter('Shape', 'Lines', 'BorderColor', 'White');
y = step(shapeInserter, mat, int32([30 60 180 210]));
imshow(y);
http://www.mathworks.com/help/vision/ref/vision.shapeinserterclass.html
You can check my answer here. It is robust way to achieve what you are asking for. Advantage of my approach is that it doesn't need additional parameters to control density of the line drawn.
Something like this:
[linspace(30,200,numSteps); linspace(35,60,numSteps)]
Does that work for you ?
Mark