I have 2 variables:
Image which contain original image.
FilteredImage which is filtered image.
Both are RGB images. I know how to calculate the bending for 2-D images
Image = unread('C:\Users\klass\Pictures\man.jpeg');
NoiseImage = minimise(Image,'gaussian');
ImageFiltered = NoiseImage;
for c = 1 : 3
ImageFiltered (:, :, c) = medfilt2(NoiseImage(:, :, c) [3, 3]
end
Bending = norm(im2double(Image - FilteredImage))/norm(im2double(FilteredImage)) * 100;
When I try to use this formula to my figures I get this error:
Error using norm
Input must be 2-D.
I tried pass 3-D images to norm() function. The work around is convert each image to 2-D by rgb2gray()function.
Therefore I evacuate banding with formula:
Bending = norm(im2double(rgb2gray(Image) - rgb2gray(FilteredImage)) / norm(im2double(rgb2gray(Image))) * 100
I need to create an image with three levels as described in the following question:
How do i create a rectangular mask at known angles?
Code:
%# Create a logical image of a circle with image size specified as follows:
imageSizeX = 401;
imageSizeY = 301;
[columngrids, rowgrids] = meshgrid(1:imageSizeX, 1:imageSizeY);
%# Next create a logical mask for the circle with specified radius and center
centerY = (imageSizeY/2) + 0.5;
centerX = (imageSizeX/2) + 0.5;
radius = 50;
Img = double( (rowgrids - centerY).^2 + (columngrids - centerX).^2 <= radius.^2 );
%# change image labels to numeric
for ii = 1:numel(Img)
if Img(ii) == 0
Img(ii) = 2; %change label from 0 to 2
end
end
%# plot image
RI = imref2d(size(Img),[0 size(Img, 2)],[0 size(Img, 1)]);
figure, imshow(Img, RI, [], 'InitialMagnification','fit');
%# create the desired angle
phi = 45;
width = 350; % Desired width in pixels
height = 300; % Desired height of bar in pixels
y = centerY - round(radius*cos(phi*pi/180)); % Find the nearest column
y0 = max(1, y-height); % Find where to start the bar
Img(y0:y, 1:width)=3;
figure, imshow(Img, RI, [], 'InitialMagnification','fit');
I have realized that rounding the part that says (radius*cos(phi*pi/180)) to find y could in most cases create an error in the desired angle. Hence, if I remove the ‘round function’, I get the actual y value at the exact point at which the desired angle is formed in the image. Nonetheless, I get the warning as stated above. However, if I go further to apply the line: Img(y0:y, 1:width)=3;, the code still works, but I notice that Matlab approximates the y value when creating the vector y0:y (I feel this is the point where I have an issue)
My question then is: is there a way I could get around this such that I create my desired angle accurately and still end up having the bar from y to y0? without having matlab approximate the y value when it is creating the vector y0:y?
Maybe if i convert to cartesian xy coordinates i could have a chance? Any ideas how to do this conversion? Many thanks for your help!
A pixels is the atomic element of a digital image. Digital images are discrete functions. A camera has discrete pixels, your screen has discrete pixels...
While reading sub-pixel values is always possible through interpolation, you cannot write pixels in the same way as the output is discrete.
Just accept that drawing is only possible with integer pixel coordinates but keep in mind that this does not stop you from doing more precise calculations behind the scenes.
So to answer your question:
You overcome the warning by using integer indices.
I'm using the watershed algorithm to try and segment touching nuclei. A typical image may look like:
or this:
I'm trying to apply the watershed algorithm with this code:
show(RGB_img)
%Convert to grayscale image
I = rgb2gray(RGB_img);
%Take structuring element of a disk of size 10, for the morphological transformations
%Attempt to subtract the background from the image: top hat is the
%subtraction of the open image from the original
%Morphological transformation to subtract background noise from the image
%Tophat is the subtraction of an opened image from the original. Remove all
%images smaller than the structuring element of 10
I1 = imtophat(I, strel('disk', 10));
%Increases contrast
I2 = imadjust(I1);
%show(I2,'contrast')
%Assume we have background and foreground and assess thresh as such
level = graythresh(I2);
%Convert to binary image based on graythreshold
BW = im2bw(I2,level);
show(BW,'C');
BW = bwareaopen(BW,8);
show(BW,'C2');
BW = bwdist(BW) <= 1;
show(BW,'joined');
%Complement because we want image to be black and background white
C = ~BW;
%Use distance tranform to find nearest nonzero values from every pixel
D = -bwdist(C);
%Assign Minus infinity values to the values of C inside of the D image
% Modify the image so that the background pixels and the extended maxima
% pixels are forced to be the only local minima in the image (So you could
% hypothetically fill in water on the image
D(C) = -Inf;
%Gets 0 for all watershed lines and integers for each object (basins)
L = watershed(D);
show(L,'L');
%Takes the labels and converts to an RGB (Using hot colormap)
fin = label2rgb(L,'hot','w');
% show(fin,'fin');
im = I;
%Superimpose ridgelines,L has all of them as 0 -> so mark these as 0(black)
im(L==0)=0;
clean_img = L;
show(clean_img)
For whatever reason after C = ~BW; the whole image goes dark. This very same code block has worked on a handful of other images, all of which were more "solid" or not as grainy as these. However, I thought I compensated for this with BW = bwdist(BW) <= 1;. I've experimented a ton and I don't really know what's happening. Any help would be great!
Ps. this is the image after BW = bwareaopen(BW,8);
Before the top-hat, you should perform a closing and an opening in order to reduce the noise.
If you perform an area opening on a noisy image, you may end up with the result on your black and white image.
So it would be:
Closing and opening
Top-Hat
Area opening if still necessary
Thresholding
Erosion and dilation to find the inner and outer markers respectively
Watershed (never use a watershed without markers).
I'm using the watershed algorithm to try and segment touching nuclei. A typical image may look like:
or this:
I'm trying to apply the watershed algorithm with this code:
show(RGB_img)
%Convert to grayscale image
I = rgb2gray(RGB_img);
%Take structuring element of a disk of size 10, for the morphological transformations
%Attempt to subtract the background from the image: top hat is the
%subtraction of the open image from the original
%Morphological transformation to subtract background noise from the image
%Tophat is the subtraction of an opened image from the original. Remove all
%images smaller than the structuring element of 10
I1 = imtophat(I, strel('disk', 10));
%Increases contrast
I2 = imadjust(I1);
%show(I2,'contrast')
%Assume we have background and foreground and assess thresh as such
level = graythresh(I2);
%Convert to binary image based on graythreshold
BW = im2bw(I2,level);
show(BW,'C');
BW = bwareaopen(BW,8);
show(BW,'C2');
BW = bwdist(BW) <= 1;
show(BW,'joined');
%Complement because we want image to be black and background white
C = ~BW;
%Use distance tranform to find nearest nonzero values from every pixel
D = -bwdist(C);
%Assign Minus infinity values to the values of C inside of the D image
% Modify the image so that the background pixels and the extended maxima
% pixels are forced to be the only local minima in the image (So you could
% hypothetically fill in water on the image
D(C) = -Inf;
%Gets 0 for all watershed lines and integers for each object (basins)
L = watershed(D);
show(L,'L');
%Takes the labels and converts to an RGB (Using hot colormap)
fin = label2rgb(L,'hot','w');
% show(fin,'fin');
im = I;
%Superimpose ridgelines,L has all of them as 0 -> so mark these as 0(black)
im(L==0)=0;
clean_img = L;
show(clean_img)
After C = ~BW; the whole image goes dark. I believe this is because the image pixels are all -inf or some smaller negative number. This is there a way around this and if so what could I change in my code to get this algorithm working? I've experimented a ton and I don't really know what's happening. Any help would be great!
The problem is with your show command. As you said in the comments this uses imshow under the hood. If you try imshow directly you'll see you also get a black image. However, if you call it with appropriate limits:
imshow(clean_img,[min(clean_img(:)), max(clean_img(:))])
you'll see everything you expect to see.
In general I usually prefer imagesc for that reason. imshow makes arbitrary judgements as to what range to represent, and I usually can't be bothered to keep up with it. I think in your case, your end image is uint16 so imshow chooses to represent the range [1, 65025]. Since all your pixel values are below 400, they look black to the naked eye for that range.
I'll try to be precise and short.
I have a volume (128x128x128) and a mask (same size with [0|1|2] values)
I want to make the 3D volume matrix a 3D image with RGB, and store in each channel (red,green,blue) the points marked in the mask.
This is to use a 2D representation by taking a slice of that 3D cube, and not compute it over and over to make things way more faster (very important in my project), so actually, the 3D volume + rgb would be like a store for 128 2D images.
The question is, what steps and how do I have to make all this:
- Create a volume 128x128x128x3 ?
- Define a new colormap (original is gray) ?
- Join each channel ?
- How do I use imagesc/whatever to show one slice of that cube with the points in the color as marked in the mask (ex: imageRGB(:,:,64)) ?
That's just my guess, but I don't even know how to do it properly...I'm a bit lost, I hope you can help me, this is a piece of code that may be wrong but may help you out
% Create the matrix 4D
ovImg = zeros(size(volImg,1),size(volImg,2),size(volImg,3),3); % 128x128x128x3
% Store in each channel the points marked as groups
ovImg(:,:,:,1) = volImg .* (mask==1);
ovImg(:,:,:,2) = volImg .* (mask==2);
ovImg(:,:,:,3) = volImg .* (mask==3);
many many thanks!!
UPDATE:
I'm having some trouble with transparency and the colormap, this is what I did.
% Create the matrix 4D
ovImg = zeros(size(volImg,1),size(volImg,2),size(volImg,3),3);
% Store in each channel the points marked as groups
ovImg(:,:,:,1) = imaNorm.*(mask==1);
ovImg(:,:,:,2) = imaNorm.*(mask==2);
ovImg(:,:,:,3) = imaNorm.*(mask==3);
[X,Y,Z] = meshgrid(1:128,1:128,1:128);
imaNorm = volImg - min(volImg(:));
maxval = max(imaNorm(:));
ovImg = imaNorm + mask * maxval;
N= ceil(maxval);
c = [linspace(0,1,N)' zeros(N,2)];
my_colormap = [c(:,[1 2 3]) ; c(:,[3 1 2]) ; c(:,[2 3 1])];
figure;
imshow(squeeze(ovImg(:,:,64)),my_colormap);
figure;
imagesc(squeeze(mask(:,:,64)));
Result (Overlayed image / mask)
Any ideas? Thanks again, everybody
FINAL UPDATE:
With the other approach that Gunther Struyf suggested, I had exactly what I wanted.
Thanks mate, I really appreciate it, hope this helps other people too.
You can use imshow with a colormap to 'fake' an RGB image from a grayscale image (which you have). For the scale I'd not multiply it, but add an offset to the value, so each mask is a different range in the colormap.
For plotting a slice of the 3d matrix, you can just index it and then squeeze it to remove the resulting singleton dimension:
Example:
[X,Y,Z]=meshgrid(1:128,1:128,1:128);
volImg =5*sin(X/3)+13*cos(Y/5)+8*sin(Z/10);
volImg=volImg-min(volImg(:));
mask = repmat(floor(linspace(0,3-2*eps,128))',[1 128 128]);
maxval=max(volImg(:));
ovImg=volImg+mask*maxval;
imshow(squeeze(ovImg(:,:,1)),jet(ceil(max(ovImg(:)))));
Unmasked, original image (imshow(squeeze(volImg(:,:,1)),jet(ceil(maxval))))
Resulting with mask (code block above):
For different colormaps, see here, or create your own colormap. Eg you're mask has three values, so let's match those with R,G and B:
N = ceil(maxval);
c = [linspace(0,1,N)' zeros(N,2)];
my_colormap = [c(:,[1 2 3]) ; c(:,[3 1 2]) ; c(:,[2 3 1])];
figure
imshow(squeeze(ovImg(:,:,1)),my_colormap);
which gives:
Other approach:
Now I understand your question, I see you got it quite right from the beginning, you only need rescale the variable to a value between 0 and 1, since from imshow:
Color intensity can be specified on the interval 0.0 to 1.0.
which you can do using:
minval=min(volImg(:));
maxval=max(volImg(:));
volImg=(volImg-minval)/(maxval-minval);
next up is your code:
ovImg = zeros([size(volImg),3]);
ovImg(:,:,:,1) = volImg .* (mask==1);
ovImg(:,:,:,2) = volImg .* (mask==2);
ovImg(:,:,:,3) = volImg .* (mask==3);
You just have to plot it now:
imshow(squeeze(ovImg(:,:,64,:)))