
Bar Code reader (code 39)

KurtB
// BarCode reader // Written by: // KJ Baldwin, UK. 2013 // Inputs: // wImage a 2D image Wave // Function: // If it can find and read bar code, this is returned as a string and added to the note of the image wave. // Notes: // Bar Code must be of type Code 39 // Bar code must appear within image horizontally (roughly). // Image should not have anything else that looks too much like a bar code in it - the algorithm here homes in on // the portion of the image that most looks like a bar code. Function/S BarCode39GetBarCodeFromImage(wImage) Wave wImage // locates the bar code in image, extracts line profile // calls read line function and put code (if found) in Image Wave note string sBarCode="" string sOldDF=GetDataFolder(1) NewDataFolder/O root:Packages NewDataFolder/O/S root:Packages:BarCode39 do if(WaveExists(wImage)==0) break endif string sNote=note(wImage) sNote=ReplaceStringByKey("BarCode",sNote,sBarCode) Note/K wImage,sNote Variable V_fitOptions=4 // find rows with signal within high-ish freq range variable vNumX=DimSize(wImage,0) variable vNumY=DimSize(wImage,1) variable vX,vY,vLocal,vLevel,vSpacing Make/O/N=(vNumY) wIntFreqY wIntFreqY[]=0 Make/O/N=(vNumX) wLine for(vY=vNumY-1;vY>=0;vY-=1) wLine[]=wImage[p][vY] FFT/OUT=4/DEST=wLine_FFT wLine vLocal=mean(wLine_FFT,0.1,0.3)-mean(wLine_FFT,0.3,0.5) wIntFreqY[vY]=vLocal endfor // vY Duplicate/O wIntFreqY,wIntFreqY_smth Smooth/M=0 9, wIntFreqY_smth DebuggerOptions variable doDebuggerOnError = V_debugOnError DebuggerOptions debugOnError=0 variable vErr try CurveFit/Q/NTHR=0 gauss wIntFreqY_smth;AbortOnRTE catch vErr=GetRTError(1) // get and clear the error break endtry DebuggerOptions debugOnError=doDebuggerOnError wave W_coef variable vCentre=round(W_coef[2]) variable vWidth=round(W_coef[3]) if((vCentre<0)||(vCentre>vNumY)) // out of range break endif // find x-loc for barcode - extract a thin line wLine[]=0 variable vWY=5 for(vY=vCentre-vWY;vY<=vCentre+vWY;vY+=1) wLine[]+=wImage[p][vY] endfor // look for x pos FFT/OUT=1/DEST=wLine_FFT wLine wLine_FFT[x2pnt(wLine_FFT,0.3),DimSize(wLine_FFT,0)-1][]=0 wLine_FFT[0,x2pnt(wLine_FFT,0.1)][]=0 IFFT/DEST=wLine_FFT_IFFT wLine_FFT Make/O/N=(vNumX) wLine2 wLine2[]=wLine_FFT_IFFT[p] variable vSegX=32 Make/O/N=(vNumX) wXSegInt wXSegInt[]=0 Make/O/N=(vSegX) wXSeg for(vX=vSegX/2;vX<vNumX-vSegX/2-1;vX+=1) wXSeg[]=wLine_FFT_IFFT[vX-vSegX/2+p] FFT/OUT=4/DEST=wXSeg_FFT wXSeg vLocal=mean(wXSeg_FFT,0.1,0.3)-mean(wXSeg_FFT,0.3,0.5) wXSegInt[vX]=vLocal endfor DebuggerOptions debugOnError=0 try CurveFit/Q/NTHR=0 gauss wXSegInt;AbortOnRTE catch vErr=GetRTError(1) // get and clear the error break endtry DebuggerOptions debugOnError=doDebuggerOnError variable vCentreX=round(W_coef[2]) variable vWidthX=round(W_coef[3]) if((vCentreX<0)||(vCentreX>vNumX)) // out of range break endif // reset extracted line for(vY=vCentre-vWY;vY<=vCentre+vWY;vY+=1) wLine[]+=wImage[p][vY] endfor // find level for edges of bars vSegX=floor(vWidthX/2) Make/O/N=(vSegX) wXSeg wXSeg[]=wLine[vCentreX-floor(vSegX/2)+p] vLevel=mean(wXSeg) // look for X-limits // first determine mean spacing between roots in wXSeg wXSeg[]=wXSeg[p]-vLevel vLocal=0 // count for(vX=1;vX<vSegX;vX+=1) if(sign(wXSeg[vX-1])!=sign(wXSeg[vX])) vLocal+=1 endif endfor vSpacing=vSegX/vLocal // mean spacing // now for whole line wLine[]=wLine[p]-vLevel variable vOldXPos,vLowX=-1,vHighX=-1 variable vXLim=4 // multiplier for indication of end of bar // walk down from centre - set low limit vX=vCentreX vOldXPos=vCentreX do if(vX<1) break endif if(abs(vX-vOldXPos)>(vXLim*vSpacing)) vLowX=vX break endif if(sign(wLine[vX-1])!=sign(wLine[vX])) vOldXPos=vX endif vX-=1 while(1) // walk up from centre - set high limit vX=vCentreX vOldXPos=vCentreX do if(vX>vNumX-2) break endif if(abs(vX-vOldXPos)>(vXLim*vSpacing)) vHighX=vX break endif if(sign(wLine[vX+1])!=sign(wLine[vX])) vOldXPos=vX endif vX+=1 while(1) if((vLowX==-1)||(vHighX==-1)) break endif ReDimension/N=(vHighX-vLowX+1) wXSeg wXSeg[]=wLine[vLowX+p] sBarCode=BarCode39GetBarCodeFromLine(wXSeg) sNote=note(wImage) sNote=ReplaceStringByKey("BarCode",sNote,sBarCode) Note/K wImage,sNote break while(1) SetDataFolder $sOldDF KillDataFolder/Z root:Packages:BarCode39 return sBarCode End // Function/S BarCode39GetBarCodeFromLine(wLine) wave wLine // returns bar code (if it can) from line segment // line segment must be zero-crossing at black-white transitions // with black negative and white positive. string sOut="" do variable vNum=DimSize(wLine,0) if(vNum<10) break endif Make/O/N=(vNum) wSpacing wSpacing[]=NaN variable vSpCount=0 // postion in wSpacing variable i,vLocal variable vOldX=-1 for(i=1;i<vNum;i+=1) if(sign(wLine[i])!=sign(wLine[i-1])) // have transition vLocal=(i-1)+abs(wLine[i-1])/abs(wLine[i]-wLine[i-1]) if(vOldX==-1) // not been set yet vOldX=vLocal else // has been set, so assign to wSpacing wSpacing[vSpCount]=vLocal-vOldX vOldX=vLocal vSpCount+=1 endif endif endfor if(mod(vSpCount,10)!=9) // must have eg 79, 89, etc transitions for valid code break endif if(vSpCount<19) // must have at least two items break endif ReDimension/N=(vSpCount) wSpacing // threshold spacing Make/N=10/FREE/O wSpacing_Hist // histogram, 10 wells Histogram/C/R=[1,88]/B=1 wSpacing,wSpacing_Hist // not use first point as this is 0, and would skew the data WaveStats/Q wSpacing_Hist if(V_min!=0) // must have a zero well to be valid break endif variable vThreshold=-1 for(i=0;i<4;i+=1) if(wSpacing_Hist[5+i]==0) vThreshold=pnt2x(wSpacing_Hist,5+i) break endif if(wSpacing_Hist[4-i]==0) vThreshold=pnt2x(wSpacing_Hist,4-i) break endif endfor if(vThreshold==-1) break endif wSpacing[]=(wSpacing[p]>vThreshold) ? 1 : 0 // try to extract code (forwards) string sBinary,sItem,sCode="" variable vIsValid=1 vNum=(vSpCount+1)/10 // number of elements for(i=0;i<vNum;i+=1) sBinary="" for(vLocal=0;vLocal<9;vLocal+=1) sBinary+=num2str(wSpacing[i*10+vLocal]) endfor sItem=BarCode39GetCharFromBinaryBar39(sBinary) if(strLen(sItem)==0) // invalid vIsValid=0 break endif sCode+=sItem endfor if(strLen(sCode)<2) // must have at least 2 chars vIsValid=0 else if(cmpStr(sCode[0],"*")!=0) // not start with * vIsValid=0 endif if(cmpStr(sCode[strLen(sCode)-1],"*")!=0) // not end with * vIsValid=0 endif endif if(vIsValid==0) // try backwards vIsValid=1 sCode="" for(i=vNum;i>0;i-=1) sBinary="" for(vLocal=0;vLocal<9;vLocal+=1) sBinary+=num2str(wSpacing[i*10-vLocal-2]) endfor sItem=BarCode39GetCharFromBinaryBar39(sBinary) if(strLen(sItem)==0) // invalid vIsValid=0 break endif sCode+=sItem endfor if(strLen(sCode)<2) // must have at least 2 chars vIsValid=0 else if(cmpStr(sCode[0],"*")!=0) // not start with * vIsValid=0 endif if(cmpStr(sCode[strLen(sCode)-1],"*")!=0) // not end with * vIsValid=0 endif endif endif if(vIsValid==0) break endif sOut=sCode[1,strLen(sCode)-2] break while(1) return sOut End // Function/S BarCode39GetCharFromBinaryBar39(sCode) string sCode // simple lookup for character codes string sChar="" strSwitch(sCode) case "010010100": sChar="*" break case "000110100": sChar="0" break case "100100001": sChar="1" break case "001100001": sChar="2" break case "101100000": sChar="3" break case "000110001": sChar="4" break case "100110000": sChar="5" break case "001110000": sChar="6" break case "000100101": sChar="7" break case "100100100": sChar="8" break case "001100100": sChar="9" break case "100001001": sChar="A" break case "001001001": sChar="B" break case "101001000": sChar="C" break case "000011001": sChar="D" break case "100011000": sChar="E" break case "001011000": sChar="F" break case "000001101": sChar="G" break case "100001100": sChar="H" break case "001001100": sChar="I" break case "000011100": sChar="J" break case "100000011": sChar="K" break case "001000011": sChar="L" break case "101000010": sChar="M" break case "000010011": sChar="N" break case "100010010": sChar="O" break case "001010010": sChar="P" break case "000000111": sChar="Q" break case "100000110": sChar="R" break case "001000110": sChar="S" break case "000010110": sChar="T" break case "110000001": sChar="U" break case "011000001": sChar="V" break case "111000000": sChar="W" break case "010010001": sChar="X" break case "110010000": sChar="Y" break case "011010000": sChar="Z" break case "010000101": sChar="-" break case "110000100": sChar="." break case "011000100": sChar=" " break case "010101000": sChar="$" break case "010100010": sChar="/" break case "010001010": sChar="+" break case "000101010": sChar="%" break endSwitch return sChar End


Forum

Support

Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More