how can I get good binary image using Otsu method for this image? - image

here is my image
a.png
for binarization I try this code.
im=rgb2gray(I);
maxp=uint16(max(max(im)));
minp=uint16(min(min(im)));
bw=im2bw(im,(double(minp+maxp))/(1.42*255));
bw=~bw;
imm=bw;
but I need binarization by otsu.how can I get good binary output using otsu method?
plz help
thanks

MATLAB has its own implementation of Otsu thresholding called multithresh. In your case the code to obtain the segmented image should be something like this:
im=rgb2gray(I); % convert image to grayscale
thresh = multithresh(im); % find one threshold (using Otsu method)
segmented_im = imquantize(im, thresh); % segment image
imagesc(segmented_im); % show segmented image
I haven't tested it so I don't know how well it would perform on your image.
EDIT:
I tested it, and it doesn't work as expected. One of the problems is that Otsu's method works well when there is a clear bimodal distribution of the pixel intensities. This bimodality is lacking in your image. A call to imhist(im) after the grayscale conversion leads to this (comments added by me):
As you can see, the distribution is almost trimodal, and the threshold selected by multithresh is the first one, while you want the second one. The first workaround that comes to my mind (especially if all the images in your dataset are similar to the one you posted, i.e. have a similar intensity distribution) is to make multithresh output two thresholds, and then selecting the last (highest) one:
thresholds = multithresh(im, 2);
thresh = thresholds(end);
Then proceed with the segmentation of the image as stated above. This second method leads to this segmentation:
EDIT 2 (putting it all together):
Indeed the output segmented_im is not a binary image, but a label image. It's easy enough to convert it to a binary image. I will include directly all the code in this next snippet:
im=rgb2gray(I); % convert image to grayscale
thresholds = multithresh(im, 2); % find two thresholds using Otsu
thresh = thresholds(end); % select larger one
segmented_im = imquantize(im, thresh); % segment image
segmented_im(segmented_im == 1) = 0; % make background black (0)
segmented_im(segmented_im == 2) = 255; % make foreground white (255)
binary_im = im2bw(segmented_im); % make binary (logical) image
imshow(binary_im); % show binary image
binary_im il a logical matrix with false (0) for background, and true (1) for foreground. segmented_im is a double matrix with 0 for background and 255 for foreground. I hope this serves your purposes!

Related

matlab template matching only for 0 (or 1) in matrix

I'm a beginner in matlab programming and i'm having some troubles with template matching. I have a few white boxes with a black border (link for pic below) along with some text and I want to extract all the boxes,and there's also one that has an X in it(it's a multiple choice answer). In the beginning I used normxcorr2 , but the problem is that due to the many white pixels of the template, I get allot of irrelevant templates like only white spaces. I was looking for an algorithm that does template matching only on 0's , so I could get templates that have black squares only. I hope I made myself clear, thanks :)
http://i91.photobucket.com/albums/k284/Chris2401/sqrTemplate.png
EDIT: May 2nd, 2014
Now understanding what you are trying to achieve, I can now help you solve your problem. As you are a beginner to MATLAB, I will use a more simpler approach (even though there are more sophisticated methods to help you figure this out) as I want to demonstrate how the algorithm works.
You basically want to implement normxcorr2, but you only want to include pixels that are marked as black in the template. You also want to skip locations in the search image that are not black. In that case, I will layout the algorithm for you step by step, then write some code.
Read in the template and extract those pixel locations that are black
In a sliding window fashion, for each image patch of the same size as the template that is in the search image...
If the entire image patch is white, skip
Extract the same locations that are black
Compute the NCC using those locations only.
The output will be a map that contains the NCC for each location in the search image
I will not handle the case of the border pixels in the search image, as I am assuming that you want to be able to match something in the search image with the full size of the template.
Here are some assumptions. Let's assume the following variables:
imTemplate - The template image, such as the one that you have showed me
imSearch - The image we wish to search for when doing this NCC stuff
I am also assuming that each of the images are binary, as the title of your post has "0 or 1" in it.
As such, I have the following code for you:
[rowsSearch colsSearch] = size(imSearch); % Get dimensions of search image
[rowsTemp colsTemp] = size(imTemplate); % Get dimensions of template image
mapBlack = imSearch == 0; % Obtain a map of where the black pixels are in the template
numPixelsCompare = sum(mapBlack(:)); % Need to know how many pixels are valid for comparison
% Obtain area of searching within the search image
startSearchRows = 1 + floor(rowsSearch/2);
endSearchRows = rowsSearch - floor(rowsSearch/2);
startSearchCols = 1 + floor(colsSearch/2);
endSearchCols = colsSearch - floor(colsSearch/2);
% Need half the dimensions of each for the template dimensions... you will
% see why we need this later
rowsTempHalf = floor(rowsTemp/2);
colsTempHalf = floor(colsTemp/2);
% Where we will store our NCC calculations
NCCMap = zeros(rowsSearch, colsSearch);
% Because you want to include all of the black pixels in your
% calculations, and these are all the same, the signal you are comparing
% to basically consists of all white pixels.
% Create a vector that consists of all 1s that is the same size as how
% many black pixels exist in the template
compareSignal = ones(numPixelsCompare, 1);
% For each location in the search image (ensuring that the full template
% is inside the image)
for i = startSearchRows : endSearchRows
for j = startSearchCols : endSearchCols
% Grab an image patch that is the same size as the template
% At each location (i,j) this serves as the CENTRE of the image
% patch, and we are grabbing pixels surrounding this centre that
% will create a patch that is the same size as the template
searchBlock = imSearch(i-rowsTempHalf:i+rowsTempHalf, ...
j-colsTempHalf:j+colsTempHalf);
% If all of the pixels are white, skip
if (all(searchBlock == 1))
continue;
end
% Extract only those pixels that are valid in the template
searchPixels = searchBlock(mapBlock);
% Must invert so that black pixels become white
% You mentioned that white pixels are "background"
searchPixels = ~searchPixels;
% Compute NCC for this patch
NCCMap(i,j) = compareSignal'*searchPixels / ...
(sqrt(compareSignal'*compareSignal) * sqrt(searchPixels'*searchPixels));
end
end
If you're a bit confused with the way I calculated the NCC, it is basically what you're used to, but I used vector algebra to compute it instead. This should hopefully give you what you want. To find the best location of where the template matched, you can do the following to extract the row and column of this location:
[r,c] = find(NCCMap == max(NCCMap(:)));
I hope this solves your question. It is a tad inefficient, and it will really start to suffer with higher resolution images, but I wanted to give you a good start so you're not sitting around idle trying to figure it out on your own.
NB: I have not tested this code yet as I don't have an example search image that you are using to solve this problem. Hopefully this will work. Leave a comment and let me know how it goes.

How to display a Gray scale image using boundary defined in another binary image

I have a original gray scale image(I m using mammogram image with labels outside image).
I need to remove some objects(Labels) in that image, So i converted that grayscale image to a binary image. Then i followed the answer method provided in
How to Select Object with Largest area
Finally i extracted an Object with largest area as binary image. I want that region in gray scale for accessing and segmenting small objects within that. For example. Minor tissues in region and also should detect its edge.
**
How can i get that separated object region as grayscale image or
anyway to get the largest object region from gray scale directly
without converting to binary or any other way.?
**
(I am new to matlab. I dono whether i explained it correctly or not. If u cant get, I ll provide more detail)
If I understood you correctly, you are looking to have a gray image with only the biggest blob being highlighted.
Code
img = imread(IMAGE_FILEPATH);
BW = im2bw(img,0.2); %%// 0.2 worked to get a good area for the biggest blob
%%// Biggest blob
[L, num] = bwlabel(BW);
counts = sum(bsxfun(#eq,L(:),1:num));
[~,ind] = max(counts);
BW = (L==ind);
%%// Close the biggest blob
[L,num] = bwlabel( ~BW );
counts = sum(bsxfun(#eq,L(:),1:num));
[~,ind] = max(counts);
BW = ~(L==ind);
%%// Original image with only the biggest blob highlighted
img1 = uint8(255.*bsxfun(#times,im2double(img),BW));
%%// Display input and output images
figure,
subplot(121),imshow(img)
subplot(122),imshow(img1)
Output
If I understand your question correctly, you want to use the binary map and access the corresponding pixel intensities in those regions.
If that's the case, then it's very simple. You can use the binary map to identify the spatial co-ordinates of where you want to access the intensities in the original image. Create a blank image, then copy over these intensities over to the blank image using those spatial co-ordinates.
Here's some sample code that you can play around with.
% Assumptions:
% im - Original image
% bmap - Binary image
% Where the output image will be stored
outImg = uint8(zeros(size(im)));
% Find locations in the binary image that are white
locWhite = find(bmap == 1);
% Copy over the intensity values from these locations from
% the original image to the output image.
% The output image will only contain those pixels that were white
% in the binary image
outImg(locWhite) = im(locWhite);
% Show the original and the result side by side
figure;
subplot(1,2,1);
imshow(im); title('Original Image');
subplot(1,2,2);
imshow(outImg); title('Extracted Result');
Let me know if this is what you're looking for.
Method #2
As suggested by Rafael in his comments, you can skip using find all together and use logical statements:
outImg = img;
outImg(~bmap) = 0;
I decided to use find as it less obfuscated for a beginner, even though it is less efficient to do so. Either method will give you the correct result.
Some food for thought
The extracted region that you have in your binary image has several holes. I suspect you would want to grab the entire region without any holes. As such, I would recommend that you fill in these holes before you use the above code. The imfill function from MATLAB works nicely and it accepts binary images as input.
Check out the documentation here: http://www.mathworks.com/help/images/ref/imfill.html
As such, apply imfill on your binary image first, then go ahead and use the above code to do your extraction.

Normalization of handwritten characters w.r.t size and position

I am doing a project on offline handwriting recognition.In the preprocessing stage,I need to normalize the handwritten part in binary image w.r.t its size and position.Can anyone tell me how to access just the writing part(black pixels) in the image and resize and shift its position?
Your problem is as broad as the field of image processing. There is no one way to segment an image into foreground and background so whatever solution you find here works on some cases and doesn't in others. However, the most basic way to segment a grayscale image is:
% invert your grayscale so text is white and background is black
gray_im = 1 - im2double(gray_im);
% compute the best global threshold
level = graythresh(gray_im);
% convert grayscale image to black and white based on best threshold
bw_im = im2bw(gray_im, level);
% find connected regions in the foreground
CC = bwconncomp(bw_im);
% if necessary, get the properties of those connected regions for further analysis
S = regionsprops(CC);
Note: Many people have much more sophisticated methods to segment and this is by no means the best way of doing it.
After post-processing, you will end up with one (or more) image containing only a single character. To resize to a specific size M x N, use:
resized_bw = imresize(single_char_im, [M N]);
To shift its position, the easiest way I know is to use circshift() function:
shifted_bw = circshift(resized_bw, [shift_pixels_up_down, shift_pixels_left_right]);
Note: circshift wraps the shifted columns or rows so if your bounding box is too tight, the best method is to pad your image, and re-crop it at the new location.

changing bits per pixel in MATLAB

How does one change the bits per pixel of an image loaded into MATLAB? I use the file dialog and the imread functions to load the image into a matrix. i just need to change that image's bits per pixel. Giving the user that ability to choose anywhere from 1 bit to 8 bits. I know how to give the users the ability to choose one I just don't know who to change it. How does one change that? (By the way I'm in MATLAB R2012a)
The way I understand it, you want to do something like this:
imdata = rgb2gray(imread('ngc6543a.jpg') ); % Assuming that we have a grayscale uint8 image
figure('name', 'Before');
imagesc(imdata);
colormap('gray');
numberOfBits = input('Enter number of bits:\n');
maxValue = 2^numberOfBits - 1;
newImage = imdata * (maxValue / 256);
figure('name', 'After');
imagesc(newImage);
colormap('gray');
The image ngc6543a.jpg is a sample image, so you can run this code immediately as it is.
This documentation page contains lots of information about what you want to do: Reducing the Number of Colors in an Image.
A simple example is the following (pretty much taken straight from that page), which will dither the image and produce a colour map (slightly different to the OP's answer - not sure which one you want to do):
>> RGB = imread('peppers.png');
>> [x,map] = rgb2ind(RGB, 2); % Reduce to a 2-colour image
>> imagesc(x)
>> colormap(map)
You should choose the number of colours based on the maximum number that however many bits can hold.

Find and crop defined image areas automatically

I want to process an image in matlab
The image consists out of a solid back ground and two specimens (top and bottom side). I already have a code that separate the top and bottom and make it two images. But the part what I don't get working is to crop the image to the glued area only (red box in the image, I've only marked the top one). However, the cropped image should be a rectangle just like the red box (the yellow background, can be discarded afterwards).
I know this can be done with imcrop, but this requires manual input from the user. The code needs to be automated such that it is possible to process more images without user input. All image will have the same colors (red for glue, black for material).
Can someone help me with this?
edit: Thanks for the help. I used the following code to solve the problem. However, I couldn't get rid of the black part right of the red box. This can be fix by taping that part off before making pictures. The code which I used looks a bit weird, but it succeeds in counting the black region in the picture and getting a percentage.
a=imread('testim0.png');
level = graythresh(a);
bw2=im2bw(a, level);
rgb2=bw2rgb(bw2);
IM2 = imclearborder(rgb2,4);
pic_negative = ait_imgneg(IM2);
%% figures
% figure()
% image(rgb2)
%
% figure()
% imshow(pic_negative)
%% Counting percentage
g=0;
for j=1:size(rgb2,2)
for i=1:size(rgb2,1)
if rgb2(i,j,1) <= 0 ...
& rgb2(i,j,2) <= 0 ...
& rgb2(i,j,3) <= 0
g=g+1;
end
end
end
h=0;
for j=1:size(pic_negative,2)
for i=1:size(pic_negative,1)
if pic_negative(i,j)== 0
h=h+1;
end
end
end
per=g/(g+h)
If anyone has some suggestions to improve the code, I'm happy to hear it.
For a straight-forward image segmentation into 2 regions (background, foreground) based on color (yellow, black are prominent in your case), an option can be clustering image color values using kmeans algorithm. For additional robustness you can transform the image from RGB to Lab* colorspace.
The example for your case follows the MATLAB Imape Processing example here.
% read and transform to L*a*b space
im_rgb = double(imread('testim0.png'))./256;
im_lab = applycform(im_rgb, makecform('srgb2lab'));
% keep only a,b-channels and form feature vector
ab = double(lab_I(:,:,2:3));
[nRows, nCols, ~] = size(ab);
ab = reshape(ab,nRows * nCols,2);
% apply k-means for 2 regions, repeat c times, e.g. c = 5
nRegions = 2;
[cluster_idx cluster_center] = kmeans(ab,nRegions, 'Replicates', 5);
% get foreground-background mask
im_regions = reshape(cluster_idx, nRows, nCols);
You can use the resulting binary image to index the regions of interest (or find the boundary) in the original reference image.
Images are saved as matrices. If you know the bounds in pixels of the crop box want to crop you can execute the crop using indexing.
M = rand(100); % create a 100x100 matrix or load it from an image
left = 50;
right = 75;
top = 80;
bottom = 10;
croppedM = M(bottom:top, left:right);
%save croppedm
You can easily get the unknown bounded crop by
1) Contour plotting the image,
2) find() on the result for the max/min X/ys,
3) use #slayton's method to perform the actual crop.
EDIT: Just looked at your actual image - it won't be so easy. But color enhance/threshold your image first, and the contours should work with reasonable accuracy. Needless to say, this requires tweaking to your specific situation.
since you've already been able to seperate the top and bottom, and also able to segment the region you want (including the small part of the right side that you don't want), I propose you just add a fix at the end of the code via the following.
after segmentation, sum each column Blue intensity value, so that you are compressing the image from 2d to 1d. so if original region is width=683 height=59, the new matrix/image will be simply width=683 height=1.
now, you can apply a small threshold to determine where the edge should lie, and apply a crop to the image at that location. Now you get your stats.

Resources