Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

segment_combine() fails with ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all() #928

Closed
eyaler opened this issue Aug 1, 2022 · 6 comments · Fixed by #933
Assignees
Labels
help wanted Request help

Comments

@eyaler
Copy link

eyaler commented Aug 1, 2022

segment_combine() fails with ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

minimal code to reproduce:

import numpy as np
from plantcv import plantcv as pcv

#works:
segs = [np.array([[[1,2]], [[3,4]]]), np.array([[[5,6]], [[7,8]]]), np.array([[[9,10]], [[11,12]]])]
pcv.morphology.segment_combine([0,1], segs, np.zeros((100,100),dtype=np.uint8))

#fails:
segs = [np.array([[[1,2]], [[3,4]]]), np.array([[[5,6]], [[7,8]]]), np.array([[[9,10]], [[11,12]]])]
pcv.morphology.segment_combine([1,2], segs, np.zeros((100,100),dtype=np.uint8))

full error:

---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

[<ipython-input-101-7ac3bd532796>](https://localhost:8080/#) in <module>()
      8 #fails:
      9 segs = [np.array([[[1,2]], [[3,4]]]), np.array([[[5,6]], [[7,8]]]), np.array([[[9,10]], [[11,12]]])]
---> 10 pcv.morphology.segment_combine([1,2], segs, np.zeros((100,100),dtype=np.uint8))

[/usr/local/lib/python3.7/dist-packages/plantcv/plantcv/morphology/segment_combine.py](https://localhost:8080/#) in segment_combine(segment_list, objects, mask)
     42         new_objects = all_objects[segment_list[0]]
     43         # Remove the objects getting combined from the list of all objects
---> 44         all_objects.remove(objects[segment_list[0]])
     45 
     46         while count < num_contours:

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Local environment:

  • OS: Linux
  • Environment: google colab, python 3.7
  • PlantCV Version 3.14.1
@eyaler eyaler added the help wanted Request help label Aug 1, 2022
@eyaler
Copy link
Author

eyaler commented Aug 1, 2022

i guess this should not work in the first place. see also: https://stackoverflow.com/questions/73190808/index-of-an-numpy-array-in-a-list

@HaleySchuhl
Copy link
Contributor

Hi @eyaler thanks for opening this issue. I wrote the combine_segments function with the hope of being able to correct misidentified segments from pcv.morphology.segment_id. The whole morphology sub-package is pretty sensitive to oddities within the plant's binary mask. Could you share your testing image and the whole workflow leading up to segment combining? It might be possible to refine upstream steps to get the results without segment combining manually, though it really does take a relatively "perfect" binary mask to have good segmentation results. Also, what measurements are you trying to extract?

@eyaler
Copy link
Author

eyaler commented Aug 1, 2022

well, I am actually abusing plantcv morphology package for a custom non-plant thing for which it turned out to work great (and i can share details later if you wish). i am quite sure the code is erroneous as one cannot refer by item to numpy array items in a python list

@HaleySchuhl
Copy link
Contributor

Very interesting! Definitely interested to hear more details of what type of data you're extracting. This sub-package has seemed to be helpful for a handful of non-plant researchers so far :D

I spent some time digging back into the code from the combine_segments function and found that the numpy delete function works well to replace the problematic code at line 50. However, this straightforward replacement really only works when two segments are getting combined. Index numbers/position of objects shift after a pair of segments get combined and the np.delete function requires the index. This step is intrinsically low throughput since it requires significant human input to decide which segments to combine, so maybe it's best to limit the functionality and instead suggest iterative use of this function in the case of combining more than two skeleton segments. I look forward to hearing your thoughts and any suggestions.

@eyaler
Copy link
Author

eyaler commented Aug 3, 2022

thanks! if you want to do it in place you could find the indices to be removed and then loop in reverse order from the end. or you could just create a new list. will share soon :)

@HaleySchuhl
Copy link
Contributor

I did some testing this morning and wanted to share the updated combine_segments function with you for further testing! Please let me know if this functionality works for your data.

# Plot segment ID numbers after combining segments

import os
import cv2
import numpy as np
from plantcv.plantcv import params
from plantcv.plantcv import fatal_error
from plantcv.plantcv import color_palette
from plantcv.plantcv._debug import _debug


def segment_combine(segment_list, objects, mask):
    """Combine user specified segments together.

    Inputs:
    segment_list  = List of segment indices to get combined
    objects       = List of contours
    mask          = Binary mask for debugging image

    Returns:
    segmented_img = Segmented image
    objects       = Updated list of contours

    :param segment_list: list
    :param objects: list
    :param mask: numpy.ndarray
    :return labeled_img: numpy.ndarray
    :return objects: list
    """
    label_coord_x = []
    label_coord_y = []
    all_objects = objects[:]
    segment_list_copy = sorted(segment_list, reverse=True)

    # If user provides a single list of objects to combine
    num_contours = len(segment_list)
    count = 1

    # Store the first object into the new object array
    combined_object = objects[segment_list_copy[0]]
    # Remove the objects getting combined from the list of all objects
    all_objects.pop(segment_list_copy[0])
    #all_objects = np.delete(arr=all_objects, obj=segment_list[0])
    #all_objects.remove(obj)

    while count < num_contours:
        # Combine segments into a single object
        combined_object = np.append(combined_object, objects[segment_list_copy[count]], 0)
        # Remove the segment that was combined from the list of all objects
        all_objects.pop(segment_list_copy[count])
        #all_objects.remove(objects[segment_list[count]])
        count += 1
    # Replace with the combined object
    all_objects.append(combined_object)

    labeled_img = mask.copy()
    labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_GRAY2RGB)

    # Color each segment a different color, use a previously saved scale if available
    rand_color = color_palette(num=len(all_objects), saved=True)
    # Plot all segment contours
    for i, cnt in enumerate(all_objects):
        cv2.drawContours(labeled_img, all_objects[i], -1, rand_color[i], params.line_thickness, lineType=8)
        # Store coordinates for labels
        label_coord_x.append(all_objects[i][0][0][0])
        label_coord_y.append(all_objects[i][0][0][1])

    # Label segments
    for i, cnt in enumerate(all_objects):
        w = label_coord_x[i]
        h = label_coord_y[i]
        text = "ID:{}".format(i)
        cv2.putText(img=labeled_img, text=text, org=(w, h), fontFace=cv2.FONT_HERSHEY_SIMPLEX,
                    fontScale=params.text_size, color=rand_color[i], thickness=2)

    _debug(visual=labeled_img, filename=os.path.join(params.debug_outdir, f"{params.device}_combined_segment_ids.png"))

    return labeled_img, all_objects

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Request help
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants