Marking termination and bifurcation finger minutiae points on an image in MATLAB - matlab

I have a project on Fingerprint Matching and I got stuck at marking the points of termination and bifurcation on the image. I have already stored pixels' coordinates. How would I go about doing this?

Assuming that your image is stored in a grayscale image called im, and your points of termination and bifurcation are stored in 2D matrices where the first column denotes the row and the second column denotes the column of each point, you can easily do this using sub2ind. sub2ind converts 2D co-ordinates into linear indices so that you can easily vectorize setting pixels in an image, or locations in a matrix quickly. As such, make your grayscale image into a RGB image by stacking the image three times as a 3D matrix. If you recall, grayscale images in the RGB colour model have all of the red, green and blue channels the same. For example, gray would be (R,G,B) = (128,128,128).
Let's call your bifurcation points as bifur and terminations as term and are in that 2D matrix format I specified earlier. As such, do something like this:
%// Get linear indices for bifurcation and termination points
ind_b = sub2ind(size(im), bifur(:,1), bifur(:,2));
ind_t = sub2ind(size(im), term(:,1), term(:,2));
%// Mark them on the image
red = im;
green = im;
blue = im;
red(ind_b) = 255;
green(ind_b) = 0;
blue(ind_b) = 0;
red(ind_t) = 0;
green(ind_t) = 255;
blue(ind_t) = 0;
im_colour = cat(3, red, green, blue);
imshow(im_colour);
Let's walk through this code slowly. I first figure out the linear indices of where the bifurcation and termination points are in the image. What I do next is create red, green and blue channels that are all copies of the original image. After this, I access each colour channel and mark the bifurcation and termination points with different colours. For the bifurcation points, I made them purely red or (R,G,B) = (255,0,0). For the termination points, I made them purely green or (R,G,B) = (0,255,0). Once I set the colours for the points, I create a colour image by stacking the channels together with cat, and specifically choosing the third dimension. I finally show the image in the end.
What you may also have to play with is the size of the markings on the image. As you can see, the pixels that are marked are just the single pixels themselves, and depending on the resolution of your image, you may or may not be able to see them properly in the image. As such, I would recommend you mark the pixels within a grid surrounding the original point. Perhaps you can make this a 5 x 5 grid surrounding the pixel of interest. With this, perhaps make a for loop for each point and mark a 5 x 5 pixel grid that surrounds this point. I'm also going to assume that your markings are sufficiently well inside the image so that we don't risk drawing the grid out of bounds. So the code would be modified like so:
%// Get linear indices for bifurcation and termination points
ind_b = [];
for idx = 1 : size(bifur,1)
[c,r] = meshgrid(bifur(idx,2)-2:bifur(idx,2)+2, bifur(idx,1)-2:bifur(idx,1)+2);
c = c(:);
r = r(:);
ind_b = [ind_b; sub2ind(size(im), r, c);
end
ind_t = [];
for idx = 1 : size(term,1)
[c,r] = meshgrid(term(idx,2)-2:term(idx,2)+2, term(idx,1)-2:term(idx,1)+2);
c = c(:);
r = r(:);
ind_t = [ind_t; sub2ind(size(im), r, c);
end
%// Mark them on the image
red = im;
green = im;
blue = im;
red(ind_b) = 255;
green(ind_b) = 0;
blue(ind_b) = 0;
red(ind_t) = 0;
green(ind_t) = 255;
blue(ind_t) = 0;
im_colour = cat(3, red, green, blue);
imshow(im_colour);
What has changed is the beginning of the code. For each point that we have for the bifurcation and the termination points, I use meshgrid to determine a 5 x 5 grid of points that surround each relevant pixel, then generate the linear indices for each point within the 5 x 5 grid. I then add this into one final array for both so that we can carry out the same marking logic we saw above.
You'll have to play around with the colours if you want them to be other than red and green, but this should be something for you to start with. If you want a list of possible colours with the RGB model, a nice tool is the RGB colour picker where you figure out which colour you want, and it gives you the RGB values to replicate that colour.
Good luck!

Related

Accurately detect color regions in an image using K-means clustering

I'm using K-means clustering in color-based image segmentation. I have a 2D image which has 3 colors, black, white, and green. Here is the image,
I want K-means to produce 3 clusters, one represents the green color region, the second one represents the white region, and the last one represents the black region.
Here is the code I used,
%Clustering color regions in an image.
%Step 1: read the image using imread, and show it using imshow.
img = (imread('img.jpg'));
figure, imshow(img), title('X axis rock cut'); %figure is for creating a figure window.
text(size(img,2),size(img,1)+15,...
'Unconventional shale x axis cut', ...
'FontSize',7,'HorizontalAlignment','right');
%Step 2: Convert Image from RGB Color Space to L*a*b* Color Space
conversionform = makecform('srgb2lab'); %the form of the conversion is defined as from rgb to l a b
lab_img = applycform(img,conversionform); %converting the rgb image to l a b image using the conversion form defined above.
%Step 3: Classify the Colors in 'a*b*' Space Using K-Means Clustering
ab = double(lab_img(:,:,2:3));
nrows = size(ab,1);
ncols = size(ab,2);
ab = reshape(ab,nrows*ncols,2);
nColors = 3;
% repeat the clustering 3 times to avoid local minima
[cluster_idx, cluster_center] = kmeans(ab,nColors,'distance','sqEuclidean', ...
'Replicates',3);
%Step 4: Label Every Pixel in the Image Using the Results from KMEANS
%For every object in your input, kmeans returns an index corresponding to a cluster. The cluster_center output from kmeans will be used later in the example. Label every pixel in the image with its cluster_index.
pixel_labels = reshape(cluster_idx,nrows,ncols);
figure, imshow(pixel_labels,[]), title('image labeled by cluster index');
segmented_images = cell(1,3);
rgb_label = repmat(pixel_labels,[1 1 3]);
for k = 1:nColors
color = img;
color(rgb_label ~= k) = 0;
segmented_images{k} = color;
end
figure, imshow(segmented_images{1}), title('objects in cluster 1');
figure, imshow(segmented_images{2}), title('objects in cluster 2');
figure, imshow(segmented_images{3}), title('objects in cluster 3');
But I'm not getting the results as required. I get one cluster with green regions, one cluster with green region boundaries, and one with gray, black, and white colors. Here are the resulting clusters.
The aim of doing this is that after getting the correct clustering results, I want to count the number of pixels in every region using the concept of connected components.
So, my aim is to know how many pixels there are in every color region. I tried another simpler way by getting the matrix of the 2D image and trying to figure out the number of pixels for every color. However, I found more than 3 RGB colors in the matrix, maybe because pixels of the same color have a slightly different color levels. That's why I went to image segmentation.
Can anyone please tell me how to fix the code above in order to get the required results?
I would also appreciate it if you give me hints on how to do this in an easier way, if there is any.
EDIT: Here is a code I made to iterate over every pixel in the image. Please notice I use 4 colors red, yellow, blue, and white instead of green, white, and black, but the idea is the same. rgb2name is the function that returns the color name given RGB color.
im= imread ('img.jpg');
[a b c] = size (im);
%disp ([a b]);
yellow=0;
blue=0;
white=0;
red=0;
for i=1:a
for j=1:b
x= impixel(im, i, j)/255 ;
color= rgb2name (x);
if (~isempty (strfind (color, 'yellow')))
yellow= yellow+1;
elseif (~isempty (strfind(color, 'red')))
red= red+1;
elseif (~isempty (strfind (color, 'blue')))
blue= blue+1;
elseif (~isempty (strfind (color, 'white')))
white= white+1;
else
%disp ('warning'); break;
end
disp (color);
disp (i);
end
end
disp (yellow)
disp (red)
disp (blue)
disp (white)
Thank You.
I thought this problem was very interesting, so I apologize ahead of time if the answer is a little overboard. In short, k-means is the right strategy, in general, for problems where you want to segment an image into a discrete color space. But, your example image, which contains primarily only three colors, each of which is well separated in color space, is easily segmented using only a histogram. See below for segmenting using thresholds.
You can easily get the pixel counts by summing each matrix. e.g., bCount = sum(blackPixels(:))
filename = '379NJ.png';
x = imread(filename);
x = double(x); % cast to floating point
x = x/max(max(max(x))); % normalize
% take histogram of green dimension
g = x(:, :, 2);
c = hist(g(:), 2^8);
% smooth the hist count
c = [zeros(1, 10), c, zeros(1, 10)];
N = 4;
for i = N+1:length(c) - N;
d(i - N) = mean(c(i -N:i));
end
d = circshift(d, [1, N/2]);
% as seen in histogram, the three colors fall nicely into 3 peaks
figure, plot(c, '.-');
[~, clusterCenters] = findpeaks(d, 'MinPeakHeight', 1e3);
% set the threshold halfway between peaks
boundaries = [floor((clusterCenters(2) - clusterCenters(1))/2), ...
clusterCenters(2) + floor((clusterCenters(3) - clusterCenters(2))/2)];
thresh1 = boundaries(1)*ones(size(g))/255;
thresh2 = boundaries(2)*ones(size(g))/255;
% categorize based on threshold
blackPixels = g < thresh1;
greenPixels = g >= thresh1 & g < thresh2;
whitePixels = g >= thresh2;
This is my approach to count the number of pixels in every region. Given that (as discussed in the comments):
the value (RGB) and the number (K) of colors are known a priori
compression artifacts and anti-aliasing generated additional colors, that must be considered as the nearest-neighbor among the K know colors.
Since you know a priori the colors, you don't need k-means. It could actually lead to bad results as in your question. The approach of #crowdedComputeeer take care of this aspect.
You can compute nearest neighbor with pdist2 directly on the pixel values. There's no need to use the really slow function that looks for the color name.
Here is the code. You can change the number and values of colors simply modifying the variable colors. This will compute the number of pixels in each color, and output the masks.
img = (imread('path_to_image'));
colors = [ 0 0 0; % black
0 1 0; % green
1 1 1]; % white
% % You can change the colors
% colors = [ 0 0 1; % red
% 1 1 0; % yellow
% 1 0 0; % blue
% 1 1 1]; % white
% Find nearest neighbour color
list = double(reshape(img, [], 3)) / 255;
[~, IDX] = pdist2(colors, list, 'euclidean', 'Smallest', 1);
% IDX contains the indices to the nearest element
N = zeros(size(colors, 1), 1);
for i = 1 : size(colors, 1)
% Count the number of pixels for each color
N(i) = sum( IDX == i );
end
% This will display the number of pixels for each color
disp(N);
% Eventually build the masks
indices = reshape(IDX, [size(img,1), size(img,2)]);
figure();
szc = size(colors,1);
for i = 1 : szc
subplot(1,szc,i);
imagesc(indices == i);
end
Resulting counts:
97554 % black
16894 % green
31852 % white
Resulting masks:
Maybe this project could help, please take a try.

Image classification - coloring features with a colormap?

I have an image with some features/regions in it (balls in the above example). I want to color each ball with a different color based on its properties. For example, that might be its diameter in pixels.
While I'm done on the feature recognition side, I'm stuck when it comes to showing results. Right now I'm doing:
my_image = imread(...);
//ball recognition and other stuff
for i = 1:number_of_balls
ball_diameter(i) = ... //I calculate the diameter of the i-th ball
ball_indices = ... //I get the linear indices of the i-th ball
//ball coloring
my_image(ball_indices) = 255; //color the red channel
my_image(ball_indices + R*C) = 0; //color the blue channel
my_image(ball_indices + 2*R*C) = 0; //color the green channel
end
figure
imshow(my_image)
colormap jet(100) //I want 100 different classes
colorbar
caxis([0, 50]) //I assume all balls have a diameter < 50
In the above code I'm tinting all balls red, which is definitely not what I'm looking for. The issue is that, even if I know ball_diameter(i), I do not know which colormap class that ball will get in. In other words, I would need something like:
for i = 1:number_of_balls
// ...
if ball_diameter *belongs to class k*
my_image(ball_indices) = jet(k, 1);
my_image(ball_indices + R*C) = jet(k,2);
my_image(ball_indices + 2*R*C) = jet(k,3);
end
end
How to, and mostly, is there any other more logical way?
You can separate the assignment of pixels to classes from their coloring for display: you can use my_image as a 2D R-by-C labeling matrix: that is each object (ball) is assigned a different index from 1 to 100 (in case you have 100 objects in the image). Now when you want to display the result you can either ask the figure to map indexes to colors for you, using colormap or explicitly create a colored image using ind2rgb.
For example
%// create the index/labeling matrix
my_image = zeros( R, C );
for ii = 1:number_of_balls
my_image( ball_indices ) = ii; %// assign index to pixels and not atual colors
end
%// color using figure
figure;
imagesc( my_image );axis image;
colormap rand(100,3); %// map indexes to colors using random mapping
%//explicitly create a color image using ind2rgb
my_color_image = ind2rgb( my_image, rand(100,3) );
figure;
imshow( my_color_image ); % display the color image
Notes:
1. IMHO it is preferable to use random color map to display categorizations of pixels, as opposed to jet with which you usually end up with very similar colors to adjacent objects, making it very difficult to visually appreciate the result.
2. IMHO it is more convenient to use label matrix, you can also save it to file as an indexed image (png format) thus visualizing, saving and loading your results simply and efficiently.
A simple way is as follows:
Make a 2D image of the same size as your original.
Set the indices of each "ball" to the diameter or other relevant value
Display with imagesc
Use caxis, colormap, colorbar etc. to adjust the categories dynamically.
For example,
a = randi(200,200); % 200 x 200 image containing values 1 to 200 at random
imagesc(a)
colorbar
The above should show a random color field with the default colormap. The colorbar goes from 1 to 200.
colormap(jet(5))
The colorbar still goes from 1 to 200, but with only 5 colors.
caxis([1 100])
The colorbar now shows the five colors scaled from 1 to 100 (with everything above 100 in the top pot).
If you want to convert your 2D image full of different diameters to a set of discrete labels indicating diameter ranges, an easy way is to use histc with the second output bin being the same size as your input image, set to which bin the diameter value fell into. The second input in this case is the edges of the bins, not the centres.
[n bin] = histc(a,0:20:201);

Fingerprint Minutiae Marking Matlab [duplicate]

I have a project on Fingerprint Matching and I got stuck at marking the points of termination and bifurcation on the image. I have already stored pixels' coordinates. How would I go about doing this?
Assuming that your image is stored in a grayscale image called im, and your points of termination and bifurcation are stored in 2D matrices where the first column denotes the row and the second column denotes the column of each point, you can easily do this using sub2ind. sub2ind converts 2D co-ordinates into linear indices so that you can easily vectorize setting pixels in an image, or locations in a matrix quickly. As such, make your grayscale image into a RGB image by stacking the image three times as a 3D matrix. If you recall, grayscale images in the RGB colour model have all of the red, green and blue channels the same. For example, gray would be (R,G,B) = (128,128,128).
Let's call your bifurcation points as bifur and terminations as term and are in that 2D matrix format I specified earlier. As such, do something like this:
%// Get linear indices for bifurcation and termination points
ind_b = sub2ind(size(im), bifur(:,1), bifur(:,2));
ind_t = sub2ind(size(im), term(:,1), term(:,2));
%// Mark them on the image
red = im;
green = im;
blue = im;
red(ind_b) = 255;
green(ind_b) = 0;
blue(ind_b) = 0;
red(ind_t) = 0;
green(ind_t) = 255;
blue(ind_t) = 0;
im_colour = cat(3, red, green, blue);
imshow(im_colour);
Let's walk through this code slowly. I first figure out the linear indices of where the bifurcation and termination points are in the image. What I do next is create red, green and blue channels that are all copies of the original image. After this, I access each colour channel and mark the bifurcation and termination points with different colours. For the bifurcation points, I made them purely red or (R,G,B) = (255,0,0). For the termination points, I made them purely green or (R,G,B) = (0,255,0). Once I set the colours for the points, I create a colour image by stacking the channels together with cat, and specifically choosing the third dimension. I finally show the image in the end.
What you may also have to play with is the size of the markings on the image. As you can see, the pixels that are marked are just the single pixels themselves, and depending on the resolution of your image, you may or may not be able to see them properly in the image. As such, I would recommend you mark the pixels within a grid surrounding the original point. Perhaps you can make this a 5 x 5 grid surrounding the pixel of interest. With this, perhaps make a for loop for each point and mark a 5 x 5 pixel grid that surrounds this point. I'm also going to assume that your markings are sufficiently well inside the image so that we don't risk drawing the grid out of bounds. So the code would be modified like so:
%// Get linear indices for bifurcation and termination points
ind_b = [];
for idx = 1 : size(bifur,1)
[c,r] = meshgrid(bifur(idx,2)-2:bifur(idx,2)+2, bifur(idx,1)-2:bifur(idx,1)+2);
c = c(:);
r = r(:);
ind_b = [ind_b; sub2ind(size(im), r, c);
end
ind_t = [];
for idx = 1 : size(term,1)
[c,r] = meshgrid(term(idx,2)-2:term(idx,2)+2, term(idx,1)-2:term(idx,1)+2);
c = c(:);
r = r(:);
ind_t = [ind_t; sub2ind(size(im), r, c);
end
%// Mark them on the image
red = im;
green = im;
blue = im;
red(ind_b) = 255;
green(ind_b) = 0;
blue(ind_b) = 0;
red(ind_t) = 0;
green(ind_t) = 255;
blue(ind_t) = 0;
im_colour = cat(3, red, green, blue);
imshow(im_colour);
What has changed is the beginning of the code. For each point that we have for the bifurcation and the termination points, I use meshgrid to determine a 5 x 5 grid of points that surround each relevant pixel, then generate the linear indices for each point within the 5 x 5 grid. I then add this into one final array for both so that we can carry out the same marking logic we saw above.
You'll have to play around with the colours if you want them to be other than red and green, but this should be something for you to start with. If you want a list of possible colours with the RGB model, a nice tool is the RGB colour picker where you figure out which colour you want, and it gives you the RGB values to replicate that colour.
Good luck!

Removing background and measuring features of an image in MATLAB

I'm trying to measure the areas of each particle shown in this image:
I managed to get the general shape of each particle using MSER shown here:
but I'm having trouble removing the background. I tried using MATLAB's imfill, but it doesn't fill all the particles because some are cut off at the edges. Any tips on how to get rid of the background or find the areas of the particles some other way?
Cheers.
Edit: This is what imfill looks like:
Edit 2: Here is the code used to get the outline. I used this for the MSER.
%Compute region seeds and elliptial frames.
%MinDiversity = how similar to its parent MSER the region is
%MaxVariation = stability of the region
%BrightOnDark is used as the void is primarily dark. It also prevents dark
%patches in the void being detected.
[r,f] = vl_mser(I,'MinDiversity',0.7,...
'MaxVariation',0.2,...
'Delta',10,...
'BrightOnDark',1,'DarkOnBright',0) ;
%Plot region frames, but not used right now
%f = vl_ertr(f) ;
%vl_plotframe(f) ;
%Plot MSERs
M = zeros(size(I)) ; %M = no of overlapping extremal regions
for x=r'
s = vl_erfill(I,x) ;
M(s) = M(s) + 1;
end
%Display region boundaries
figure(1) ;
clf ; imagesc(I) ; hold on ; axis equal off; colormap gray ;
%Create contour plot using the values
%0:max(M(:))+.5 is the no of contour levels. Only level 0 is needed so
%[0 0] is used.
[c,h]=contour(M,[0 0]) ;;
set(h,'color','r','linewidth',1) ;
%Retrieve the image data from the contour image
f = getframe;
I2 = f.cdata;
%Convert the image into binary; the red outlines are while while the rest
%is black.
I2 = all(bsxfun(#eq,I2,reshape([255 0 0],[1 1 3])),3);
I2 = imcrop(I2,[20 1 395 343]);
imshow(~I2);
Proposed solution / trick and code
It seems you can work with M here. One trick that you can employ here would be to pad zeros all across the boundaries of the image M and then fill its holes. This would take care of filling the blobs that were touching the boundaries before, as now there won't be any blob touching the boundaries because of the zeros padding.
Thus, after you have M, you can add this code -
%// Get a binary version of M
M_bw = im2bw(M);
%// Pad zeros all across the grayscale image
padlen = 2; %// length of zeros padding
M_pad = padarray(M_bw,[padlen padlen],0);
%// Fill the holes
M_pad_filled = imfill(M_pad,'holes');
%// Get the background mask after the holes are gone
background_mask = ~M_pad_filled(padlen+1:end-padlen,padlen+1:end-padlen);
%// Overlay the background mask on the original image to show that you have
%// a working background mask for use
I(background_mask) = 0;
figure,imshow(I)
Results
Input image -
Foreground mask (this would be ~background_mask) -
Output image -

remove the holes in an image by average values of surrounding pixels

can any one please help me in filling these black holes by values taken from neighboring non-zero pixels.
thanks
One nice way to do this is to is to solve the linear heat equation. What you do is fix the "temperature" (intensity) of the pixels in the good area and let the heat flow into the bad pixels. A passable, but somewhat slow, was to do this is repeatedly average the image then set the good pixels back to their original value with newImage(~badPixels) = myData(~badPixels);.
I do the following steps:
Find the bad pixels where the image is zero, then dilate to be sure we get everything
Apply a big blur to get us started faster
Average the image, then set the good pixels back to their original
Repeat step 3
Display
You could repeat averaging until the image stops changing, and you could use a smaller averaging kernel for higher precision---but this gives good results:
The code is as follows:
numIterations = 30;
avgPrecisionSize = 16; % smaller is better, but takes longer
% Read in the image grayscale:
originalImage = double(rgb2gray(imread('c:\temp\testimage.jpg')));
% get the bad pixels where = 0 and dilate to make sure they get everything:
badPixels = (originalImage == 0);
badPixels = imdilate(badPixels, ones(12));
%# Create a big gaussian and an averaging kernel to use:
G = fspecial('gaussian',[1 1]*100,50);
H = fspecial('average', [1,1]*avgPrecisionSize);
%# User a big filter to get started:
newImage = imfilter(originalImage,G,'same');
newImage(~badPixels) = originalImage(~badPixels);
% Now average to
for count = 1:numIterations
newImage = imfilter(newImage, H, 'same');
newImage(~badPixels) = originalImage(~badPixels);
end
%% Plot the results
figure(123);
clf;
% Display the mask:
subplot(1,2,1);
imagesc(badPixels);
axis image
title('Region Of the Bad Pixels');
% Display the result:
subplot(1,2,2);
imagesc(newImage);
axis image
set(gca,'clim', [0 255])
title('Infilled Image');
colormap gray
But you can get a similar solution using roifill from the image processing toolbox like so:
newImage2 = roifill(originalImage, badPixels);
figure(44);
clf;
imagesc(newImage2);
colormap gray
notice I'm using the same badPixels defined from before.
There is a file on Matlab file exchange, - inpaint_nans that does exactly what you want. The author explains why and in which cases it is better than Delaunay triangulation.
To fill one black area, do the following:
1) Identify a sub-region containing the black area, the smaller the better. The best case is just the boundary points of the black hole.
2) Create a Delaunay triangulation of the non-black points in inside the sub-region by:
tri = DelaunayTri(x,y); %# x, y (column vectors) are coordinates of the non-black points.
3) Determine the black points in which Delaunay triangle by:
[t, bc] = pointLocation(tri, [x_b, y_b]); %# x_b, y_b (column vectors) are coordinates of the black points
tri = tri(t,:);
4) Interpolate:
v_b = sum(v(tri).*bc,2); %# v contains the pixel values at the non-black points, and v_b are the interpolated values at the black points.