"Pretty" Print of barcodedecode.m
% This is the only matlab file I've ever published, so comments are welcome
% I mainly used matlab as a toybox before recoding in C++ so this code is not
% so correct or nicely written
% Input on correct matlab style, or even better, more sensible ways of coding
% some of this stuff is welcome
% Main Assumptions
% * barcode covers most of the view.
% * no serious distortion across the view.
% * Code 39 only. The main property of code 39 used here is that the widths are in the ratio 1:3
% There's a few things I've done to the C that arn't covered here.
% * Variable length average. This was added because daylight and nightime LED light
% work best with different average lengths.
% (c) 2004 Peter Bradshaw. All rights reserved, get in touch if you
% want to talk. I'm sure there's nothing groundbreaking in here,
% and it may, at best, be used as a rough reference.
% It would be great to hear if it helps anyone out or if it's bloody
% useless and I should be doing something else.
% The script loads an image from a file.
cd('C:\work\Barcode Player\captures\2nd camera\')
[filename,pathname] = uigetfile('*.bmp','Find image file');
barcodeim = imread(strcat(pathname,filename));
%plot the image
subplot(2,2,1);
image(barcodeim);
xlabel(filename);
all_results = [];%[0,'.'];
all_counts = [];
% I originally had a single line, but I moved to a loop
% you'll probably have to alter these numbers after looking at the output
% there's currenltly no auto sensing of the appropriate area.
for linetotake = (150:2:250);%360;(300:2:380);%
line([0 640],[linetotake linetotake],'Color','k')
%figure;
trace = double(squeeze(barcodeim(linetotake,:,2)));
%plot(trace);
% Make a simple moving average, I tried a few other things, but
% this seems to work best with the data I've found. Lots of webcams
% preprocess data in the driver before handing it off.
filtleno2m1 = 10;
filtlen = filtleno2m1*2 + 1;
filtered = conv(ones(1,filtlen)/filtlen,trace);
paddedtrace = [zeros(1,filtleno2m1) trace zeros(1,filtleno2m1) ];
maxlev = [];
minlev = [];
levellen = 1;%8;%30;
% At one point I was playing with using (local min+local max)/2 as the the compare value.
for i = (filtleno2m1:length(filtered)-filtleno2m1)
maxlev(i) = max(paddedtrace(i-levellen:i+levellen));
minlev(i) = min(paddedtrace(i-levellen:i+levellen));
end
cmp = [];
maxlev(length(filtered)) = 1;
minlev(length(filtered)) = 1;
cmp(length(filtered)) = 1;
%filtered = 0.5*(maxlev+minlev);
comparewht = 5;
compareblk = -comparewht;
%run thru and make an array of compare results.
for i = (filtleno2m1:length(filtered)-filtleno2m1)
if(paddedtrace(i) > filtered(i))
cmp(i) = comparewht;
else
cmp(i) = compareblk;
end
end
% Plot a line for the traces above to see what we're comparing.
subplot(2,2,2);
plot([paddedtrace' filtered' minlev' maxlev']);
hold on;
plot( cmp','x-');
hold off;
oldcmp = 80;
len = 1;
histo=zeros(length(filtered),1);
uphisto=zeros(length(filtered),1);
downhisto=zeros(length(filtered),1);
linemarks = [];
% collect the lengths of the long and short bars
% of both colours. Webcam's somtimes 'spread' whites, and also
% our compare values may be biased.
for i = (1:length(filtered))
if(cmp(i) ~= oldcmp)
if (oldcmp == compareblk) || (oldcmp == comparewht)
linemarks = [linemarks ; [oldcmp len 0]];
histo(len)=histo(len)+1;
if(oldcmp>0)
uphisto(len)=uphisto(len)+1;
else
downhisto(len)=downhisto(len)+1;
end
end
oldcmp = cmp(i);
len = 1;
else
len = len+1;
end
end
% find the two peaks (from the thin and 3 times as wide wide bars)
[upsmallcnt,firstupmax] = max(uphisto);
for secondupmax = (firstupmax+1:length(uphisto));
[junk,seconduptry] = max(uphisto(secondupmax : length(uphisto)) ) ;
seconduptry = seconduptry + secondupmax - 1;
if seconduptry ~= secondupmax
secondupmax = seconduptry;
break;
end
end
[downsmallcnt,firstdownmax] = max(downhisto(1:5));
for seconddownmax = (firstdownmax+1:length(downhisto));
[junk,seconddowntry] = max(downhisto(seconddownmax : length(downhisto)) ) ;
seconddowntry = seconddowntry + seconddownmax - 1;
if seconddowntry ~= seconddownmax
seconddownmax = seconddowntry;
break;
end
end
uplimit = secondupmax * 2.4;
downlimit = seconddownmax * 2.4;
% skip past any wide bars at the beginning.
barsstart = 1;
for i = (1:length(linemarks)/2)
if(linemarks(i,1)>0)
if(linemarks(i,2)>uplimit)
barsstart = i+1;
end
else
if(linemarks(i,2)>downlimit)
barsstart = i+1;
end
end
end
% and the end
barsend = length(linemarks);
for i = (length(linemarks):-1:length(linemarks)/2)
if(linemarks(i,1)>0)
if(linemarks(i,2)>uplimit)
barsend = i-1;
end
else
if(linemarks(i,2)>downlimit)
barsend = i-1;
end
end
end
% grab the interesting part
bartrace = linemarks(barsstart:barsend,:);
% use the average of the two peaks for comparisons
% the 2/4 numbers are here because I was fiddling proportions
upcmp = (firstupmax*2 + secondupmax*2)/4;
downcmp = (firstdownmax*2 + seconddownmax*2)/4;
% draw two small plots of the stuff we've just been working with
subplot(4,2,5);
bar(uphisto(1:20));
hold on;
stem([upcmp firstupmax secondupmax],[6 7 7],'r-');
hold off;
subplot(4,2,7);
bar(downhisto(1:20));
hold on;
stem([downcmp firstdownmax seconddownmax],[6 7 7],'r-');
hold off;
% now we have our thresholds, we convert to a string representing
% wide and thin (upper and lower case) black and white bars (b and w)
barstring = '';
for i = (1:size(bartrace,1))
if(bartrace(i,1)>0)
% its white
bartrace(i,3)=upcmp;
if(bartrace(i,2)>upcmp)
barstring = strcat(barstring,'W');
else
barstring = strcat(barstring,'w');
end
else
% its black
bartrace(i,3)=downcmp;
if(bartrace(i,2)>downcmp)
barstring = strcat(barstring,'B');
else
barstring = strcat(barstring,'b');
end
end
end
%subplot(2,2,3);
%chart the results from the above
subplot(2,2,4);
if(size(bartrace,1))
plot(bartrace);
end
xlabel(barstring);
% There are a few properites of the barcode I didn't take advantage of
% such as the regular spacing of the white bars
% always a w between each number
%
% These are encodings of the numbers
% w w W w w w
barpats = [ 'BwbWbwbwB', %1 X..X....X 10001
'bwBWbwbwB', %2 ..XX....X 01001
'BwBWbwbwb', %3 X.XX..... 11000
'bwbWBwbwB', %4 ...XX...X 00100
'BwbWBwbwb', %5 X..XX.... 10100
'bwBWBwbwb', %6 ..XXX.... 01100
'bwbWbwBwB', %7 ...X..X.X 00011
'BwbWbwBwb', %8 X..X..X.. 10010
'bwBWbwBwb', %9 ..XX..X.. 01010
'bwbWBwBwb', %0 ...XX.X.. 00110
];
barchars = '1234567890';
% as there's 10 elements in each bar, as a first step
% we look for any matches and see at what offset they
% occur. Hopefully one offset will be much more
% popular than others. As so often happens, I thought
% I was being really clever, but it turns our that
% Code 39 designed so this doesn't really happen.
modulo = zeros(10,1);
for i = (1:length(barpats))
% searchstring = strcat('w',barpats(i),'w')
searchstring = barpats(i,:);
res = mod(strfind(barstring,searchstring),10);
for i2=(1:length(res))
modulo(res(i2)+1) = modulo(res(i2)+1)+1;
end
end
[cnt,maxindex]=max(modulo);
correctmodulo = maxindex-1;
% so now we have the correct modulo lets build a string
% its much the same loop as above except we check for the
% correct modulo, and slot the numbers into a string
% if it's all correct.
%thenumber = ones(1,floor(length(barstring)/10)) * '.';
thenumber = ones(1,30) * '.';
% This disabled hack corrected the width of some white bars
% alwaysbig = correctmodulo+3;
% if(alwaysbig>10)
% alwaysbig = alwaysbig - 10;
% end
% for i = ( mod(alwaysbig,2) :2:length(barstring))
% barstring(i)='w'; % fixing message??
% end
%
% for i = (alwaysbig:10:length(barstring))
% barstring(i)='W'; % fixing message??
% end
goodnumbers = 0;
% make the right string
for i = (1:length(barpats))
searchstring = barpats(i,:);
res = strfind(barstring,searchstring);
for i2=(1:length(res))
if mod(res(i2),10) == correctmodulo
thenumber(1,(res(i2)-correctmodulo)/10 +1) = barchars(i);
goodnumbers=goodnumbers+1;
end
end
end
%tidy up the number a little before printing
for i = (1:length(thenumber))
if(0 ~= thenumber(1))
break;
end
thenumber = thenumber(2:length(thenumber));
end
for i = (1:length(thenumber))
if(0 ~= thenumber(length(thenumber)))
break;
end
thenumber = thenumber (1:length(thenumber)-1);
end
for i = (1:length(thenumber))
if(0 == thenumber(i))
thenumber(1,i)=',';
end
end
thenumber = char(thenumber);
subplot(2,2,2);
xlabel(thenumber);
all_counts = [all_counts;goodnumbers];
all_results = [all_results;thenumber];
end
% plot( paddedtrace - filtered);
% could find bar frequency with this??
% plot(xcorr(trace),'-+');