pybricks-micropython icon indicating copy to clipboard operation
pybricks-micropython copied to clipboard

pybricks/parameters/pb_type_color: add distance method

Open Novakasa opened this issue 2 years ago • 5 comments

Exposing the new HCL-bicone color cost function (https://github.com/pybricks/pybricks-micropython/pull/104) makes it possible for users to create their own smarter color detection algorithms, for example by adding a hysteresis. This PR adds it as a color.distance(other_color) method to a Color instance. On movehub, this adds 84 bytes. The function name get_distance_to would have added 100 bytes.

Example use:

hsv = sensor.hsv()
if hsv.distance(Color.GREEN) < hsv.distance(Color.RED):
    print("green!")
else:
    print("red!")

Not sure if this would add less firmware size if it were just a module-level function, but to me it makes a bit more sense as a Color method.

Novakasa avatar Nov 27 '23 19:11 Novakasa

Coverage Status

coverage: 56.535% (-0.06%) from 56.596% when pulling 30e4df1e5728fde58e083b516ed57f7acd8ca44c on Novakasa:expose-colordist into a17fb1ae92d37e659d99c625954e23abc27664f9 on pybricks:master.

coveralls avatar Nov 27 '23 20:11 coveralls

Not sure if we should call this distance() since it actually returns the squared cartesian distance in the HCL bicone mapping. Longer names cost firmware size though. If we document the method thoroughly, maybe distance is fine?

BTW, I'm keeping this as draft for now since I'm waiting to rebase this once version metadata reflects alpha release, then I'll also add changelog entry.

If we want to merge this, I'll also add a PR for the docs.

Novakasa avatar Nov 30 '23 10:11 Novakasa

Can you add some end-user Python samples?

Is this primarily to map(color1, color2) --> cost, or would you eventually like to be able to pass a custom cost function to detectable_colors?

laurensvalk avatar Nov 30 '23 12:11 laurensvalk

This is simply intended to expose the function pbio_color_get_bicone_squared_distance(hsv_a, hsv_b) to the user. I added a simple example of the proposed API to the first comment.

The purpose is to allow more specialized ways of deciding what color is measured depending on what the user needs. For example, a kind of hysteresis might be implemented like this (edit: tested now, fixed indentation and typo):

# calibrate colors here
colors = [Color.GREEN, Color.RED, Color.BLUE]

current_color = colors[0]
new_color_threshold = 0.7  # only switch to new color, if (dist_to_new_col / dist_to_old_col) < 0.7

while True:  
    hsv = sensor.hsv()
    current_dist = hsv.distance(current_color)
    best_dist = current_dist
    best_color = None
    for color in colors:
        dist = hsv.distance(color)
        if dist < best_dist and dist < new_color_threshold*current_dist:
            best_color = color
            best_dist = dist
    if best_color is not None:
        current_color = best_color
        print("new color detected!", current_color)
        
    wait(100)

Novakasa avatar Nov 30 '23 13:11 Novakasa