% 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 imagesubplot(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 endbarsend = 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 partbartrace = linemarks(barsstart:barsend,:);% use the average of the two peaks for comparisons% the 2/4 numbers are here because I was fiddling proportionsupcmp = (firstupmax*2 + secondupmax*2)/4; downcmp = (firstdownmax*2 + seconddownmax*2)/4;% draw two small plots of the stuff we've just been working withsubplot(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 whitebartrace(i,3)=upcmp; if(bartrace(i,2)>upcmp) barstring = strcat(barstring,'W'); else barstring = strcat(barstring,'w'); end else% its blackbartrace(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 abovesubplot(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 wbarpats = [ '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??% endgoodnumbers = 0;% make the right stringfor 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 printingfor 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),'-+');