Negative values in Watershed algorithm leading to black image - matlab

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.

Related

Image single background color - Matlab

In the code below I rotated an image. How can I get a single background color (white or black)?
code:
close all;
clear;
clc;
url='http://www.clker.com/cliparts/T/i/o/c/X/Q/airplane-md.png';
RI = imread(url);
I = rgb2gray(RI);
BI = imbinarize(I);
LI = bwlabel(BI);
mea = regionprops(LI, 'All');
RI = imrotate(RI, -mea(1).Orientation,'loose');
imshow(RI);
Given that the image is a simple logo (as opposed to a photo for instance) you can likely use logical indexing to change all of the black pixels added by imrotate to white pixels.
I don't have the image processing toolbox so I couldn't run your code, but the sample below should illustrate:
%Load RBG image to test on
RI = imread('peppers.png');
%Create black region to remove
RI(100:150,100:150,:) = 0;
figure()
imshow(RI)
title('Original Image')
%Replace all black pixels with white
inds = sum(RI,3)==0;
RI_new = RI;
RI_new(repmat(inds,1,1,3))=255;
figure()
imshow(RI_new)
title('New Image')
In comparison to the answer from #SardarUsama, this has the weakness of assuming there are no black pixels in your original image but the advantage of using built-in Matlab functions only.
Edit: Updated to show example on RGB image rather than grayscale
Your original image has white background. When you rotate it, you get black pixels in the background to fill up the image matrix. This may be due to that the preallocation of the rotated image matrix is done with zeros which then translates to black (implemented probably in imrotatemex and in the lines 116 and 118 of imrotate). You can use these alternate implementations of imrotate but preallocate the matrix with ones (for double data) or 255 (for uint8 data).
For example, in line 31 of Rody's implementation, i.e.:
imagerot = zeros([max(dest) p],class(image));
Change this line to:
imagerot = 255*ones([max(dest) p],'uint8'); %Your image is uint8 in this case
Result:

how to segment the colony When I can't use bw image

I tried to use image processing toolbox to count my colony. I used imfindcircles to find the colony and count. But I got some problems:
(1)Due to that my colony could be white and black, I tried to find the colony by using Bright or Dark in ObjectPolarity, and use a if loop to select which one I finally choose. But the first step of my if loop doesn't really work.
(2) For using imfindcircles to find the circle, I found it works for the colony in white, while the method is a disaster for the black colony. I'm kind of desperate now because I can't find other ways to segment the colony.
So finally i need to: label each colony in the plate, count the colony number, calculate each colony size, extract the mean gray value for each colony (the colour).
Thank you very much!!!
So here is my code:
im = imread(test.jpeg)
imshow(im)
% to find the colony
[centersBright, radiiBright] = imfindcircles(im,[30 60],'ObjectPolarity','bright','Sensitivity',0.925,'Method','twostage','EdgeThreshold',0.1)
[centersDark, radiiDark] = imfindcircles(im,[30 60],'ObjectPolarity','dark','Sensitivity',0.925,'Method','twostage','EdgeThreshold',0.15)
% to select which one is the correct count. if one of the length(centres) value is lower than 10, I consider it as an error. But if both length(centres) is low than 10, I consider it as a treatment effect and accept the value.
if length(centersDark)<10<length(centersBright)
centers=centersBright
radii=radiiBright
elseif length(centersBright)<10<length(centersDark)
centers=centersDark
radii=radiiDark
else
centers=[centersBright,centersDark]
radii=[radiiBright,radiiDark]
end
% view and label the colony
h = viscircles(centers,radii)
for k = 1:length(radii)
string = sprintf('%d',k)
text(centers(k,1),centers(k,2),string,'color','y','HorizontalAlignment', 'center','VerticalAlignment', 'middle')
area(k)=pi*radii(k)^2
end
Suggested Solution
While it's hard to distinguish between the colonies and their surrounding in the black and white version of your input, it is not hard to do so in the hue space by using thresholding.
The reason is that the colonies have a unique hue which is different from their background.
This will be more noticable after converting to HSV space:
Therefore, I suggest the folowing solution:
convert input image to HSV space
use thresholding on the hue component.
extract connected components
Use the connected components which are large enough for the mask
perform imclose opertation for cleaning the artifacts
Code
%reads the image
I = imread('colony2.png');
%convert to hsv
hsvIm = rgb2hsv(I);
%thresholding on the hue space
bwIm = hsvIm(:,:,1) < 0.15;
%find connected components
CC = bwconncomp(bwIm);
%choose only cc with sufficient amount of pixels
numPixels = cellfun(#numel,CC.PixelIdxList);
relevantCC = CC.PixelIdxList(numPixels > 10);
%generate a binary mask with these conencted componants
colonyMask = false(size(bwIm));
for ii=1:length(relevantCC)
colonyMask(relevantCC{ii}) = true;
end
%perform morpholopical operations for cleaning
colonyMask = imclose(colonyMask,strel('disk',1));
%display result
h = imshow(I); % Save the handle; we'll need it later
set(h, 'AlphaData', ~colonyMask);
Results
Selection of threshold
The threshold was chosen by choosing the first pick in the histogram of the hue component
histogram(hsvIm(:,:,1))

Watershed Algorithm setting removing all connected components

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

Scaling fft2 after zeropad frequency domain

I have an image I1=(50x50) with two circles - A (which pixels have an intensity value of 100) and B (intensity values of 500).
I want to resize the image to I2=(100x100). I started by doing a FFT2 of the image, after that I zero padded it, making the FT a matrix of 100x100 and then doing the IFFT2 again.
FT = fftshift(fft2(M));
FT = padarray(FT,[50 50]);
I2 = ifft2(ifftshift(FT));
So now I have a new image with size 100x100 but now the amplitude for the circles is different. How do I correct this scaling problem?
After this I apply a mask to select only the circle A so I have now I3=(100x100) with only the circle A. To this image I am applying a FFT2 again and taking only the 50x50 part in the center of this FT and doing the IFFT2 to get another image PSF=(50x50) where I can see a point spread function (PSF) of this circle.
FT = fftshift(fft2(I3));
FTcenter = FT(25:75,25:75);
I4 = ifft2(ifftshift(FTcenter));
Again, should I apply any scaling here as well?
I love using the built-in interpft for FFT-based sinc-interpolation because it takes all the legwork out of zero-padding, shifting, scaling, etc. (and you can inspect how it does it: edit interpft, it’s all legit). It only does 1D interpolation but you can run it twice in both dimensions for 2D.
I made a 100 by 100 grayscale image containing two circles, with min/max intensities [29, 255]:
Then, in the Matlab REPL:
>> im = double(imread('circles.png'));
>> whos im
Name Size Bytes Class Attributes
im 100x100 80000 double
>> imup = interpft(interpft(im, 2*size(im,1), 1), 2*size(im,2), 2);
>> whos imup
Name Size Bytes Class Attributes
imup 200x200 320000 double
This creates the 2× upsampled image. It’s min and max are [23.059, 271.66], outside the bounds of the original [29, 255], so I rescale the values of the upscaled image to live in the same bounds as the original image, convert to uint8, and save it as PNG:
>> rescaled = interp1([min(imup(:)), max(imup(:))], [min(im(:)), max(im(:))], imup);
>> imwrite(uint8(rescaled), 'circles-up.png')
Looking closely at this result, you can see why the min/max values of the upscaled array are broader than the original: this kind of sinc-interpolation causes Gibbs ringing at sharp transitions in the image like the circle edges, which causes the upscaled image to overshoot peaks and undershoot troughs.
interpft can both interpolate (upsample) as well as decimate (downsample). So here’s a complete script—with one tweak: I “invert” the colors from my original image so the background is black instead of white: this makes masking easier:
clearvars
interpft2 = #(x, sz) interpft(interpft(x, sz(1), 1), sz(2), 2);
im = 255 - double(imread('circles.png'));
imup = interpft2(im, 2*size(im));
mask = double(imread('circles-mask.png')) ~= 255;
psf = interpft2(mask .* imup, round(size(imup) / 2));
imwrite(uint8(255 - round(psf)), 'psf.png')
Here is the upsampled circles-mask.png mask (where black means data to keep):
And here's the resulting psf image (inverted), which is the same as the original circles.png image without the little circle:
It appears that, because of linearity, applying the mask in the 2× upsampled image is equivalent to just applying a smaller mask in the original image.
So I should ask if what I did above match what you’re trying to do with the PSF? I’d like to again emphasize that the interpolating and decimating steps you describe in your question (FFT→zeropad→IFFT and FFT→keep center→IFFT) are exactly what interpft does (plus the bookkeeping to make sure amplitudes and symmetries are correct).

what is the right way to compute the measures for images with different color properties

I need a little help guys in Matlab in Matrix Dimensions,
I Have two images imported by imread function:
im1 = imread('1.jpg');
im2 = imread('2.jpg');
im1 is the reference image, while im2 is the Noisy image.
In the workspace window, Matlab shows the im2 Dimensions like this: 768x1024x3
while im2 displayed as: 768x1024
They are both RGB, there's no greyscale images,
In fact the second image is the a compressed image (performed compression algorithm on it ) while the first image is natural JPEG Image, untouched
and for calculating MSE/PNSR for both images, the matrix dimensions must be the same.
I Will need to transform im1 dimensions to be 3d like the first image (768x1024)
I tried this functions (squeeze, reshape) and with no success
You were on the right track with repmat. Here's the correct syntax:
im2 = repmat(im2, [1 1 3]);
This says you want 1 replicate along the first dimension, 1 replicate along the second dimension, and 3 replicates along the third dimension.
Are you sure that both are RGB images because im2 has only one channel and it looks grayscale but it can also be a colormap image in that case try
[im2, map] = imread('im2.jpg');
and see if anything is appearing in map variable. If the image is indeed colormap image, the map variable should be of size 256 X 3.
What donda has suggested is repeating the grayscale channel 3 times to make it of size 768x1024x3. Another possibility is that noisy image was created by converting RGB image to grayscale or by taking green channel of RGB image. Verify the source of the image in that case.
About PSNR computation I have a feeling that there is some problem with your code. I have given my code below use this and see if it works. Get back to me if you face any problem.
function [Psnr_DB] = psnr(I,I_out)
I = double(I);
I_out = double(I_out);
total_error = 0;
for iterz = 1:size(I,3)
for iterx = 1:size(I,1)
for itery = 1:size(I,2)
total_error = total_error + (I(iterx,itery,iterz)-I_out(iterx,itery,iterz))^2;
end
end
end
MSE = total_error/numel(I);
Psnr = (255^2)/MSE;
Psnr_DB = 10*log10(Psnr) %#ok<NOPRT>