#Example code that scans pictures for code39 barcodes. # #Jay Summet # CS 1301 # Released to the public domain Oct 24th 2008 # #(This is example code to accompany a lecture, Look for a barcode.py # module that is greatly improved if you want to actually use the code.) from myro import * #Dictionary containing the wide/narrow and white/black bar code to code39 #character mappings. Captial letters indicate wide bars, lowercase letters #indicate narrow bars, w/W = White and b/B = Black. code39dict = { 'BwbWbwbwB': "1", 'bwBWbwbwB': "2", 'BwBWbwbwb': "3", 'bwbWBwbwB': "4", 'BwbWBwbwb': "5", 'bwBWBwbwb': "6", 'bwbWbwBwB': "7", 'BwbWbwBwb': "8", 'bwBWbwBwb': "9", 'bwbWBwBwb': "0", 'BwbwbWbwB': "A", 'bwBwbWbwB': "B", 'BwBwbWbwb': "C", 'bwbwBWbwB': "D", 'BwbwBWbwb': "E", 'bwBwBWbwb': "F", 'bwbwbWBwB': "G", 'BwbwbWBwb': "H", 'bwBwbWBwb': "I", 'bwbwBWBwb': "J", 'BwbwbwbWB': "K", 'bwBwbwbWB': "L", 'BwBwbwbWb': "M", 'bwbwBwbWB': "N", 'BwbwBwbWb': "O", 'bwBwBwbWb': "P", 'bwbwbwBWB': "Q", 'BwbwbwBWb': "R", 'bwBwbwBWb': "S", 'bwbwBwBWb': "T", 'BWbwbwbwB': "U", 'bWBwbwbwB': "V", 'BWBwbwbwb': "W", 'bWbwBwbwB': "X", 'BWbwBwbwb': "Y", 'bWBwBwbwb': "Z", 'bWbwbwBwB': "-", 'BWbwbwBwb': ".", 'bWBwbwBwb': " ", #space 'bWbwBwBwb': "*", #Start/Stop character 'bWbWbWbwb': "$", 'bWbWbwbWb': "/", 'bWbwbWbWb': "+", 'bwbWbWbWb': "%", } # Threshold a picture and return a picture that is # made up of black and white pixels only! # def threshold(pic): for i in getPixels(pic): g = getGreen(i) if( g < 127): setRed(i,0) setGreen(i,0) setBlue(i,0) else: setRed(i,255) setGreen(i,255) setBlue(i,255) return(pic) #Given a picture, scan the midline and read out the white/black pattern! def makeScanLine(pic): midline = getHeight(pic) / 2 show(pic,"threshold") values = [] for x in range(0, getWidth(pic)): pix = getPixel(pic,x,midline) values = values + [ getGreen(pix)] #Draw a red line over the midline setRed(pix,255) setGreen(pix,0) setBlue(pix,0) return(values) #Measures the width of the white and black bars, and saves that data. def parseScanline(scanLine): barData = [] barColor = scanLine[0] barLength = 1 i = 1 while (i < len(scanLine)): if (barColor == scanLine[i]): #Bar is still going! barLength = barLength + 1 else: #A new bar has started! #Save the old bar data! myTuple = (barLength,barColor) barData.append(myTuple) #Start the new bar! barLength = 1 barColor = scanLine[i] i= i + 1 #Don't forget to record the last bar we were working on when the #while loop ended! myTuple = (barLength,barColor) barData.append(myTuple) return(barData) #Removes bars that are a single pixel wide! These are errors! def removeSingles(barData): newBarData = [] for bar in barData: if (bar[0] != 1): newBarData.append(bar) return(newBarData) #Makes the decision of what is a narrow or wide bar by sorting the #sizes of the bars and looking for the difference between the narrow and #wide bars. Note that we have more narrow bars than wide bars! #So we find the middle (median) value and call that a narrow bar, then find # the value that is 3/4 of the way up the list as the wide size. #We set the threshold to be halfway between the narrow and wide sizes. def calculateWidthThreshold(barData): #Load just the widths! barWidths = [] for x in barData: barWidths.append(x[0]) barWidths.sort() #Find the size of a narrow bar! medianIdx = len(barWidths) / 2 narrowSize= barWidths[medianIdx] #Go to the 3/4 point to find the size of a wide bar! wideIdx = medianIdx + (medianIdx / 2) wideSize = barWidths[wideIdx] print "narrowSize is:", narrowSize print "wideSize is:", wideSize #Calculate the threshold as 1/2 way between the wide and narrow sizes! threshold = narrowSize + ( (wideSize - narrowSize) / 2) print "threshold is:", threshold return( threshold ) #Changes the barData into actual narrow or wide white or black bars! def decodeBars(barData,widthThreshold): #Now, we decode the bars! barString = "" for bar in barData: if(bar[1] == 255): #It's a white bar! if(bar[0] >= widthThreshold): barString = barString + "W" #It's a wide white bar! else: barString = barString + "w" #It's a narrow white bar else: #It's a black bar! if(bar[0] >= widthThreshold): barString = barString + "B" #It's a wide black bar! else: barString = barString + "b" #it's a narrow black bar! return(barString) #Given a sequence of wide and narrow black and white bars (represented #with the capital and small letters W and B), this function will return #the barcode data (if it exists) or None if it does not find a start and #stop code with valid data between it. def findCode39(barString): codeData = "" #Search for a start code! startLoc = barString.find("bWbwBwBwb") if(startLoc == -1): #No start character found! return(None) startLoc = startLoc + 10 #each code is 9 bars long, #plus one bar to separate them! #Cycle through the codes until we hit an invalid code or #the start/stop code again! while( startLoc < len(barString)): code = barString[startLoc:startLoc+9] letter = code39dict.get(code,-1) print "found symbol for:", letter if (letter == -1): #Invalid code! return(None) else: #Valid code! if(letter == '*'): #Found end of barcode! return(codeData) #Return the data else: #Add it to our codeData codeData = codeData + letter #We advance by 10 because each set of 9 bars for a #character is separated by a white bar (usually small) #We are currently not checking to verify that it is white # startLoc = startLoc + 10 #We got to the end of the barString without finding a stop code! #Abort! return(None) p = loadPicture("barcode_color.gif") show(p,"original") bwPic = threshold(p) show(bwPic,"threshold") scanline = makeScanLine(bwPic) show(bwPic,"scanline") barData = parseScanline(scanline) barData = removeSingles(barData) print "barData is:", barData widthThreshold = calculateWidthThreshold(barData) barString = decodeBars(barData,widthThreshold) print "barString is:", barString codeData = findCode39(barString) print "barcode is:", codeData