Segment out those objects that have holes in it - matlab

I have a binary image, that has circles and squares in it.
imA = imread('blocks1.png');
A = im2bw(imA);
figure,imshow(A);title('Input Image - Blocks');
imBinInv = ~A;
figure(2); imshow(imBinInv); title('Inverted Binarized Original Image');
Some circles and squares have small holes in them based on which, I have to generate an image which has only those circles and squares that have holes/missing point in them. How can I code that?
PURPOSE: Later on, using regionprops in MATLAB, I will extract the information that from those objects, how many are circles and squares.

You should use the Euler characteristic. It's a topological invariant which describes the amount of holes in a object in the 2D case. You can calculate it using regionprops too:
STATS = regionprops(L, 'EulerNumber');
Any single object with no holes will have an Euler characteristic of 1, any single object with 1 hole will have an Euler characteristic of 0, two holes -> -1 etc. So you can segment out all the objects with EC < 1. It's pretty fast to calculate too.
imA = imread('blocks1.png');
A = logical(imA);
L = bwlabel(A); %just for visualizing, you can call regionprops straight on A
STATS = regionprops(L, 'EulerNumber');
holeIndices = find( [STATS.EulerNumber] < 1 );
holeL = false(size(A));
for i = holeIndices
holeL( L == i ) = true;
end
Output holeL:

There might be a faster way, but this should work:
Afilled = imfill(A,'holes'); % fill holes
L = bwlabel(Afilled); % label each connected component
holes = Afilled - A; % get only holes
componentLabels = unique(nonzeros(L.*holes)); % get labels of components which have at least one hole
A = A.*L; % label original image
A(~ismember(A,componentLabels)) = 0; % delete all components which have no hole
A(A~=0)=1; % turn back from labels to binary - since you are later continuing with regionprops you maybe don't need this step.

Related

How to to identify letters on a license plate with varying perspectives

I am making a script in Matlab that takes in an image of the rear of a car. After some image processing I would like to output the original image of the car with a rectangle around the license plate of the car. Here is what I have written so far:
origImg = imread('CAR_IMAGE.jpg');
I = imresize(origImg, [500, NaN]); % easier viewing and edge connecting
G = rgb2gray(I);
M = imgaussfilt(G); % blur to remove some noise
E = edge(M, 'Canny', 0.4);
% I can assume all letters are somewhat upright
RP = regionprops(E, 'PixelIdxList', 'BoundingBox');
W = vertcat(RP.BoundingBox); W = W(:,3); % get the widths of the BBs
H = vertcat(RP.BoundingBox); H = H(:,4); % get the heights of the BBs
FATTIES = W > H; % find the BBs that are more wide than tall
RP = RP(FATTIES);
E(vertcat(RP.PixelIdxList)) = false; % remove more wide than tall regions
D = imdilate(E, strel('disk', 1)); % dilate for easier viewing
figure();
imshowpair(I, D, 'montage'); % display original image and processed image
Here are some examples:
From here I am unsure how to isolate the letters of the license plate, particularly like in the second example above where each letter has a decreased area due to the perspective of the image. My first idea was to get the bounding box of all regions and keep only the regions where the perimeter to area ratio is "similar" but this resulted in removing the letters of the plate that were connected when I dilate the image like the K and V in the fourth example above.
I would appreciate some suggestions on how I should go about isolating these letters. No code is necessary, and any advice is appreciated.
So I continued to work despite not receiving any answers here on SO and managed to get a working version through trial and error. All of the following code comes after the code in my original question and all plots below are from the first example image above. First, I found the variance for every single pixel row of the image and plotted them like so:
V = var(D, 0, 2);
X = 1:length(V);
figure();
hold on;
scatter(X, V);
I then fit a very high order polynomial to this scatter plot and saved the values where the slope of the polynomial was zero and the variance value was very low (i.e. the dark row of pixels immediately before or after a row with some white):
P = polyfit(X', V, 25);
PV = polyval(P, X);
Z = X(find(PV < 0.03 & abs(gradient(PV)) < 0.0001));
plot(X, PV); % red curve on plot
scatter(Z, zeros(1,length(Z))); % orange circles on x-axis
I then calculate the integral of the polynomial between any consecutive Z values (my dark rows), and save the two Z values between which the integral is the largest, which I mark with lines on the plot:
MAX_INTEG = -1;
MIN_ROW = -1;
MAX_ROW = -1;
for i = 1:(length(Z)-1)
TEMP_MIN = Z(i);
TEMP_MAX = Z(i+1);
Q = polyint(P);
TEMP_INTEG = diff(polyval(Q, [TEMP_MIN, TEMP_MAX]));
if (TEMP_INTEG > MAX_INTEG)
MAX_INTEG = TEMP_INTEG;
MIN_ROW = TEMP_MIN;
MAX_ROW = TEMP_MAX;
end
end
line([MIN_ROW, MIN_ROW], [-0.1, max(V)+0.1]);
line([MAX_ROW, MAX_ROW], [-0.1, max(V)+0.1]);
hold off;
Since the X-values of these lines correspond row numbers in the original image, I can crop my image between MIN_ROW and MAX_ROW:
I repeat the above steps now for the columns of pixels, crop, and remove any excess black rows of columns to result in the identified plate:
I then perform 2D cross correlation between this cropped image and the edged image D using Matlab's xcorr2 to locate the plate in the original image. After finding the location I just draw a rectangle around the discovered plate like so:

Find the real time co-ordinates of the four points marked in red in the image

To be exact I need the four end points of the road in the image below.
I used find[x y]. It does not provide satisfying result in real time.
I'm assuming the images are already annotated. In this case we just find the marked points and extract coordinates (if you need to find the red points dynamically through code, this won't work at all)
The first thing you have to do is find a good feature to use for segmentation. See my SO answer here what-should-i-use-hsv-hsb-or-rgb-and-why for code and details. That produces the following image:
we can see that saturation (and a few others) are good candidate colors spaces. So now you must transfer your image to the new color space and do thresholding to find your points.
Points are obtained using matlab's region properties looking specifically for the centroid. At that point you are done.
Here is complete code and results
im = imread('http://i.stack.imgur.com/eajRb.jpg');
HUE = 1;
SATURATION = 2;
BRIGHTNESS = 3;
%see https://stackoverflow.com/questions/30022377/what-should-i-use-hsv-hsb-or-rgb-and-why/30036455#30036455
ViewColoredSpaces(im)
%convert image to hsv
him = rgb2hsv(im);
%threshold, all rows, all columns,
my_threshold = 0.8; %determined empirically
thresh_sat = him(:,:,SATURATION) > my_threshold;
%remove small blobs using a 3 pixel disk
se = strel('disk',3');
cleaned_sat = imopen(thresh_sat, se);% imopen = imdilate(imerode(im,se),se)
%find the centroids of the remaining blobs
s = regionprops(cleaned_sat, 'centroid');
centroids = cat(1, s.Centroid);
%plot the results
figure();
subplot(2,2,1) ;imshow(thresh_sat) ;title('Thresholded saturation channel')
subplot(2,2,2) ;imshow(cleaned_sat);title('After morpphological opening')
subplot(2,2,3:4);imshow(im) ;title('Annotated img')
hold on
for (curr_centroid = 1:1:size(centroids,1))
%prints coordinate
x = round(centroids(curr_centroid,1));
y = round(centroids(curr_centroid,2));
text(x,y,sprintf('[%d,%d]',x,y),'Color','y');
end
%plots centroids
scatter(centroids(:,1),centroids(:,2),[],'y')
hold off
%prints out centroids
centroids
centroids =
7.4593 143.0000
383.0000 87.9911
435.3106 355.9255
494.6491 91.1491
Some sample code would make it much easier to tailor a specific solution to your problem.
One solution to this general problem is using impoint.
Something like
h = figure();
ax = gca;
% ... drawing your image
points = {};
points = [points; impoint(ax,initialX,initialY)];
% ... generate more points
indx = 1 % or whatever point you care about
[currentX,currentY] = getPosition(points{indx});
should do the trick.
Edit: First argument of impoint is an axis object, not a figure object.

How can i remove overlaping circles after Hough Transform segmentation

I'm working in image segmentation, testing a lot of different segmentation algorithms, in order to do a comparitive study. At the moment i'm using Hough transform to find circles in the image. The images that i'm using have plenty objects, so when Í count the objects the result is hudge. I think the problem, is the overlaping circle. Do you know how can i maybe remove the overlaping circles to have a result more close to reality?
The code that i'm using is:
clear all, clc;
% Image Reading
I=imread('0001_c3.png');
figure(1), imshow(I);set(1,'Name','Original')
image used
% Gaussian Filter
W = fspecial('gaussian',[10,10]);
J = imfilter(I,W);
figure(2);imshow(J);set(2,'Name','Filtrada média');
X = rgb2gray(J);
figure(3);imshow(X);set(3,'Name','Grey');
% Finding Circular objects -- Houng Transform
[centers, radii, metric] = imfindcircles(X,[10 20], 'Sensitivity',0.92,'Edge',0.03); % [parasites][5 30]
centersStrong = centers(1:60,:); % number of objects
radiiStrong = radii(1:60);
metricStrong = metric(1:60);
viscircles(centersStrong, radiiStrong,'EdgeColor','r');
length(centers)% result=404!
You could simply loop over the circles and check if others are "close" to them. If so, you ignore them.
idx_mask = ones(size(radii));
min_dist = 1; % relative value. Tweak this if slight overlap is OK.
for i = 2:length(radii)
cur_cent = centers(i, :);
for j = 1:i-1
other_cent = centers(j,:);
x_dist = other_cent(1) - cur_cent(1);
y_dist = other_cent(2) - cur_cent(2);
if sqrt(x_dist^2+y_dist^2) < min_dist*(radii(i) + radii(j)) && idx_mask(j) == 1
idx_mask(i) = 0;
break
end
end
end
%%
idx_mask = logical(idx_mask);
centers_use = centers(idx_mask, :);
radii_use = radii(idx_mask, :);
metric_use = metric(idx_mask, :);
viscircles(centers_use, radii_use,'EdgeColor','b');
The picture shows all circles in red, and the filtered circles in blue.
The if clause checks two things:
- Are the centers of the circles closer than the sum of their radii?
- Is the other circle still on the list of considered circles?
If the answer to both questions is yes, then ignore the "current circle".
The way the loop is set up, it will keep circles that are higher up (have a lower row index). As is, the circles are already ordered by descending metric. In other words, as is this code will keep circles with a higher metric.
The code could optimized so that the loops run faster, but I don't think you'll have millions of circles in a single picture. I tried writing it in a way that it's easier to read for humans.

Matlab: Matrix manipulation: Change values of pixels around center pixel in binary matrix

I have another problem today:
I have a binary matrix t, in which 1 represents a river channel, 0 represents flood plane and surrounding mountains:
t = Alog>10;
figure
imshow(t)
axis xy
For further calculations, I would like to expand the area of the riverchannel a few pixels in each direction. Generally speaking, I want to have a wider channel displayed in the image, to include a larger region in a later hydraulic model.
Here is my attempt, which does work in certain regions, but in areas where the river runs diagonal to the x-y axis, it does not widen the channel. There seems to be a flow in approaching this, which I cannot quite grasp.
[q,o] = find(t == 1);
qq = zeros(length(q),11);
oo = zeros(length(o),11);
% add +-5 pixel to result
for z=1:length(q)
qq(z,:) = q(z)-5:1:q(z)+5;
oo(z,:) = o(z)-5:1:o(z)+5;
end
% create column vectors
qq = qq(:);
oo = oo(:);
cords = [oo qq]; % [x y]
% remove duplicates
cords = unique(cords,'rows');
% get limits of image
[limy limx] = size(t);
% restrict to x-limits
cords = cords(cords(:,1)>=1,:);
cords = cords(cords(:,1)<=limx,:);
% restrict to y-limits
cords = cords(cords(:,2)>=1,:);
cords = cords(cords(:,2)<=limy,:);
% test image
l = zeros(size(img));
l(sub2ind(size(l), cords(:,2)',cords(:,1)')) = 1;
figure
imshow(l)
axis xy
This is the image I get:
It does widen the channel in some areas, but generally there seems to be a flaw with my approach. When I use the same approach on a diagonal line of pixels, it will not widen the line at all, because it will just create more pairs of [1 1; 2 2; 3 3; etc].
Is there a better approach to this or even something from the realm of image processing?
A blur filter with a set diameter should be working somewhat similar, but I could not find anything helpful...
PS: I wasn't allowed to add the images, although I already have 10 rep, so here are the direct links:
http://imageshack.us/a/img14/3122/channelthin.jpg
http://imageshack.us/a/img819/1787/channelthick.jpg
If you have the image processing toolbox, you should use the imdilate function. This performs the morphological dilation operation. Try the following code:
SE = strel('square',3);
channelThick = imdilate(channelThin,SE);
where SE is a 3x3 square structuring element used to dilate the image stored in channelThin. This will expand the regions in channelThin by one pixel in every direction. To expand more, use a larger structuring element, or multiple iterations.
You may apply morphological operations from image processing. Morphological dilation can be used in your example.
From the image processing toolbox, you can use bwmorth command BW2 = bwmorph(BW,'dilate') or imdilate command IM2 = imdilate(IM,SE).
Where IM is your image and SE is the structuring element. You can set SE = ones(3); to dilate the binary image by "one pixel" - but it can be changed depending on your application. Or you can dilate the image several times with the same structuring element if needed.

Measuring weighted mean length from an electrophoresis gel image

Background:
My question relates to extracting feature from an electrophoresis gel (see below). In this gel, DNA is loaded from the top and allowed to migrate under a voltage gradient. The gel has sieves so smaller molecules migrate further than longer molecules resulting in the separation of DNA by length. So higher up the molecule, the longer it is.
Question:
In this image there are 9 lanes each with separate source of DNA. I am interested in measuring the mean location (value on the y axis) of each lane.
I am really new to image processing, but I do know MATLAB and I can get by with R with some difficulty. I would really appreciate it if someone can show me how to go about finding the mean of each lane.
Here's my try. It requires that the gels are nice (i.e. straight lanes and the gel should not be rotated), but should otherwise work fairly generically. Note that there are two image-size-dependent parameters that will need to be adjusted to make this work on images of different size.
%# first size-dependent parameter: should be about 1/4th-1/5th
%# of the lane width in pixels.
minFilterWidth = 10;
%# second size-dependent parameter for filtering the
%# lane profiles
gaussWidth = 5;
%# read the image, normalize to 0...1
img = imread('http://img823.imageshack.us/img823/588/gele.png');
img = rgb2gray(img);
img = double(img)/255;
%# Otsu thresholding to (roughly) find lanes
thMsk = img < graythresh(img);
%# count the mask-pixels in each columns. Due to
%# lane separation, there will be fewer pixels
%# between lanes
cts = sum(thMsk,1);
%# widen the local minima, so that we get a nice
%# separation between lanes
ctsEroded = imerode(cts,ones(1,minFilterWidth));
%# use imregionalmin to identify the separation
%# between lanes. Invert to get a positive mask
laneMsk = ~repmat(imregionalmin(ctsEroded),size(img,1),1);
Image with lanes that will be used for analysis
%# for each lane, create an averaged profile
lblMsk = bwlabel(laneMsk);
nLanes = max(lblMsk(:));
profiles = zeros(size(img,1),nLanes);
midLane = zeros(1,nLanes);
for i = 1:nLanes
profiles(:,i) = mean(img.*(lblMsk==i),2);
midLane(:,i) = mean(find(lblMsk(1,:)==i));
end
%# Gauss-filter the profiles (each column is an
%# averaged intensity profile
G = exp(-(-gaussWidth*5:gaussWidth*5).^2/(2*gaussWidth^2));
G=G./sum(G);
profiles = imfilter(profiles,G','replicate'); %'
%# find the minima
[~,idx] = min(profiles,[],1);
%# plot
figure,imshow(img,[])
hold on, plot(midLane,idx,'.r')
Here's my stab at a simple template for an interactive way to do this:
% Load image
img = imread('gel.png');
img = rgb2gray(img);
% Identify lanes
imshow(img)
[x,y] = ginput;
% Invert image
img = max(img(:)) - img;
% Subtract background
[xn,yn] = ginput(1);
noise = img((yn-2):(yn+2), (xn-2):(xn+2));
noise = mean(noise(:));
img = img - noise;
% Calculate means
means = (1:size(img,1)) * double(img(:,round(x))) ./ sum(double(img(:,round(x))), 1);
% Plot
hold on
plot(x, means, 'r.')
The first thing to do to is convert your RGB image to grayscale:
gr = rgb2gray(imread('gelk.png'));
Then, take a look at the image intensity histogram using imhist. Notice anything funny about it? Use imcontrast(imshow(gr)) to pull up the contrast adjustment tool. I found that eliminating the weird stuff after the major intensity peak was beneficial.
The image processing task itself can be divided into several steps.
Separate each lane
Identify ('segment') the band in each lane
Calculate the location of the bands
Step 1 can be done "by hand," if the lane widths are guaranteed. If not, the line detection offered by the Hough transform is probably the way to go. The documentation on the Image Processing Toolbox has a really nice tutorial on this topic. My code recapitulates that tutorial with better parameters for your image. I only spent a few minutes with them, I'm sure you can improve the results by tuning the parameters further.
Step 2 can be done in a few ways. The easiest technique to use is Otsu's method for thresholding grayscale images. This method works by determining a threshold that minimizes the intra-class variance, or, equivalently, maximizes the inter-class variance. Otsu's method is present in MATLAB as the graythresh function. If Otsu's method isn't working well you can try multi-level Otsu or a number of other histogram based threshold determination methods.
Step 3 can be done as you suggest, by calculating the mean y value of the segmented band pixels. This is what my code does, though I've restricted the check to just the center column of each lane, in case the separation was off. I'm worried that the result may not be as good as calculating the band centroid and using its location.
Here is my solution:
function [locations, lanesBW, lanes, cols] = segmentGel(gr)
%%# Detect lane boundaries
unsharp = fspecial('unsharp'); %# Sharpening filter
I = imfilter(gr,unsharp); %# Apply filter
bw = edge(I,'canny',[0.01 0.3],0.5); %# Canny edges with parameters
[H,T,R] = hough(bw); %# Hough transform of edges
P = houghpeaks(H,20,'threshold',ceil(0.5*max(H(:)))); %# Find peaks of Hough transform
lines = houghlines(bw,T,R,P,'FillGap',30,'MinLength',20); %# Use peaks to identify lines
%%# Plot detected lines above image, for quality control
max_len = 0;
imshow(I);
hold on;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');
%# Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
%# Determine the endpoints of the longest line segment
len = norm(lines(k).point1 - lines(k).point2);
if ( len > max_len)
max_len = len;
end
end
hold off;
%%# Use first endpoint of each line to separate lanes
cols = zeros(length(lines),1);
for k = 1:length(lines)
cols(k) = lines(k).point1(1);
end
cols = sort(cols); %# The lines are in no particular order
lanes = cell(length(cols)-1,1);
for k = 2:length(cols)
lanes{k-1} = im2double( gr(:,cols(k-1):cols(k)) ); %# im2double for compatibility with greythresh
end
otsu = cellfun(#graythresh,lanes); %# Calculate threshold for each lane
lanesBW = cell(size(lanes));
for k = 1:length(lanes)
lanesBW{k} = lanes{k} < otsu(k); %# Apply thresholds
end
%%# Use segmented bands to determine migration distance
locations = zeros(size(lanesBW));
for k = 1:length(lanesBW)
width = size(lanesBW{k},2);
[y,~] = find(lanesBW{k}(:,round(width/2))); %# Only use center of lane
locations(k) = mean(y);
end
I suggest you carefully examine not only each output value, but the results from each step of the function, before using it for actual research purposes. In order to get really good results, you will have to read a bit about Hough transforms, Canny edge detection and Otsu's method, and then tune the parameters. You may also have to alter how the lanes are split; this code assumes that there will be lines detected on either side of the image.
Let me add another implementation similar in concept to that of #JohnColby's, only without the manual user-interaction:
%# read image
I = rgb2gray(imread('gele.png'));
%# middle position of each lane
%# (assuming lanes are somewhat evenly spread and of similar width)
x = linspace(1,size(I,2),10);
x = round( (x(1:end-1)+x(2:end))./2 );
%# compute the mean value across those columns
m = mean(I(:,x));
%# find the y-indices of the mean values
[~,idx] = min( bsxfun(#minus, double(I(:,x)), m) );
%# show the result
figure(1)
imshow(I, 'InitialMagnification',100, 'Border','tight')
hold on, plot(x, idx, ...
'Color','r', 'LineStyle','none', 'Marker','.', 'MarkerSize',10)
and applied on the smaller image: