Separating Background and Foreground - image

I am new to Matlab and to Image Processing as well. I am working on separating background and foreground in images like this
I have hundreds of images like this, found here. By trial and error I found out a threshold (in RGB space): the red layer is always less than 150 and the green and blue layers are greater than 150 where the background is.
so if my RGB image is I and my r,g and b layers are
redMatrix = I(:,:,1);
greenMatrix = I(:,:,2);
blueMatrix = I(:,:,3);
by finding coordinates where in red, green and blue the values are greater or less than 150 I can get the coordinates of the background like
[r1 c1] = find(redMatrix < 150);
[r2 c2] = find(greenMatrix > 150);
[r3 c3] = find(blueMatrix > 150);
now I get coordinates of thousands of pixels in r1,c1,r2,c2,r3 and c3.
My questions:
How to find common values, like the coordinates of the pixels where red is less than 150 and green and blue are greater than 150?
I have to iterate every coordinate of r1 and c1 and check if they occur in r2 c2 and r3 c3 to check it is a common point. but that would be very expensive.
Can this be achieved without a loop ?
If somehow I came up with common points like [commonR commonC] and commonR and commonC are both of order 5000 X 1, so to access this background pixel of Image I, I have to access first commonR then commonC and then access image I like
I(commonR(i,1),commonC(i,1))
that is expensive too. So again my question is can this be done without loop.
Any help would be appreciated.
I got solution with #Science_Fiction answer's
Just elaborating his/her answer
I used
mask = I(:,:,1) < 150 & I(:,:,2) > 150 & I(:,:,3) > 150;

No loop is needed. You could do it like this:
I = imread('image.jpg');
redMatrix = I(:,:,1);
greenMatrix = I(:,:,2);
blueMatrix = I(:,:,3);
J(:,:,1) = redMatrix < 150;
J(:,:,2) = greenMatrix > 150;
J(:,:,3) = blueMatrix > 150;
J = 255 * uint8(J);
imshow(J);
A greyscale image would also suffice to separate the background.
K = ((redMatrix < 150) + (greenMatrix > 150) + (blueMatrix > 150))/3;
imshow(K);
EDIT
I had another look, also using the other images you linked to.
Given the variance in background colors, I thought you would get better results deriving a threshold value from the image histogram instead of hardcoding it.
Occasionally, this algorithm is a little to rigorous, e.g. erasing part of the clothes together with the background. But I think over 90% of the images are separated pretty well, which is more robust than what you could hope to achieve with a fixed threshold.
close all;
path = 'C:\path\to\CUHK_training_cropped_photos\photos';
files = dir(path);
bins = 16;
for f = 3:numel(files)
fprintf('%i/%i\n', f, numel(files));
file = files(f);
if isempty(strfind(file.name, 'jpg'))
continue
end
I = imread([path filesep file.name]);
% Take the histogram of the blue channel
B = I(:,:,3);
h = imhist(B, bins);
h2 = h(bins/2:end);
% Find the most common bin in the *upper half*
% of the histogram
m = bins/2 + find(h2 == max(h2));
% Set the threshold value somewhat below
% the value corresponding to that bin
thr = m/bins - .25;
BW = im2bw(B, thr);
% Pad with ones to ensure background connectivity
BW = padarray(BW, [1 1], 1);
% Find connected regions in BW image
CC = bwconncomp(BW);
L = labelmatrix(CC);
% Crop back again
L = L(2:end-1,2:end-1);
% Set the largest region in the orignal image to white
for c = 1:3
channel = I(:,:,c);
channel(L==1) = 255;
I(:,:,c) = channel;
end
% Show the results with a pause every 16 images
subplot(4,4,mod(f-3,16)+1);
imshow(I);
title(sprintf('Img %i, thr %.3f', f, thr));
if mod(f-3,16)+1 == 16
pause
clf
end
end
pause
close all;
Results:

Your approach seems basic but decent. Since for this particular image the background is composed of mainly blue so you be crude and do:
mask = img(:,:,3) > 150;
This will set those pixels which evaluate to true for > 150 to 0 and false to 1. You will have a black and white image though.
imshow(mask);
To add colour back
mask3d(:,:,1) = mask;
mask3d(:,:,2) = mask;
mask3d(:,:,3) = mask;
img(mask3d) = 255;
imshow(img);
Should give you the colour image of face hopefully, with a pure white background. All this requires some trial and error.

Related

Detection of pellet on petri dish

I am currently doing a project on morphology of filamentous fungi during batch fermentation (Yes, I am not a software engineer.. Biotech). Where I am taken pictures of the morphology in a petri dish. I am developing a "fast" method to describe the pellets (small aggregates of fungi) that occurs during the fermentation. To do this I am writing a code in MatLab.
Depending on the color of the pellets (light or dark) the pictures are taken on differen backgrounds, black or white. I am inverting the picture if the mean gray value is below 70 to distinguish between backgrounds.
Pictures:
White background
Dark background
I have several problems:
Detecting the edge of the petri dish so it won't be regarded as an object (Currently done with the edge('log',) function). The edge is detected, but i miss some parts, think because of the lower light in top.
Proper thresholding inside the dish
Detection of pellets - right now it is done by a combination of running through each color channel, but might be done with some blob detection?
Does anybody have some inputs?
My code is as following:
close all
clear all
clc
%Empty arrays to hold data
metricD=[];
areaD=[];
perimeterD=[];
% Specify the folder where the files live.
myFolder = pwd;
% Check to make sure that folder actually exists. Warn user if it doesn't.
if ~isdir(myFolder)
errorMessage = sprintf('Error: The following folder does not exist:\n%s', myFolder);
uiwait(warndlg(errorMessage));
return;
end
% Get a list of all files in the folder with the desired file name pattern.
filePattern = fullfile(myFolder, '*.jpg'); % Change to whatever pattern you need.
theFiles = dir(filePattern);
% Show debugging plots
plotFig = 0;
% parameters that can be tuned
% how many colors channels we minimum want to see a spore in
% e.g. set to 1 for image "P. f Def C.tif"
labelcutOff = 1;
% remove areas larger than
removeLargerthan = 500000;
for k = 1 : length(theFiles)
baseFileName = theFiles(k).name;
fullFileName = fullfile(myFolder, baseFileName);
%% reading as an image array with im
I = imread(fullFileName);
% convert to grayscale
Ig = rgb2gray(I);
if plotFig
figure;imagesc(I)
figure;imagesc(Ig)
end
mm=mean(mean(Ig));
if mm < 70
I=imcomplement(I);
Ig = imcomplement(Ig);
end
% BLOB DETCTION
% h = fspecial('log', [15 15], 2);
% imLOG = imfilter(Ig, h);
% figure;imagesc(imLOG)
%% find petridish by edges and binary operations
% HACK - NOT HOW IT SHOULD BE DONE
Ig = wiener2(Ig,[5 5]);
imEdge = edge(Ig,'log');
circle = bwareaopen(imEdge,50);
circle = imclose(circle,strel('disk',30));
circle = bwareaopen(circle,8000);
% circle = imfill(circle,'holes');
circle = bwconvhull(circle);
circle = imerode(circle,strel('disk',150));
if plotFig
figure;imagesc(circle)
end
%% Get thresholds inside dish using otsu on each channel
imR = double(I(:,:,1)) .* circle;
imG = double(I(:,:,2)) .* circle;
imB = double(I(:,:,3)) .* circle;
thresR = graythresh(uint8(imR(circle))) *max(imR(circle));
thresG = graythresh(uint8(imG(circle))) *max(imG(circle));
thresB = graythresh(uint8(imB(circle))) *max(imB(circle));
if plotFig
figure;imagesc(imR)
figure;imagesc(imG)
figure;imagesc(imB)
end
%% classify inside dish
% check if it should be smaller or larger than
if sum(imR(circle) < thresR) > sum(imR(circle) > thresR)
labelR = imR > thresR;
else
labelR = imR < thresR;
end
if sum(imG(circle) < thresG) > sum(imG(circle) > thresG)
labelG = imG > thresG;
else
labelG = imG < thresG;
end
if sum(imB(circle) < thresB) > sum(imB(circle) > thresB)
labelB = imB > thresB;
else
labelB = imB < thresB;
end
if plotFig
figure;imagesc(labelR)
figure;imagesc(labelG)
figure;imagesc(labelB)
end
labels = (labelR + labelG + labelB) .* circle;
labels(labels < labelcutOff) = 0;
labels = imfill(labels,'holes');
labels = bwareaopen(labels,30);
if plotFig
figure;imagesc(labels)
end
%% clean up labels
labelBig = bwareaopen(labels,removeLargerthan);
labels = labels - labelBig;
if plotFig
figure;imagesc(labels)
end
BN = labels;
%% old script
stats = regionprops(BN,'Basic');
obj2 = numel(stats);
[B,L] = bwboundaries(BN,'holes');
figure
% imshow(label2rgb(L, #jet, [.5 .5 .5]))
imshow(I)
hold on
title(baseFileName)
for j = 1:length(B)
boundary = B{j};
plot(boundary(:,2), boundary(:,1),'w','LineWidth',2)
end
%region stats
stats = regionprops(L,'Area','Centroid');
%Threshold for printing in end
threshold = 0.2;
%Conversion factor pixel to cm
conversionF=9/2125;
% loop over the boundaries
for j = 1:length(B)
% obtain (X,Y) boundary coordinates corresponding to label 'j'
boundary = B{j};
% compute a simple estimate of the object's perimeter
delta_sq = diff(boundary).^2;
perimeter = sum(sqrt(sum(delta_sq,2)));
perimeterD(j,k)=perimeter*conversionF;
% obtain the area calculation corresponding to label 'k'
area = stats(j).Area;
areaD(j,k)=area*conversionF^2;
% compute the roundness metric
metric = 4*pi*area/perimeter^2;
metricD(j,k)=metric;
% display the results
metric_string = sprintf('%d. %2.2f', j,metric);
text(boundary(1,2)-50,boundary(1,1)+23,metric_string,'Color','k',...
'FontSize',14,'FontWeight','bold');
end
drawnow; % Force display to update immediately.
end
%Calculating stats
areaM=mean(areaD);
pM=mean(perimeterD);
metricD(metricD==Inf)=0;
mM=mean(metricD);
Hint:
A morphological top-hat filter fllowed by binarization (with a constant threshold ?) can be a good start. And filtering on the blob size will do a reasonable cleanup.
For the edges, try circular Hough.

how to make smooth and remove the white edge? Besides, why the black lines arise in my answer? How to solve it?

Based on Shai and Biguri's codes and comments, I have finished a color picture like this:
A problem arises, how to remove the white edge and make it smooth? One solution may be to build 3x3 matrix or bigger and average. But the calculations should be large for every white-edge points. Or there may be some useful functions in Matlab to deal well with this problems?
If you have a license for the image processing toolbox, you can try using for example medfilt2 to apply a median filter on the image. A 11 by 11 median filter should do the trick. It is not very difficult to reimplement the filter yourself if you don't have the toolbox.
This is just one of the possibilities, you can use many different filters that will have different impacts on sharpness ang edge removal.
Edit:
Here is a quick median filter implementation (it may contain errors and could be optimized):
function ret = imageMedianFilter(im, np)
if(size(np,2) == 1)
npx = np;
npy = np;
else
npx = np(1);
npy = np(2);
end
ret = zeros(size(im,1),size(im,2));
for xpos = 1:size(im,1)
for ypos = 1:size(im,2)
curval = double(0);
if(xpos + npx - 1) > size(im,1)
npixels_x = size(im,1) - xpos + 1;
else
npixels_x = npx;
end
if(ypos + npy - 1) > size(im,2)
npixels_y = size(im,2) - ypos + 1;
else
npixels_y = npy;
end
a = im(xpos:xpos+npixels_x-1 , ypos:ypos+npixels_y-1);
a = reshape(a,1,size(a,1)*size(a,2));
curval = median(a);
ret(xpos , ypos) = curval;
end
end
ret = uint8(ret);
end
You can use it on R,G and B components as shown by Rotem below:
RGB = cat(3, imageMedianFilter(RGB(:,:,1), [11,11]), imageMedianFilter(RGB(:,:,2), [11,11]), imageMedianFilter(RGB(:,:,3), [11,11]));
(assuming your image is named RGB).
Here is my solution. I take n*n patch to average the near RGB. But there is a problem arising. Why the right down side of processed picture showing black lines?
clc;clf;close all;clear all;
img = imread('sample2color_t1.bmp'); %// read image
bw = img(:,:,1) > 128; %// convert to binary mask
[lb,lab] = bwlabel(bw,4); %// extract distinct regions
[a,b,c]=size(img);
R=ones(a,b);
G=ones(a,b);
B=ones(a,b);
%I have omitted other colors process codes. Below it is the white edges code.
r=[];c=[];
[r,c] = find(lb ==0);
for i=1:length(r)
R(r(i),c(i))=1;
G(r(i),c(i))=1;
B(r(i),c(i))=1;
end
scale=5;%步长1,8连通
for i=1:length(r)
sumR=0;sumG=0;sumB=0;
for j=0:2*scale
for k=0:2*scale
sumR=sumR+R(r(i)-scale+j,c(i)-scale+k);
sumG=sumG+G(r(i)-scale+j,c(i)-scale+k);
sumB=sumB+B(r(i)-scale+j,c(i)-scale+k);
end
end
R(r(i),c(i))=sumR/(2*scale+1)^2;
G(r(i),c(i))=sumG/(2*scale+1)^2;
B(r(i),c(i))=sumB/(2*scale+1)^2;
end
imPaint=cat(3,R,G,B);
figure;
imshow(imPaint);

How does this MATLAB code make an image into a binary image?

v = videoinput('winvideo', 1, 'YUY2_320x240');
s = serial('COM1', 'BaudRate', 9600);
fopen(s);
while(1)
h = getsnapshot(v);
rgb = ycbcr2rgb(h);
for i = 1:240
for j = 1:320
if rgb(i,j,1) > 140 && rgb(i,j,2) < 100 % use ur own conditions
bm(i, j) = 1;
else
bm(i, j) = 0;
end
end
end
This is the code i got from my senior regarding image processing using MATLAB. The above code is to convert the image to binary image, But in the code rgb(i, j, 1) > 140 I didn't understand that command. How to select that 140 and what does that rgb(i, j, 1) mean?
You have an RGB image rgb where the third dimension are the RGB color planes. Thus, rgb(i,j,1) is the red value at row i, column j.
By doing rgb(i,j,1)>140 it tests if this red value is greater than 140. The value 140 appears to be ad hoc, picked for a specific task.
The code is extremely inefficient as there is no need for a loop:
bm = rgb(:,:,1)>140 & rgb(:,:,2)<100;
Note the change from && to the element-wise operator &. Here I'm assuming that the size of rgb is 240x320x3.
Edit: The threshold values you choose completely depend on the task, but a common approach to automatic thresholding is is Otsu's method, graythresh. You can apply it to a single color plane to get a threshold:
redThresh = graythresh(rgb(:,:,1)) * 255;
Note that graythresh returns a value on [0,1], so you have to scale that by the data range.

How to find more than one matching pattern using Normalized Correalation

I'm using normxcorr2 to find the area that exactly match with my pattern and i also want to find the other area(in the red rectangle) that is look like the pattern. I think it will be works if i can find the next maximum and so on and that value must not in the first maximum area or the first one that it has been detected but i can't do it. Or if you have any idea that using normxcorr2 to find the others area please advise me, I don't have any idea at all.
Here's my code. I modified from this one http://www.mathworks.com/products/demos/image/cross_correlation/imreg.html
onion = imread('pattern103.jpg'); %pattern image
peppers = imread('rsz_1jib-159.jpg'); %Original image
onion = rgb2gray(onion);
peppers = rgb2gray(peppers);
%imshow(onion)
%figure, imshow(peppers)
c = normxcorr2(onion,peppers);
figure, surf(c), shading flat
% offset found by correlation
[max_c, imax] = max(abs(c(:)));
[ypeak, xpeak] = ind2sub(size(c),imax(1));
corr_offset = [(xpeak-size(onion,2))
(size(onion,1)-ypeak)]; %size of window show of max value
offset = corr_offset;
xoffset = offset(1);
yoffset = offset(2);
xbegin = round(xoffset+1); fprintf(['xbegin = ',num2str(xbegin)]);fprintf('\n');
xend = round(xoffset+ size(onion,2));fprintf(['xend = ',num2str(xbegin)]);fprintf('\n');
ybegin = round(yoffset+1);fprintf(['ybegin = ',num2str(ybegin)]);fprintf('\n');
yend = round(yoffset+size(onion,1));fprintf(['yend = ',num2str(yend)]);fprintf('\n');
% extract region from peppers and compare to onion
extracted_onion = peppers(ybegin:yend,xbegin:xend,:);
if isequal(onion,extracted_onion)
disp('pattern103.jpg was extracted from rsz_org103.jpg')
end
recovered_onion = uint8(zeros(size(peppers)));
recovered_onion(ybegin:yend,xbegin:xend,:) = onion;
figure, imshow(recovered_onion)
[m,n,p] = size(peppers);
mask = ones(m,n);
i = find(recovered_onion(:,:,1)==0);
mask(i) = .2; % try experimenting with different levels of
% transparency
% overlay images with transparency
figure, imshow(peppers(:,:,1)) % show only red plane of peppers
hold on
h = imshow(recovered_onion); % overlay recovered_onion
set(h,'AlphaData',mask)

Determine a regions average in MATLAB

I need some help with RGB capture in a image.
I am using impixel to manualy get RGB from a picture, but i would like to create a grid of let's say 20x20 px boxes where it will automatically tell me for each box a RGB value. So in a picture lets say i have 20 boxes it will tell me 20 RGB values. Yeah an if there is 20% or more of white space that it ignores that rgb box.
Can you point me to some links or give me a general idea how to do this.
Best regards
P.S. image is just a .jpg, the background is white an in the middle there is an item.
UPDATE
This is my code for collecting RGB using impixel
px=impixel(img);
st = num2cell(px,1);
zstup = cellfun(#sum,st);
zred = size(px,1);
rez = bsxfun(#rdivide,zstup,zred);
trez=round(rez);
What I want to do is :
http://imageshack.us/photo/my-images/696/exsample.jpg/
So every box like A1, A2, and so on will return RGB value like trez in my code.
So in my code i save my trez data in a table and it is like in excell lets say 220 | 23 | 34, now if i do that to another fruit i will have
220 | 23 | 34
123 | 212| 78
and so on...
Returning to automatization, A7 and A 15 would not be good RGB canditades because they have more then 50% white area so everything that has 20% white will be ignored.
So A31 is good and the RGB value needs to be saved.
So all in all here i would have my be 6 RGB values that would have to be automatically saved like the above example.
I know how to save to table i just need help for the gathering rgb values in every box.
Depending on your exact needs I see two solutions:
Downscale the image using impyramid(img, 'reduce'). This gives you a smaller image consisting of average values of the original image. Then do what you did before to access single pixels. Repeat as often as necessary to get 2x2, 4x4, 8x8 or larger "boxes".
Or you could use define a box (or arbitrary shape) as a matrix of ones and zeros and use the regionprops function in order to get information about the images content depending on the fields containing ones:
roi = zeros(size(img))
roi(1:10,1:10) = 1;
r = regionprops(roi, img, 'MeanIntensity')
average = r.MeanIntensity
This is my complete code for automatic color grabing from pictures in folder. So the program asks you to chose a folder and after that you get a table full of information about color and roundess. I am using this code to get color from fruits that have white background . It does everything by itself. Hope it helps someone.
clear all;
clc;
uiwait(msgbox('Chose the folder where your pictures are kept. Click OK to continue..'));
% Opening the folder
folder = uigetdir(pwd);
filePattern = fullfile(folder, '*.jpg');
jpegFiles = dir(filePattern);
for k = 1:length(jpegFiles)
baseFileName = jpegFiles(k).name;
fullFileName = fullfile(folder, baseFileName);
[pathstr, name, ext] = fileparts(fullFileName);
naziv_voca=name;
%Taking RGB color
slika = imread(fullFileName);
[redovi stupci RGBboje] = size(slika);
red_ink = floor(redovi/10);
stup_ink = floor(stupci/10);
r = 1;
c = 1;
for stupac = 1 : stup_ink : stupci
for red = 1 : red_ink : redovi
red1 = red;
red2 = red1 + red_ink;
stupac1 = stupac;
stupac2 = stupac1 + stup_ink;
red2 = min(red2, redovi);
stupac2 = min(stupac2, stupci);
crveniS = slika(red1:red2, stupac1:stupac2, 1);
zeleniS = slika(red1:red2, stupac1:stupac2, 2);
plaviS = slika(red1:red2, stupac1:stupac2, 3);
crvena(r,c) = mean2(crveniS);
zelena(r,c) = mean2(zeleniS);
plava(r,c) = mean2(plaviS);
r = r + 1;
if r >redovi
r = 1;
end
end
c = c + 1;
if c >1
c = 1;
end
end
RGB=[crvena,zelena,plava];
bijela=[255 255 255];
tolerancija = 50;
rez = RGB((abs(RGB(:,1)-bijela(1)) > tolerancija) | (abs(RGB(:,2)-bijela(2)) > tolerancija),:);
trez=round(rez);
%Taking shape
pic = rgb2gray(slika);
threshold = graythresh(pic);
bw = im2bw(pic,threshold);
fbw = ones(size(bw))-imfill(ones(size(bw))-bw);
invImg = ~fbw;
f = bwlabel(invImg);
S = regionprops(f,'Area','Perimeter','centroid');
Thr=100;
S=S([S.Area]>Thr);
score = (min(sqrt([S.Area]),[S.Perimeter]/4)./(max(sqrt([S.Area]), [S.Perimeter]/4))).^2;
score=max(score);
%Inserting data into table and creating data
if exist('tablica.mat','file')
vel=size(trez,1);
for z=1:vel
s=load('tablica');
olddata=s.data;
temp=trez(z,:);
dataCell= [naziv_voca,num2cell(temp),num2cell(score)];
data=[olddata;dataCell];
save('tablica.mat','-append','data');
end
else
stupac_rgb = num2cell(trez,1);
zstupac = cellfun(#sum,stupac_rgb);
zred = size(trez,1);
rez = bsxfun(#rdivide,zstupac,zred);
trez=round(rez);
data= [naziv_voca,num2cell(trez),num2cell(score)];
save('tablica','data')
end
end
uiwait(msgbox('Your information is saved'));

Resources