I am having some problems comparing the elements in different cell arrays.
The context of this problem is that I am using the bwboundaries function in MATLAB to trace the outline of an image. The image is of a structural cross section and I am trying to find if there is continuity throughout the section (i.e. there is only one outline produced by the bwboundaries command).
Having done this and found where the is more than one section traced (i.e. it is not continuous), I have used the cornermetric command to find the corners of each section.
The code I have is:
%% Define the structural section as a binary matrix (Image is an I-section with the web broken)
bw(20:40,50:150) = 1;
bw(160:180,50:150) = 1;
bw(20:60,95:105) = 1;
bw(140:180,95:105) = 1;
Trace = bw;
[B] = bwboundaries(Trace,'noholes'); %Traces the outer boundary of each section
L = length(B); % Finds number of boundaries
if L > 1
disp('Multiple boundaries') % States whether more than one boundary found
end
%% Obtain perimeter coordinates
for k=1:length(B) %For all the boundaries
perim = B{k}; %Obtains perimeter coordinates (as a 2D matrix) from the cell array
end
%% Find the corner positions
C = cornermetric(bw);
Areacorners = find(C == max(max(C))) % Finds the corner coordinates of each boundary
[rowindexcorners,colindexcorners] = ind2sub(size(Newgeometry),Areacorners)
% Convert corner coordinate indexes into subcripts, to give x & y coordinates (i.e. the same format as B gives)
%% Put these corner coordinates into a cell array
Cornerscellarray = cell(length(rowindexcorners),1); % Initialises cell array of zeros
for i =1:numel(rowindexcorners)
Cornerscellarray(i) = {[rowindexcorners(i) colindexcorners(i)]};
%Assigns the corner indicies into the cell array
%This is done so the cell arrays can be compared
end
for k=1:length(B) %For all the boundaries found
perim = B{k}; %Obtains coordinates for each perimeter
Z = perim; % Initialise the matrix containing the perimeter corners
Sectioncellmatrix = cell(length(rowindexcorners),1);
for i =1:length(perim)
Sectioncellmatrix(i) = {[perim(i,1) perim(i,2)]};
end
for i = 1:length(perim)
if Sectioncellmatrix(i) ~= Cornerscellarray
Sectioncellmatrix(i) = [];
%Gets rid of the elements that are not corners, but keeps them associated with the relevent section
end
end
end
This creates an error in the last for loop. Is there a way I can check whether each cell of the array (containing an x and y coordinate) is equal to any pair of coordinates in cornercellarray? I know it is possible with matrices to compare whether a certain element matches any of the elements in another matrix. I want to be able to do the same here, but for the pair of coordinates within the cell array.
The reason I don't just use the cornercellarray cell array itself, is because this lists all the corner coordinates and does not associate them with a specific traced boundary.
Many-to-many comparisons cannot be done with the equal sign. You need to use ismember instead.
%# catenate all corners in one big corner array
Cornerscellarray = cat(1,Cornerscellarray{:});
%# loop through each section cell and remove all that is not corners
for i = 1:length(perim)
%# check for corners
cornerIdx = ismember(Sectioncellmatrix{i},Cornerscellarray,'rows');
%# only keep good entries
Sectioncellmatrix{i} = Sectioncellmatrix{i}(cornerIdx,:);
end
Also, this code really looks like it could be a bit optimized. For example, you could use bwlabel to label your arrays, read the label with the corner coordinates to associate corners with the features.
Like so:
bw(20:40,50:150) = 1;
bw(160:180,50:150) = 1;
bw(20:60,95:105) = 1;
bw(140:180,95:105) = 1;
%# get corners
cornerProbability = cornermetric(bw);
cornerIdx = find(cornerProbability==max(cornerProbability(:)));
%# Label the image. bwlabel puts 1 for the first feature, 2 for the second, etc.
%# Since concave corners are placed just outside the feature, grow the features
%# a little before labeling
bw2 = imdilate(bw,ones(3));
labeledImage = bwlabel(bw2);
%# read the feature number associated with the corner
cornerLabels = labeledImage(cornerIdx);
%# find all corners that are associated with feature 1
corners_1 = cornerIdx(cornerLabels==1);
Related
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.
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;
I'm making an image processing project and I have stuck in one the project's steps. Here is the situation;
This is my mask:
and I want to detect the maximum-sized rectangle that can fit into this mask like this.
I'm using MATLAB for my project. Do you know any fast way to accomplish this aim. Any code sample, approach or technique would be great.
EDIT 1 : The two algorithms below are works with lot's of the cases. But both of them give wrong results in some difficult cases. I'am using both of them in my project.
This approach starts with the entire image and shrinks each border in turn pixel-by-pixel until it finds an acceptable rectangle.
It takes ~0.02 seconds to run on the example image, so it's reasonably fast.
EDIT: I should clarify that this isn't meant to be a universal solution. This algorithm relies on the rectangle being centered and having roughly the same aspect ratio as the image itself. However, in the cases where it is appropriate, it is fast. #DanielHsH offered a solution which they claim works in all cases.
The code:
clear; clc;
tic;
%% // read image
imrgb= imread('box.png');
im = im2bw(rgb2gray(imrgb)); %// binarize image
im = 1-im; %// convert "empty" regions to 0 intensity
[rows,cols] = size(im);
%% // set up initial parameters
ULrow = 1; %// upper-left row (param #1)
ULcol = 1; %// upper-left column (param #2)
BRrow = rows; %// bottom-right row (param #3)
BRcol = cols; %// bottom-right column (param #4)
parameters = 1:4; %// parameters left to be updated
pidx = 0; %// index of parameter currently being updated
%% // shrink region until acceptable
while ~isempty(parameters); %// update until all parameters reach bounds
%// 1. update parameter number
pidx = pidx+1;
pidx = mod( pidx-1, length(parameters) ) + 1;
p = parameters(pidx); %// current parameter number
%// 2. update current parameter
if p==1; ULrow = ULrow+1; end;
if p==2; ULcol = ULcol+1; end;
if p==3; BRrow = BRrow-1; end;
if p==4; BRcol = BRcol-1; end;
%// 3. grab newest part of region (row or column)
if p==1; region = im(ULrow,ULcol:BRcol); end;
if p==2; region = im(ULrow:BRrow,ULcol); end;
if p==3; region = im(BRrow,ULcol:BRcol); end;
if p==4; region = im(ULrow:BRrow,BRcol); end;
%// 4. if the new region has only zeros, stop shrinking the current parameter
if isempty(find(region,1))
parameters(pidx) = [];
end
end
toc;
params = [ULrow ULcol BRrow BRcol]
area = (BRrow-ULrow)*(BRcol-ULcol)
The results for this image:
Elapsed time is 0.027032 seconds.
params =
10 25 457 471
area =
199362
Code to visualize results:
imrgb(params(1):params(3),params(2):params(4),1) = 0;
imrgb(params(1):params(3),params(2):params(4),2) = 255;
imrgb(params(1):params(3),params(2):params(4),3) = 255;
imshow(imrgb);
Another example image:
Here is a correct answer.
You must use dynamic programming! Other methods of direct calculation (like cutting 1 pixel from each edge) might produce sub-optimal results. My method guarantees that it selects the largest possible rectangle that fits in the mask. I assume that the mask has 1 big convex white blob of any shape with black background around it.
I wrote 2 methods. findRect() which finds the best possible square (starting on x,y with length l). The second method LargestInscribedImage() is an example of how to find any rectangle (of any aspect ratio). The trick is to resize the mask image, find a square and resize it back.
In my example the method finds the larges rectangle that can be fit in the mask having the same aspect ration as the mask image. For example if the mask image is of size 100x200 pixels than the algorithm will find the largest rectangle having aspect ratio 1:2.
% ----------------------------------------------------------
function LargestInscribedImage()
% ----------------------------------------------------------
close all
im = double(imread('aa.bmp')); % Balck and white image of your mask
im = im(:,:,1); % If it is colored RGB take only one of the channels
b = imresize(im,[size(im,1) size(im,1)]); Make the mask square by resizing it by its aspect ratio.
SC = 1; % Put 2..4 to scale down the image an speed up the algorithm
[x1,y1,l1] = findRect(b,SC); % Lunch the dyn prog algorithm
[x2,y2,l2] = findRect(rot90(b),SC); % rotate the image by 90deg and solve
% Rotate back: x2,y2 according to rot90
tmp = x2;
x2 = size(im,1)/SC-y2-l2;
y2 = tmp;
% Select the best solution of the above (for the original image and for the rotated by 90degrees
if (l1>=l2)
corn = sqCorn(x1,y1,l1);
else
corn = sqCorn(x2,y2,l2);
end
b = imresize(b,1/SC);
figure;imshow(b>0); hold on;
plot(corn(1,:),corn(2,:),'O')
corn = corn*SC;
corn(1,:) = corn(1,:)*size(im,2)/size(im,1);
figure;imshow(im); hold on;
plot(corn(1,:),corn(2,:),'O')
end
function corn = sqCorn(x,y,l)
corn = [x,y;x,y+l;x+l,y;x+l,y+l]';
end
% ----------------------------------------------------------
function [x,y,l] = findRect(b,SC)
b = imresize(b,1/SC);
res = zeros(size(b,1),size(b,2),3);
% initialize first col
for i = 1:1:size(b,1)
if (b(i,1) > 0)
res(i,1,:) = [i,1,0];
end
end
% initialize first row
for i = 1:1:size(b,2)
if (b(1,i) > 0)
res(1,i,:) = [1,i,0];
end
end
% DynProg
for i = 2:1:size(b,1)
for j = 2:1:size(b,2)
isWhite = b(i,j) > 0;
if (~isWhite)
res(i,j,:)=res(i-1,j-1,:); % copy
else
if (b(i-1,j-1)>0) % continuous line
lineBeg = [res(i-1,j-1,1),res(i-1,j-1,2)];
lineLenght = res(i-1,j-1,3);
if ((b(lineBeg(1),j)>0)&&(b(i,lineBeg(2))>0)) % if second diag is good
res(i,j,:) = [lineBeg,lineLenght+1];
else
res(i,j,:)=res(i-1,j-1,:); % copy since line has ended
end
else
res(i,j,:) = [i,j,0]; % Line start
end
end
end
end
% check last col
[maxValCol,WhereCol] = max(res(:,end,3));
% check last row
[maxValRow,WhereRow] = max(res(end,:,3));
% Find max
x= 0; y = 0; l = 0;
if (maxValCol>maxValRow)
y = res(WhereCol,end,1);
x = res(WhereCol,end,2);
l = maxValCol;
else
y = res(end,WhereRow,1);
x = res(end,WhereRow,2);
l = maxValRow;
end
corn = [x,y;x,y+l;x+l,y;x+l,y+l]';
% figure;imshow(b>0); hold on;
% plot(corn(1,:),corn(2,:),'O')
return;
end
The black boundaries in your image are curved and not closed. For example, in the top right corner, the black boundaries won't meet and form a closed contour. Therefore, a simple strategy in one of my comments will not work.
I am now providing you with a skeleton of a code which you can play with and add conditions as per your need. My idea is as follows:
To find left-side x-coordinate of the rectangle, first count the white pixels each column of the image contains:
%I assume that the image has already been converted to binary.
whitePixels=sum(img,1);
Then find the rate of change:
diffWhitePixels=diff(whitePixels);
If you see the bar plot of diffWhitePixels then you will observe various large entries (which indicate that the white region is still not in a straight line, and it is not a proper place to put the rectangles left vertical edge). Small entries (in your image, less than 5) indicate you can put the rectangle edge there.
You can do similar things to determine right, top and bottom edge positions of the rectangles.
Discussion:
First of all, the problem is ill-posed in my opinion. What do you mean by maximum-sized rectangle? Is it maximum area or length of side? In all possible cases, I don't think above method can get the correct answer. I can think of two or three cases right now where above method would fail, but it will at least give you the right answer on images similar to the given image, provided you adjust the values.
You can put some constraints once you know how your images are going to look. For example, if the black boundary curves inside, you can say that you don't want a column such as [0;0;...0;1;1;...0;0;...;0;1;1;...;1] i.e. zeros surrounded by ones. Another constraint could be how many black pixels do you want to allow? You can also crop the image till to remove extra black pixels. In your image, you can crop the image (programmatically) from the left and the bottom edge. Cropping an image is probably necessary, and definitely the better thing to do.
I have a 5000x5000 grid, and I'm trying to implement a simple model of cancer division in MATLAB. Initially, it picks a random point (x,y) and makes that cell a cancer cell. On the first iteration, it divides - the parent cell stays in it's place, the daughter cell is randomly assigned to any neighbouring cell.
Easy so far.
My problem is this: on successive iterations, a daughter cell will often be assigned to a cell that already has a cancer cell. In this case, I want the daughter cell to take its place and "bump" the cell already there to an adjacent cell. If that adjacent cell is empty, it is filled and the process stops. If not, the cell already in that place is bumped and so on until the last cell finds an empty space and the process stops.
This should be simple, but I have no idea how to code it up and what kind of loops to use.
I'm a physical scientists rather than a programmer, so please treat me like a simpleton!
Here is a function I hacked together that roughly meets the specs you provided.
I does slow down as the number of cancerous cells gets large.
Basically I have a few variables, the NxN matrix that represents the grid of cell locations (i call this a plate as grid is the name of an existing matlab function)
A vector of points that I can iterate through quickly. I pick a seed location and then run a while loop until the grid is full.
On each loop iteration I perform the following for each cell:
Generate a random number to determine if that cell should divide
Generate a random direction to divide
Find the first open plate position in that direction
Populate that position
I haven't tested it extensively but it appears to work.
function simulateCancer(plateSize, pDivide)
plate = zeros(plateSize, plateSize);
nCells = 1;
cellLocations = zeros(plateSize*plateSize,2);
initX = randi(plateSize);
initY = randi(plateSize);
cellLocations(nCells,:) = [initX, initY];
plate(initX, initY) = 1;
f = figure;
a = axes('Parent', f);
im = imagesc(plate, 'Parent', a);
while(nCells < (plateSize * plateSize))
currentGeneration = currentGeneration+1;
for i = 1:nCells
divide = rand();
if divide <= pDivide
divideLocation = cellLocations(i,:);
divideDir = randi(4);
[x, y, v] = findNewLocation(divideLocation(1), divideLocation(2), plate, divideDir);
if (v==1)
nCells = nCells+1;
plate(x,y) = 1;
cellLocations(nCells,:) = [x,y];
end
end
end
set(im,'CData', plate);
pause(.1);
end
end
function [x,y, valid] = findNewLocation(xin, yin, plate, direction)
x = xin;
y = yin;
valid = 1;
% keep looking for new spot if current spot is occupied
while( plate(x, y) == 1)
switch direction
case 1 % divide up
y = y-1;
case 2 % divide down
y = y+1;
case 3 % divide left
x = x-1;
case 4 % divide down
x = x+1;
otherwise
warning('Invalid direction')
x = xin;
y = yin;
return;
end
%if there has been a collision with a wall then just quit
if y==0 || y==size(plate,2)+1 || x==0 || x==size(plate,1)+1 % hit the top
x = xin; %return original values to say no division happend
y = yin;
valid = 0;
return;
end
end
end
Note: Instead of thinking of pushing cells, I coded this in a way that leaves cells where they currently are and creates the new cell at the end of the row/column. Semantically its different but logically it has the same end result, as long as you don't care about the generations.
Inspired by an another question, I though of using image processing techniques to implement this simulation. Specifically we can use morphological dilation to spread the cancerous cells.
The idea is to dilate each pixel using a structuring element that looks like:
1 0 0
0 1 0
0 0 0
where the center is fixed, and the other 1 is placed at random at one of the other eight remaining positions. This would effectively extend the pixel in that direction.
The way the dilation is performed is by created a blank image, with only one pixel set, then accumulating all the results using a simple OR operation.
To speed things up, we don't need to consider every pixel, only those on the perimeter of the current blocks formed by the clusters of cancerous cells. The pixels on the inside are already surrounded by cancer cells, and would have no effect if dilated.
To speed even further, we perform the dilation on all pixels that are chosen to be extended in the same direction in one call. Thus every iteration, we perform at most 8 dilation operations.
This made the code relatively fast (I tested up to 1000x1000 grid). Also it maintains the same timing across all iterations (will not slow down as the grid starts to fill up).
Here is my implementation:
%# initial grid
img = false(500,500);
%# pick 10 random cells, and set them as cancerous
img(randi(numel(img),[10 1])) = true;
%# show initial image
hImg = imshow(img, 'Border','tight', 'InitialMag',100);
%# build all possible structing elements
%# each one dilates in one of the 8 possible directions
SE = repmat([0 0 0; 0 1 0; 0 0 0],[1 1 8]);
SE([1:4 6:9] + 9*(0:7)) = 1;
%# run simulation until all cells have cancer
BW = false(size(img));
while ~all(img(:)) && ishandle(hImg)
%# find pixels on the perimeter of all "blocks"
on = find(bwperim(img,8));
%# percentage chance of division
on = on( rand(size(on)) > 0.5 ); %# 50% probability of cell division
if isempty(on), continue; end
%# decide on a direction for each pixel
d = randi(size(SE,3),[numel(on) 1]);
%# group pixels according to direction chosen
dd = accumarray(d, on, [8 1], #(x){x});
%# dilate each group of pixels in the chosen directions
%# to speed up, we perform one dilation for all pixels with same direction
for i=1:8
%# start with an image with only those pixels set
BW(:) = false;
BW(dd{i}) = true;
%# dilate in the specified direction
BW = imdilate(BW, SE(:,:,i));
%# add results to final image
img = img | BW;
end
%# show new image
set(hImg, 'CData',img)
drawnow
end
I also created an animation of the simulation on a 500x500 grid, with 10 random initial cancer cells (warning: the .gif image is approximately 1MB in size, so may take some time to load depending on your connection)
I have a set of points represented as a 2 row by n column matrix.
These points make up a connected boundary or edge. I require a function that traces this contour from a start point P1 and stop at an end point P2. It also needs to be able trace the contour in a clockwise or anti-clockwise direction. I was wondering if this can be achieved by using some of Matlab's functions.
I have tried to write my own function but this was riddled with bugs and I have also tried using bwtraceboundary and indexing however this has problematic results as the points within the matrix are not in the order that create the contour.
Thank you in advance for any help.
Btw, I have included a link to a plot of the set of points. It is half the outline of a hand.
The function would ideally trace the contour from ether the red star to the green triangle. Returning the points in order of traversal.
EDIT: This is perhaps a work around to a larger problem I am trying to solve but would it be possible to test if a point on the blue boundary edge is connected to the contour that is between either the red stars or green triangular points.
i.e. for a point on the blue boundary, if you were to trace the contour by hand from the left red asterixs to the green triangle the function would return true if the point is on the connected boundary between the two points and false otherwise.
alt text http://img717.imageshack.us/img717/9814/hand1.png
If the points are so close together, you should be able to do the trace by always looking for the next closest point in the list.
If the point were farther apart, the problem would not be solvable - imagine the five points where four are corners and one is in the center: what is the 'correct' way of tracing the line?
%%# create some points
npts = 100;
x = linspace(-1,1,100)'; %'
y = 1 - x.^2;
pts = [x,y];
%# shuffle the points
newOrder = randperm(npts);
pts = pts(newOrder,:);
%# find index of start, end point
startIdx = find(newOrder == 1);
endIdx = find(newOrder == npts);
%# this brings us to where you are - pts as a nx2 array
%# startIdx indicates the star, and endIdx indicates the triangle.
%# pre-assign output - traceIdx, which contains the ordered indices of the point on the trace
traceIdx = NaN(npts,1);
%# create distance matrix
distances = squareform(pdist(pts));
%# eliminate zero-distance along the diagonal, b/c we don't want points linking to themselves
distances(logical(eye(npts))) = NaN;
%# starting from startIdx: always find the closest next point, store in traceIdx,
%# check whether we've arrived at the end, and repeat if we haven't
done = false;
traceCt = 1;
traceIdx(1) = startIdx;
while ~done
%# find the index of the next, closest point
[dummy,newIdx] = min(distances(traceIdx(traceCt),:));
%# store new index and up the counter
traceCt = traceCt + 1;
traceIdx(traceCt) = newIdx;
%# check whether we're done
if newIdx == endIdx
done = true;
else
%# mask the backward distance so that there's no turning back
distances(newIdx,traceIdx(traceCt-1)) = NaN;
end %# if
end %# while ~done
%# remove NaNs
traceIdx(~isfinite(traceIdx)) = [];
%# plot result with a line connecting the dots to demonstrate that everything went well.
figure,
plot(pts(traceIdx,1),pts(traceIdx,2),'-o')
hold on,
plot(pts(startIdx,1),pts(startIdx,2),'*r')
plot(pts(endIdx,1),pts(endIdx,2),'>g')