Friday, February 20, 2015

OpenMV: Counting Pips on Dice

One of our backers, Damage, had a great project idea for OpenMV Cam: count a dice roll visually. Here's what I ended up with.



I used simple blob detection. What ended up working best was to first find the white dice (I had no real dice; these are paper cutouts).

To look for color blobs you supply the threshold() function with a color, an RGB value, and a "distance" value (how close a pixel is to the specified color). Behind the scenes this is based on LAB colorspace and euclidean distance between colors.

    bin = image.threshold([(160, 210, 255)], 20)

The dice are actually blueish-white. I run the blob detection example, then stop it. Then move the mouse over the dice in a few areas and make a note of the approximate RGB values. Then I tweak the RGB values and distance until I'm fairly accurately detecting only the white dice.

Note the dice are actually bluish-white
To filter out any missing pixels, merge the nearby detected ones by calling dilate() then erode() with pixel size parameters.

    # image closing  
    bin.dilate(5)
    bin.erode(2)

Then call find_blobs() on the binary image and it'll return a list of (x, y, color-index) tuples. If you are trying to match multiple colors, the color-index tells you which color the blob matches. Putting it all together, this is the code for finding blue-white blobs:

    # Find white dice
    dbin = image.threshold([(160, 210, 255)], 20)
    # image closing
    dbin.dilate(5)
    dbin.erode(2)
    # find dice
    dice = dbin.find_blobs()
    # Draw rectangles around detected dice
    for d in dice:
        image.draw_rectangle(d[0:4])

Now to find the pips. Same process all over again, only this time, the color is sort of a bluish black. I had to experiment with the color and the dilate/erode calls to get it working.

    # Find pips in dice blobs    
    binary  = image.threshold([(40, 60, 110)], 25)
    # Image closing
    binary.dilate(4)
    binary.erode(1)
    # Detect blobs in image
    blobs = binary.find_blobs()

Finally, go through all the pips and count the ones that are inside the bounds of the white blobs, the dice. Then display the numbers in the corners of the dice and the total at the bottom. The find_blobs() function returns a list of (x, y, width, height) for each blob.

    # Count pips
    pips = 0
    for d in dice:
        dr = (d[0], d[1], d[0]+d[2], d[1]+d[3])
        subpips = 0
        for p in blobs:
            pr = (p[0], p[1], p[0]+p[2], p[1]+p[3])
            if pr[0] > dr[0] and pr[2] < dr[2] and 
               pr[1] > dr[1] and pr[3] < dr[3]:
                subpips += 1
                image.draw_rectangle(p[0:4])
        image.draw_string(d[0]-8, d[1]-8, str(subpips), 
                          (50, 255, 50))
        pips += subpips
    image.draw_string(55, 120, "total="+str(pips), 
                      (50, 255, 50))

And that's all there is. It works pretty ok for having spent very little time on it. There's some room for improvement. More tuning might help. Better lighting. Real dice. Also, when you roll a 6, it merges adjacent pips.

That's because the firmware normally ignores blobs that are too small. Small blobs next to each other can only be detected if you dilate() enough to make them bigger than the threshold, but that merges them because they're close together. We just have to lower the blob size threshold, I think.

Also, we might get more accurate detection by refactoring threshold() to take separate parameters for color and lightness thresholds.

Meanwhile, OpenMV Cam's Kickstarter ends Feb 25. If you want one click here.

Tuesday, February 17, 2015

OpenMV Cam, Final Week

OpenMV Cam, 1.8" x 1.4", final week on Kickstarter
If you haven't heard of OpenMV Cam, it's a small machine vision module that's affordable, and the first that runs Python and is expandable with shields. It's funded and we're entering our final week on Kickstarter so don't miss out.

Wait. Python? Yes, it runs Micro Python on the module.

That means with some simple Python scripts, your project can detect and track a human face, or an object, or you can track multiple colored objects of different colors, you can take a picture, record movies, interface with UART, SPI, I2C, control servos and more.

For NoCo Mini-Maker Faire, I set up a demo with my red Magician robot rotating to track a yellow object (placed on my Pololu 3pi). I tweaked the color tracking script in a few minutes to output the blob coordinates over serial to the PIPduino controlling the Magician, then wrote the Arduino code in another few minutes to spin in place based on blob position. Then tuned the color matching, et voila:


As for shields, we now have a thermopile array sensor shield available. We're working on a WiFi shield, LCD Shield, and a few others for later release.

IR / Thermopile bundle: OpenMV Cam plus IR lens, thermopile shield, USB

With the thermopile shield, based on a 16x4 Melexis MLX90620 sensor, you write a simple python script to overlay a heat map on the camera image as in this demo video (we used an LCD prototype shield here, but you get the idea):


So if you want to join our community, and get yourself an OpenMV Cam at Kickstarter pricing, here's the link to make a pledge: http://bit.ly/OMVKS - we appreciate any support you can offer, thanks!