I want to extract the rectangular-like shapes (some may have triangular extensions on one side) with an image like this;
What I have done in MATLAB is;
BW=imread('Capture2.JPG');
BW=~im2bw(BW);
SE = strel('rectangle',[1 6]);
BW = ~imclose(BW,SE);
BW2 = imfill(~BW,'holes');
figure
imshow(~BW)
figure
imshow(BW2);
s = regionprops(BW,'BoundingBox','Area','PixelIdxList');
s = s(2:end);
[lab, numberOfClosedRegions] = bwlabel(BW);
figure
imshow(BW)
for i=1:numel(s)
rec = s(i);
ratio = rec.BoundingBox(4)/rec.BoundingBox(3);%height/width
% ratio > 0.3 && ratio < 51.6 && rec.Area > 1100 && rec.Area < 22500
% if ratio > 0.16
rectangle('Position',s(i).BoundingBox,'EdgeColor','r','LineWidth',2);
text(rec.BoundingBox(1),rec.BoundingBox(2),num2str(i),'fontsize',16);
% end
end
What I have come up is;
As it is seen, there are regions find as part of texts, shape inside of a block(index = 3) and non-block region(index = 11). I need to discard the inside regions and non-block areas.
The other issue is since regions are defined by white areas I need to get the black borders of the blocks so that I can capture the block itself, not the inner white region. How can I solve these issues?
I both tried inverting the image and using the methods but no success.
EDIT: Code improvement & additional image
One of the images can be like this including non-rectangular but object of interest shapes (leftmost).
Another issue if image not as good as it should be some, line considered as open especially diagonal and 1px wide ones which causes regionprops misses them.
Code improvement;
close all;
image=imread('Capture1.JPG');
BW = image;
BW = ~im2bw(BW);
SE = strel('rectangle',[3 6]);
BW = ~imclose(BW,SE); % closes some caps to be closed region
r = regionprops(BW,'PixelIdxList');
BW(r(1).PixelIdxList) = 0; %removes outermost white space allowing to connection lines disapear
se = strel('rectangle',[6 1]);
BW = imclose(BW,se);% closes some caps to be closed region
BW = imfill(BW,'holes');
s = regionprops(BW,{'Area', 'ConvexArea', 'BoundingBox','ConvexHull','PixelIdxList'});
%mostly the area and convex area are similar but if convex area is much greater than the area itself it is a complex shape like concave intermediate sections then remove
noidx = [];
for i=1:numel(s)
rec = s(i);
if rec.Area*1.5 < rec.ConvexArea
BW(rec.PixelIdxList) = 0;
noidx(end+1)=i;
end
end
s(noidx)=[];
%no condition for remaining regions
figure
imshow(BW)
for i=1:numel(s)
rec = s(i);
ratio = rec.BoundingBox(4)/rec.BoundingBox(3);%height/width
% ratio > 0.3 && ratio < 51.6 && rec.Area > 1100 && rec.Area < 22500
% if ratio > 0.16
rectangle('Position',s(i).BoundingBox,'EdgeColor','r','LineWidth',2);
text(rec.BoundingBox(1),rec.BoundingBox(2),num2str(i),'fontsize',16,'color','red');
% end
end
Result is;
Advantage is all the remaining regions are region if interest no exception and no condition for area constraint etc. because image size can be different thus the area.
But even this doesn't work on second image. Because of the text below the blocks (which is always the case -> first image was cleared to be uploaded) and the diagonal tips of the leftmost blocks considered open lines.
By adding two conditions I got some good results:
The rectangle need to be fully closed
The area need to be bigger than x pixels (1100 in this case)
In order to check if the rectangle is closed or not, I created an index for each polygon. Those index have the same shape as the rectangles. So if sum(~BW(index)) == sum(index(:)) it mean that the polygon is closed.
The updated code:
warning off
BW=imread('test.jpg');
BW=~im2bw(BW);
SE = strel('rectangle',[1 6]);
BW = ~imclose(BW,SE);
BW2 = imfill(~BW,'holes');
s = regionprops(BW,'BoundingBox','Area','PixelIdxList');
s = s(2:end);
[lab, numberOfClosedRegions] = bwlabel(BW);
figure
imshow(imread('test.jpg'))
inc = 1;
for i=1:numel(s)
rec = s(i);
s(i).BoundingBox = floor(s(i).BoundingBox + [-1,-1,2,2]);
%Creation of the index
clear ind
ind = zeros(size(BW));
y = s(i).BoundingBox(1);
x = s(i).BoundingBox(2);
h = s(i).BoundingBox(3);
w = s(i).BoundingBox(4);
ind(x:x+w,[y,y+h]) = 1;
ind([x,x+w],y:y+h) = 1;
ind = logical(ind);
if sum(~BW(ind)) == sum(ind(:)) && rec.Area > 1100
rectangle('Position',s(i).BoundingBox,'EdgeColor','r','LineWidth',1);
text(rec.BoundingBox(1),rec.BoundingBox(2),num2str(inc),'fontsize',16);
inc = inc + 1;
end
end
RESULT
How can you discard non-rectangular regions? Well I'm sure you can come up with some mathematical properties which are pretty unique to rectangles.
The area is the product of width and heigth.
The perimeter is the twice the sum of width and height.
It obviously has 4 rectangular corners.
I guess the first property would be sufficient and that you can come up with more rules if you need to.
You can get rid of unwanted rectangles and other small stuff with a minimum size constraint or check if they are enclosed by a rectangle.
This should be pretty straight forward.
Related
I'm working on part of an application where I now need to compare two images together and check how similar they are with one another. This is currently done by converting the two images into binary, counting the number of black and white pixels, before finally dividing the number of white pixels with the total number of pixels present inside the image.
The problem I am facing now though is the background for the images I am using since it is counting towards the 'similarity score' and making it an inaccurate result. I have an idea on how to resolve this issue, I just don't know how to put into practice.
For example, say this was the image file and it's pixel values.
0000000000
0000110000
0001001000
0011001100
0001001000
0000110000
0000000000
My idea is to search each row and column from left to right, right to left, up to down and down to up, to detect the number of the black pixels contained in the background of the image. Then hopefully once it detects a white pixel, it stops searching that row/column and moves onto the next. Then finally, when the search is completed, I will know how many black pixels the background takes up and I can then subtract that value away from my grand total, which will the allow me to have a more accurate reading.
I just don't know how to search in this way and I don't want to eliminate every black pixel on screen as they are important because they are also contained inside these objects (example below). Does anybody know how I'd go about doing it this way, or knows an even simpler way?
Current Code:
for i = 1:length(jpgFiles)
baseFileName = jpgFiles(i).name;
fullFileName = fullfile(referenceFolder, baseFileName);
%Reading in the images & converting the images to Black & White
a = im2bw(imread(firstImage));
b = im2bw(imread(fullFileName));
compiledImage = a==b;
fprintf(1, 'Reading %s\n', fullFileName);
axes(handles.axes4);
disp(compiledImage); %Displays the values if the pixels match
[X, Y] = find(a ~= b); %Find coordinates for which the two images are equal
imshow(a); %Show first image
hold on %Retains the current plot and certain axes properties
plot(Y, X, 'y.'); %Overlay those coordinates
hold off %Resets axes properties to their default before drawing new plots
%Getting the values of white and black pixels
blackCount = sum(sum(a==b ==0));
whiteCount = sum(sum(a==b));
disp('Black Count:');
disp(blackCount);
disp('White Count:');
disp(whiteCount);
%Calculating the percentage
maxTotal = blackCount + whiteCount;
decimalValue = whiteCount / maxTotal;
percentageValue = sprintf('%.0f%%',100*decimalValue);
disp(decimalValue);
disp(percentageValue);
%Title settings and delay, if needed
title('Strongest Features (Yellow Hides Any Discrepancies)', 'fontweight', 'bold'); %Sets the title
drawnow;
%pause(5);
%Adding into the image array
imageArray{j} = baseFileName;
j = j + 1;
%Adding into the popular features array
popFeaturesArray{i} = 100*decimalValue;
i = i + 1;
end
Image Examples:
you can do that with some morphological operations:
c1 = rgb2gray(imread('c1.png'));
bw = imfill(imclose(c1 ~= 0,ones(3)),'holes');
bw = bwareafilt(bw,2);
% plotting
A = imoverlay(bw,(c1 > 0).*bw,'b');
imshow(A)
bw is the foreground here.
Edit - if you don't have bwareafilt you can do instead:
n = 2; % keep the two largest areas
cc = bwconncomp(bw);
pixels = cc.PixelIdxList;
sizes = cellfun(#numel,pixels);
[~,idxs] = sort(sizes,'descend');
pixels = cell2mat(pixels(idxs(1:min(n,end)))');
bw(:) = false;
bw(pixels) = 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'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 an image of some deer behind a fence and I'm trying to remove the fence so that we can look straight at the deer without a fence in the way. So far I've, to a decent quality, segmented off (determined the coordinates of) the fence and am trying to replace the colors of these coordinates with nearby colors to simulate removing the fence.
Is there some image processing function in MATLAB that can help me easily accomplish my goal? I have tried the below code in which I try to find the nearest coordinate that isn't part of the fence, but "~ismember([i_temp,j],[ii,jj])" doesn't work because I'm trying to compare coordinates, but instead it seems to be comparing i_temp and j as separate variables, i.e. is i_temp not in ii or jj and is j in ii or jj.
%% 5. Locate pixel locations of the segmented image
% Now that the fence has been segmented. Locate the pixel locations of the
% segmented image
[ii,jj] = find(Img_threshold2==1);
n = length(ii); % Get a count of the number of coordinates
Rout = Rin;
Gout = Gin;
Bout = Bin;
%% 6. Recolor the areas of the fence with the nearest color available
for k=1:n
i = ii(k);
j = jj(k);
keepLooping = true;
i_add = 0;
j_add = 0;
i_coord = 0;
j_coord = 0;
% Find the nearest coordinate that is not a part of the fence
while keepLooping
i_add = i_add + 1;
j_add = j_add + 1;
% Check right
i_temp = i + i_add;
if ~ismember([i_temp,j],[ii,jj])
i_coord = i_temp;
j_coord = j;
break
end
% Check left
i_temp = i - i_add;
if ~ismember([i_temp,j],[ii,jj])
i_coord = i_temp;
j_coord = j;
break
end
% I would do check up and down, but the left/right doesn't even work
% since ismember doesn't work as I expected
end
% Replace the color of the fence coordinate to the nearest non-fence
% coordinate determined above
Rout(i,j) = Rin(i_coord,j_coord);
Gout(i,j) = Rin(i_coord,j_coord);
Bout(i,j) = Rin(i_coord,j_coord);
end
EDIT 9/20/14:
Tried bwdist with the below code, where Iin is the input image and image_threshold2 is the segmented fence. Rather than getting the nearest color, the fence turned turquoise, which makes no sense since there is no turquoise in the image. Here is a screenshot demonstrating what happened. I cropped the image so I can test on a small area of the image, the actual image is much larger and has the actual deer and whatnot. Screenshot: http://gyazo.com/32ab37b8d2d9e137103d330a39d4ecfa
[D,IDX] = bwdist(~Img_threshold2);
% Replace the color of the fence coordinate to the nearest non-fence
% coordinate determined above
Iout = Iin;
Iout(Img_threshold2)=Iout(IDX(Img_threshold2));
Make a binary image where 1 is the fence and 0 the remain. Then use the [D,IDX] = bwdist(BW) function of Matlab. In IDX you have the closest non-fence index. Then you just have to assign to the voxel in the fence its closest non-fence value.
[D,IDX] = bwdist(~mask_fence)
I(mask_fence)=I(IDX(mask_fence))
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)