1
|
|
2
|
%Student Dave's tutorial on: Image processing for object
|
3
|
%tracking (aka giving eyes to your robot :)
|
4
|
%Copyright Student Dave's Tutorials 2012
|
5
|
%if you would like to use this code, please feel free, just remember to
|
6
|
%reference and tell your friends! :)
|
7
|
%requires matlabs image processing toolbox
|
8
|
|
9
|
|
10
|
%What the heck does this code do!?
|
11
|
%the code finds the hexbug buy using a series of basic, but effective
|
12
|
%images processing techniques (formal talk for a second -->) :
|
13
|
% 1) Averaged background subtraction
|
14
|
% 2) Noise reduction via image smoothing using 2-d gaussian filter.
|
15
|
% 3) Threshold and point detection in binary image.
|
16
|
|
17
|
clear all;
|
18
|
close all;
|
19
|
set(0,'DefaultFigureWindowStyle','docked') %dock the figures..just a personal preference you don't need this.
|
20
|
|
21
|
base_dir = 'E:\Dropbox\Student_dave\hexbug_frames\';
|
22
|
|
23
|
cd(base_dir);
|
24
|
|
25
|
%% get listing of frames so that you can cycle through them easily.
|
26
|
f_list = dir('*png');
|
27
|
|
28
|
|
29
|
%% make average of background images (i.e. images with no objects of interest)
|
30
|
% Here we just read in a set of images (N) and then take the average of
|
31
|
% them so that we are confident we got a good model of what the background
|
32
|
% looks like (i.e. a template free from any potential weird image artifacts
|
33
|
|
34
|
N = 20; % num of frames to use to make averaged background...that is, images with no bug!
|
35
|
img = zeros(288,352,N); %define image stack for averaging (if you don't know what this is, just load the image and check it with size())
|
36
|
for i = 1:N
|
37
|
img_tmp = imread(f_list(i).name); %read in the given image
|
38
|
img(:,:,i) = img_tmp(:,:,1); % we don't really care about the rgb image values, so we just take the first dimension of the image
|
39
|
end
|
40
|
bck_img = (mean(img,3)); %take the average across the image stack..and bingo! there's your background template!
|
41
|
subplot(121);imagesc(bck_img)
|
42
|
subplot(122);imagesc(img(:,:,1))
|
43
|
colormap(gray)
|
44
|
clear img; % free up memory.
|
45
|
|
46
|
%initialize gaussian filter
|
47
|
|
48
|
%using fspecial, we will make a gaussian template to convolve (pass over)
|
49
|
%over the image to smooth it.
|
50
|
hsize = 20;
|
51
|
sigma = 10;
|
52
|
gaus_filt = fspecial('gaussian',hsize , sigma);
|
53
|
subplot(121); imagesc(gaus_filt)
|
54
|
subplot(122); mesh(gaus_filt)
|
55
|
colormap(jet)
|
56
|
|
57
|
%this one is just for making the coordinate locations more visible.
|
58
|
SE = strel('diamond', 7); %another tool make for making fun matrice :) this one makes a matrice object for passing into imdilate())
|
59
|
|
60
|
|
61
|
%% iteratively (frame by frame) find bug!
|
62
|
CM_idx = zeros(length(f_list),2); % initize the variable that will store the bug locations (x,y)
|
63
|
|
64
|
for i = 1:2:length(f_list)
|
65
|
|
66
|
img_tmp = double(imread(f_list(i).name)); %load in the image and convert to double too allow for computations on the image
|
67
|
img = img_tmp(:,:,1); %reduce to just the first dimension, we don't care about color (rgb) values here.
|
68
|
subplot(221);imagesc(img);
|
69
|
title('Raw');
|
70
|
|
71
|
%{
|
72
|
%VERY HARD TRACKING
|
73
|
%for frames 230:280, make the bug very hard to track
|
74
|
if (i > 230) && (i < 280) && (mod(i,3) == 0 )
|
75
|
J = imnoise(img,'speckle');
|
76
|
img = img+J*200;
|
77
|
end
|
78
|
%}
|
79
|
|
80
|
|
81
|
%subtract background from the image
|
82
|
sub_img = (img - bck_img);
|
83
|
subplot(222);imagesc(sub_img);
|
84
|
title('background subtracted');
|
85
|
%gaussian blurr the image
|
86
|
gaus_img = filter2(gaus_filt,sub_img,'same');
|
87
|
subplot(223);imagesc(gaus_img);
|
88
|
title('gaussian smoothed');
|
89
|
%threshold the image...here i just made a quick histogram to see what
|
90
|
%value the bug was below
|
91
|
subplot(224);hist(gaus_img(:));
|
92
|
thres_img = (gaus_img < -15);
|
93
|
subplot(224);imagesc(thres_img);
|
94
|
title('thresholded');
|
95
|
|
96
|
|
97
|
%% TRACKING! (i.e. get the coordinates of the bug )
|
98
|
%quick solution for finding center of mass for a BINARY image
|
99
|
%basically, just get indices of all locations above threshold (1) and
|
100
|
%take the average, for both the x and y directions. This will give you
|
101
|
%the average location in each dimension, and hence the center of the
|
102
|
%bug..unless of course, something else (like my hand) passes threshold
|
103
|
%:P
|
104
|
%if doesn't find anything, it randomly picks a pixel
|
105
|
[x,y] = find (thres_img);
|
106
|
if ~isempty(x)
|
107
|
CM_idx(i,:) = ceil([mean(x) mean(y)]+1); % i used ceiling to avoid zero indices, but it makes the system SLIGHTLY biased, meh, no biggie, not the point here :).
|
108
|
else
|
109
|
CM_idx(i,:) = ceil([rand*200 rand*200]);
|
110
|
end
|
111
|
|
112
|
|
113
|
|
114
|
%{
|
115
|
%NOT SO HARD TRACKING
|
116
|
%for frames 230:280, make the bugtracking just a lil noisy by randomly sampling
|
117
|
%around the bugtracker
|
118
|
if (i > 230) && (i < 280) && (mod(i,2) == 0 )
|
119
|
CM_idx(i,:) = [round(CM_idx(i,1) + randn*10) round(CM_idx(i,2) + randn*10)];
|
120
|
end
|
121
|
%}
|
122
|
|
123
|
%{
|
124
|
%NO TRACKING
|
125
|
%for frames 230:280, make the bugtracking just a lil noisy by randomly sampling
|
126
|
%around the bugtracker
|
127
|
if (i > 230) && (i < 280)
|
128
|
CM_idx(i,:) = [NaN NaN];
|
129
|
end
|
130
|
%}
|
131
|
|
132
|
|
133
|
%% now, we visual everything :)
|
134
|
|
135
|
%create a dilated dot at this point for visualize
|
136
|
%make binary image with single coordinate of bug = 1 and rest zeros.
|
137
|
%then dilate that point to make a more visible circle.
|
138
|
bug_img = zeros(size(thres_img));
|
139
|
bug_img(CM_idx(i,1),CM_idx(i,2)) = 1;
|
140
|
|
141
|
%{
|
142
|
% if you are running the "no tracking segment above, you'll need to
|
143
|
% skip over that segment, and thus use this code
|
144
|
if ~((i > 230) && (i < 280))
|
145
|
bug_img(CM_idx(i,1),CM_idx(i,2)) = 1;
|
146
|
end
|
147
|
%}
|
148
|
|
149
|
bug_img = imdilate(bug_img, SE);
|
150
|
subplot(224);imagesc(thres_img + bug_img);
|
151
|
title('thresholded and extracted (red diamond)');
|
152
|
axesHandles = get(gcf,'children');
|
153
|
set(axesHandles, 'XTickLabel', [], 'XTick', []);
|
154
|
set(axesHandles, 'YTickLabel', [], 'YTick', []) ;
|
155
|
|
156
|
pause(.01)
|
157
|
end
|
158
|
|
159
|
%save out the hexbug coordinates
|
160
|
|
161
|
%save('CM_idx_no.mat', 'CM_idx')
|
162
|
|
163
|
|
164
|
|
165
|
%{
|
166
|
%nice and elegant solution for center of mass of gray scale image (i.e. doesn't have to be binary like in our case)----------------------------------
|
167
|
% http://www.mathworks.com/matlabcentral/newsreader/author/109726
|
168
|
%These next 4 lines produce a matrix C whose rows are
|
169
|
% the pixel coordinates of the image A
|
170
|
C=cellfun(@(n) 1:n, num2cell(size(thres_img)),'uniformoutput',0);
|
171
|
[C{:}]=ndgrid(C{:});
|
172
|
C=cellfun(@(x) x(:), C,'uniformoutput',0);
|
173
|
C=[C{:}];
|
174
|
%This line computes a weighted average of all the pixel coordinates.
|
175
|
%The weight is proportional to the pixel value.
|
176
|
CenterOfMass=thres_img(:).'*C/sum(thres_img(:),'double')
|
177
|
%---------------------------------------------------------------------
|
178
|
%}
|