How To: Read a Part Marking from a Camera Image

Reading a printed part number off a camera image, from a fresh installation to a setting you can put into production.

The example is a six-digit part number printed on a label. Adapt the character set and the layout to your marking as you go.

Before you start

  • LumiScan Cam Driver, or another image source, running on the same device - the same ctrlX CORE or ctrlX IPC, or the same PC - and delivering images.

  • Its image node address to hand, e.g. hdvisionsystems/lumiscan-cam-driver/camera1/output/image.

  • A part in front of the camera, with the marking in shot, in focus and well lit.

1. Point the reader at the camera

write  hdvisionsystems/lumiscan-text-reader/module-1/read-text/input/input-image-address
       = "hdvisionsystems/lumiscan-cam-driver/camera1/output/image"

2. Take a first reading

Leave everything else at its default and run it:

write  .../module-1/read-text/execute
read   .../module-1/read-text/output/status-code   -> 1
read   .../module-1/read-text/output/status-text
read   .../module-1/read-text/output/result

A negative status code means it did not run - status-text says why. -3 is almost always a wrong image address.

At this point the reading is usually poor: too many words, some of them nonsense picked out of the background. That is expected, and the next three steps fix it.

3. Crop to the marking

Find the pixel coordinates of the label in the image - on ctrlX OS the ctrlX camera view will show them - and set a region a little larger than the text:

write  .../input/crop/startX = 620
write  .../input/crop/startY = 310
write  .../input/crop/width  = 260
write  .../input/crop/height = 90

Run it again. Most of the spurious words should be gone.

This is the step that helps most, and it also makes every reading faster. If the part can shift between cycles, make the crop large enough to cover the whole range it can occupy - a marking that drifts outside the crop reads as nothing, which is a better failure than reading as something wrong.

4. Restrict the character set

A six-digit part number contains only digits, so say so:

write  .../input/characters/mode = "DIGITS"

This is what stops 0 being read as O and 5 as S. If the number also contains letters, use UPPERCASE_LETTERS with allow = "0123456789" instead, or CUSTOM with allow set to exactly the characters your marking can contain.

5. Tell it what it is looking at

One line of print in a tight crop:

write  .../input/layout = "SINGLE_LINE"

Run it again. output/result.text should now be the part number and nothing else.

6. Choose a confidence threshold

Run five or ten known-good parts and note mean_score from each result, and the individual words[].score.

Correct readings on a decent image score well above 0.8. Set the threshold below your worst known-good reading and above the noise:

write  .../input/min-score = 0.7

min-score filters words[] only; text is always the engine’s full output. Check word_count rather than text when you need to know whether the reading was good.

7. Save the settings

Nothing above survives a restart until you save it:

write  hdvisionsystems/lumiscan-text-reader/system/save-module-inputs/input/module-id = "module-1"
write  hdvisionsystems/lumiscan-text-reader/system/save-module-inputs/execute

8. Wire it into the control program

Per cycle:

  1. Write read-text/execute - a blocking call, so there is no ready flag to poll.

  2. Check output/status-code. Negative means the reading failed and produced nothing.

  3. Read output/result.

  4. Check word_count is 1 and words[0].score is acceptable, then compare words[0].text against the expected part number.

Read the result before triggering the next reading: the next execute clears it.