Decoding images of seven-segment display devices

Sometimes electronic instruments display nicely a measurement we'd like to process or store, but have no external output of the display. If so, one way round it is to aim a webcam at the display in a controlled-light environment & process the image.

Typical image as seen

Solution??

I wrote code to do this in Liberty BASIC. It looks in the middle of each segment at ONE pixel, determines if it is off or on, and from the 7 segments found to be on decides on the digit. NB I like continental 'crossed sevens'.

It would be better to take a bigger sample area and average, and even better to do auto-contrast enhancement. All do-able, but it works well enough for me.



'   ****************************************
'   **                                    **
'   **           SevenSegment6.bas        **
'   **                                    **
'   **  Jan 2013      tenochtitlanuk      **
'   ****************************************

nomainwin

global h1DC

WindowWidth  = 650
WindowHeight = 360

graphicbox #w.gb1, 10, 10, 620, 106
textbox    #w.tb1, 10,150, 620,  30
textbox    #w.tb2, 10,190, 620,  60

open "Decode Seven Segment Display" for window as #w

#w "trapclose [quit]"
#w.gb1  "down"
loadbmp "dvm", "7Seg5.bmp"
#w.gb1  "drawbmp dvm 0 0 ; flush ; color red ; size 4"
#w.tb1 "!font courier_new bold 7"
#w.tb2 "!font courier_new bold 24"
#w.tb2 " Displaying 7-segment image"

handlegb1  =hwnd( #w.gb1)
calldll #user32, "GetDC", handlegb1 as ulong, h1DC as ulong

horizSepn  =62.55
display$   =""
greyLevel  =140
SevenSeg$  =""

timer 5000, [onward]
wait
[onward]
timer 0

for xPos =0 to 9   '   find value at this posn as integer 0 -->9
    getValue =0: state$ =""
    if getPixelGreyVal( horizSepn *xPos +32, 13) <greyLevel then state$ ="0" else state$ ="1"
        #w.gb1  "set "; horizSepn *xPos +32; " "; 13
    if getPixelGreyVal( horizSepn *xPos +47, 28) <greyLevel then state$ =state$ +"0" else state$ =state$ +"1"
        #w.gb1  "set "; horizSepn *xPos +47; " "; 28
    if getPixelGreyVal( horizSepn *xPos +42, 74) <greyLevel then state$ =state$ +"0" else state$ =state$ +"1"
        #w.gb1  "set "; horizSepn *xPos +42; " "; 74
    if getPixelGreyVal( horizSepn *xPos +23, 92) <greyLevel then state$ =state$ +"0" else state$ =state$ +"1"
        #w.gb1  "set "; horizSepn *xPos +23; " "; 92
    if getPixelGreyVal( horizSepn *xPos + 8, 72) <greyLevel then state$ =state$ +"0" else state$ =state$ +"1"
        #w.gb1  "set "; horizSepn *xPos + 8; " "; 72
    if getPixelGreyVal( horizSepn *xPos +14, 30) <greyLevel then state$ =state$ +"0" else state$ =state$ +"1"
        #w.gb1  "set "; horizSepn *xPos +14; " "; 30
    if getPixelGreyVal( horizSepn *xPos +27, 51) <greyLevel then state$ =state$ +"0" else state$ =state$ +"1"
        #w.gb1  "set "; horizSepn *xPos +27; " "; 51

    select case state$
        case "1111110"
            it =0
        case "0110000"
            it =1
        case "1101101"
            it =2
        case "1111001"
            it =3
        case "0110011"
            it =4
        case "1011011"
            it =5
        case "1011111"
            it =6
        case "1110001"
            it =7
        case "1111111"
            it =8
        case "1110011"
            it =9
        case else
            it =-999
    end select

    if it =-999 then display$ =display$ +"_" else display$ =display$ +str$( it)

    SevenSeg$ =SevenSeg$ +state$+ " "+str$( it) +"  "
next xPos

#w.gb1 "flush"
#w.tb1 SevenSeg$
#w.tb2 " Detected display was "; display$

wait

function getPixelGreyVal( x, y)   '   return mean grey value, unweighted.
      x =int( x)
      y =int( y)
      calldll #gdi32, "GetPixel", h1DC as ulong, x as long, y as long, pixcol as ulong
      if pixcol >2^24 then notice str$( x) +" " +str$( y) +"Whoops!!": end
      bl = int(  pixcol /( 256*256))
      gr = int( (pixcol               -bl *256*256) / 256)
      re = int(  pixcol               -bl *256*256 - gr *256)
      getPixelGreyVal =( re +bl +gr) /3
end function

[quit]
    callDll #user32, "ReleaseDC", handlegb1 as ulong, h1DC as ulong, result as ushort
    close #w
    end

function getPixelRGB$( x, y)   '   return the three RGB components as a string.
      calldll #gdi32, "GetPixel", h1DC as ulong, x as long, y as long, pixcol as ulong
      bl = int(  pixcol /( 256*256))
      gr = int( (pixcol               -bl *256*256) / 256)
      re = int(  pixcol               -bl *256*256 - gr *256)
      getPixelRGB$ =str$( re) +" " +str$( gr) +" " +str$( bl)
end function

Afterthoughts

This is certainly not the only way to do it. Convolution or OCR would be more universal. But it seemed a natural follow on from my early microcomputer days, when turning a screen pixel off or on could control external devices via a photodiode!