Matlab Shape Labelling - matlab

I have a set of shapes in an image I would like to label according to their area, I have used bwboundaries to find them, and regionprops to determine their area. I would like to label them such that they are labelled different based on whether their area is above or below the threshold i have determined.
I've thought about using inserObjectAnnotation, but I'm not sure how to add on a condition based on their area into the function?

Assuming TH to be the threshold area and BW to be the binary image and if you are okay with labeling them as o's and x's with matlab figure text at their centers (centroids to be exact), based on the thresholding, see if this satisfies your needs -
stats = regionprops(BW,'Area')
stats2 = regionprops(BW,'Centroid')
figure,imshow(BW)
for k = 1:numel(stats)
xy = stats2(k).Centroid
if (stats(k).Area>TH)
text(xy(1),xy(2),'L') %// Large Shape
else
text(xy(1),xy(2),'S') %// Small Shape
end
end
Sample output -

You could use CC = bwconncomp(BW,conn).
To get the number of pixels of every connected compontent you can use:
numPixels = cellfun(#numel,CC.PixelIdxList);
In CC.PixelIdxList you have a list of all found objects and the indices of the pixels belonging to the components. I guess to label your areas you could do something like:
for ind = 1:size(CC.PixelIdxList,2)
Image(CC.PixelIdxList{ind}) = ind;
end

Related

How can I traverse through pixels?

Suppose, I have the following image in my hand.
I have marked some pixels of the image as follows,
Now, I have obtained the pixel mask,
How can I traverse through only those pixels that are in that mask?
Given a binary mask, mask, where you want to iterate over all the true pixels in mask, you have at least two options that are both better than the double for loop example.
1) Logical indexing.
I(mask) = 255;
2) Use find.
linearIdx = find(mask);
I(linearIdx) = 255;
The original question:
How can I save only those pixels which I am interested in?
...
Question: Now, in the Step#2, I want to save those pixels in a data-structure (or, whatever) d so that I can apply another function f2(I, d, p,q,r) which does something on that image on the basis of those pixels d.
Create a binary mask
Try using a logical mask of the image to keep track of the pixels of interest.
I'll make up a random image for example here:
randImg = rand(64,64,3);
imgMask = false(size(randImg(:,:,1)));
imgMask(:,[1:4:end]) = true; % take every four columns This would be your d.
% Show what we are talking about
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
maskImg(imgMaskForRGB) = randImg(imgMaskForRGB);
figure('name','Psychadelic');
subplot(2,1,1);
imagesc(randImg);
title('Random image');
subplot(2,1,2);
imagesc(maskImg);
title('Masked pixels of interest');
Here's what it looks like:
It will be up to you to determine how to store and use the image mask (d in your case) as I am not sure how your functions are written. Hopefully this example will give you an understanding of how it can be done though.
EDIT
You added a second question since I posted:
But, now the problem is, how am I going to traverse through those pixels in K?
Vectrorization
To set all pixels to white:
randImg(imgMaskForRGB) = 255;
In my example, I accessed all of the pixels of interest at the same time with my mask in a vectorized fashion.
I translated my 2D mask into a 3D mask, in order to grab the RGB values of each pixel. That was this code:
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
Then to access all of these pixels in the image of interest, I used this call:
randImg(imgMaskForRGB)
These are your pixels of interest. If you want to divide these values in 1/2 you could do something like this:
randImg(imgMaskForRGB) = randImg(imgMaskForRGB)/2;
Loops
If you really want to traverse, one pixel at a time, you can always use a double for loop:
for r=1:size(randImg,1)
for c=1:size(randImg,2)
if(imgMask(r,c)) % traverse all the pixels
curPixel = randImg(r,c,:); % grab the ones that are flagged
end
end
end
Okay. I have solved this using the answer of #informaton,
I = imread('gray_bear.png');
J = rgb2gray(imread('marked_bear.png'));
mask = I-J;
for r=1:size(I,1)
for c=1:size(I,2)
if(mask(r,c))
I(r,c) = 255;
end
end
end
imshow(I);

Select second largest area in image

I use bwareaopen to remove small objects. Is there a function to remove the big objects? I'm trying to adapt bwareaopen however haven't been successful so far. Thanks
For ref: Here's a link to the help of bwareaopen.
I found an easy way to tackle this problem described here:
"To keep only objects between, say, 30 pixels and 50 pixels in area, you can use the BWAREAOPEN command, like this:"
LB = 30;
UB = 50;
Iout = xor(bwareaopen(I,LB), bwareaopen(I,UB));
Another way if you don't want to use bwareaopen is to use regionprops, specifically with the Area and PixelIdxList attributes, filter out the elements that don't conform to the area range you want, then use the remaining elements and create a new mask. Area captures the total area of each shape while PixelIdxList captures the column major linear indices of the locations inside the image that belong to each shape. You would use the Area attribute to perform your filtering while you would use the PixelIdxList attribute to create a new output image and set these locations to true that are within the desired area range:
% Specify lower and upper bounds
LB = 30;
UB = 50;
% Run regionprops
s = regionprops(I, 'Area', 'PixelIdxList');
% Get all of the areas for each shape
areas = [s.Area];
% Remove elements from output of regionprops
% that are not within the range
s = s(areas >= LB & areas <= UB);
% Get the column-major locations of the shapes
% that have passed the check
idx = {s.PixelIdxList};
idx = cat(1, idx{:});
% Create an output image with the passed shapes
Iout = false(size(I));
Iout(idx) = true;

How to perfom horizontal rank filtering for edge detection?

I do some recognizing of plate numbers, but I'm stuck when doing extraction on them. I read references that state I have to perform horizontal rank filtering but I don't know how to do it.
Code:
a = imread('Izy.jpg');
b = imresize(a,0.5);
c = rgb2gray(b);
Py = [ -1,0,1; -1,0,1 ; -1,0,1];
Gp = conv2(c,Py);
Gpx = edge(Gp,'prewitt');
cl = bwareaopen(Gpx,10);
imshow(cl)
I've read references that say we have to use vertical edge prewitt; I'm using horizontal rank filtering to extract the plate number.
I just tried a code to work for this particular image, but not a general one. Here we have the input image.
a = imread('Izy.jpg');
b = imresize(a,0.5);
c = rgb2gray(b);
The code until here reads the image into variable a, scales it down to half the size (stored in b) and then converts it from a 3 channel image to grayscale (stored in c). I would suggest to use Canny edge detector over Prewitt detector as follows:
Gpx = edge(c,'canny');
L = bwlabel(Gpx,8);
The edge detected binary image (stored in Gpx) will have several foreground images or regions. A region is a collection of pixels, which each of the member pixels connected to at least 1 of it's 8 neighboring pixels. These regions be labeled with the use of bwlabel() and stored in L.
Using regionprops('properties','properties',..), we get a structure with different properties of the regions present in the image.
Here, area and eccentricity properties are calculated for the regions. eccentricity values helps us identify is a region is a circular or linear. For circle, the value is 0 and for a line, the value is 1
imgstat = regionprops(L,'Area','Eccentricity');
area = [imgstat.Area];
ecc = [imgstat.Eccentricity];
Since we have 129 (size(imgstat)) regions in the image, each region will have some area and eccentricity values. Use trial and error to identify the required values for area and eccentricity properties.
Apply the approximate property values to get the boundary of the license plate, and store the region index in label. logical_result extracts and stores the required region from the labeled image.
label = find((area > 150) & (ecc < 0.95) & (ecc > 0.9));
logical_result = ismember(L,label);
figure;imshow(logical_result)
The image stored in logical_result
Now, calculate the boundary values fromlogical_result. These values are recovered as indices and so, we have to convert them into subscripts using ind2sub(). From the boundary values we can extract the region of interest from the original image
[row, col] = ind2sub([size(c,1), size(c,2)],find(logical_result == 1));
result = c(min(row):max(row),min(col):max(col));
imshow(result);
This gives the following result:

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:

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.