Friday 12 March 2021

Playing with colour recognition #2: resistor value reader

This post is about a practical example of colour recognition.

 

It describes an experiment to determine resistor values from their coloured bands.

Its an interesting, but difficult task.

Building on the simple idea in my earlier post Playing with colour recognition #1 of using a colour look-up table, I wanted to get an idea of how difficult it might be to read colour-coded resistor values using photographic images and some simple Gambas code.

Initially I just drew some examples using uniform blocks of colour and wrote a simple program to look-up the colours, then converted these colours to resistor values. But using photos of resistors is a whole new ball game.

the colour look-up file

I'm using a file which contains hundreds of colours, where initially each had a unique name. I've gradually changed the name of some of the colours, e.g. Topaz is now called Red, Yale Blue is now called Blue. The idea is to rename (say) "reds" that are obviously red, but not rename a "red" that could be seen as a brown or an orange. We need to keep shades of colours (between obvious colours) with their non-red names.

The 10 basic colours used for the resistor colour code are:-

value/colour

black

1  brown

2  red

3  orange

4  yellow

5  green

6  blue

7  violet

8  grey

9  white

So the colour look-up file has to be edited to suit these 10 colours (i.e. I've edited the names of a range of acceptable blacks, browns, reds & so on, leaving suitable shades in between with their original unique names.

my experimental code

There are many limitations with my current code. It serves largely as a test bed to try out ideas. It doesn't select its own starting point; the user has to click a point on the resistor photo to the left of the first coloured band.



By clicking the 'sample' button, the code then takes a sample slice through the image from left-to-right and "looks-up" the colour of all pixels in this slice.

 

Filter techniques include:-

Image Blur: this is a good way to minimise digital noise in a photo

Averaging: this can be simple averaging of each R,G,B component (red, green, blue) of the pixel based upon a selected sample group size, or a moving mean approach where value #1 is averaged with the next group of samples, then value #2, value #3 and so on. For example with a sample group size of 5, the first moving mean average is for values 1 - 5, the second is for values 2 - 6, third for 3 - 7 and so on.

Common: This is where consecutive single colours are counted, and if the count is => than the threshold, then the colour is included. All others are deleted from the list.

And of course any colours identified that do not have a colour name in the resistor colour list (e.g. black, brown, red & so on) are rejected.  


Gambas code snippets

The value of each pixel is made up of the red, green and blue colour bytes (RGB) and for most operations (such as averaging) we need to extract RGB.

Public Function Pixel2RGB(iPixels As Integer) As Integer[]
Dim index As Integer
Dim intRGB As Integer[]
 
  'extract RGB values
  intRGB = [Shr(iPixels, 16) And &FF, Shr(iPixels, 8) And &FF, iPixels And &FF]
  For index = 1 To 3
    FMain.GridView1[index, 1].Text = intRGB[index - 1]
  Next
  Return intRGB
End

My Averaging filter starts by filling a target array with a non-colour (i.e. not a resistor colour, a colour called "neutral" in the look-up table) then iterates through the array of RGB sample values creating averages which are then stored in the target array, before being returned by the function.

Public Function Average(iSlice As Integer[], iSamples As Integer) As Integer[]
Dim index As Integer
Dim indexRGB As Integer
Dim indexS As Integer
Dim indexAve As Integer
Dim intSumOfSamples As Integer
Dim intColoursRGB As New Integer[3, FMain.IMAGE_WIDTH_MAX]    'RGB, samples. Image width must not be > limit
 
  'fill array with a non-colour called "neutral"
  For index = 0 To FMain.IMAGE_WIDTH_MAX - 1
    intColoursRGB[0, index] = 150
    intColoursRGB[1, index] = 140
    intColoursRGB[2, index] = 110
  Next
 
  'calculate the simple average over iSamples
  ' for each channel (R G B)
  For index = 0 To iSlice.Max / 3 Step iSamples
    For indexRGB = 0 To 2
      For indexS = 0 To iSamples - 1
        intSumOfSamples += iSlice[indexRGB, index + indexS]
      Next
      intColoursRGB[indexRGB, indexAve] = Int(intSumOfSamples / iSamples)
      intSumOfSamples = 0
    Next
    Inc indexAve
  Next
  Return intColoursRGB
End

The Moving Mean filter code is very similar:-

Public Function MovingMean(iSlice As Integer[], iSamples As Integer) As Integer[]
Dim index As Integer
Dim indexRGB As Integer
Dim indexS As Integer
Dim indexAve As Integer
Dim intSumOfSamples As Integer
Dim intColoursRGB As New Integer[3, FMain.IMAGE_WIDTH_MAX]    'RGB, samples. Image width must not be > limit
 
  'fill array with a non-colour called "neutral"
  For index = 0 To FMain.IMAGE_WIDTH_MAX - 1
    intColoursRGB[0, index] = 150
    intColoursRGB[1, index] = 140
    intColoursRGB[2, index] = 110
  Next
 
  'calculate the moving mean over iSamples
  ' for each channel (R G B)
  For index = 0 To (iSlice.Max / 3) - iSamples Step 3
    For indexRGB = 0 To 2
      For indexS = 0 To iSamples - 1
        intSumOfSamples += iSlice[indexRGB, index + indexS]
      Next
      intColoursRGB[indexRGB, indexAve] = Int(intSumOfSamples / iSamples)
      intSumOfSamples = 0
    Next
    Inc indexAve
  Next
  Return intColoursRGB
End


more things to do

Only the first 3 coloured bands are used to determine the resistor value for the E12 & E24 Resistor Series.

The first 2 colours are digits and the third is a zero multiplier, so coloured bands yellow, violet, red = 4700 Ohms.

Actual values for E12 are only from multiples of this number sequence:-

 1, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2

...so this knowledge could be used to create another filter for E12. Likewise there is a defined sequence for E24 values.

My "Common" filter could be a lot smarter. For example, suppose we had a sequence of detected colours like this:-

black, black, black, navy blue, black, black, black

...the filter could just disregard singletons in a sequence of promising colours!

Clearly an automatic selection point system, were the software determines the best point in the image to start slicing, would be an interesting challenge.

Also, the code should be able to work with resistors presented the wrong way around (i.e. reading from right-to-left or left-to-right).

A calibration colour card would be another interesting inclusion; each photograph would include a colour card used to recalibrate the system, thereby allowing for differing lighting conditions, colour-casts & so on.

 

This is a slow-burn, experimental background project...so don't hold your breath waiting for the next post! 


Update: the full Gambas Project/code is available on GambasOne: https://forum.gambas.one/viewtopic.php?p=4083#p4083




No comments:

Post a Comment