% 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),'-+');