Matlab: Removing white cells from image - matlab

I would like to only remove white blood cells and keep red blood cells from the image below. What is the best way to do this in Matlab?
format longg;
format compact;
fontSize = 16;
rgbImage = imread('E:\GP_Final\DS\CL_13-09_image2.jpg');
[rows columns numberOfColorBands] = size(rgbImage);
% imshow(rgbImage, []);
% title('Original Color Image', 'FontSize', fontSize);
hsv = rgb2hsv(rgbImage);
% figure(2),imshow(hsv);
% Display the color channels.
hueImage = hsv(:, :, 1);
saturationImage = hsv(:, :, 2);
valueImage = hsv(:, :, 3);
subplot(2, 2, 2);
imshow(hueImage, []);
title('Hue Channel', 'FontSize', fontSize);
subplot(2, 2, 3);
imshow(saturationImage, []);
title('Saturation Channel', 'FontSize', fontSize);
subplot(2, 2, 4);
imshow(valueImage, [])
title('Value Channel', 'FontSize', fontSize);
[pixelCounts values] = hist(hueImage, 500);
figure;
bar(values, pixelCounts);
title('Histogram of Hue Channel', 'FontSize', fontSize);
redPixels = hueImage > 0.3 & hueImage >0.8 & valueImage <= 0.9;
% figure(10);
% imshow(redPixels);
% title('Map of red Pixels', 'FontSize', fontSize);
saturationImage(redPixels) = saturationImage(redPixels) *3.5;
% figure(7),imshow(saturationImage);
% title('New Saturation Channel', 'FontSize', fontSize);
% Combine back to form new hsv image
hsvImage = cat(3, hueImage, saturationImage, valueImage);
% Convert back to RGB color space.
rgbImage = hsv2rgb(hsvImage);
figure(8), imshow(rgbImage);
title('RGB Image with Enhanced red', 'FontSize', fontSize);
se1 = strel('disk',1);
erodedBW = imerode(redPixels,se1);
se2 = strel('disk',2);
dilatedBW2 = imdilate(erodedBW,se2);
se3 = strel('disk',1);
openedBW = imopen(dilatedBW2,se3);
filledBW=imfill(openedBW,'holes');
figure(3), imshow(filledBW);title('after fill holes ');
bw3=bwareaopen(filledBW,80);
figure(5), imshow(bw3);title('after remove small objects ');
that is i did but it not work for all images, there is any way to solve it ?

You wish to remove out the deep purple cell in the middle of the image. It's a very simple task in looking at the colour distribution in the HSV colour space. I wouldn't look at the hue in this case because the colour distribution of the image will most likely have similar hues. The saturation is what I would aim for.
Let's read in the image from StackOverflow, convert the image to HSV and look at the saturation component:
im = imread('http://i.stack.imgur.com/OQUKj.jpg');
hsv = rgb2hsv(im2double(im));
imshow(hsv(:,:,2))
We get this image:
You can clearly see that the "red" blood cell has a higher saturation than the background, and so we can do some simple thresholding. A saturation of 0.4 seems to work for me:
mask = hsv(:,:,2) > 0.4;
imshow(mask);
We get this:
There are some spurious pixels, so we can remove this with a bwareaopen operation. Any pixels whose areas are below 300 I remove:
mask_remove = bwareaopen(mask, 300);
imshow(mask_remove);
We get this:
Now, there are holes in this remaining cell. We can remedy this by filling these holes using imfill and selecting the holes option:
mask_fill = imfill(mask_remove, 'holes');
imshow(mask_fill);
We get this:
I'm going to dilate this mask slightly to make sure we get rid of the rest of the purple surroundings:
se = strel('square', 7);
mask_final = imdilate(mask_fill, se);
The last thing to do is to use this mask and mask the original image, then produce a final image with the white blood cell remove. Simply invert the mask and multiply this with the original image, then fill in the missing information with white:
mask_final = repmat(mask_final, [1 1 3]);
out = im .* uint8(~mask_final);
out(mask_final) = 255;
imshow(out);
We get this:

You can import the image by dragging it into the workspace or clicking the 'import' button in Matlab. This will give you a width x height x 3 matrix which contains the RGB values per pixel. By thresholding the red, green a blue values, you can select which pixels to edit. Using imview() and imsave(), you can view and store the result.
To edit multiple images, you can use (replace png with the extension you have):
fileNames = [cellstr('name1'), cellstr('name2')]; % The images to process without extension.
for i = 1:length(fileNames)
img = imread([char(fileNames(i)), '.png']);
% Process the image here.
% ...
imwrite([char(fileNames(i)), '-edited.png'], 'png');
end

Related

MATLAB: Smooth curves on tilted edges - Edge Detection (Polyfit or fitPolynomialRANSAC?)

i have images with bands. The bands are not always 100% straight and not always 100% horizontal. They can be tilted and bent. Each band has a constant width.
I need the width of each band on the image and show the edges of each band as smooth as possible:
Example_edge_detection.png Example_straight.png Example_tilted.png
I have already a solution for 100% straight-horizontal bands. But i dont know how to do it with tilted bands. Maybe Polyfit or houghlines are needed? Please help me. Thank You!
Here the working code for the straight-horizontal bands!
clc;
close all;
clear;
workspace;
format long g;
format compact;
fontSize = 20;
folder = pwd;
baseFileName = 'Example_straight.png';
grayImage = imread(baseFileName);
[rows, columns, numberOfColorChannels] = size(grayImage);
if numberOfColorChannels > 1
grayImage = min(grayImage, [], 3);
end
hFig = gcf;
hFig.WindowState = 'maximized';
grayImage = adapthisteq(grayImage);
verticalProfile = mean(grayImage, 2);
threshold = 118;
binaryImage = imfill(grayImage < threshold, 'holes');
% Take the 3 largest blobs
binaryImage = bwareafilt(binaryImage, 3);
% Snip off small tendrils using imopen()
binaryImage = imopen(binaryImage, true(1, 3));
% Take the 3 largest blobs
binaryImage = bwareafilt(binaryImage, 3);
subplot(1, 1, 1);
imshow(baseFileName);
hFig = gcf;
hFig.WindowState = 'maximized'; %
axis('on', 'image');
title('Edge Detection', 'FontSize', fontSize, 'Interpreter', 'None');
binaryProfile = (verticalProfile > threshold)';
bandStarts = strfind(binaryProfile, [0, 1]);
bandStops = strfind(binaryProfile, [1, 0]);
for k = 1 : length(bandStarts)
yline(bandStarts(k), 'Color', 'r', 'LineWidth', 1);
yline(bandStops(k), 'Color', 'r', 'LineWidth', 1);
end
It looks like your algorithm currently just does a simple threshold to find horizontal lines, where the threshold is applied to the difference in the mean horizontal pixel value between two adjacent rows. If that's good enough to get the job done, that's good enough for me.
As for non-horizontal lines, maybe you can try a routine like this to simply straighten out the image first.

Image object width and height - Matlab

Based on this question Width and height of a rotated polyshape object - Matlab I was running the cool code. It can extract the height and width of the bounding box but how can I get the “real” object maximum height and width (which is not always the bounding box like in the image attached)?
Code:
clc;
clear all;
Image = imread('E:/moon.gif');
BW = imbinarize(Image);
BW = imfill(BW,'holes');
BW = bwareaopen(BW, 100);
stat = regionprops(BW,'ConvexHull','MinFeretProperties');
% Compute Feret diameter perpendicular to the minimum diameter
for ii=1:numel(stat)
phi = stat(ii).MinFeretAngle; % in degrees
p = stat(ii).ConvexHull * [cosd(phi),-sind(phi); sind(phi),cosd(phi)];
minRect = max(p) - min(p); % this is the size (width and height) of the minimal bounding box
stat(ii).MinPerpFeretDiameter = minRect(2); % add height to the measurement structure
width = minRect(1)
height = minRect(2)
end
You can try this code:-
clc; % Clear the command window.
close all; % Close all figures (except those of imtool.)
workspace; % Make sure the workspace panel is showing.
format long g;
format compact;
fontSize = 25;
%===============================================================================
% Get the name of the first image the user wants to use.
baseFileName = 'A.jpg';
folder = fileparts(which(baseFileName)); % Determine where demo folder is (works with all versions).
fullFileName = fullfile(folder, baseFileName);
% Check if file exists.
if ~exist(fullFileName, 'file')
% The file doesn't exist -- didn't find it there in that folder.
% Check the entire search path (other folders) for the file by stripping off the folder.
fullFileNameOnSearchPath = baseFileName; % No path this time.
if ~exist(fullFileNameOnSearchPath, 'file')
% Still didn't find it. Alert user.
errorMessage = sprintf('Error: %s does not exist in the search path folders.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
%=======================================================================================
% Read in demo image.
rgbImage = imread(fullFileName);
% Get the dimensions of the image.
[rows, columns, numberOfColorChannels] = size(rgbImage);
% Display the original image.
subplot(2, 2, 1);
imshow(rgbImage, []);
axis on;
caption = sprintf('Original Color Image, %s', baseFileName);
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
% Set up figure properties:
% Enlarge figure to full screen.
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0.05 1 0.95]);
% Get rid of tool bar and pulldown menus that are along top of figure.
% set(gcf, 'Toolbar', 'none', 'Menu', 'none');
% Give a name to the title bar.
set(gcf, 'Name', 'Demo by ImageAnalyst', 'NumberTitle', 'Off')
drawnow;
hp = impixelinfo(); % Set up status line to see values when you mouse over the image.
[rows, columns, numberOfColorChannels] = size(rgbImage)
if numberOfColorChannels > 1
% It's not really gray scale like we expected - it's color.
% Use weighted sum of ALL channels to create a gray scale image.
% grayImage = rgb2gray(rgbImage);
% ALTERNATE METHOD: Convert it to gray scale by taking only the green channel,
% which in a typical snapshot will be the least noisy channel.
grayImage = rgbImage(:, :, 1); % Take red channel.
end
% Display the image.
subplot(2, 2, 2);
imshow(grayImage, []);
axis on;
caption = sprintf('Gray Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
hp = impixelinfo();
drawnow;
% Display the histogram of the image.
subplot(2, 2, 3);
histogram(grayImage, 256);
caption = sprintf('Histogram of Gray Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
grid on;
drawnow;
%=======================================================================================
binaryImage = grayImage < 150;
% Keep only the largest blob.
binaryImage = bwareafilt(binaryImage, 1);
% Display the masked image.
subplot(2, 2, 3);
imshow(binaryImage, []);
axis on;
caption = sprintf('Binary Image');
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
hp = impixelinfo();
drawnow;
% Get the bounding box of the blob.
props = regionprops(binaryImage, 'BoundingBox');
boundingBox = [props.BoundingBox]
% Display the original image.
subplot(2, 2, 4);
imshow(rgbImage, []);
axis on;
caption = sprintf('Original Color Image, %s', baseFileName);
title(caption, 'FontSize', fontSize, 'Interpreter', 'None');
% Plot the bounding box over it.
hold on;
rectangle('Position', boundingBox, 'LineWidth', 2, 'EdgeColor', 'r')

How can I extract text from image respecting the distance between words?

I am trying to extract text from an image of printed text. Along with the text itself, I am interested in detecting the spaces between the words as well. The spaces between words are not consistent (deliberate) and that is something I want detected.
To achieved this I first had to extract the text lines. I achieved that using the projection profile code attached (code copied from one of the answers by ImageAnalyst).
One way I thought of achieving this was by counting the number of white pixels between the words, if I know the number of pixels taken by a single space (say n), I could just determine the number of spaces by dividing the white pixels between the words by this 'n' to get the number of spaces.
I tried that but it did not go as planned, the results are very conflicting, even when compared against known ground truth values. Determining a baseline of every text line is proving to be difficult, for a single space between two words I am getting different pixel count. This is because as counting the white pixels from letter d to b is different from counting the white pixels from c to s (the white pixels within the curve of c is also sometimes counted.)
Any guidance or suggestions would be greatly appreciated.
Thank you
code I used :
clc;
close all;
clear;
fontSize = 16;
img = imread('Lines.png');
[rows, columns, dim] = size(img);
if dim > 1
grayImage = img(:, :, 2);
end
% Display the original gray scale image.
subplot(3, 3, 1);
imshow(grayImage, []);
title('Original image', 'FontSize', fontSize);
axis on;
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1]); % Enlarge figure to full screen.
% Threshold the image.
binaryImage = grayImage < 210;
% Get rid of small areaas of 14 pixels or less
binaryImage = ~bwareaopen(binaryImage, 15);
subplot(2, 3, 2);
imshow(binaryImage);
title('Binary Image', 'FontSize', fontSize);
axis on;
% Vertical Profile
verticalProfile = sum(binaryImage, 2);
subplot(3, 3, [2 3]);
plot(verticalProfile, 'b');
grid on;
title('Vertical Profile', 'FontSize', fontSize);
rowsWithText = verticalProfile < 600;
% Find top and bottom lines
topLines = find(diff(rowsWithText) == 1);
bottomLines = find(diff(rowsWithText) == -1);
for j = 1 : length(topLines)
topRow = topLines(j);
bottomRow = bottomLines(j);
thisLine = binaryImage(topRow:bottomRow, :);
subplot(3, 3, [4 5 6]);
imshow(thisLine, []);
axis on;
caption = sprintf('Line %d of the text', j);
title(caption, 'FontSize', fontSize);
% Horizontal profile
horizontalProfile = sum(thisLine, 1);
subplot(3, 3, [7 8 9]);
plot(horizontalProfile);
grid on;
caption = sprintf('Horizontal Profile of Line %d of the text', j);
title(caption, 'FontSize', fontSize);
promptMessage = sprintf('lines %d', j);
titleBarCaption = 'Continue?';
button = questdlg(promptMessage, titleBarCaption, 'Continue', 'Cancel', 'Continue');
if strcmpi(button, 'Cancel')
return;
end
end
msgbox('Done!');
picture : Image of my program
Try playing around with something like this:
% define min length of gap between words; play around with this value
min_gap_length = 5;
% define wordStarts/wordEnds like you've defined topLines/bottomLines
wordStarts = find(diff(horizontalProfile) == -1);
wordEnds = find(diff(horizontalProfile) == 1);
if wordStarts(1) > wordEnds(1)
wordStarts = [1, wordStarts];
end
if wordEnds(end) < wordStarts(end)
wordEnds = [wordEnds, length(horizontalProfile)];
end
% restrict wordStarts/wordEnds to gaps of over min_gap_length
I = wordStarts(2:end) - wordEnds(1:end-1) > min_gap_length;
wordStarts = wordStarts([true, I]);
wordEnds = wordEnds([I, true]);

MATLAB overlaying white area of binary image with rgb image

I am using MATLAB 2012b.
I was able to get the objects outline in an image:
using method of Active Contour Segmentation and the result is in binary mask:
How can I fill the white area of the binary image with the original rgb image?
Basically what I want is to make the background completely black.
Here is my code:
gambarOri = imread(pathGambar);
A = rgb2gray(gambarOri );
mask = zeros(size(A)); mask(10:end-10,10:end-10) = 1;
BW = activecontour(A, mask, 500);
figure, subplot(1, 2, 1), imshow(A), title('Grayscale');
subplot(1, 2, 2), imshow(BW), title('Segmented image in Binary');
You cannon overlay an RGB on a binary image, since the data types do not match. What you can do is to modify your RGB image according to the binary image. For instance you can replace values of the RGB image in false areas of BW with zeros:
% separating RGB channels:
R = gambarOri(:, :, 1);
G = gambarOri(:, :, 2);
B = gambarOri(:, :, 3);
% placing zeros where BW is FALSE:
R(~BW) = 0;
G(~BW) = 0;
B(~BW) = 0;
% concatenate color layers into final result:
blackBG = cat(3, R, G, B);
% figure; imshow(blackBG)
With this result for your provided image:
You can use bsxfun:
blackBG = uint8(bsxfun(#times, double(gambarOri), BW));

Drawing the major and minor axis of an elliptical object in MATLAB

This program currently inputs an image of a coin, thresholds it, binarizes it, and finds the major and minor axis lengths of the segmented elliptical using the regionprops function. How do I output a subplot where I draw the axes used to calculate the 'MajorAxisLength' and 'MinorAxisLength' over the original image?
I have appended my code for your perusal.
% Read in the image.
folder = 'C:\Documents and Settings\user\My Documents\MATLAB\Work';
baseFileName = 'coin2.jpg';
fullFileName = fullfile(folder, baseFileName);
fullFileName = fullfile(folder, baseFileName);
if ~exist(fullFileName, 'file')
fullFileName = baseFileName; % No path this time.
if ~exist(fullFileName, 'file')
%Alert user.
errorMessage = sprintf('Error: %s does not exist.', fullFileName);
uiwait(warndlg(errorMessage));
return;
end
end
rgbImage = imread(fullFileName);
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows columns numberOfColorBands] = size(rgbImage);
% Display the original color image.
subplot(2, 3, 1);
imshow(rgbImage, []);
title('Original color Image', 'FontSize', fontSize);
% Enlarge figure to full screen.
set(gcf, 'Position', get(0,'Screensize'));
% Extract the individual red color channel.
redChannel = rgbImage(:, :, 1);
% Display the red channel image.
subplot(2, 3, 2);
imshow(redChannel, []);
title('Red Channel Image', 'FontSize', fontSize);
% Binarize it
binaryImage = redChannel < 100;
% Display the image.
subplot(2, 3, 3);
imshow(binaryImage, []);
title('Thresholded Image', 'FontSize', fontSize);
binaryImage = imfill(binaryImage, 'holes');
labeledImage = bwlabel(binaryImage);
area_measurements = regionprops(labeledImage,'Area');
allAreas = [area_measurements.Area];
biggestBlobIndex = find(allAreas == max(allAreas));
keeperBlobsImage = ismember(labeledImage, biggestBlobIndex);
measurements = regionprops(keeperBlobsImage,'MajorAxisLength','MinorAxisLength')
% Display the original color image with outline.
subplot(2, 3, 4);
imshow(rgbImage);
hold on;
title('Original Color Image with Outline', 'FontSize',fontSize);
boundaries = bwboundaries(keeperBlobsImage);
blobBoundary = boundaries{1};
plot(blobBoundary(:,2), blobBoundary(:,1), 'g-', 'LineWidth', 1);
hold off;
I had the same task as you for some project I did 2 years ago. I've modified the code I used then for you below. It involved calculating the covariance matrix for the datapoints and finding their eigenvalues/eigenvectors. Note here that because of circular symmetry, the minor and major axis will be somewhat "random". Also note that I have made the image binary in a very naïve way to keep the code simple.
% Load data and make bw
clear all;close all; clc;
set(0,'Defaultfigurewindowstyle','docked')
I = imread('american_eagle_gold_coin.jpg');
Ibw = im2bw(I,0.95);
Ibw = not(Ibw);
figure(1);clf
imagesc(Ibw);colormap(gray)
%% Calculate axis and draw
[M N] = size(Ibw);
[X Y] = meshgrid(1:N,1:M);
%Mass and mass center
m = sum(sum(Ibw));
x0 = sum(sum(Ibw.*X))/m;
y0 = sum(sum(Ibw.*Y))/m;
%Covariance matrix elements
Mxx = sum(sum((X-x0).^2.*Ibw))/m;
Myy = sum(sum((Y-y0).^2.*Ibw))/m;
Mxy = sum(sum((Y-y0).*(X-x0).*Ibw))/m;
MM = [Mxx Mxy; Mxy Myy];
[U S V] = svd(MM);
W = V(:,1)/sign(V(1,1)); %Extremal directions (normalized to have first coordinate positive)
H = V(:,2);
W = 2*sqrt(S(1,1))*W; %Scaling of extremal directions to give ellipsis half axis
H = 2*sqrt(S(2,2))*H;
figure(1)
hold on
plot(x0,y0,'r*');
quiver(x0,y0,W(1),H(1),'r')
quiver(x0,y0,W(2),H(2),'r')
hold off
Look at the documentation for the Orientation attribute that regionprops() can return to you.
This gives the angle between the positive x-axis and the major axis of the ellipse. You should be able to derive the equation for the major axis line in terms of that angle, and then just make a grid of x-axis points, and compute the major axis line's value for all the points in your grid, then just plot it like you would plot any other curve in MATLAB.
To do the same for the minor axis, just note that it will be 90 degrees further counter-clockwise from the major axis, then repeat the step above.
Usually one does it with computing eigenvectors, as explained in the Wikipedia article Image moment under 'examples'. That would be the correct way.
But I wonder, if you know the centroid and the boundingbox from MATLAB, then the endpoint of the major axis must be in the upper left or upper right corner. So checking (apart from noise) these two corners, if there are pixel, would give you the major axis. The minor axis then is just orthogonal to it with respect to the centroid.
Sorry for not having MATLAB code ready.
The reasoning is not that wrong, but not so good either, using the orientation as written above is better ;)