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
// 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