pybricks/parameters/pb_type_color: add distance method
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.
coverage: 56.535% (-0.06%) from 56.596% when pulling 30e4df1e5728fde58e083b516ed57f7acd8ca44c on Novakasa:expose-colordist into a17fb1ae92d37e659d99c625954e23abc27664f9 on pybricks:master.
Download the artifacts for this pull request:
- cityhub-firmware-build-3159-gitd37e1dcf.zip
- essentialhub-firmware-build-3159-gitd37e1dcf.zip
- movehub-firmware-build-3159-gitd37e1dcf.zip
- mpy-cross.zip
- nxt-firmware-build-3159-gitd37e1dcf.zip
- pr_number.zip
- primehub-firmware-build-3159-gitd37e1dcf.zip
- pybricks-micropython-build-3159-gitd37e1dcf.zip
- technichub-firmware-build-3159-gitd37e1dcf.zip
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.
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?
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)