Decoding Barcodes - PowerPoint PPT Presentation

About This Presentation
Title:

Decoding Barcodes

Description:

They encode numbers and symbols using black and white bars. ... Code39 (Sometimes called 3 from 9) barcodes use 9 bars to represent each symbol. ... – PowerPoint PPT presentation

Number of Views:257
Avg rating:3.0/5.0
Slides: 64
Provided by: ccGa
Category:

less

Transcript and Presenter's Notes

Title: Decoding Barcodes


1
Decoding Barcodes
Institute for Personal Robots in Education
(IPRE)?
2
  • Barcodes are designed to be machine readable
  • They encode numbers and symbols using black and
    white bars.
  • The examples on this page are standard 1D
    barcodes using the Code39 encoding scheme.
  • Usually read by laser scanners, they can also be
    read using a camera.

3
  • Code39 (Sometimes called 3 from 9) barcodes use 9
    bars to represent each symbol.
  • The bars can be black or white.
  • The bars are either narrow or wide.
  • Wide bars must be 2.1 to 3 times larger than
    narrow bars.
  • Each symbol pattern starts and ends with a black
    bar.
  • A valid barcode starts and ends with the STAR ()
    symbol, which is used as a delimiter.

The STAR () symbol is made up of a narrow black
bar, a wide white bar, a narrow black bar, a
narrow white bar, a wide black bar, a narrow
white bar, a wide black bar, a narrow white bar,
and a narrow black bar.
4
  • Code39 (Sometimes called 3 from 9) barcodes use 9
    bars to represent each symbol.
  • The bars can be black or white.
  • The bars are either narrow or wide.
  • Wide bars must be 2.1 to 3 times larger than
    narrow bars.
  • Each symbol pattern starts and ends with a black
    bar.
  • A valid barcode starts and ends with the STAR ()
    symbol, which is used as a delimiter.

This could also be represented as the string
bWbwBwBwb
5
  • How many bars is in a barcode that encodes 3
    symbols?
  • Although each symbol pattern starts and ends with
    a black bar, patterns must be separated by a
    white bar (typically narrow), so each symbol
    except the last is represented with 10 bars in
    total. (The last symbol has 9 bars, and does not
    need a separator after it.)?
  • Don't forget the Start and Stop symbol!

6
  • How many bars is in a barcode that encodes 3
    symbols?
  • Although each symbol pattern starts and ends with
    a black bar, patterns must be separated by a
    white bar (typically narrow), so each symbol
    except the last is represented with 10 bars in
    total. (The last symbol has 9 bars, and does not
    need a separator after it.)?
  • Don't forget the Start and Stop symbol!
  • 3 symbols start stop 5 symbols, at 9 bars
    each, plus 4 narrow white bars to separate the
    symbols is 9 5 4, or 105 1 to make 49 bars
    total!

7
All of the symbol patterns
What symbol is on the right?
8
It's an I
What symbol is on the right?
9
But what does a barcode look like from the robot?
  • The robot's camera has relatively low resolution
    (256x192 pixels).
  • To decode a barcode successfully from an image,
    we need multiple pixels for each bar . This means
    that we are limited in the size of barcodes we
    can successfully use.
  • Here is a picture of a two symbol (4 patterns
    total) barcode taken with a (VERY) carefully
    aimed robot camera

10
It's sort of messy!
  • High contrast elements (black and white lines)
    generate color artifacts due to the bayer filter
    layout in the camera.

11
Step 1 Lets clean it up!
  • Convert to black and white with a thresholding
    process!

12
Step 1 Lets clean it up!
  • Convert to black and white with a thresholding
    process!
  • For each pixel, check to see if it's brighter
    than a threshold (say, 127).
  • If yes, set the color to white!
  • If no, set the color to black!

13
Threshold Code
14
Threshold Code
def threshold(pic) for i in getPixels(pic)
g getGreen(i)? if( g setRed(i,0)? setGreen(i,0)?
setBlue(i,0)? else
setRed(i,255)? setGreen(i,255)?
setBlue(i,255)? return(pic)?
15
Threshold Code How to improve it!
  • Note that we are using the green value as a proxy
    for the brightness (or luminance) of the pixel.
  • To do this correctly, we should calculate the
    luminance of the pixel with the following
    formulaY 0.2126 Red 0.7152 Green
    0.0722 Blue
  • Notice how the Green component makes up 70 of
    the Luminance (Y) value?
  • That is why it's almost OK to cheat and just use
    the green channel!

16
Now what?
  • We have a thresholded image, now we have to scan
    across it to look for bars.
  • Lets start out with a simpler task, just scan
    across it and save a list of the pixel values
    (white255 or black0) in a list.
  • But where do we scan?

17
Now what?
  • We have a thresholded image, now we have to scan
    across it to look for bars.
  • Lets start out with a simpler task, just scan
    across it and save a list of the pixel values
    (white255 or black0) in a list.
  • But where do we scan?
  • How about the middle?
  • How do you find the middle of the image?

18
Our Image
X255
X0
Width 256
Y0
Height 192
Y191
19
Our Image Middle
X255
X0
Width 256
Y0
Middle Height / 2
Height 192
Y191
20
Code to save pixel values along a horizontal
scanline
def makeScanLine(bwPic)
return(values)?
21
Code to save pixel values along a horizontal
scanline
def makeScanLine(bwPic) height
getHeight(bwPic)? mid height / 2 width
getWidth(bwPic)? values for
x in range(0, width) pix
getPixel(bwPic, x, mid)? val
getGreen(pix)? values.append(val)?
return(values)?
22
Example Scanline Data
0, 0, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0,
255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255,
255, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255,
255, 255, 255, 255, 255, 255, 0, 0, 255, 255,
255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0,
0, 0, 0, 0, 0, 0, 255, 255, 0, 255, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255,
255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0,
0, 0, 255, 255, 255, 255, 0, 0, 255, 255, 255,
255, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 255,
255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0,
255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255,
255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 255, 255,
255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255
Note the large runs of white at the beginning and
end of the barcode!
23
How to improve our data?
  • Scanline data presents the raw pixel data, but
    it's not very easy to understand.
  • Lets scan for runs of pixels of the same color.
  • Convert this0,0, 255,255,255,255,255, 0,0,
    255,255, 0,0,0,0, 255,255, 0,0,0,0, 255,255,
    0,0,0,0

24
How to improve our data?
  • Scanline data presents the raw pixel data, but
    it's not very easy to understand.
  • Lets scan for runs of pixels of the same color.
  • Convert this0,0, 255,255,255,255,255, 0,0,
    255,255, 0,0,0,0, 255,255, 0,0,0,0, 255,255,
    0,0,0,0to this (2,0), (5,255), (2,0),
    (2,255), (4,0), (2,255), (4,0), (2,255),
    (2,0)

25
How to improve our data?
  • Scanline data presents the raw pixel data, but
    it's not very easy to understand.
  • Lets scan for runs of pixels of the same color.
  • Convert this0,0, 255,255,255,255,255, 0,0,
    255,255, 0,0,0,0, 255,255, 0,0,0,0, 255,255,
    0,0,0,0to this (2,0), (5,255), (2,0),
    (2,255), (4,0), (2,255), (4,0), (2,255),
    (2,0)Which could be read asbWbwBwBwb

26
Code to spot runs of the same color
def parseScanline(scanLine)
return(barData)?
27
Code to spot runs of the same color
def parseScanline(scanLine) barData
previous scanLine0 length 0
for element in scanLine if
(element ! previous) a change has occured!
myTuple (length, previous)?
barData.append( myTuple ) add run info
to barData list length 1
previous element else No
change. length length 1
28
Don't forget to record the last run!
def parseScanline(scanLine) barData
previous scanLine0 length 0
for element in scanLine if
(element ! previous) a change has occured!
myTuple (length, previous)?
barData.append( myTuple ) add run info
to barData list length 1
previous element else No
change. length length 1
Rescue the last bit of data stored in the
previous and length variables! myTuple
(length, previous)? barData.append( myTuple
)? return(barData)?
29
Some real data!
  • Actual scanline data (2, 0), (26, 255), (3,
    0), (8, 255), (3, 0), (3, 255), (8, 0), (4,
    255), (8, 0), (4, 255), (2, 0), (4, 255), (8, 0),
    (4, 255),(4, 0), (8, 255), (2, 0), (4, 255), (4,
    0), (4, 255), (8, 0), (2, 255), (1, 0), (1,
    255), (6, 0), (4, 255), (4, 0), (8, 255), (8,
    0), (4, 255), (2, 0), (4, 255), (4, 0), (4, 255),
    (2, 0), (8, 255), (4, 0), (4, 255), (8, 0), (4,
    255), (7, 0), (3, 255), (4, 0), (39, 255)
  • Can you spot the narrow and wide bars?

30
Some real data!
  • Actual scanline data (2, 0), (26, 255), (3,
    0), (8, 255), (3, 0), (3, 255), (8, 0), (4,
    255), (8, 0), (4, 255), (2, 0), (4, 255), (8, 0),
    (4, 255),(4, 0), (8, 255), (2, 0), (4, 255), (4,
    0), (4, 255), (8, 0), (2, 255), (1, 0), (1,
    255), (6, 0), (4, 255), (4, 0), (8, 255), (8,
    0), (4, 255), (2, 0), (4, 255), (4, 0), (4, 255),
    (2, 0), (8, 255), (4, 0), (4, 255), (8, 0), (4,
    255), (7, 0), (3, 255), (4, 0), (39, 255)
  • Narrow bars look to be around 3-4 pixels in size,
    and wide bars appear to be around 6-8 pixels in
    size!

31
Some real data! With real-world problems!
  • Wait! What's that black bar doing at the front of
    our image (2,0) before all that white space
    (26,255)?
  • Actual scanline data (2, 0), (26, 255), (3,
    0), (8, 255), (3, 0), (3, 255), (8, 0), (4,
    255), (8, 0), (4, 255), (2, 0), (4, 255), (8, 0),
    (4, 255),(4, 0), (8, 255), (2, 0), (4, 255), (4,
    0), (4, 255), (8, 0), (2, 255), (1, 0), (1,
    255), (6, 0), (4, 255), (4, 0), (8, 255), (8,
    0), (4, 255), (2, 0), (4, 255), (4, 0), (4, 255),
    (2, 0), (8, 255), (4, 0), (4, 255), (8, 0), (4,
    255), (7, 0), (3, 255), (4, 0), (39, 255)

32
Some real data!
  • Wait! What's that black bar doing at the front of
    our image (2,0) before all that white space
    (26,255)?
  • Actual scanline data (2, 0), (26, 255), (3,
    0), (8, 255), (3, 0), (3, 255), (8, 0), (4,
    255), (8, 0), (4, 255), (2, 0), (4, 255), (8, 0),
    (4, 255),(4, 0), (8, 255), (2, 0), (4, 255), (4,
    0), (4, 255), (8, 0), (2, 255), (1, 0), (1,
    255), (6, 0), (4, 255), (4, 0), (8, 255), (8,
    0), (4, 255), (2, 0), (4, 255), (4, 0), (4, 255),
    (2, 0), (8, 255), (4, 0), (4, 255), (8, 0), (4,
    255), (7, 0), (3, 255), (4, 0), (39, 255)

Zoom in
33
Some real data!
  • The robot's camera has a bug! It produces two
    columns of black pixels on the left of every
    image!
  • But no problems! We'll just make sure that our
    barcode parsing code can handle random bars
    before the barcode officially starts!

Zoom in
34
Another problem!
  • Wait! What are those single pixel black and white
    bars doing in the middle of our image?
  • Actual scanline data (2, 0), (26, 255), (3,
    0), (8, 255), (3, 0), (3, 255), (8, 0), (4,
    255), (8, 0), (4, 255), (2, 0), (4, 255), (8, 0),
    (4, 255),(4, 0), (8, 255), (2, 0), (4, 255), (4,
    0), (4, 255), (8, 0), (2, 255), (1, 0), (1,
    255), (6, 0), (4, 255), (4, 0), (8, 255), (8,
    0), (4, 255), (2, 0), (4, 255), (4, 0), (4, 255),
    (2, 0), (8, 255), (4, 0), (4, 255), (8, 0), (4,
    255), (7, 0), (3, 255), (4, 0), (39, 255)

35
Another problem!
  • Wait! What are those single pixel black and white
    bars doing in the middle of our image?
  • Actual scanline data (2, 0), (26, 255), (3,
    0), (8, 255), (3, 0), (3, 255), (8, 0), (4,
    255), (8, 0), (4, 255), (2, 0), (4, 255), (8, 0),
    (4, 255),(4, 0), (8, 255), (2, 0), (4, 255), (4,
    0), (4, 255), (8, 0), (2, 255), (1, 0), (1,
    255), (6, 0), (4, 255), (4, 0), (8, 255), (8,
    0), (4, 255), (2, 0), (4, 255), (4, 0), (4, 255),
    (2, 0), (8, 255), (4, 0), (4, 255), (8, 0), (4,
    255), (7, 0), (3, 255), (4, 0), (39, 255)

Zoom in
36
Another problem!
  • We need to remove those single pixel errors!

Zoom in
37
Code to remove single pixel errors
  • Remove single pixel errors!
  • Example data (4, 255), (8, 0), (2, 255), (1,
    0), (1, 255), (6, 0), (4, 255)
  • We want (4, 255), (8, 0), (2, 255), (6, 0),
    (4, 255)

38
Code to remove single pixel errors
def removeSingles(barData) return(newBa
rData)?
39
Code to remove single pixel errors
def removeSingles(barData) newBarData for
item in barData length item0 if (length
! 1) newBarData.append(item)? return(newBa
rData)?
40
Good data, but how to find Wide and Narrow bars?
  • (2, 0), (26, 255), (3, 0), (8, 255), (3, 0), (3,
    255), (8, 0), (4, 255), (8, 0), (4, 255),
  • (2, 0), (4, 255), (8, 0), (4, 255), (4, 0), (8,
    255), (2, 0), (4, 255), (4, 0), (4, 255),
  • (8, 0), (2, 255), (6, 0), (4, 255), (4, 0), (8,
    255), (8, 0), (4, 255), (2, 0), (4, 255),
  • (4, 0), (4, 255), (2, 0), (8, 255), (4, 0), (4,
    255), (8, 0), (4, 255), (7, 0), (3, 255),
  • (4, 0), (255, 39)
  • How do we pick the threshold that separates wide
    from narrow bars?
  • Look at just the widths
  • 2, 26, 3, 8, 3, 3, 8, 4, 8, 4, 2, 4, 8, 4, 4, 8,
    2, 4, 4, 4, 8, 2, 6, 4, 4, 8, 8, 4, 2, 4, 4, 4,
    2, 8, 4, 4, 8, 4, 7, 3, 4, 39

41
Good data, but how to find Wide and Narrow bars?
  • SORT the widths
  • 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 26, 39
  • Eyeball it! What would make a good threshold?

42
Good data, but how to find Wide and Narrow bars?
  • SORT the widths
  • 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 26, 39
  • Eyeball it! A 5 or 6 would make a good
    threshold! But how does the computer figure that
    out?

43
Good data, but how to find Wide and Narrow bars?
  • SORT the widths
  • 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 26, 39
  • What is the median width?
  • 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
    4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7, 8, 8,
    8, 8, 8, 8, 8, 8, 8, 8, 26, 39

44
Good data, but how to find Wide and Narrow bars?
What is the median width? 2, 2, 2, 2, 2, 2, 3,
3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8,
8, 26, 39 How about ¾ of the way up the
list? 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 26, 39
45
Good data, but how to find Wide and Narrow bars?
Pick a threshold halfway between the median (4
narrow bar size) and the ¾ point ( 8 wide bar
size) 8 4 2 ( 2 bigger than median value
is 6!)? 4 2 6 Anything 6 pixels or larger
is a wide bar!
46
Code to find the width threshold
def calculateWidthThreshold(barData)
return( threshold )?
47
Code to find the width threshold
def calculateWidthThreshold(barData) Load
just the widths! barWidths for x in
barData barWidths.append(x0)?
barWidths.sort()? Find the size of a
narrow bar! medianIdx len(barWidths) / 2
narrowSize barWidthsmedianIdx Go to the
3/4 point, find the size of a wide bar!
wideIdx medianIdx (medianIdx / 2)?
wideSize barWidthswideIdx Calculate the
threshold dist (wideSize narrowSize) / 2
threshold narrowSize dist return(
threshold )?
48
Decoding the bars!
  • Now that we know the width threshold, we can
    convert our barData into a string representing
    the barcode! (made up of the letters b,B,w,W)?
  • For examplebarData (4, 255), (8, 0), (7,
    255), (3, 0) should produce a string like
    thiswBWb(narrow white, wide Black, white
    White, narrow black)?

49
Decoding the bars!
def decodeBars(barData,widthThreshold)
return(barString)?
50
Decoding the bars!
def decodeBars(barData,widthThreshold)
barString "" for bar in barData
if(bar1 255) It's a white bar!
if(bar0 widthThreshold) It's a wide
white bar! barString barString
"W" else It's a narrow
white bar barString barString
"w" else It's a black bar!
if(bar0 widthThreshold) It's a wide
black bar! barString barString
"B" else it's a narrow
black bar! barString barString
"b" return(barString)?
51
Parsing the barcode string
  • Actual barString bWbWbwBwBwbwBwbWbwbwBwBwbWBwbwb
    wbWbwBwBwbW
  • Now we just have to parse this string to find our
    barcode!
  • All (valid) barcodes start with the pattern
    bWbwBwBwb or the symbol
  • Lets go looking for it!

52
Parsing the barcode string
  • There it is! bWbWbwBwBwbwBwbWbwbwBwBwbWBwbwbwbWbw
    BwBwbW

53
Parsing the barcode string
  • Now, a white bar will separate the start symbol
    from the first data symbol! bWbWbwBwBwbwBwbWbwbwB
    wBwbWBwbwbwbWbwBwBwbW

54
Parsing the barcode string
  • The second symbol is 9 bars long, and is also
    followed by a white bar! BwbWbwBwBwbwBwbWbwbwBwBw
    bWBwbwbwbWbwBwBwbW
  • So if we look up BwbWbwbwB we can figure out
    what our first symbol is!

55
All of the symbol patterns
How do we get all those symbols into our code to
do the lookup?
56
All of the symbol patterns
Luckily I've already typed the codes in for you,
look on the website for the code39dict.py file!
Use a dictionary!code39dict
'BwbWbwbwB' "1", 'bwBWbwbwB' "2",
'BwBWbwbwb' "3", 'bwbWBwbwB' "4",
'BwbWBwbwb' "5", ... 'BwbwbWbwB' "A",
'bwBwbWbwB' "B", 'BwBwbWbwb' "C",
... 'bWbwBwBwb' "", Start/Stop character

57
Parsing the barcode string
  • code39dict
  • 'BwbWbwbwB' "1",
  • answer code39dictBwbWbwbwB
  • print answer
  • 1
  • Our first symbol is a 1!

58
Parsing the barcode string
  • Each symbol is 9 bars long, and separated by a
    white bar! ...wBwbWbwbwBwBwbWBwbwbwbWbwBwBwbW
  • Our second symbol is a 5

59
Parsing the barcode string
  • The second symbol is 9 bars long, and is also
    followed by a white bar! ...BwbWBwbwbwbWbwBwBwbW
  • And our last symbol should look familiar, because
    it is the or Start/Stop symbol, and is the
    same as our first symbol!
  • Note that the large white area after the barcode
    is represented by a single W.
  • All together, our barcode reads 15
  • We don't report the 's, so our number is 15

60
Code to find the start symbol!
def findCode39(barString)
61
Code to find the start symbol!
def findCode39(barString) Search for a start
code! startLoc barString.find("bWbwBwBwb")?
if(startLoc -1) No start character
found return(None)? Beginning of first data
symbol... each code is 9 bars long, plus one
bar to separate them! startLoc startLoc 10
Initialize a variable to store our
code codeData
62
Code to read each symbol!
while( startLoc barStringstartLocstartLoc9 letter
code39dict.get(code,-1)? if (letter -1)
Invalid code! return(None)? else
Valid code! if(letter '') Found
end of barcode return(codeData) Return
the data! else
Add letter to our codeData codeData
codeData letter We advance by 10 to the
next code symbol startLoc startLoc
10 did not find a stop code! Abort!
return(None)?
63
Barcodes Not that hard after all!
  • Questions?
Write a Comment
User Comments (0)
About PowerShow.com