Getting whole line by combining its parts - matlab

I have an image and my aim is to get whole line which is shown with red line. I am working with matlab and I don't want to use IM2 = imdilate(IM,SE) function.
Is there any function or method to do that?
The image:
Note: Sorry for bad red line. I drew it with paint.
Edit:
The original image is below:

Here's what I have after using imdilate at an intermediate step -
%// Read in image and convert to a binary one
im = imread('Line.jpg');
bw = im2bw(im);
%// There seems to be a thin white boundary across the image, make it false(black)
bw1 = false(size(bw));
bw1(5:end-5,5:end-5) = bw(5:end-5,5:end-5);
bw1(biggest_blob(bw1)) = 0; %// remove biggest blob (bottom left corner one)
SE = strel('disk', 11, 8); %// structuring element for dilation
bw2 = imdilate(bw1,SE); %// dilate the image
bw3 = bwmorph(bw2,'thin',Inf); %// thin it
out = biggest_blob(bw3); %// out of many thinned lines, select the biggest one
Please remember that the motive behind removing the biggest blob at the start of the codes is that without that being removed, we would have gotten the biggest blob being attached to the island blobs that we were trying to connect/combine and thus would have messed up the desired output.
Associated function (taken from Select largest object in an image) -
function out = biggest_blob(BW)
%// Find and labels blobs in the binary image BW
[L, num] = bwlabel(BW, 8);
%// Count of pixels in each blob, basically this should give the area of each blob
counts = sum(bsxfun(#eq,L(:),1:num));
%// Get the label(ind) cooresponding to blob with the maximum area
%// which would be the biggest blob
[~,ind] = max(counts);
%// Get only the logical mask of the biggest blob by comparing all labels
%// to the label(ind) of the biggest blob
out = (L==ind);
return;
Result -

Related

Object extraction for a low contrast image

I have an image with very low contrast, from which I would like to extract a text object. Since it has a low contrast, I tried a few methods but no method gave me a satisfying result. I used watershed to extract the text object, but as the contrast is poor the extraction was not successful.
My program for watershed is:
I_cropped=imread(strcat('C:\Id\',currentfilename));
I_cropped = rgb2gray(I_cropped);
I_eq = histeq(I_cropped);
figure,imshow(I_eq);
bw = im2bw(I_eq, graythresh(I_eq));
bw2 = imfill(bw,'holes');
bw3 = imopen(bw2, ones(5,5));
bw4 = bwareaopen(bw3, 40);
bw = im2bw(I_eq, graythresh(I_eq));
figure,imshow(bw);
mask_em = imextendedmax(I_eq, 30);
mask_em = imclose(mask_em, ones(5,5));
mask_em = imfill(mask_em, 'holes');
mask_em = bwareaopen(mask_em, 40);
figure,imshow(mask_em);
I_eq_c = imcomplement(I_eq);
figure,imshow(I_eq_c);
I_mod = imimposemin(I_eq_c, ~bw4 | mask_em);
figure,imshow(I_mod);
L = watershed(I_mod);
figure,imshow(label2rgb(L));
I applied laplacian filter and to enhance edge, but it was not effective.
My objective is to extract text object. What method should I try for such low contrast image?
The image is attached:
Here is a way to do it.
First apply a median filter with a large kernel on the image to remove outliers and then apply a threshold to convert to a binary image. Note that playing with the kernel size of the filter alters the threshold level you need to use. Play around with it to see the output changing.
Then invert the image and apply regionprops to detect objects in the image. After that a little math to deduce the x and y origin (defining upper left corner) as well as the width and length of the large bounding box enclosing all the letters from the image.
Here is the code:
clear
clc
close all
Im = rgb2gray(imread('Imtext.png'));
%// Apply median filter to remove outliers
Im = medfilt2(Im,[9 9]);
%// Clear borders and apply threshold, then invert image
ImBW = imclearborder(~(im2bw(Im,.55)));
%// Find bounding boxes to delineate text regions.
S = regionprops(ImBW,'BoundingBox');
%// Concatenate all bounding boxes and obtain x,y, width and length of big
%// bounding box enclosing everything
bb = cat(1,S.BoundingBox);
LargeBB_x = min(bb(:,1));
LargeBB_y = min(bb(:,2));
LargeBB_height = max(bb(:,4));
%// Find last column in which pixel value is 1; that's the end
%// of the bounding box.
[~,ic] = find(ImBW==1);
MaxCol = max(ic(:));
LargeBB_width = MaxCol-LargeBB_x;
%// Display boxes
imshow(ImBW)
hold on
rectangle('Position',[LargeBB_x LargeBB_y LargeBB_width LargeBB_height],'EdgeColor','r','LineWidth',2)
hold off
And the output:
Or with the original image:

How to calculate radius of ring in MATLAB?

I have a stack of cortical bone images, high resolution and binarized. How do I go about calculating the mean inner and outer radii for each image? Here is an example of the kind of images I need to process:
This could be one approach -
%// Read in image and conert to binary
im = im2bw(imread('http://s9.postimg.org/aew1l7tvz/4_COPY_copy.png'));
figure, imshow(im), title('Original Image')
%// Fill holes giving us the "outer blob"
outer_blob = imfill(im,'holes');
outer_blob = biggest_blob(outer_blob); %// remove noise
figure, imshow(outer_blob), title('Outer Blob')
%// Get the inner filled blob by "removing" the original image from outer blob
inner_blob = outer_blob & ~im;
inner_blob = biggest_blob(inner_blob); %// remove noise
figure, imshow(inner_blob), title('Inner Blob')
%// Find the equivalent/mean inner and outer radii
inner_dia = regionprops(inner_blob,'Equivdiameter');
inner_radius = inner_dia.EquivDiameter/2
outer_dia = regionprops(outer_blob,'Equivdiameter');
outer_radius = outer_dia.EquivDiameter/2
Associated function code -
function out = biggest_blob(BW)
%// Find and labels blobs in the binary image BW
[L, num] = bwlabel(BW, 8);
%// Count of pixels in each blob, basically this should give the area of each blob
counts = sum(bsxfun(#eq,L(:),1:num));
%// Get the label(ind) cooresponding to blob with the maximum area
%// which would be the biggest blob
[~,ind] = max(counts);
%// Get only the logical mask of the biggest blob by comparing all labels
%// to the label(ind) of the biggest blob
out = (L==ind);
return;
Code run and debug images -
inner_radius =
211.4740
outer_radius =
267.8926

Split up a binary image using their white boundaries in MATLAB

I'm trying to read the values in this image into variables using OCR in MATLAB. I'm having trouble doing so, so I tried to split up this image into smaller parts using the white boundary lines then trying to read it, but I dont know how to do this. Any help would be appreciated, thanks.
If the blocks are always delimited by a completely vertical line, you can find where they are by comparing the original image (here transformed from RGB to grayscale to be a single plane) to a matrix that is made of repeats of the first row of the original image only. Since the lines are vertical the intensity of the pixels in the first line will be the same throughout. This generates a binary mask that can be used in conjunction with a quick thresholding to reject those lines that are all black pixels in every row. Then invert this mask and use regionprops to locate the bounding box of each region. Then you can pull these out and do what you like.
If the lines dividing the blocks of text are not always vertical or constant intensity throughout then there's a bit more work that needs to be done to locate the dividing lines, but nothing that's impossible. Some example data would be good to have in that case, though.
img = imread('http://puu.sh/cU3Nj/b020b60f0b.png');
imshow(img);
imgGray = rgb2gray(img);
imgMatch = imgGray == repmat(imgGray(1,:), size(imgGray, 1), 1);
whiteLines = imgMatch & (imgGray > 0);
boxes = regionprops(~whiteLines, 'BoundingBox');
for k = 1:6
subplot(3,2,k)
boxHere = round(boxes(k).BoundingBox);
imshow(img(boxHere(2):(boxHere(2)+boxHere(4)-1), boxHere(1):(boxHere(1)+boxHere(3)-1), :));
end
You can sum along the columns of a binary image corresponding to that input image and find peaks from the sum values. This is precisely achieved in the code here -
img = imread('http://puu.sh/cU3Nj/b020b60f0b.png');
BW = im2bw(img,0.1); %// convert to a binary image with a low threshold
peak_sum_max = 30; %// max of sum of cols to act as threshold to decide as peak
peaks_min_width = 10; %// min distance between peaks i.e. min width of each part
idx = find( sum(BW,1)>=peak_sum_max );
split_idx = [1 idx( [true diff(idx)>peaks_min_width ] )];
split_imgs = arrayfun(#(x) img(:,split_idx(x):split_idx(x+1)),...
1:numel(split_idx)-1,'Uni',0);
%// Display split images
for iter = 1:numel(split_imgs)
figure,imshow(split_imgs{iter})
end
Please note that the final output split_imgs is a cell array with each cell holding image data for each split image.
If you would like to have the split images directly without the need for messing with cell arrays, after you have split_idx, you can do this -
%// Get and display split images
for iter = 1:numel(split_idx)-1
split_img = img(:,split_idx(iter):split_idx(iter+1));
figure,imshow(split_img)
end
There is now a built-in ocr function in the Computer Vision System Toolbox.

reducing data that are very close to one another in matlab to get one set of data

I am trying to perform image analysis to get the boundary of an image. The problem is, there is an overlap of more than one point on the data. The problem occurs due to the pixel width of the boundary. ie, my algorithm reads the image, and because the boundary is thick, I get multiple ( sometimes 2 or 3) data points that represent the boundary .As you can see in the image, I have analysed an image and have the scatter plot showing the coordinates. The boundary has more than one element representing it. I need to reduce my vectors (of coordinates) such that a boundary is represented by only one line. I attach the code along with the question for your reference. I am doing this so as to get one single data that can represent the whole image.
clc;close all;
clear all;
folder = 'C:\Users\Adi\Desktop'; % image folder
baseFileName = 'part8.jpg';
% Get the full filename, with path prepended.
fullFileName = fullfile(folder, baseFileName);
rgbImage = imread(fullFileName); % read the image
% Get the dimensions of the image. numberOfColorBands should be = 3.
[rows, columns, numberOfColorBands] = size(rgbImage);
figure;
greenChannel = rgbImage(:, :, 2);
binaryImage = greenChannel < 200; % convert image to BW
imshow(binaryImage);
i=1;
j=1;
data=zeros(rows,columns);
n=1;% sample every n rows or col of the image
img2=zeros(rows/n,columns/n);
for col=1:n:columns %reducing the size of the image
for row=1:n:rows
if(binaryImage(row,col)==1)
% data(row,col)=1;
x(i)=row;
y(i)=col; % locate where the image is present
img2(x(i),y(i))=1;
i=i+1;
end
end
end
figure;
scatter(x,y);
figure;
imshow(img2);
blankImage=zeros(length(1:rows),length(1:columns));
m=1; % counter variable
for k=2:rows-1
for l=2:columns-1
if((binaryImage(k+1,l)==0)&&(binaryImage(k,l+1)==0)&&(binaryImage(k-1,l)==0)&&(binaryImage(k,l-1)==0)&&(binaryImage(k,l)==0))
% % if all surrounding pixels are black, ignore them
blankImage(k,l)=0;
elseif((binaryImage(k+1,l)==1)&&(binaryImage(k,l+1)==1)&&(binaryImage(k-1,l)==1)&&(binaryImage(k,l-1)==1)&&(binaryImage(k,l)==1))
%if all surrounding pix are white ,ignore them
blankImage(k,l)=0;
else
blankImage(k,l)=1; % get the boundary elements
x_brep(m)=k;
y_brep(m)=l;
m=m+1;
end
end
end
figure;
imshow(blankImage);
figure;
scatter(x_brep,y_brep);
Sample image and output:
If you have the image processing toolbox, you can take your original image, and use bwmorph - for example, thin or skel, to reduce it to a single pixel line. Note that you may need to invert your image (so that it is a white line on a black background).
Presuming your original image as shown is already binary and is stored in BW (but with line as black on white), it should be as simple as (~BW just inverts the image).
BW2 = bwmorph(~BW,'thin', Inf);
If your image is not already binary you will need to convert it to binary first.

Reflecting points in an RGB image over a reflection line (MATLAB)

I have an RGB image of size MxNx3. Let's imagine we have a reflection line somewhere in the bottom half of the image. How do I reflect all the points above the line onto the line? I know what I need to do but I can't get it right on MATLAB. Thanks for any help.
For example, in the image below the blue line is the reflection line.
Code
%%// Read input image
img =imread(IMG_FILEPATH);
%%// Let user select the mask, top of which will basically act
%%// as the reflection line
figure,imshow(img)
[mask,xi,yi] = roipoly(img);
%%// Remove the last element as that is same as the first one
xi(end)=[];
yi(end)=[];
%%// Find the two corner points each on the left and right sides of the mask
pt_matrix = [xi yi]
[val,ind] = sort(xi)
left_two_pts = pt_matrix(ind(1:2),:)
right_two_pts = pt_matrix(ind(end-1:end),:)
four_pts = round([left_two_pts;right_two_pts])
%%// Remove a 5x5 neighborhood around the four corners, so that biggest
%%// blob that is the line could be separated out
BW1 = edge(mask,'canny');
for k = 1:4
BW1(four_pts(k,2)-2:four_pts(k,2)+2,four_pts(k,1)-2:four_pts(k,2)+1) = 0;
end
%%// Get the biggest blob that is the reflection line
[L, num] = bwlabel(BW1);
counts = sum(bsxfun(#eq,L(:),1:num));
[~,ind] = max(counts);
BW1 = (L==ind);
%%// Connect the endpoints of the line to left and right sides of the image
xlimit = [find(sum(BW1,1),1) find(sum(BW1,1),1,'last')];
[row1,col1] = ind2sub(size(BW1),find(BW1));
BW1(row1(1),1:col1(1)-1)=1;
BW1(row1(end),col1(end)+1:end)=1;
%%// Select only one per column for the reflection
[xt0,yt0] = find(BW1);
[yt1,a2,a3] =unique(yt0,'first');
xt1=xt0(a2);
sz1 = size(BW1,1)-xt1;
%%// Perform the reflection
for k = 1:numel(yt1)
img(xt1(k):end,k,:) = img(xt1(k):-1:xt1(k)-sz1(k),k,:);
end
figure,imshow(img)
Typical mask with roipoly would look like -
Output
Note: User has to select exactly two points on the left side to represent the left side border of the mask and exactly two points on the right side for the right side border. Also, there must enough image pixels on top of the line to reflect over the line.
I'm assuming the line is stored in a mask, as stated by the OP. I will assume the mask is black above the line and white below it. Here is a "fancy" way to solve the problem. :)
% 1. Open your image (MxNx3 matrix).
img = im2double(imread('original.png'));
% 2. Open your 'line image' as a logical array (MxNx3 matrix)
line = imread('line.png') > 0;
% 3. Now, we will "align" the upper part of the image based on the line,
% so that the line will be straight at the bottom of the image. We will
% do that by sorting the 'line image', moving the ones of the mask
% above. The code was adapted from:
% http://www.mathworks.com/matlabcentral/newsreader/view_thread/28715
upper = zeros(size(line));
upper(~line) = -1;
upper = sort(upper, 'descend');
upper(upper == -1) = img(~line);
% 4. Let's concatenate both the image with it's mirror below.
imgConcat = [upper; upper(end:-1:1, :, :)];
% 5. Also, The line mask will be concatenated to it's negative, and we'll
% invert the order of the rows.
lineConcat = [line; ~line];
lineConcat = lineConcat(end:-1:1,:,:);
% 6. Now we repeat the "alignment procedure" used on step 4 so that the
% image will be positioned on the upper part. We will also remove the
% lower part, now containing only zeros.
mirror = zeros(size(lineConcat));
mirror(lineConcat) = -1;
mirror = sort(mirror, 'ascend');
mirror(mirror == -1) = imgConcat(lineConcat);
mirror = mirror(1:end/2,:,:);
Here you see the result (step by step);
To generate that image, I used this code:
% Display the results, step by step (final result is in the 'mirror' matrix).
subplot(2,3,1), imshow(img, []); title('Step 1. Original image')
subplot(2,3,2), imshow(double(line), []); title('Step 2. Line image');
subplot(2,3,3), imshow(upper, []); title('Step 3. Image "alignment"');
subplot(2,3,4), imshow(imgConcat, []); title('Step 4. Mirror concatenation');
subplot(2,3,5), imshow(double(lineConcat), []); title('Step 5. Mask concatenation');
subplot(2,3,6), imshow(mirror, []); title('Step 6. Result by a final alignment');
Assuming that the line is already defined, the simple way to go about this would be to scan each column and, starting at the location of the line, copy as many elements from above the line as will fit below the line, in reverse order.
So for each column c, let line[c] be the row number of the line in that column. Then
maxR = size(c);
c(line[c]:end) = c(line[c]-1:-1:line[c]-(maxR - line[c] + 1));
That part at the end, line[c]-(maxR - line[c] + 1) is what tells it how much to copy. It just takes the distance between the bottom of the column to the line and uses that as the number of pixels to copy.
Of course this has to be generalized to account for 3 channels, but I didn't want to clutter up the concept. Other than that, you just have to put that in a loop over all the columns, c.
This also assumes that there is enough data above the line to fill the area below the line. If not, you'll have to decide how to handle this case.
I'm sure there are ways to vectorize this and take out the loop, but I'll leave that to the experts :)