Beispiel #1
0
def plant_cv(img):
    counter = 0
    debug = None

    counter, s = pcv.rgb2gray_hsv(img, 's', counter, debug)
    counter, s_thresh = pcv.binary_threshold(s, 145, 255, 'light', counter,
                                             debug)
    counter, s_mblur = pcv.median_blur(s_thresh, 5, counter, debug)

    # Convert RGB to LAB and extract the Blue channel
    counter, b = pcv.rgb2gray_lab(img, 'b', counter, debug)

    # Threshold the blue image
    counter, b_thresh = pcv.binary_threshold(b, 145, 255, 'light', counter,
                                             debug)
    counter, b_cnt = pcv.binary_threshold(b, 145, 255, 'light', counter, debug)
    # Join the thresholded saturation and blue-yellow images
    counter, bs = pcv.logical_or(s_mblur, b_cnt, counter, debug)
    counter, masked = pcv.apply_mask(img, bs, 'white', counter, debug)

    #----------------------------------------
    # Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels
    counter, masked_a = pcv.rgb2gray_lab(masked, 'a', counter, debug)
    counter, masked_b = pcv.rgb2gray_lab(masked, 'b', counter, debug)

    # Threshold the green-magenta and blue images
    counter, maskeda_thresh = pcv.binary_threshold(masked_a, 115, 255, 'dark',
                                                   counter, debug)
    counter, maskeda_thresh1 = pcv.binary_threshold(masked_a, 135, 255,
                                                    'light', counter, debug)
    counter, maskedb_thresh = pcv.binary_threshold(masked_b, 128, 255, 'light',
                                                   counter, debug)

    # Join the thresholded saturation and blue-yellow images (OR)
    counter, ab1 = pcv.logical_or(maskeda_thresh, maskedb_thresh, counter,
                                  debug)
    counter, ab = pcv.logical_or(maskeda_thresh1, ab1, counter, debug)
    counter, ab_cnt = pcv.logical_or(maskeda_thresh1, ab1, counter, debug)

    # Fill small objects
    counter, ab_fill = pcv.fill(ab, ab_cnt, 200, counter, debug)

    # Apply mask (for vis images, mask_color=white)
    counter, masked2 = pcv.apply_mask(masked, ab_fill, 'white', counter, debug)

    zeros = np.zeros(masked2.shape[:2], dtype="uint8")
    merged = cv2.merge([zeros, ab_fill, zeros])

    return merged, masked2
Beispiel #2
0
def watershed_segmentation(rgb_img, mask, distance=10):
    """Uses the watershed algorithm to detect boundary of objects. Needs a marker file which specifies area which is
       object (white), background (grey), unknown area (black).

    Inputs:
    rgb_img             = image to perform watershed on needs to be 3D (i.e. np.shape = x,y,z not np.shape = x,y)
    mask                = binary image, single channel, object in white and background black
    distance            = min_distance of local maximum

    Returns:
    analysis_images     = list of output images

    :param rgb_img: numpy.ndarray
    :param mask: numpy.ndarray
    :param distance: int
    :return analysis_images: list
    """
    params.device += 1
    # # Will be depricating opencv version 2
    # if cv2.__version__[0] == '2':
    #     dist_transform = cv2.distanceTransform(mask, cv2.cv.CV_DIST_L2, maskSize=0)
    # else:
    dist_transform = cv2.distanceTransformWithLabels(mask, cv2.DIST_L2, maskSize=0)[0]

    localMax = peak_local_max(dist_transform, indices=False, min_distance=distance, labels=mask)

    markers = ndi.label(localMax, structure=np.ones((3, 3)))[0]
    dist_transform1 = -dist_transform
    labels = watershed(dist_transform1, markers, mask=mask)

    img1 = np.copy(rgb_img)

    for x in np.unique(labels):
        rand_color = color_palette(len(np.unique(labels)))
        img1[labels == x] = rand_color[x]

    img2 = apply_mask(img1, mask, 'black')

    joined = np.concatenate((img2, rgb_img), axis=1)

    estimated_object_count = len(np.unique(markers)) - 1

    analysis_image = []
    analysis_image.append(joined)

    if params.debug == 'print':
        print_image(dist_transform, os.path.join(params.debug_outdir, str(params.device) + '_watershed_dist_img.png'))
        print_image(joined, os.path.join(params.debug_outdir, str(params.device) + '_watershed_img.png'))
    elif params.debug == 'plot':
        plot_image(dist_transform, cmap='gray')
        plot_image(joined)

    outputs.add_observation(variable='estimated_object_count', trait='estimated object count',
                            method='plantcv.plantcv.watershed', scale='none', datatype=int,
                            value=estimated_object_count, label='none')

    # Store images
    outputs.images.append(analysis_image)

    return analysis_image
Beispiel #3
0
def watershed_segmentation(rgb_img, mask, distance=10):
    """Uses the watershed algorithm to detect boundary of objects. Needs a marker file which specifies area which is
       object (white), background (grey), unknown area (black).

    Inputs:
    rgb_img             = image to perform watershed on needs to be 3D (i.e. np.shape = x,y,z not np.shape = x,y)
    mask                = binary image, single channel, object in white and background black
    distance            = min_distance of local maximum

    Returns:
    analysis_images     = list of output images

    :param rgb_img: numpy.ndarray
    :param mask: numpy.ndarray
    :param distance: int
    :return analysis_images: list
    """
    params.device += 1

    # Store debug mode
    debug = params.debug
    params.debug = None

    dist_transform = cv2.distanceTransformWithLabels(mask, cv2.DIST_L2, maskSize=0)[0]

    localMax = peak_local_max(dist_transform, indices=False, min_distance=distance, labels=mask)

    markers = ndi.label(localMax, structure=np.ones((3, 3)))[0]
    dist_transform1 = -dist_transform
    labels = watershed(dist_transform1, markers, mask=mask)

    img1 = np.copy(rgb_img)

    for x in np.unique(labels):
        rand_color = color_palette(len(np.unique(labels)))
        img1[labels == x] = rand_color[x]

    img2 = apply_mask(img1, mask, 'black')

    joined = np.concatenate((img2, rgb_img), axis=1)

    estimated_object_count = len(np.unique(markers)) - 1

    # Reset debug mode
    params.debug = debug
    if params.debug == 'print':
        print_image(dist_transform, os.path.join(params.debug_outdir, str(params.device) + '_watershed_dist_img.png'))
        print_image(joined, os.path.join(params.debug_outdir, str(params.device) + '_watershed_img.png'))
    elif params.debug == 'plot':
        plot_image(dist_transform, cmap='gray')
        plot_image(joined)

    outputs.add_observation(variable='estimated_object_count', trait='estimated object count',
                            method='plantcv.plantcv.watershed', scale='none', datatype=int,
                            value=estimated_object_count, label='none')

    # Store images
    outputs.images.append([dist_transform, joined])

    return joined
def _max(img, hmax, mask, x, y, h, w, type):
    imgcp = np.copy(img)
    cv2.rectangle(mask, (x, y), (x + w, y + h), (255, 255, 255), -1)
    mask_binary = mask[:, :, 0]
    retval, mask_binary = cv2.threshold(mask_binary, 254, 255, cv2.THRESH_BINARY)
    masked = apply_mask(imgcp, mask_binary, 'black')
    max1 = np.amax(masked)
    alpha = hmax / float(max1)
    corrected = np.asarray(np.where(img <= max1, np.multiply(alpha, img), hmax), type)

    return corrected
def _max(img, hmax, mask, x, y, h, w, type):
    imgcp = np.copy(img)
    cv2.rectangle(mask, (x, y), (x + w, y + h), (255, 255, 255), -1)
    mask_binary = mask[:, :, 0]
    retval, mask_binary = cv2.threshold(mask_binary, 254, 255, cv2.THRESH_BINARY)
    masked = apply_mask(imgcp, mask_binary, 'black')
    max1 = np.amax(masked)
    alpha = hmax / float(max1)
    corrected = np.asarray(np.where(img <= max1, np.multiply(alpha, img), hmax), type)

    return corrected
def read_true_positive(true_positive_file):
    class args:
        #image = "C:\\Users\\RensD\\OneDrive\\studie\\Master\\The_big_project\\top_perspective\\0214_2018-03-07 08.55 - 26_true_positive.png"
        image = true_positive_file
        outdir = "C:\\Users\\RensD\\OneDrive\\studie\\Master\\The_big_project\\top_perspective\\output"
        debug = "None"
        result = "results.txt"

    # Get options
    pcv.params.debug = args.debug  #set debug mode
    pcv.params.debug_outdir = args.outdir  #set output directory

    # Read image (readimage mode defaults to native but if image is RGBA then specify mode='rgb')
    # Inputs:
    #   filename - Image file to be read in
    #   mode - Return mode of image; either 'native' (default), 'rgb', 'gray', or 'csv'
    img, path, filename = pcv.readimage(filename=args.image, mode='rgb')

    s = pcv.rgb2gray_hsv(rgb_img=img, channel='s')
    mask, masked_image = pcv.threshold.custom_range(rgb_img=s,
                                                    lower_thresh=[10],
                                                    upper_thresh=[255],
                                                    channel='gray')
    masked = pcv.apply_mask(rgb_img=img, mask=mask, mask_color='white')
    #new_im = Image.fromarray(mask)
    #name = "positive_test.png"
    #Recognizing objects
    id_objects, obj_hierarchy = pcv.find_objects(masked, mask)
    roi1, roi_hierarchy = pcv.roi.rectangle(img=masked,
                                            x=0,
                                            y=0,
                                            h=960,
                                            w=1280)  # Currently hardcoded
    with HiddenPrints():
        roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(
            img=img,
            roi_contour=roi1,
            roi_hierarchy=roi_hierarchy,
            object_contour=id_objects,
            obj_hierarchy=obj_hierarchy,
            roi_type=roi_type)

    obj, mask = pcv.object_composition(img=img,
                                       contours=roi_objects,
                                       hierarchy=hierarchy3)
    #new_im.save(name)
    return (mask)
Beispiel #7
0
def watershed_segmentation(rgb_img, mask, distance=10):
    """Uses the watershed algorithm to detect boundary of objects. Needs a marker file which specifies area which is
       object (white), background (grey), unknown area (black).

    Inputs:
    rgb_img             = image to perform watershed on needs to be 3D (i.e. np.shape = x,y,z not np.shape = x,y)
    mask                = binary image, single channel, object in white and background black
    distance            = min_distance of local maximum

    Returns:
    watershed_header    = shape data table headers
    watershed_data      = shape data table values
    analysis_images     = list of output images

    :param rgb_img: numpy.ndarray
    :param mask: numpy.ndarray
    :param distance: int
    :return watershed_header: list
    :return watershed_data: list
    :return analysis_images: list
    """
    params.device += 1
    # # Will be depricating opencv version 2
    # if cv2.__version__[0] == '2':
    #     dist_transform = cv2.distanceTransform(mask, cv2.cv.CV_DIST_L2, maskSize=0)
    # else:
    dist_transform = cv2.distanceTransformWithLabels(mask, cv2.DIST_L2, maskSize=0)[0]

    localMax = peak_local_max(dist_transform, indices=False, min_distance=distance, labels=mask)

    markers = ndi.label(localMax, structure=np.ones((3, 3)))[0]
    dist_transform1 = -dist_transform
    labels = watershed(dist_transform1, markers, mask=mask)

    img1 = np.copy(rgb_img)

    for x in np.unique(labels):
        rand_color = color_palette(len(np.unique(labels)))
        img1[labels == x] = rand_color[x]

    img2 = apply_mask(img1, mask, 'black')

    joined = np.concatenate((img2, rgb_img), axis=1)

    estimated_object_count = len(np.unique(markers)) - 1

    analysis_image = []
    analysis_image.append(joined)

    watershed_header = (
        'HEADER_WATERSHED',
        'estimated_object_count'
    )

    watershed_data = (
        'WATERSHED_DATA',
        estimated_object_count
    )

    if params.debug == 'print':
        print_image(dist_transform, os.path.join(params.debug_outdir, str(params.device) + '_watershed_dist_img.png'))
        print_image(joined, os.path.join(params.debug_outdir, str(params.device) + '_watershed_img.png'))
    elif params.debug == 'plot':
        plot_image(dist_transform, cmap='gray')
        plot_image(joined)

    # Store into global measurements
    if not 'watershed' in outputs.measurements:
        outputs.measurements['watershed'] = {}
    outputs.measurements['watershed']['estimated_object_count'] = estimated_object_count

    # Store images
    outputs.images.append(analysis_image)

    return watershed_header, watershed_data, analysis_image
def analyze_nir_intensity(gray_img,
                          mask,
                          bins,
                          histplot=False,
                          filename=False):
    """This function calculates the intensity of each pixel associated with the plant and writes the values out to
       a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant.

    Inputs:
    gray_img     = 8- or 16-bit grayscale image data
    mask         = Binary mask made from selected contours
    bins         = number of classes to divide spectrum into
    histplot     = if True plots histogram of intensity values
    filename     = False or image name. If defined print image

    Returns:
    hist_header  = NIR histogram data table headers
    hist_data    = NIR histogram data table values
    analysis_img = output image

    :param gray_img: numpy array
    :param mask: numpy array
    :param bins: int
    :param histplot: bool
    :param filename: str
    :return hist_header: list
    :return hist_data: list
    :return analysis_img: str
    """
    params.device += 1

    # apply plant shaped mask to image
    mask1 = binary_threshold(mask, 0, 255, 'light')
    mask1 = (mask1 / 255)
    masked = np.multiply(gray_img, mask1)

    # calculate histogram
    if gray_img.dtype == 'uint16':
        maxval = 65536
    else:
        maxval = 256

    # Make a pseudo-RGB image
    rgbimg = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR)

    hist_nir, hist_bins = np.histogram(masked, bins, (1, maxval), False, None,
                                       None)

    hist_bins1 = hist_bins[:-1]
    hist_bins2 = [l for l in hist_bins1]

    hist_nir1 = [l for l in hist_nir]

    # make hist percentage for plotting
    pixels = cv2.countNonZero(mask1)
    hist_percent = (hist_nir / float(pixels)) * 100

    # report histogram data
    hist_header = ['HEADER_HISTOGRAM', 'bin-number', 'bin-values', 'nir']

    hist_data = ['HISTOGRAM_DATA', bins, hist_bins2, hist_nir1]

    analysis_img = []

    # make mask to select the background
    mask_inv = cv2.bitwise_not(mask)
    img_back = cv2.bitwise_and(rgbimg, rgbimg, mask=mask_inv)
    img_back1 = cv2.applyColorMap(img_back, colormap=1)

    # mask the background and color the plant with color scheme 'jet'
    cplant = cv2.applyColorMap(rgbimg, colormap=2)
    masked1 = apply_mask(cplant, mask, 'black')
    cplant_back = cv2.add(masked1, img_back1)

    if filename:
        path = os.path.dirname(filename)
        fig_name = 'NIR_pseudocolor_colorbar.svg'
        if not os.path.isfile(path + '/' + fig_name):
            plot_colorbar(path, fig_name, bins)

        fig_name_pseudo = (os.path.splitext(filename)[0] +
                           '_nir_pseudo_col.jpg')
        print_image(cplant_back, fig_name_pseudo)
        analysis_img.append(['IMAGE', 'pseudo', fig_name_pseudo])

    if params.debug is not None:
        if params.debug == "print":
            print_image(
                masked1,
                os.path.join(params.debug_outdir,
                             str(params.device) + "_nir_pseudo_plant.jpg"))
            print_image(
                cplant_back,
                os.path.join(params.debug_outdir,
                             str(params.device) +
                             "_nir_pseudo_plant_back.jpg"))
        if params.debug == "plot":
            plot_image(masked1)
            plot_image(cplant_back)

    if histplot is True:
        import matplotlib
        matplotlib.use('Agg', warn=False)
        from matplotlib import pyplot as plt

        # plot hist percent
        plt.plot(hist_percent, color='green', label='Signal Intensity')
        plt.xlim([0, (bins - 1)])
        plt.xlabel(('Grayscale pixel intensity (0-' + str(bins) + ")"))
        plt.ylabel('Proportion of pixels (%)')

        if filename:
            fig_name_hist = (os.path.splitext(filename)[0] + '_nir_hist.svg')
            plt.savefig(fig_name_hist)
            analysis_img.append(['IMAGE', 'hist', fig_name_hist])
        if params.debug == "print":
            plt.savefig(
                os.path.join(params.debug_outdir,
                             str(params.device) + "_nir_histogram.png"))
        if params.debug == "plot":
            plt.figure()
        plt.clf()

    return hist_header, hist_data, analysis_img
Beispiel #9
0
                                threshold=135,
                                max_value=255,
                                object_type='light')

#Setting threshold continued
b_cnt = pcv.threshold.binary(gray_img=b,
                             threshold=135,
                             max_value=255,
                             object_type='light')

# In[112]:

#Join the blue and yellow binary images
bs = pcv.logical_and(bin_img1=s_mblur, bin_img2=b_cnt)

masked = pcv.apply_mask(img=img1, mask=bs, mask_color='white')

#identify objects
obj2 = id_objects, obj_hierarchy = pcv.find_objects(img=masked, mask=bs)

#Define Range of Intrest
# Inputs:
#   img - RGB or grayscale image to plot the ROI on
#   x - The x-coordinate of the upper left corner of the rectangle
#   y - The y-coordinate of the upper left corner of the rectangle
#   h - The height of the rectangle
#   w - The width of the rectangle
roi1, roi_hierarchy = pcv.roi.rectangle(img=img1, x=75, y=60, h=20, w=20)

# In[113]:
Beispiel #10
0
def main():
    # Get options
    args = options()

    # Set variables
    pcv.params.debug = args.debug  # Replace the hard-coded debug with the debug flag
    pcv.params.debug_outdir = args.outdir  # set output directory

    ### Main pipeline ###

    # Read image (readimage mode defaults to native but if image is RGBA then specify mode='rgb')
    img, path, filename = pcv.readimage(args.image, mode='rgb')

    # Read reference image for colour correction (currently unused)
    #ref_img, ref_path, ref_filename = pcv.readimage(
    #    "/home/leonard/Dropbox/2020-01_LAC_phenotyping/images/top/renamed/20200128_2.jpg",
    #    mode="rgb")

    # Find colour cards
    #df, start, space = pcv.transform.find_color_card(rgb_img=ref_img)
    #ref_mask = pcv.transform.create_color_card_mask(rgb_img=ref_img, radius=10, start_coord=start, spacing=space, ncols=4, nrows=6)

    df, start, space = pcv.transform.find_color_card(rgb_img=img)
    img_mask = pcv.transform.create_color_card_mask(rgb_img=img,
                                                    radius=10,
                                                    start_coord=start,
                                                    spacing=space,
                                                    ncols=4,
                                                    nrows=6)

    output_directory = "."

    # Correct colour (currently unused)
    #target_matrix, source_matrix, transformation_matrix, corrected_img = pcv.transform.correct_color(ref_img, ref_mask, img, img_mask, output_directory)

    # Check that the colour correction worked (source~target should be strictly linear)
    #pcv.transform.quick_color_check(source_matrix = source_matrix, target_matrix = target_matrix, num_chips = 24)

    # Write the spacing of the colour card to file as size marker
    with open(os.path.join(path, 'output/size_marker_trays.csv'), 'a') as f:
        writer = csv.writer(f)
        writer.writerow([filename, space[0]])

    ### Crop tray ###

    # Define a bounding rectangle around the colour card
    x_cc, y_cc, w_cc, h_cc = cv2.boundingRect(img_mask)
    x_cc = int(round(x_cc - 0.3 * w_cc))
    y_cc = int(round(y_cc - 0.3 * h_cc))
    h_cc = int(round(h_cc * 1.6))
    w_cc = int(round(w_cc * 1.6))

    # Crop out colour card
    start_point = (x_cc, y_cc)
    end_point = (x_cc + w_cc, y_cc + h_cc)
    colour = (0, 0, 0)
    thickness = -1
    card_crop_img = cv2.rectangle(img, start_point, end_point, colour,
                                  thickness)

    # Convert RGB to HSV and extract the value channel
    v = pcv.rgb2gray_hsv(card_crop_img, "v")

    # Threshold the value image
    v_thresh = pcv.threshold.binary(
        v, 100, 255, "light"
    )  # start threshold at 150 with bright corner-markers, 100 without

    # Fill out bright imperfections (siliques and other dirt on the background)
    v_thresh = pcv.fill(
        v_thresh, 100)  # fill at 500 with bright corner-markers, 100 without

    # Create bounding rectangle around the tray
    x, y, w, h = cv2.boundingRect(v_thresh)

    # Crop image to tray
    #crop_img = card_crop_img[y:y+h, x:x+int(w - (w * 0.03))] # crop extra 3% from right because of tray labels
    crop_img = card_crop_img[y:y + h, x:x + w]  # crop symmetrically

    # Save cropped image for quality control
    pcv.print_image(crop_img,
                    filename=path + "/output/" + "cropped" + filename + ".png")

    ### Threshold plants ###

    # Threshold the green-magenta, blue, and hue channels
    a_thresh, _ = pcv.threshold.custom_range(img=crop_img,
                                             lower_thresh=[0, 0, 0],
                                             upper_thresh=[255, 108, 255],
                                             channel='LAB')
    b_thresh, _ = pcv.threshold.custom_range(img=crop_img,
                                             lower_thresh=[0, 0, 135],
                                             upper_thresh=[255, 255, 255],
                                             channel='LAB')
    h_thresh, _ = pcv.threshold.custom_range(img=crop_img,
                                             lower_thresh=[35, 0, 0],
                                             upper_thresh=[70, 255, 255],
                                             channel='HSV')

    # Join the thresholds (AND)
    ab = pcv.logical_and(b_thresh, a_thresh)
    abh = pcv.logical_and(ab, h_thresh)

    # Fill small objects depending on expected plant size based on DPG (make sure to take the correct file suffix jpg/JPG/jpeg...)
    match = re.search("(\d+).(\d)\.jpg$", filename)

    if int(match.group(1)) < 10:
        abh_clean = pcv.fill(abh, 50)
        print("50")
    elif int(match.group(1)) < 15:
        abh_clean = pcv.fill(abh, 200)
        print("200")
    else:
        abh_clean = pcv.fill(abh, 500)
        print("500")

    # Dilate to close broken borders
    abh_dilated = pcv.dilate(abh_clean, 3, 1)

    # Close holes
    # abh_fill = pcv.fill_holes(abh_dilated) # silly -- removed
    abh_fill = abh_dilated

    # Apply mask (for VIS images, mask_color=white)
    masked = pcv.apply_mask(crop_img, abh_fill, "white")

    # Save masked image for quality control
    pcv.print_image(masked,
                    filename=path + "/output/" + "masked" + filename + ".png")

    ### Filter and group contours ###

    # Identify objects
    id_objects, obj_hierarchy = pcv.find_objects(crop_img, abh_fill)

    # Create bounding box with margins to avoid border artifacts
    roi_y = 0 + crop_img.shape[0] * 0.05
    roi_x = 0 + crop_img.shape[0] * 0.05
    roi_h = crop_img.shape[0] - (crop_img.shape[0] * 0.1)
    roi_w = crop_img.shape[1] - (crop_img.shape[0] * 0.1)
    roi_contour, roi_hierarchy = pcv.roi.rectangle(crop_img, roi_y, roi_x,
                                                   roi_h, roi_w)

    # Keep all objects in the bounding box
    roi_objects, roi_obj_hierarchy, kept_mask, obj_area = pcv.roi_objects(
        img=crop_img,
        roi_type='partial',
        roi_contour=roi_contour,
        roi_hierarchy=roi_hierarchy,
        object_contour=id_objects,
        obj_hierarchy=obj_hierarchy)

    # Cluster the objects by plant
    clusters, contours, hierarchies = pcv.cluster_contours(
        crop_img, roi_objects, roi_obj_hierarchy, 3, 5)

    # Split image into single plants
    out = args.outdir
    #output_path, imgs, masks = pcv.cluster_contour_splitimg(crop_img,
    #                                                        clusters,
    #                                                        contours,
    #                                                        hierarchies,
    #                                                        out,
    #                                                        file = filename)

    ### Analysis ###

    # Approximate the position of the top left plant as grid start
    coord_y = int(
        round(((crop_img.shape[0] / 3) * 0.5) + (crop_img.shape[0] * 0.025)))
    coord_x = int(
        round(((crop_img.shape[1] / 5) * 0.5) + (crop_img.shape[1] * 0.025)))

    # Set the ROI spacing relative to image dimensions
    spc_y = int((round(crop_img.shape[0] - (crop_img.shape[0] * 0.05)) / 3))
    spc_x = int((round(crop_img.shape[1] - (crop_img.shape[1] * 0.05)) / 5))

    # Set the ROI radius relative to image width
    if int(match.group(1)) < 16:
        r = int(round(crop_img.shape[1] / 12.5))
    else:
        r = int(round(crop_img.shape[1] / 20))

    # Make a grid of ROIs at the expected positions of plants
    # This allows for gaps due to dead/not germinated plants, without messing up the plant numbering
    imgs, masks = pcv.roi.multi(img=crop_img,
                                nrows=3,
                                ncols=5,
                                coord=(coord_x, coord_y),
                                radius=r,
                                spacing=(spc_x, spc_y))

    # Loop through the ROIs in the grid
    for i in range(0, len(imgs)):
        # Find objects within the ROI
        filtered_contours, filtered_hierarchy, filtered_mask, filtered_area = pcv.roi_objects(
            img=crop_img,
            roi_type="partial",
            roi_contour=imgs[i],
            roi_hierarchy=masks[i],
            object_contour=id_objects,
            obj_hierarchy=obj_hierarchy)
        # Continue only if not empty
        if len(filtered_contours) > 0:
            # Combine objects within each ROI
            plant_contour, plant_mask = pcv.object_composition(
                img=crop_img,
                contours=filtered_contours,
                hierarchy=filtered_hierarchy)

            # Analyse the shape of each plant
            analysis_images = pcv.analyze_object(img=crop_img,
                                                 obj=plant_contour,
                                                 mask=plant_mask)

            pcv.print_image(analysis_images,
                            filename=path + "/output/" + filename + "_" +
                            str(i) + "_analysed.png")

            # Determine color properties
            color_images = pcv.analyze_color(crop_img, plant_mask, "hsv")

            # Watershed plant area to count leaves (computationally intensive, use when needed)
            #watershed_images = pcv.watershed_segmentation(crop_img, plant_mask, 15)

            # Print out a .json file with the analysis data for the plant
            pcv.outputs.save_results(filename=path + "/" + filename + "_" +
                                     str(i) + '.json')

            # Clear the measurements stored globally into the Ouptuts class
            pcv.outputs.clear()
Beispiel #11
0
def main():
    # Get options
    args = options()

    # Set variables
    device = 0
    pcv.params.debug = args.debug
    img_file = args.image

    # Read image
    img, path, filename = pcv.readimage(filename=img_file, mode='rgb')

    # Process saturation channel from HSV colour space
    s = pcv.rgb2gray_hsv(rgb_img=img, channel='s')
    lp_s = pcv.laplace_filter(s, 1, 1)
    shrp_s = pcv.image_subtract(s, lp_s)
    s_eq = pcv.hist_equalization(shrp_s)
    s_thresh = pcv.threshold.binary(gray_img=s_eq,
                                    threshold=215,
                                    max_value=255,
                                    object_type='light')
    s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5)

    # Process green-magenta channel from LAB colour space
    b = pcv.rgb2gray_lab(rgb_img=img, channel='a')
    b_lp = pcv.laplace_filter(b, 1, 1)
    b_shrp = pcv.image_subtract(b, b_lp)
    b_thresh = pcv.threshold.otsu(b_shrp, 255, object_type='dark')

    # Create and apply mask
    bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_thresh)
    filled = pcv.fill_holes(bs)
    masked = pcv.apply_mask(img=img, mask=filled, mask_color='white')

    # Extract colour channels from masked image
    masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')

    # Threshold the green-magenta and blue images
    maskeda_thresh = pcv.threshold.binary(gray_img=masked_a,
                                          threshold=115,
                                          max_value=255,
                                          object_type='dark')
    maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a,
                                           threshold=140,
                                           max_value=255,
                                           object_type='light')
    maskedb_thresh = pcv.threshold.binary(gray_img=masked_b,
                                          threshold=128,
                                          max_value=255,
                                          object_type='light')

    # Join the thresholded saturation and blue-yellow images (OR)
    ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)

    # Produce and apply a mask
    opened_ab = pcv.opening(gray_img=ab)
    ab_fill = pcv.fill(bin_img=ab, size=200)
    closed_ab = pcv.closing(gray_img=ab_fill)
    masked2 = pcv.apply_mask(img=masked, mask=bs, mask_color='white')

    # Identify objects
    id_objects, obj_hierarchy = pcv.find_objects(img=masked2, mask=ab_fill)

    # Define region of interest (ROI)
    roi1, roi_hierarchy = pcv.roi.rectangle(img=masked2,
                                            x=250,
                                            y=100,
                                            h=200,
                                            w=200)

    # Decide what objects to keep
    roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(
        img=img,
        roi_contour=roi1,
        roi_hierarchy=roi_hierarchy,
        object_contour=id_objects,
        obj_hierarchy=obj_hierarchy,
        roi_type='partial')

    # Object combine kept objects
    obj, mask = pcv.object_composition(img=img,
                                       contours=roi_objects,
                                       hierarchy=hierarchy3)

    ############### Analysis ################

    outfile = False
    if args.writeimg == True:
        outfile = args.outdir + "/" + filename

    # Analyze the plant
    analysis_image = pcv.analyze_object(img=img, obj=obj, mask=mask)
    color_histogram = pcv.analyze_color(rgb_img=img,
                                        mask=kept_mask,
                                        hist_plot_type='all')
    top_x, bottom_x, center_v_x = pcv.x_axis_pseudolandmarks(img=img,
                                                             obj=obj,
                                                             mask=mask)
    top_y, bottom_y, center_v_y = pcv.y_axis_pseudolandmarks(img=img,
                                                             obj=obj,
                                                             mask=mask)

    # Print results of the analysis
    pcv.print_results(filename=args.result)
    pcv.output_mask(img,
                    kept_mask,
                    filename,
                    outdir=args.outdir,
                    mask_only=True)
Beispiel #12
0
def iterate_rois(img, c, h, rc, rh, args, masked=True, gi=False, shape=False, hist=True, hue=False):
    """Analyze each ROI separately and store results

    Parameters
    ----------
    img : ndarray
        rgb image
    c : list
        object contours
    h : list
        object countour hierarchy
    rc : list
        roi contours
    rh : list
        roi contour hierarchy
    threshold_mask : ndarray
        binary image (mask) from threshold steps
    args : dict
        commandline arguments and metadata from running the workflow
    masked : boolean
        whether to print masked rgb images for each roi
    gi : boolean
        whether to print greenness index false color
    shape : boolean
        whether to print object shapes on an image
    hist : boolean
        whether to print color histogram
    hue : boolean
        whether to save hsv color info and print the hue false color image

    Returns
    -------
        binary image of plant mask that includes both threshold and roi filter steps : ndarray

    """

    final_mask = np.zeros(shape=np.shape(img)[0:2], dtype='uint8')

    # Compute greenness
    if gi:
        img_gi = cppc.compute.greenness_index(img=img, mask=final_mask+1)

    if hue:
        img_h = pcv.rgb2gray_hsv(img, 'h')

    for i, rc_i in enumerate(rc):
        rh_i = rh[i]

        # Add ROI number to output. Before roi_objects so result has NA if no object.
        pcv.outputs.add_observation(
            sample='default',
            variable='roi',
            trait='roi',
            method='roi',
            scale='int',
            datatype=int,
            value=i,
            label='#')

        roi_obj, hierarchy_obj, submask, obj_area = pcv.roi_objects(
            img, roi_contour=rc_i, roi_hierarchy=rh_i, object_contour=c, obj_hierarchy=h, roi_type='partial')

        if obj_area == 0:

            print('\t!!! No object found in ROI', str(i))
            pcv.outputs.add_observation(
                sample='default',
                variable='plantarea',
                trait='plant area in sq mm',
                method='observations.area*pixelresolution^2',
                scale=cppc.pixelresolution,
                datatype="<class 'float'>",
                value=0,
                label='sq mm')

        else:

            # Combine multiple objects
            # ple plant objects within an roi together
            plant_object, plant_mask = pcv.object_composition(
                img=img, contours=roi_obj, hierarchy=hierarchy_obj)

            final_mask = pcv.image_add(final_mask, plant_mask)

            if gi:
                # Save greenness for individual ROI
                grnindex = cppc.utils.mean(img_gi, plant_mask)
                grnindexstd = cppc.utils.std(img_gi, plant_mask)
                pcv.outputs.add_observation(
                    sample='default',
                    variable='greenness_index',
                    trait='mean normalized greenness index',
                    method='g/sum(b+g+r)',
                    scale='[0,1]',
                    datatype="<class 'float'>",
                    value=float(grnindex),
                    label='/1')

                pcv.outputs.add_observation(
                    sample='default',
                    variable='greenness_index_std',
                    trait='std normalized greenness index',
                    method='g/sum(b+g+r)',
                    scale='[0,1]',
                    datatype="<class 'float'>",
                    value=float(grnindexstd),
                    label='/1')

            # Analyze all colors
            if hist:
                colorhist = pcv.analyze_color(img, plant_mask, 'all')
            elif hue:
                _ = pcv.analyze_color(img, plant_mask, 'hsv')

            # Analyze the shape of the current plant (always do this even if shape is False so you can get plant_area)
            img_shape = pcv.analyze_object(img, plant_object, plant_mask)
            plant_area = pcv.outputs.observations['default']['area']['value'] * cppc.pixelresolution**2
            pcv.outputs.add_observation(
                sample='default',
                variable='plantarea',
                trait='plant area in sq mm',
                method='observations.area*pixelresolution^2',
                scale=cppc.pixelresolution,
                datatype="<class 'float'>",
                value=plant_area,
                label='sq mm')
        # end if-else

        # At this point we have observations for one plant
        # We can write these out to a unique results file
        write_output(args, i)

        if args.writeimg and obj_area != 0:
            if shape:
                imgdir = os.path.join(args.outdir, 'shape_images', args.plantbarcode)
                os.makedirs(imgdir, exist_ok=True)
                pcv.print_image(img_shape, os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-shape.png'))

            if hist:
                imgdir = os.path.join(args.outdir, 'colorhist_images', args.plantbarcode)
                os.makedirs(imgdir, exist_ok=True)
                pcv.print_image(colorhist, os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-colorhist.png'))

            if masked:
                # save masked rgb image for entire tray but only 1 plant
                imgdir = os.path.join(args.outdir, 'maskedrgb_images')
                os.makedirs(imgdir, exist_ok=True)
                img_masked = pcv.apply_mask(img, plant_mask, 'black')
                pcv.print_image(
                    img_masked,
                    os.path.join(imgdir,
                                 args.imagename + '-roi' + str(i) + '-masked.png'))

            if hue:
                # save hue false color image for entire tray but only 1 plant
                imgdir = os.path.join(args.outdir, 'hue_images')
                os.makedirs(imgdir, exist_ok=True)
                fig_hue = pcv.visualize.pseudocolor(img_h*2, obj=None,
                                                    mask=plant_mask,
                                                    cmap=cppc.viz.get_cmap('hue'),
                                                    axes=False,
                                                    min_value=0, max_value=179,
                                                    background='black', obj_padding=0)
                fig_hue = cppc.viz.add_scalebar(fig_hue,
                                    pixelresolution=cppc.pixelresolution,
                                    barwidth=10,
                                    barlabel='1 cm',
                                    barlocation='lower left')
                fig_hue.set_size_inches(6, 6, forward=False)
                fig_hue.savefig(os.path.join(imgdir, args.imagename + '-roi' + str(i) + '-hue.png'),
                                bbox_inches='tight',
                                dpi=300)
                fig_hue.clf()

            if gi:
                # save grnness image of entire tray but only 1 plant
                imgdir = os.path.join(args.outdir, 'grnindex_images')
                os.makedirs(imgdir, exist_ok=True)
                fig_gi = pcv.visualize.pseudocolor(img_gi,
                                                   obj=None,
                                                   mask=plant_mask,
                                                   cmap='viridis',
                                                   axes=False,
                                                   min_value=0.3,
                                                   max_value=0.6,
                                                   background='black',
                                                   obj_padding=0)
                fig_gi = cppc.viz.add_scalebar(
                    fig_gi,
                    pixelresolution=cppc.pixelresolution,
                    barwidth=10,
                    barlabel='1 cm',
                    barlocation='lower left')
                fig_gi.set_size_inches(6, 6, forward=False)
                fig_gi.savefig(os.path.join(
                    imgdir,
                    args.imagename + '-roi' + str(i) + '-greenness.png'),
                               bbox_inches='tight',
                               dpi=300)
                fig_gi.clf()

        # end roi loop
    return final_mask
Beispiel #13
0
# Inputs:
#   bin_img1 - Binary image data to be compared to bin_img2
#   bin_img2 - Binary image data to be compared to bin_img1
bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_thresh)
pcv.print_image(img=bs, filename="upload/output_imgs/BSature_img.jpg")

# In[10]:

# Appy Mask (for VIS images, mask_color='white')

# Inputs:
#   rgb_img - RGB image data
#   mask - Binary mask image data
#   mask_color - 'white' or 'black'
rgb_img = img
masked = pcv.apply_mask(rgb_img, mask=bs, mask_color='white')
pcv.print_image(img=masked, filename="upload/output_imgs/VIS_img.jpg")

# In[11]:

# Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels

masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')
pcv.print_image(img=masked_b, filename="upload/output_imgs/GrayScale_img.jpg")
pcv.print_image(img=masked_a, filename="upload/output_imgs/Green-Magenta.jpg")

# In[12]:

maskeda_thresh = pcv.threshold.binary(gray_img=masked_a,
                                      threshold=115,
Beispiel #14
0
def generateMask(input, output, maskType=MASK_TYPES['BW']):
    pcv.params.debug = True  #set debug mode
    # pcv.params.debug_outdir="./output.txt" #set output directory

    # Read image (readimage mode defaults to native but if image is RGBA then specify mode='rgb')
    # Inputs:
    #   filename - Image file to be read in
    #   mode - Return mode of image; either 'native' (default), 'rgb', 'gray', 'envi', or 'csv'
    img, path, filename = pcv.readimage(filename=input, mode='rgb')

    s = pcv.rgb2gray_hsv(rgb_img=img, channel='s')

    # Threshold the saturation image
    s_thresh = pcv.threshold.binary(gray_img=s,
                                    threshold=85,
                                    max_value=255,
                                    object_type='light')

    # Median Blur
    s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5)
    s_cnt = pcv.median_blur(gray_img=s_thresh, ksize=5)

    # Convert RGB to LAB and extract the Blue channel
    b = pcv.rgb2gray_lab(rgb_img=img, channel='b')

    # Threshold the blue image
    b_thresh = pcv.threshold.binary(gray_img=b,
                                    threshold=160,
                                    max_value=255,
                                    object_type='light')
    b_cnt = pcv.threshold.binary(gray_img=b,
                                 threshold=160,
                                 max_value=255,
                                 object_type='light')

    # Fill small objects
    # b_fill = pcv.fill(b_thresh, 10)

    # Join the thresholded saturation and blue-yellow images
    bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_cnt)

    # Apply Mask (for VIS images, mask_color=white)
    masked = pcv.apply_mask(img=img, mask=bs, mask_color='white')

    # Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels
    masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')

    # Threshold the green-magenta and blue images
    maskeda_thresh = pcv.threshold.binary(gray_img=masked_a,
                                          threshold=115,
                                          max_value=255,
                                          object_type='dark')
    maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a,
                                           threshold=135,
                                           max_value=255,
                                           object_type='light')
    maskedb_thresh = pcv.threshold.binary(gray_img=masked_b,
                                          threshold=128,
                                          max_value=255,
                                          object_type='light')

    # Join the thresholded saturation and blue-yellow images (OR)
    ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)
    if maskType == MASK_TYPES['BW']:
        pcv.print_image(ab, filename=output)
        return (True, None)

    # Fill small objects
    ab_fill = pcv.fill(bin_img=ab, size=200)

    # Apply mask (for VIS images, mask_color=white)
    masked2 = pcv.apply_mask(img=masked, mask=ab_fill, mask_color='black')
    if maskType == MASK_TYPES['COLORED']:
        pcv.print_image(masked2, filename=output)
        return (True, None)

    return (False, 'Unknown mask type.')
    """
Beispiel #15
0
def main():
    # Get options
    args = options()

    debug = args.debug

    # Read image
    img, path, filename = pcv.readimage(args.image)

    # Pipeline step
    device = 0

    device, img1 = pcv.white_balance(device, img, debug, roi=(1000, 1000, 500, 500))

    device, a = pcv.rgb2gray_lab(img1, 'a', device, debug)

    device, img_binary = pcv.binary_threshold(a, 116, 255, 'dark', device, debug)

    mask = np.copy(img_binary)
    device, fill_image = pcv.fill(img_binary, mask, 300, device, debug)

    device, id_objects, obj_hierarchy = pcv.find_objects(img1, fill_image, device, debug)

    device, roi, roi_hierarchy = pcv.define_roi(img1, 'rectangle', device, None, 'default', debug, True,
                                                1800, 1600, -1500, -500)

    device, roi_objects, roi_obj_hierarchy, kept_mask, obj_area = pcv.roi_objects(img1, 'partial', roi, roi_hierarchy,
                                                                                  id_objects, obj_hierarchy, device,
                                                                                  debug)

    outfile = os.path.join(args.outdir, filename)

    device, color_header, color_data, color_img = pcv.analyze_color(img1, img1, kept_mask, 256, device, debug, None,
                                                                    'v', 'img', 300, outfile)

    device, masked = pcv.apply_mask(img1, kept_mask, 'white', device, debug)
    device, dilated = pcv.dilate(kept_mask, 10, 2, device, debug)
    device, plant_objects, plant_hierarchy = pcv.find_objects(img1, dilated, device, debug)

    img_copy = np.copy(img1)

    color = [(255, 0, 255), (0, 255, 0), (66, 134, 244), (255, 255, 0)]

    for i in range(0, len(plant_objects)):
        if len(plant_objects[i]) < 100:
            pass
        else:
            background = np.zeros((np.shape(img1)), np.uint8)
            cv2.drawContours(background, plant_objects, i, (255, 255, 255), -1, lineType=8, hierarchy=plant_hierarchy)
            device, grayimg = pcv.rgb2gray(background, device, debug)
            device, masked1 = pcv.apply_mask(masked, grayimg, 'white', device, debug)
            device, a1 = pcv.rgb2gray_lab(masked1, 'a', device, debug)
            device, img_binary1 = pcv.binary_threshold(a1, 116, 255, 'dark', device, debug)
            device, single_object, single_hierarchy = pcv.find_objects(masked1, img_binary1, device, debug)
            device, obj, mask = pcv.object_composition(img1, single_object, single_hierarchy, device, debug)
            device, shape_header, shape_data, shape_img = pcv.analyze_object(img, "img", obj, mask, device, debug)
            cv2.drawContours(img_copy, plant_objects, i, color[i], -1, lineType=8, hierarchy=plant_hierarchy)
            plantsize = "Plant matching this color is " + str(shape_data[1]) + " pixels large"
            cv2.putText(img_copy, plantsize, (500, (i + 1) * 300), cv2.FONT_HERSHEY_SIMPLEX, 5, color[i], 10)

    pcv.print_image(img_copy, os.path.join(args.outdir, "arabidopsis-out_shapes.jpg"))
def main():

    # Get options
    pcv.params.debug = args.debug  #set debug mode
    pcv.params.debug_outdir = args.outdir  #set output directory

    # Read image (readimage mode defaults to native but if image is RGBA then specify mode='rgb')
    # Inputs:
    #   filename - Image file to be read in
    #   mode - Return mode of image; either 'native' (default), 'rgb', 'gray', or 'csv'
    img, path, filename = pcv.readimage(filename=args.image, mode='rgb')

    ### SELECTING THE PLANT

    ### Attempt 5 combineren
    # Parameters
    hue_lower_tresh = 22  # 24
    hue_higher_tresh = 50  # 50
    saturation_lower_tresh = 138  # 140
    saturation_higher_tresh = 230  # 230
    value_lower_tresh = 120  # 125
    value_higher_tresh = 255  # 255
    # RGB color space
    green_lower_tresh = 105  # 110
    green_higher_tresh = 255  # 255
    red_lower_tresh = 22  # 24
    red_higher_thresh = 98  # 98
    blue_lower_tresh = 85  # 85
    blue_higher_tresh = 253  # 255
    # CIELAB color space
    #lab_blue_lower_tresh = 0            # Blue yellow channel
    #lab_blue_higher_tresh = 255

    s = pcv.rgb2gray_hsv(rgb_img=img, channel='h')
    mask, masked_image = pcv.threshold.custom_range(
        rgb_img=s,
        lower_thresh=[hue_lower_tresh],
        upper_thresh=[hue_higher_tresh],
        channel='gray')
    masked = pcv.apply_mask(rgb_img=img, mask=mask, mask_color='white')
    # Filtered on Hue
    s = pcv.rgb2gray_hsv(rgb_img=masked, channel='s')
    mask, masked_image = pcv.threshold.custom_range(
        rgb_img=s,
        lower_thresh=[saturation_lower_tresh],
        upper_thresh=[saturation_higher_tresh],
        channel='gray')
    masked = pcv.apply_mask(rgb_img=masked, mask=mask, mask_color='white')
    #filtered on saturation
    s = pcv.rgb2gray_hsv(rgb_img=masked, channel='v')
    mask, masked_image = pcv.threshold.custom_range(
        rgb_img=s,
        lower_thresh=[value_lower_tresh],
        upper_thresh=[value_higher_tresh],
        channel='gray')
    masked = pcv.apply_mask(rgb_img=masked, mask=mask, mask_color='white')
    #filtered on value
    mask, masked = pcv.threshold.custom_range(
        rgb_img=masked,
        lower_thresh=[0, green_lower_tresh, 0],
        upper_thresh=[255, green_higher_tresh, 255],
        channel='RGB')
    masked = pcv.apply_mask(rgb_img=masked, mask=mask, mask_color='white')
    #filtered on green
    mask, masked = pcv.threshold.custom_range(
        rgb_img=masked,
        lower_thresh=[red_lower_tresh, 0, 0],
        upper_thresh=[red_higher_thresh, 255, 255],
        channel='RGB')
    masked = pcv.apply_mask(rgb_img=masked, mask=mask, mask_color='white')
    #filtered on red
    mask_old, masked_old = pcv.threshold.custom_range(
        rgb_img=masked,
        lower_thresh=[0, 0, blue_lower_tresh],
        upper_thresh=[255, 255, blue_higher_tresh],
        channel='RGB')
    masked = pcv.apply_mask(rgb_img=masked_old,
                            mask=mask_old,
                            mask_color='white')
    #filtered on blue
    #b = pcv.rgb2gray_lab(rgb_img = masked, channel = 'b')   # Converting toe CIElab blue_yellow image
    #b_thresh =pcv.threshold.binary(gray_img = b, threshold=lab_blue_lower_tresh, max_value = lab_blue_higher_tresh)

    ###_____________________________________ Now to identify objects
    masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')

    # Threshold the green-magenta and blue images
    maskeda_thresh = pcv.threshold.binary(
        gray_img=masked_a,
        threshold=125,  # original 115
        max_value=255,
        object_type='dark')
    maskeda_thresh1 = pcv.threshold.binary(
        gray_img=masked_a,
        threshold=140,  # original 135
        max_value=255,
        object_type='light')
    maskedb_thresh = pcv.threshold.binary(gray_img=masked_b,
                                          threshold=128,
                                          max_value=255,
                                          object_type='light')

    ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)

    # Fill small objects
    # Inputs:
    #   bin_img - Binary image data
    #   size - Minimum object area size in pixels (must be an integer), and smaller objects will be filled
    ab = pcv.median_blur(gray_img=ab, ksize=3)
    ab_fill = pcv.fill(bin_img=ab, size=1000)
    #print("filled")
    # Apply mask (for VIS images, mask_color=white)
    masked2 = pcv.apply_mask(rgb_img=masked, mask=ab_fill, mask_color='white')
    # ID the objects
    id_objects, obj_hierarchy = pcv.find_objects(masked2, ab_fill)
    # Let's just take the largest
    roi1, roi_hierarchy = pcv.roi.rectangle(img=masked2,
                                            x=0,
                                            y=0,
                                            h=960,
                                            w=1280)  # Currently hardcoded

    # Decide which objects to keep
    # Inputs:
    #    img            = img to display kept objects
    #    roi_contour    = contour of roi, output from any ROI function
    #    roi_hierarchy  = contour of roi, output from any ROI function
    #    object_contour = contours of objects, output from pcv.find_objects function
    #    obj_hierarchy  = hierarchy of objects, output from pcv.find_objects function
    #    roi_type       = 'partial' (default, for partially inside), 'cutto', or
    #    'largest' (keep only largest contour)
    with HiddenPrints():
        roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(
            img=img,
            roi_contour=roi1,
            roi_hierarchy=roi_hierarchy,
            object_contour=id_objects,
            obj_hierarchy=obj_hierarchy,
            roi_type='partial')
    # Object combine kept objects
    # Inputs:
    #   img - RGB or grayscale image data for plotting
    #   contours - Contour list
    #   hierarchy - Contour hierarchy array
    obj, mask = pcv.object_composition(img=img,
                                       contours=roi_objects,
                                       hierarchy=hierarchy3)
    #print("final plant")
    new_im = Image.fromarray(masked2)
    new_im.save("output//" + args.filename + "last_masked.png")

    ##################_________________ Analysis

    outfile = args.outdir + "/" + filename
    # Here come all the analyse functions.
    # pcv.acute_vertex(img, obj, 30, 15, 100)

    color_img = pcv.analyze_color(rgb_img=img,
                                  mask=kept_mask,
                                  hist_plot_type=None)
    #new_im = Image.fromarray(color_img)
    #new_im.save(args.filename + "color_img.png")

    # Find shape properties, output shape image (optional)

    # Inputs:
    #   img - RGB or grayscale image data
    #   obj- Single or grouped contour object
    #   mask - Binary image mask to use as mask for moments analysis
    shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask)
    new_im = Image.fromarray(shape_img)
    new_im.save("output//" + args.filename + "shape_img.png")
    # Shape properties relative to user boundary line (optional)

    # Inputs:
    #   img - RGB or grayscale image data
    #   obj - Single or grouped contour object
    #   mask - Binary mask of selected contours
    #   line_position - Position of boundary line (a value of 0 would draw a line
    #                   through the bottom of the image)
    boundary_img1 = pcv.analyze_bound_horizontal(img=img,
                                                 obj=obj,
                                                 mask=mask,
                                                 line_position=1680)
    new_im = Image.fromarray(boundary_img1)
    new_im.save("output//" + args.filename + "boundary_img.png")
    # Determine color properties: Histograms, Color Slices, output color analyzed histogram (optional)

    # Inputs:
    #   rgb_img - RGB image data
    #   mask - Binary mask of selected contours
    #   hist_plot_type - None (default), 'all', 'rgb', 'lab', or 'hsv'
    #                    This is the data to be printed to the SVG histogram file
    color_histogram = pcv.analyze_color(rgb_img=img,
                                        mask=kept_mask,
                                        hist_plot_type='all')
    #new_im = Image.fromarray(color_histogram)
    #new_im.save(args.filename + "color_histogram_img.png")

    # Pseudocolor the grayscale image

    # Inputs:
    #     gray_img - Grayscale image data
    #     obj - Single or grouped contour object (optional), if provided the pseudocolored image gets
    #           cropped down to the region of interest.
    #     mask - Binary mask (optional)
    #     background - Background color/type. Options are "image" (gray_img, default), "white", or "black". A mask
    #                  must be supplied.
    #     cmap - Colormap
    #     min_value - Minimum value for range of interest
    #     max_value - Maximum value for range of interest
    #     dpi - Dots per inch for image if printed out (optional, if dpi=None then the default is set to 100 dpi).
    #     axes - If False then the title, x-axis, and y-axis won't be displayed (default axes=True).
    #     colorbar - If False then the colorbar won't be displayed (default colorbar=True)
    pseudocolored_img = pcv.visualize.pseudocolor(gray_img=s,
                                                  mask=kept_mask,
                                                  cmap='jet')
    #new_im = Image.fromarray(pseudocolored_img)
    #new_im.save(args.filename + "pseudocolored.png")

    # Write shape and color data to results file
    pcv.print_results(filename=args.result)
def main_side():
    # Setting "args"

    # Get options
    pcv.params.debug = args.debug  #set debug mode
    pcv.params.debug_outdir = args.outdir  #set output directory

    # Read image (readimage mode defaults to native but if image is RGBA then specify mode='rgb')
    # Inputs:
    #   filename - Image file to be read in
    #   mode - Return mode of image; either 'native' (default), 'rgb', 'gray', or 'csv'
    filename = args.image
    img = cv2.imread(args.image, flags=0)
    #img = pcv.invert(img)
    path, img_name = os.path.split(args.image)
    img_bkgrd = cv2.imread("background.png", flags=0)
    #print(img)
    #print(img_bkgrd)
    bkg_sub_img = pcv.image_subtract(img_bkgrd, img)
    bkg_sub_thres_img, masked_img = pcv.threshold.custom_range(
        rgb_img=bkg_sub_img,
        lower_thresh=[50],
        upper_thresh=[255],
        channel='gray')
    # Laplace filtering (identify edges based on 2nd derivative)

    # Inputs:
    #   gray_img - Grayscale image data
    #   ksize - Aperture size used to calculate the second derivative filter,
    #           specifies the size of the kernel (must be an odd integer)
    #   scale - Scaling factor applied (multiplied) to computed Laplacian values
    #           (scale = 1 is unscaled)
    lp_img = pcv.laplace_filter(gray_img=img, ksize=1, scale=1)

    # Plot histogram of grayscale values
    pcv.visualize.histogram(gray_img=lp_img)

    # Lapacian image sharpening, this step will enhance the darkness of the edges detected
    lp_shrp_img = pcv.image_subtract(gray_img1=img, gray_img2=lp_img)

    # Plot histogram of grayscale values, this helps to determine thresholding value
    pcv.visualize.histogram(gray_img=lp_shrp_img)
    # Sobel filtering
    # 1st derivative sobel filtering along horizontal axis, kernel = 1)

    # Inputs:
    #   gray_img - Grayscale image data
    #   dx - Derivative of x to analyze
    #   dy - Derivative of y to analyze
    #   ksize - Aperture size used to calculate 2nd derivative, specifies the size of the kernel and must be an odd integer
    # NOTE: Aperture size must be greater than the largest derivative (ksize > dx & ksize > dy)
    sbx_img = pcv.sobel_filter(gray_img=img, dx=1, dy=0, ksize=1)

    # 1st derivative sobel filtering along vertical axis, kernel = 1)
    sby_img = pcv.sobel_filter(gray_img=img, dx=0, dy=1, ksize=1)

    # Combine the effects of both x and y filters through matrix addition
    # This will capture edges identified within each plane and emphasize edges found in both images

    # Inputs:
    #   gray_img1 - Grayscale image data to be added to gray_img2
    #   gray_img2 - Grayscale image data to be added to gray_img1
    sb_img = pcv.image_add(gray_img1=sbx_img, gray_img2=sby_img)

    # Use a lowpass (blurring) filter to smooth sobel image

    # Inputs:
    #   gray_img - Grayscale image data
    #   ksize - Kernel size (integer or tuple), (ksize, ksize) box if integer input,
    #           (n, m) box if tuple input
    mblur_img = pcv.median_blur(gray_img=sb_img, ksize=1)

    # Inputs:
    #   gray_img - Grayscale image data
    mblur_invert_img = pcv.invert(gray_img=mblur_img)

    # combine the smoothed sobel image with the laplacian sharpened image
    # combines the best features of both methods as described in "Digital Image Processing" by Gonzalez and Woods pg. 169
    edge_shrp_img = pcv.image_add(gray_img1=mblur_invert_img,
                                  gray_img2=lp_shrp_img)

    # Perform thresholding to generate a binary image
    tr_es_img = pcv.threshold.binary(gray_img=edge_shrp_img,
                                     threshold=145,
                                     max_value=255,
                                     object_type='dark')

    # Do erosion with a 3x3 kernel (ksize=3)

    # Inputs:
    #   gray_img - Grayscale (usually binary) image data
    #   ksize - The size used to build a ksize x ksize
    #            matrix using np.ones. Must be greater than 1 to have an effect
    #   i - An integer for the number of iterations
    e1_img = pcv.erode(gray_img=tr_es_img, ksize=3, i=1)
    # Bring the two object identification approaches together.
    # Using a logical OR combine object identified by background subtraction and the object identified by derivative filter.

    # Inputs:
    #   bin_img1 - Binary image data to be compared in bin_img2
    #   bin_img2 - Binary image data to be compared in bin_img1
    comb_img = pcv.logical_or(bin_img1=e1_img, bin_img2=bkg_sub_thres_img)

    # Get masked image, Essentially identify pixels corresponding to plant and keep those.

    # Inputs:
    #   rgb_img - RGB image data
    #   mask - Binary mask image data
    #   mask_color - 'black' or 'white'
    masked_erd = pcv.apply_mask(rgb_img=img, mask=comb_img, mask_color='black')

    # Need to remove the edges of the image, we did that by generating a set of rectangles to mask the edges
    # img is (1280 X 960)
    # mask for the bottom of the image

    # Inputs:
    #   img - RGB or grayscale image data
    #   p1 - Point at the top left corner of the rectangle (tuple)
    #   p2 - Point at the bottom right corner of the rectangle (tuple)
    #   color 'black' (default), 'gray', or 'white'
    #
    masked1, box1_img, rect_contour1, hierarchy1 = pcv.rectangle_mask(img=img,
                                                                      p1=(500,
                                                                          875),
                                                                      p2=(720,
                                                                          960))
    # mask the edges
    masked2, box2_img, rect_contour2, hierarchy2 = pcv.rectangle_mask(img=img,
                                                                      p1=(1,
                                                                          1),
                                                                      p2=(1279,
                                                                          959))
    bx12_img = pcv.logical_or(bin_img1=box1_img, bin_img2=box2_img)
    inv_bx1234_img = bx12_img  # we dont invert
    inv_bx1234_img = bx12_img
    #inv_bx1234_img = pcv.invert(gray_img=bx12_img)

    edge_masked_img = pcv.apply_mask(rgb_img=masked_erd,
                                     mask=inv_bx1234_img,
                                     mask_color='black')
    #print("here we create a mask")
    mask, masked = pcv.threshold.custom_range(rgb_img=edge_masked_img,
                                              lower_thresh=[25],
                                              upper_thresh=[175],
                                              channel='gray')
    masked = pcv.apply_mask(rgb_img=masked, mask=mask, mask_color='white')
    #print("end")
    # Identify objects

    # Inputs:
    #   img - RGB or grayscale image data for plotting
    #   mask - Binary mask used for detecting contours
    id_objects, obj_hierarchy = pcv.find_objects(img=edge_masked_img,
                                                 mask=mask)

    # Define ROI

    # Inputs:
    #   img - RGB or grayscale image to plot the ROI on
    #   x - The x-coordinate of the upper left corner of the rectangle
    #   y - The y-coordinate of the upper left corner of the rectangle
    #   h - The height of the rectangle
    #   w - The width of the rectangle
    roi1, roi_hierarchy = pcv.roi.rectangle(img=edge_masked_img,
                                            x=100,
                                            y=100,
                                            h=800,
                                            w=1000)

    # Decide which objects to keep

    # Inputs:
    #    img            = img to display kept objects
    #    roi_contour    = contour of roi, output from any ROI function
    #    roi_hierarchy  = contour of roi, output from any ROI function
    #    object_contour = contours of objects, output from pcv.find_objects function
    #    obj_hierarchy  = hierarchy of objects, output from pcv.find_objects function
    #    roi_type       = 'partial' (default, for partially inside), 'cutto', or
    #    'largest' (keep only largest contour)
    with HiddenPrints():
        roi_objects, hierarchy5, kept_mask, obj_area = pcv.roi_objects(
            img=edge_masked_img,
            roi_contour=roi1,
            roi_hierarchy=roi_hierarchy,
            object_contour=id_objects,
            obj_hierarchy=obj_hierarchy,
            roi_type='largest')

    rgb_img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB)

    # Inputs:
    #   img - RGB or grayscale image data for plotting
    #   contours - Contour list
    #   hierarchy - Contour hierarchy array
    o, m = pcv.object_composition(img=rgb_img,
                                  contours=roi_objects,
                                  hierarchy=hierarchy5)

    ### Analysis ###

    outfile = False
    if args.writeimg == True:
        outfile = args.outdir + "/" + filename

    # Perform signal analysis

    # Inputs:
    #   img - RGB or grayscale image data
    #   obj- Single or grouped contour object
    #   mask - Binary image mask to use as mask for moments analysis
    shape_img = pcv.analyze_object(img=img, obj=o, mask=m)
    new_im = Image.fromarray(shape_img)
    new_im.save("output//" + args.filename + "shape_img_side.png")

    # Inputs:
    #   gray_img - 8 or 16-bit grayscale image data
    #   mask - Binary mask made from selected contours
    #   bins - Number of classes to divide the spectrum into
    #   histplot - If True, plots the histogram of intensity values
    nir_hist = pcv.analyze_nir_intensity(gray_img=img,
                                         mask=kept_mask,
                                         bins=256,
                                         histplot=True)

    # Pseudocolor the grayscale image to a colormap

    # Inputs:
    #     gray_img - Grayscale image data
    #     obj - Single or grouped contour object (optional), if provided the pseudocolored image gets cropped down to the region of interest.
    #     mask - Binary mask (optional)
    #     background - Background color/type. Options are "image" (gray_img), "white", or "black". A mask must be supplied.
    #     cmap - Colormap
    #     min_value - Minimum value for range of interest
    #     max_value - Maximum value for range of interest
    #     dpi - Dots per inch for image if printed out (optional, if dpi=None then the default is set to 100 dpi).
    #     axes - If False then the title, x-axis, and y-axis won't be displayed (default axes=True).
    #     colorbar - If False then the colorbar won't be displayed (default colorbar=True)
    pseudocolored_img = pcv.visualize.pseudocolor(gray_img=img,
                                                  mask=kept_mask,
                                                  cmap='viridis')

    # Perform shape analysis

    # Inputs:
    #   img - RGB or grayscale image data
    #   obj- Single or grouped contour object
    #   mask - Binary image mask to use as mask for moments analysis
    shape_imgs = pcv.analyze_object(img=rgb_img, obj=o, mask=m)

    # Write shape and nir data to results file
    pcv.print_results(filename=args.result)
Beispiel #18
0
def main():
    # Get options
    args = options()

    pcv.params.debug = args.debug  # set debug mode
    pcv.params.debug_outdir = args.outdir  # set output directory

    # Read image
    img, path, filename = pcv.readimage(filename=args.image)

    # Convert RGB to HSV and extract the saturation channel
    s = pcv.rgb2gray_hsv(rgb_img=img, channel='s')

    # Threshold the saturation image
    s_thresh = pcv.threshold.binary(gray_img=s,
                                    threshold=85,
                                    max_value=255,
                                    object_type='light')

    # Median Blur
    s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5)
    s_cnt = pcv.median_blur(gray_img=s_thresh, ksize=5)

    # Convert RGB to LAB and extract the Blue channel
    b = pcv.rgb2gray_lab(rgb_img=img, channel='b')

    # Threshold the blue image
    b_thresh = pcv.threshold.binary(gray_img=b,
                                    threshold=160,
                                    max_value=255,
                                    object_type='light')
    b_cnt = pcv.threshold.binary(gray_img=b,
                                 threshold=160,
                                 max_value=255,
                                 object_type='light')

    # Fill small objects
    # b_fill = pcv.fill(b_thresh, 10)

    # Join the thresholded saturation and blue-yellow images
    bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_cnt)

    # Apply Mask (for VIS images, mask_color=white)
    masked = pcv.apply_mask(img=img, mask=bs, mask_color='white')

    # Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels
    masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')
    cv2.imwrite("masked.jpeg", masked)

    # Threshold the green-magenta and blue images
    maskeda_thresh = pcv.threshold.binary(gray_img=masked_a,
                                          threshold=115,
                                          max_value=255,
                                          object_type='dark')
    maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a,
                                           threshold=135,
                                           max_value=255,
                                           object_type='light')
    maskedb_thresh = pcv.threshold.binary(gray_img=masked_b,
                                          threshold=128,
                                          max_value=255,
                                          object_type='light')

    # Join the thresholded saturation and blue-yellow images (OR)
    ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)

    # Fill small objects
    ab_fill = pcv.fill(bin_img=ab, size=200)

    # Apply mask (for VIS images, mask_color=white)
    masked2 = pcv.apply_mask(img=masked, mask=ab_fill, mask_color='white')
    cv2.imwrite("masked2.jpeg", masked2)
Beispiel #19
0
def main():
    
    # Get options
    args = options()
    
    # Set variables
    pcv.params.debug = args.debug        # Replace the hard-coded debug with the debug flag
    img_file = args.image     # Replace the hard-coded input image with image flag

    ############### Image read-in ################

    # Read target image
    img, path, filename = pcv.readimage(filename = img_file, mode = "rgb")
    
    ############### Find scale and crop ################
    
    # find colour card in the image to be analysed
    df, start, space = pcv.transform.find_color_card(rgb_img = img)
    if int(start[0]) < 2000:
            img = imutils.rotate_bound(img, -90)
            rotated = 1
            df, start, space = pcv.transform.find_color_card(rgb_img = img)
    else: rotated = 0
    #if img.shape[0] > 6000:
    #    rotated = 1
    #else: rotated = 0
    img_mask = pcv.transform.create_color_card_mask(rgb_img = img, radius = 10, start_coord = start, spacing = space, ncols = 4, nrows = 6)
    
    # write the spacing of the colour card to file as size marker   
    with open(r'size_marker.csv', 'a') as f:
        writer = csv.writer(f)
        writer.writerow([filename, space[0]])

    # define a bounding rectangle around the colour card
    x_cc,y_cc,w_cc,h_cc = cv2.boundingRect(img_mask)
    x_cc = int(round(x_cc - 0.3 * w_cc))
    y_cc = int(round(y_cc - 0.3 * h_cc))
    h_cc = int(round(h_cc * 1.6))
    w_cc = int(round(w_cc * 1.6))

    # crop out colour card
    start_point = (x_cc, y_cc)
    end_point = (x_cc+w_cc, y_cc+h_cc)
    colour = (0, 0, 0)
    thickness = -1
    crop_img = cv2.rectangle(img, start_point, end_point, colour, thickness)
    
    ############### Fine segmentation ################
    
    # Threshold A and B channels of the LAB colourspace and the Hue channel of the HSV colourspace
    l_thresh, _ = pcv.threshold.custom_range(img=crop_img, lower_thresh=[70,0,0], upper_thresh=[255,255,255], channel='LAB')
    a_thresh, _ = pcv.threshold.custom_range(img=crop_img, lower_thresh=[0,0,0], upper_thresh=[255,145,255], channel='LAB')
    b_thresh, _ = pcv.threshold.custom_range(img=crop_img, lower_thresh=[0,0,123], upper_thresh=[255,255,255], channel='LAB')
    h_thresh_low, _ = pcv.threshold.custom_range(img=crop_img, lower_thresh=[0,0,0], upper_thresh=[130,255,255], channel='HSV')
    h_thresh_high, _ = pcv.threshold.custom_range(img=crop_img, lower_thresh=[150,0,0], upper_thresh=[255,255,255], channel='HSV')
    h_thresh = pcv.logical_or(h_thresh_low, h_thresh_high)

    # Join the thresholded images to keep only consensus pixels
    ab = pcv.logical_and(b_thresh, a_thresh)
    lab = pcv.logical_and(l_thresh, ab)
    labh = pcv.logical_and(lab, h_thresh)

    # Fill small objects
    labh_clean = pcv.fill(labh, 200)

    # Dilate to close broken borders
    #labh_dilated = pcv.dilate(labh_clean, 4, 1)
    labh_dilated = labh_clean

    # Apply mask (for VIS images, mask_color=white)
    masked = pcv.apply_mask(crop_img, labh_dilated, "white")

    # Identify objects
    contours, hierarchy = pcv.find_objects(crop_img, labh_dilated)

    # Define ROI

    if rotated == 1:
        roi_height = 3000
        roi_lwr_bound = y_cc + (h_cc * 0.5) - roi_height
        roi_contour, roi_hierarchy= pcv.roi.rectangle(x=1000, y=roi_lwr_bound, h=roi_height, w=2000, img=crop_img)
    else:
        roi_height = 1500
        roi_lwr_bound = y_cc + (h_cc * 0.5) - roi_height
        roi_contour, roi_hierarchy= pcv.roi.rectangle(x=2000, y=roi_lwr_bound, h=roi_height, w=2000, img=crop_img)

    # Decide which objects to keep
    filtered_contours, filtered_hierarchy, mask, area = pcv.roi_objects(img = crop_img,
                                                                roi_type = 'partial',
                                                                roi_contour = roi_contour,
                                                                roi_hierarchy = roi_hierarchy,
                                                                object_contour = contours,
                                                                obj_hierarchy = hierarchy)
    # Combine kept objects
    obj, mask = pcv.object_composition(crop_img, filtered_contours, filtered_hierarchy)

    ############### Analysis ################

    outfile=False
    if args.writeimg==True:
        outfile_black=args.outdir+"/"+filename+"_black"
        outfile_white=args.outdir+"/"+filename+"_white"
        outfile_analysed=args.outdir+"/"+filename+"_analysed"

    # analyse shape
    shape_img = pcv.analyze_object(crop_img, obj, mask)
    pcv.print_image(shape_img, outfile_analysed)

    # analyse colour
    colour_img = pcv.analyze_color(crop_img, mask, 'hsv')

    # keep the segmented plant for visualisation
    picture_mask = pcv.apply_mask(crop_img, mask, "black")
    pcv.print_image(picture_mask, outfile_black)
    
    picture_mask = pcv.apply_mask(crop_img, mask, "white")
    pcv.print_image(picture_mask, outfile_white)

    # print out results
    pcv.outputs.save_results(filename=args.result, outformat="json")
def main():

    # Set variables
    args = options()
    pcv.params.debug = args.debug

    # Read and rotate image
    img, path, filename = pcv.readimage(filename=args.image)
    img = pcv.rotate(img, -90, False)

    # Create mask from LAB b channel
    l = pcv.rgb2gray_lab(rgb_img=img, channel='b')
    l_thresh = pcv.threshold.binary(gray_img=l,
                                    threshold=115,
                                    max_value=255,
                                    object_type='dark')
    l_mblur = pcv.median_blur(gray_img=l_thresh, ksize=5)

    # Apply mask to image
    masked = pcv.apply_mask(img=img, mask=l_mblur, mask_color='white')
    ab_fill = pcv.fill(bin_img=l_mblur, size=50)

    # Extract plant object from image
    id_objects, obj_hierarchy = pcv.find_objects(img=img, mask=ab_fill)
    roi1, roi_hierarchy = pcv.roi.rectangle(img=masked,
                                            x=150,
                                            y=270,
                                            h=100,
                                            w=100)
    roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(
        img=img,
        roi_contour=roi1,
        roi_hierarchy=roi_hierarchy,
        object_contour=id_objects,
        obj_hierarchy=obj_hierarchy,
        roi_type='partial')
    obj, mask = pcv.object_composition(img=img,
                                       contours=roi_objects,
                                       hierarchy=hierarchy3)

    ############### Analysis ################

    # Analyze shape properties
    analysis_image = pcv.analyze_object(img=img, obj=obj, mask=mask)
    boundary_image2 = pcv.analyze_bound_horizontal(img=img,
                                                   obj=obj,
                                                   mask=mask,
                                                   line_position=370)

    # Analyze colour properties
    color_histogram = pcv.analyze_color(rgb_img=img,
                                        mask=kept_mask,
                                        hist_plot_type='all')

    # Analyze shape independent of size
    top_x, bottom_x, center_v_x = pcv.x_axis_pseudolandmarks(img=img,
                                                             obj=obj,
                                                             mask=mask)
    top_y, bottom_y, center_v_y = pcv.y_axis_pseudolandmarks(img=img,
                                                             obj=obj,
                                                             mask=mask)

    # Print results
    pcv.print_results(filename='{}'.format(args.result))
    pcv.print_image(img=color_histogram,
                    filename='{}_color_hist.jpg'.format(args.outdir))
    pcv.print_image(img=kept_mask, filename='{}_mask.jpg'.format(args.outdir))
                             object_type='light')

# In[18]:

# Optional step in vis workflow that I tried. Fills small objects, not very useful here.
b_fill = pcv.fill(b_thresh, 10)

# In[22]:

# Joining the s_mblur image with the b_cnt image.
bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_cnt)

# In[24]:

# The image above is now used as a "mask" over the original image to wipe the background.
masked = pcv.apply_mask(img=img, mask=bs, mask_color='white')

# In[25]:

# Extracting the Green-Magenta channel.
masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')

# In[26]:

# Extracting the Blue-Yellow channel.
masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')

# In[27]:

# Thresholding the Green-Magenta channel using "dark".
maskeda_thresh = pcv.threshold.binary(gray_img=masked_a,
Beispiel #22
0
    # Collect the cropped image
    cropped_img = image[int(roi[1]):int(roi[1] + roi[3]),
                        int(roi[0]):int(roi[0] + roi[2])]

    # Convert RGB to HSV and extract saturation channel
    # The HSV value can be changed to be h, s, or v depending on the colour of the flower
    saturation_img = pcv.rgb2gray_hsv(cropped_img, 'h')

    # Threshold the saturation img
    # Depending on the output of the saturation image, the value can be light or dark
    # Light or dark is what defines what part of the image its to be removed
    saturation_thresh = pcv.threshold.binary(saturation_img, 85, 255, 'light')

    # Apply median blur
    saturation_mblur = pcv.median_blur(saturation_thresh, 5)

    # Convert RGB to LAB and extract blue channel
    # Like the HSV function this can be l, a, or b depending on the colour of the flower
    blue_channel_img = pcv.rgb2gray_lab(cropped_img, 'l')
    blue_channel_cnt = pcv.threshold.binary(blue_channel_img, 160, 255,
                                            'light')

    # Join thresholded saturated and blue channel imgs
    blue_saturated = pcv.logical_or(saturation_mblur, blue_channel_cnt)

    # Apply black colour mask
    masked = pcv.apply_mask(cropped_img, blue_saturated, 'black')

    # Save image
    cv2.imwrite('tmp/' + image, masked)
def plantCVProcess(img, x, y, w, h):

    # Convert RGB to HSV and extract the saturation channel
    s = pcv.rgb2gray_hsv(rgb_img=img, channel='s')

    # Threshold the saturation image
    s_thresh = pcv.threshold.binary(gray_img=s, threshold=85, max_value=255, object_type='light')

    # Median Blur
    s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5)
    s_cnt = pcv.median_blur(gray_img=s_thresh, ksize=5)

    # Convert RGB to LAB and extract the Blue channel
    b = pcv.rgb2gray_lab(rgb_img=img, channel='b')

    # Threshold the blue image
    b_thresh = pcv.threshold.binary(gray_img=b, threshold=160, max_value=255, object_type='light')
    b_cnt = pcv.threshold.binary(gray_img=b, threshold=160, max_value=255, object_type='light')

    # Fill small objects
    # b_fill = pcv.fill(b_thresh, 10)

    # Join the thresholded saturation and blue-yellow images
    bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_cnt)

    # Apply Mask (for VIS images, mask_color=white)
    masked = pcv.apply_mask(img=img, mask=bs, mask_color='white')

    # Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels
    masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')

    # Threshold the green-magenta and blue images
    maskeda_thresh = pcv.threshold.binary(gray_img=masked_a, threshold=115, max_value=255, object_type='dark')
    maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a, threshold=135, max_value=255, object_type='light')
    maskedb_thresh = pcv.threshold.binary(gray_img=masked_b, threshold=128, max_value=255, object_type='light')

    # Join the thresholded saturation and blue-yellow images (OR)
    ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)

    # Fill small objects
    ab_fill = pcv.fill(bin_img=ab, size=200)

    # Apply mask (for VIS images, mask_color=white)
    masked2 = pcv.apply_mask(img=masked, mask=ab_fill, mask_color='white')

    # Identify objects
    id_objects, obj_hierarchy = pcv.find_objects(img=masked2, mask=ab_fill)

    # Define ROI
    roi1, roi_hierarchy = pcv.roi.rectangle(img=masked2, x=x, y=y, h=h, w=w)

    # Decide which objects to keep
    roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(img=img, roi_contour=roi1,
                                                               roi_hierarchy=roi_hierarchy,
                                                               object_contour=id_objects,
                                                               obj_hierarchy=obj_hierarchy,
                                                               roi_type='partial')

    # Object combine kept objects
    obj, mask = pcv.object_composition(img=img, contours=roi_objects, hierarchy=hierarchy3)

    ############### Analysis ################

    # Find shape properties, output shape image (optional)
    shape_imgs = pcv.analyze_object(img=img, obj=obj, mask=mask)

    # Shape properties relative to user boundary line (optional)
    boundary_img1 = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=mask, line_position=1680)

    # Determine color properties: Histograms, Color Slices, output color analyzed histogram (optional)
    color_histogram = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type='all')

    # Pseudocolor the grayscale image
    pseudocolored_img = pcv.visualize.pseudocolor(gray_img=s, mask=mask, cmap='jet')

    return print_results()
def root():
    				uploaded_file = st.file_uploader("Choose an image...", type="jpg")
    				if uploaded_file is not None:
    					inp = Image.open(uploaded_file)
    					inp.save('input.jpg')
    					img, path, filename = pcv.readimage(filename='input.jpg')
    					image = Image.open('input.jpg')
    					st.image(image, caption='Original Image',use_column_width=True)
                    # Convert RGB to HSV and extract the saturation channel
    # Inputs:
    #   rgb_image - RGB image data 
    #   channel - Split by 'h' (hue), 's' (saturation), or 'v' (value) channel
					
    					s = pcv.rgb2gray_hsv(rgb_img=img, channel='s')
    					pcv.print_image(s, "plant/rgbtohsv.png")
    					image = Image.open('plant/rgbtohsv.png')
    					st.image(image, caption='RGB to HSV', use_column_width=True)
    					s_thresh = pcv.threshold.binary(gray_img=s, threshold=85, max_value=255, object_type='light')
    					pcv.print_image(s_thresh, "plant/binary_threshold.png")
    					image = Image.open('plant/binary_threshold.png')
    					st.image(image, caption='Binary Threshold',use_column_width=True)
                   
    # Median Blur to clean noise 

    # Inputs: 
    #   gray_img - Grayscale image data 
    #   ksize - Kernel size (integer or tuple), (ksize, ksize) box if integer input,
    #           (n, m) box if tuple input 

    					s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5)
    					pcv.print_image(s_mblur, "plant/Median_blur.png")
    					image = Image.open('plant/Median_blur.png')
    					st.image(image, caption='Median Blur',use_column_width=True)
                    
     # An alternative to using median_blur is gaussian_blur, which applies 
    # a gaussian blur filter to the image. Depending on the image, one 
    # technique may be more effective than others. 

    # Inputs:
    #   img - RGB or grayscale image data
    #   ksize - Tuple of kernel size
    #   sigma_x - Standard deviation in X direction; if 0 (default), 
    #            calculated from kernel size
    #   sigma_y - Standard deviation in Y direction; if sigmaY is 
    #            None (default), sigmaY is taken to equal sigmaX
                
    					gaussian_img = pcv.gaussian_blur(img=s_thresh, ksize=(5, 5), sigma_x=0, sigma_y=None)
    # Convert RGB to LAB and extract the blue channel ('b')

    # Input:
    #   rgb_img - RGB image data 
    #   channel- Split by 'l' (lightness), 'a' (green-magenta), or 'b' (blue-yellow) channel
    					b = pcv.rgb2gray_lab(rgb_img=img, channel='b')
    					b_thresh = pcv.threshold.binary(gray_img=b, threshold=160, max_value=255, 
                                object_type='light')
                     # Join the threshold saturation and blue-yellow images with a logical or operation 

    # Inputs: 
    #   bin_img1 - Binary image data to be compared to bin_img2
    #   bin_img2 - Binary image data to be compared to bin_img1

    
    					bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_thresh)
    					pcv.print_image(bs, "plant/threshold comparison.png")
    					image = Image.open('plant/threshold comparison.png')
    					st.image(image, caption='Threshold Comparision',use_column_width=True)
                    
 # Appy Mask (for VIS images, mask_color='white')

    # Inputs:
    #   img - RGB or grayscale image data 
    #   mask - Binary mask image data 
    #   mask_color - 'white' or 'black' 
    
    					masked = pcv.apply_mask(img=img, mask=bs, mask_color='white')
    					pcv.print_image(masked, "plant/Apply_mask.png")
    					image = Image.open('plant/Apply_mask.png')
    					st.image(image, caption='Applied Mask',use_column_width=True)
                   # Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels
    					masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    					masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')
                     # Threshold the green-magenta and blue images
    					maskeda_thresh = pcv.threshold.binary(gray_img=masked_a, threshold=115, max_value=255, object_type='dark')
    					maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a, threshold=135,max_value=255, object_type='light')
    					maskedb_thresh = pcv.threshold.binary(gray_img=masked_b, threshold=128, max_value=255, object_type='light')
    					pcv.print_image( maskeda_thresh, "plant/maskeda_thresh.png")
    					pcv.print_image(maskeda_thresh1, "plant/maskeda_thresh1.png")
    					pcv.print_image(maskedb_thresh, "plant/maskedb_thresh1.png")
  
    					image = Image.open('plant/maskeda_thresh.png')
    					st.image(image, caption='Threshold green-magneta and blue image',use_column_width=True)


   # Join the thresholded saturation and blue-yellow images (OR)
    					ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    					ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)
        # Opening filters out bright noise from an image.

# Inputs:
#   gray_img - Grayscale or binary image data
#   kernel - Optional neighborhood, expressed as an array of 1's and 0's. If None (default),
#   uses cross-shaped structuring element.
    					opened_ab = pcv.opening(gray_img=ab)

# Depending on the situation it might be useful to use the 
# exclusive or (pcv.logical_xor) function. 

# Inputs: 
#   bin_img1 - Binary image data to be compared to bin_img2
#   bin_img2 - Binary image data to be compared to bin_img1
    					xor_img = pcv.logical_xor(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
# Fill small objects (reduce image noise) 

# Inputs: 
#   bin_img - Binary image data 
#   size - Minimum object area size in pixels (must be an integer), and smaller objects will be filled
    					ab_fill = pcv.fill(bin_img=ab, size=200)
# Closing filters out dark noise from an image.

# Inputs:
#   gray_img - Grayscale or binary image data
#   kernel - Optional neighborhood, expressed as an array of 1's and 0's. If None (default),
#   uses cross-shaped structuring element.
    					closed_ab = pcv.closing(gray_img=ab_fill)
# Apply mask (for VIS images, mask_color=white)
    					masked2 = pcv.apply_mask(img=masked, mask=ab_fill, mask_color='white')
# Identify objects
# Inputs: 
#   img - RGB or grayscale image data for plotting 
#   mask - Binary mask used for detecting contours 
    					id_objects, obj_hierarchy = pcv.find_objects(img=masked2, mask=ab_fill)
# Define the region of interest (ROI) 
# Inputs: 
#   img - RGB or grayscale image to plot the ROI on 
#   x - The x-coordinate of the upper left corner of the rectangle 
#   y - The y-coordinate of the upper left corner of the rectangle 
#   h - The height of the rectangle 
#   w - The width of the rectangle 
    					roi1, roi_hierarchy= pcv.roi.rectangle(img=masked2, x=50, y=50, h=100, w=100)
# Decide which objects to keep
# Inputs:
#    img            = img to display kept objects
#    roi_contour    = contour of roi, output from any ROI function
#    roi_hierarchy  = contour of roi, output from any ROI function
#    object_contour = contours of objects, output from pcv.find_objects function
#    obj_hierarchy  = hierarchy of objects, output from pcv.find_objects function
#    roi_type       = 'partial' (default, for partially inside the ROI), 'cutto', or 
#                     'largest' (keep only largest contour)
    					roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(img=img, roi_contour=roi1, 
                                                               roi_hierarchy=roi_hierarchy, 
                                                               object_contour=id_objects, 
                                                               obj_hierarchy=obj_hierarchy,
                                                               roi_type='partial')
# Object combine kept objects
# Inputs:
#   img - RGB or grayscale image data for plotting 
#   contours - Contour list 
#   hierarchy - Contour hierarchy array 
    					obj, mask = pcv.object_composition(img=img, contours=roi_objects, hierarchy=hierarchy3)
############### Analysis ################ 
# Find shape properties, data gets stored to an Outputs class automatically
# Inputs:
#   img - RGB or grayscale image data 
#   obj- Single or grouped contour object
#   mask - Binary image mask to use as mask for moments analysis 
    					analysis_image = pcv.analyze_object(img=img, obj=obj, mask=mask)
    					pcv.print_image(analysis_image, "plant/analysis_image.png")
    					image = Image.open('plant/analysis_image.png')
    					st.image(image, caption='Analysis_image',use_column_width=True)
# Shape properties relative to user boundary line (optional)
# Inputs:
#   img - RGB or grayscale image data 
#   obj - Single or grouped contour object 
#   mask - Binary mask of selected contours 
#   line_position - Position of boundary line (a value of 0 would draw a line 
#                   through the bottom of the image) 
    					boundary_image2 = pcv.analyze_bound_horizontal(img=img, obj=obj, mask=mask, 
                                               line_position=370)
    					pcv.print_image(boundary_image2, "plant/boundary_image2.png")
    					image = Image.open('plant/boundary_image2.png')
    					st.image(image, caption='Boundary Image',use_column_width=True)
# Determine color properties: Histograms, Color Slices and Pseudocolored Images, output color analyzed images (optional)
# Inputs:
#   rgb_img - RGB image data
#   mask - Binary mask of selected contours 
#   hist_plot_type - None (default), 'all', 'rgb', 'lab', or 'hsv'
#                    This is the data to be printed to the SVG histogram file  
    					color_histogram = pcv.analyze_color(rgb_img=img, mask=kept_mask, hist_plot_type='all')
# Print the histogram out to save it 
    					pcv.print_image(img=color_histogram, filename="plant/vis_tutorial_color_hist.jpg")
    					image = Image.open('plant/vis_tutorial_color_hist.jpg')
    					st.image(image, caption='Color Histogram',use_column_width=True)
# Divide plant object into twenty equidistant bins and assign pseudolandmark points based upon their 
# actual (not scaled) position. Once this data is scaled this approach may provide some information 
# regarding shape independent of size.
# Inputs:
#   img - RGB or grayscale image data 
#   obj - Single or grouped contour object 
#   mask - Binary mask of selected contours 
    					top_x, bottom_x, center_v_x = pcv.x_axis_pseudolandmarks(img=img, obj=obj, mask=mask)
    					top_y, bottom_y, center_v_y = pcv.y_axis_pseudolandmarks(img=img, obj=obj, mask=mask)
# The print_results function will take the measurements stored when running any (or all) of these functions, format, 
# and print an output text file for data analysis. The Outputs class stores data whenever any of the following functions
# are ran: analyze_bound_horizontal, analyze_bound_vertical, analyze_color, analyze_nir_intensity, analyze_object, 
# fluor_fvfm, report_size_marker_area, watershed. If no functions have been run, it will print an empty text file 
    					pcv.print_results(filename='vis_tutorial_results.txt')
Beispiel #25
0
def cluster_contour_splitimg(device, img, grouped_contour_indexes, contours, outdir=None, file=None,
                             filenames=None, debug=None):

    """
    This function takes clustered contours and splits them into multiple images, also does a check to make sure that
    the number of inputted filenames matches the number of clustered contours.

    Inputs:
    device                  = Counter for image processing steps
    img                     = ideally a masked RGB image.
    grouped_contour_indexes = output of cluster_contours, indexes of clusters of contours
    contours                = contours to cluster, output of cluster_contours
    outdir                  = out directory for output images
    file                    = the name of the input image to use as a plantcv name,
                              output of filename from read_image function
    filenames               = input txt file with list of filenames in order from top to bottom left to right
                              (likely list of genotypes)
    debug                   = print debugging images

    Returns:
    device                  = pipeline step counter
    output_path             = array of paths to output images

    :param device: int
    :param img: ndarray
    :param grouped_contour_indexes: list
    :param contours: list
    :param outdir: str
    :param file: str
    :param filenames: str
    :param debug: str
    :return device: int
    :return output_path: str
    """

    # get names to split also to check the target number of objects

    i = datetime.now()
    timenow = i.strftime('%m-%d-%Y_%H:%M:%S')

    if file == None:
        filebase = timenow
    else:
        filebase = file[:-4]

    if filenames == None:
        l = len(grouped_contour_indexes)
        namelist = []
        for x in range(0, l):
            namelist.append(x)
    else:
        with open(filenames, 'r') as n:
            namelist = n.read().splitlines()
        n.close()

    # make sure the number of objects matches the namelist, and if not, remove the smallest grouped countor
    # removing contours is not ideal but the lists don't match there is a warning to check output

    if len(namelist) == len(grouped_contour_indexes):
        corrected_contour_indexes = grouped_contour_indexes
    elif len(namelist) < len(grouped_contour_indexes):
        print("Warning number of names is less than number of grouped contours, attempting to fix, to double check "
              "output")
        diff = len(grouped_contour_indexes) - len(namelist)
        size = []
        for i, x in enumerate(grouped_contour_indexes):
            totallen = []
            for a in x:
                g = i
                la = len(contours[a])
                totallen.append(la)
            sumlen = np.sum(totallen)
            size.append((sumlen, g, i))

        dtype = [('len', int), ('group', list), ('index', int)]
        lencontour = np.array(size, dtype=dtype)
        lencontour = np.sort(lencontour, order='len')

        rm_contour = lencontour[diff:]
        rm_contour = np.sort(rm_contour, order='group')
        corrected_contour_indexes = []

        for x in rm_contour:
            index = x[2]
            corrected_contour_indexes.append(grouped_contour_indexes[index])

    elif len(namelist) > len(grouped_contour_indexes):
        print("Warning number of names is more than number of  grouped contours, double check output")
        diff = len(namelist) - len(grouped_contour_indexes)
        namelist = namelist[0:-diff]
        corrected_contour_indexes = grouped_contour_indexes

    # create filenames

    group_names = []
    for i, x in enumerate(namelist):
        plantname = str(filebase) + '_' + str(x) + '_p' + str(i) + '.jpg'
        group_names.append(plantname)

    # split image

    output_path = []

    for y, x in enumerate(corrected_contour_indexes):
        if outdir != None:
            savename = os.path.join(str(outdir), group_names[y])
        else:
            savename = os.path.join(".", group_names[y])
        iy, ix, iz = np.shape(img)
        mask = np.zeros((iy, ix, 3), dtype=np.uint8)
        masked_img = np.copy(img)
        for a in x:
            cv2.drawContours(mask, contours, a, (255, 255, 255), -1, lineType=8)

        mask_binary = mask[:, :, 0]

        if np.sum(mask_binary) == 0:
            pass
        else:
            retval, mask_binary = cv2.threshold(mask_binary, 254, 255, cv2.THRESH_BINARY)
            device, masked1 = apply_mask(masked_img, mask_binary, 'white', device, debug)
            if outdir != None:
                print_image(masked1, savename)
            output_path.append(savename)

            if debug == 'print':
                print_image(masked1, (str(device) + '_clusters.png'))
            elif debug == 'plot':
                if len(np.shape(masked1)) == 3:
                    plot_image(masked1)
                else:
                    plot_image(masked1, cmap='gray')
                    plot_image(masked1)

    return device, output_path
def main():
    # Get options
    args = options()

    debug = args.debug

    # Read image
    img, path, filename = pcv.readimage(args.image)

    # Pipeline step
    device = 0

    device, img1 = pcv.white_balance(device, img, debug,
                                     (100, 100, 1000, 1000))
    img = img1

    seedmask, path1, filename1 = pcv.readimage(args.mask)
    device, seedmask = pcv.rgb2gray(seedmask, device, debug)
    device, inverted = pcv.invert(seedmask, device, debug)
    device, masked_img = pcv.apply_mask(img, inverted, 'white', device, debug)

    device, img_gray_sat = pcv.rgb2gray_hsv(masked_img, 's', device, debug)

    device, img_binary = pcv.binary_threshold(img_gray_sat, 50, 255, 'light',
                                              device, debug)

    img_binary1 = np.copy(img_binary)
    device, fill_image = pcv.fill(img_binary1, img_binary, 300, device, debug)

    device, seed_objects, seed_hierarchy = pcv.find_objects(
        img, fill_image, device, debug)

    device, roi1, roi_hierarchy1 = pcv.define_roi(img, 'rectangle', device,
                                                  None, 'default', debug, True,
                                                  1800, 1600, -1500, -500)

    device, roi_objects, roi_obj_hierarchy, kept_mask, obj_area = pcv.roi_objects(
        img, 'partial', roi1, roi_hierarchy1, seed_objects, seed_hierarchy,
        device, debug)

    img_copy = np.copy(img)
    for i in range(0, len(roi_objects)):
        rand_color = color_palette(1)
        cv2.drawContours(img_copy,
                         roi_objects,
                         i,
                         rand_color[0],
                         -1,
                         lineType=8,
                         hierarchy=roi_obj_hierarchy)

    pcv.print_image(
        img_copy,
        os.path.join(args.outdir, filename[:-4]) + "-seed-confetti.jpg")

    shape_header = []  # Store the table header
    table = []  # Store the PlantCV measurements for each seed in a table
    for i in range(0, len(roi_objects)):
        if roi_obj_hierarchy[0][i][
                3] == -1:  # Only continue if the object is an outermost contour

            # Object combine kept objects
            # Inputs:
            #    contours = object list
            #    device   = device number. Used to count steps in the pipeline
            #    debug    = None, print, or plot. Print = save to file, Plot = print to screen.
            device, obj, mask = pcv.object_composition(
                img, [roi_objects[i]], np.array([[roi_obj_hierarchy[0][i]]]),
                device, None)
            if obj is not None:
                # Measure the area and other shape properties of each seed
                # Inputs:
                #    img             = image object (most likely the original), color(RGB)
                #    imgname         = name of image
                #    obj             = single or grouped contour object
                #    device          = device number. Used to count steps in the pipeline
                #    debug           = None, print, or plot. Print = save to file, Plot = print to screen.
                #    filename        = False or image name. If defined print image
                device, shape_header, shape_data, shape_img = pcv.analyze_object(
                    img, "img", obj, mask, device, None)

                if shape_data is not None:
                    table.append(shape_data[1])

    data_array = np.array(table)
    maxval = np.argmax(data_array)
    maxseed = np.copy(img)
    cv2.drawContours(maxseed, roi_objects, maxval, (0, 255, 0), 50)

    imgtext = "This image has " + str(len(data_array)) + " seeds"
    sizeseed = "The largest seed is in green and is " + str(
        data_array[maxval]) + " pixels"
    cv2.putText(maxseed, imgtext, (500, 500), cv2.FONT_HERSHEY_SIMPLEX, 5,
                (0, 255, 0), 10)
    cv2.putText(maxseed, sizeseed, (500, 1000), cv2.FONT_HERSHEY_SIMPLEX, 5,
                (255, 0, 255), 10)
    pcv.print_image(maxseed,
                    os.path.join(args.outdir, filename[:-4]) + "-maxseed.jpg")
def report_size_marker_area(img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None,
                            thresh=None):
    """Detects a size marker in a specified region and reports its size and eccentricity

    Inputs:
    img             = An RGB or grayscale image to plot the marker object on
    roi_contour     = A region of interest contour (e.g. output from pcv.roi.rectangle or other methods)
    roi_hierarchy   = A region of interest contour hierarchy (e.g. output from pcv.roi.rectangle or other methods)
    marker          = 'define' or 'detect'. If define it means you set an area, if detect it means you want to
                      detect within an area
    objcolor        = Object color is 'dark' or 'light' (is the marker darker or lighter than the background)
    thresh_channel  = 'h', 's', or 'v' for hue, saturation or value
    thresh          = Binary threshold value (integer)

    Returns:
    analysis_images = List of output images

    :param img: numpy.ndarray
    :param roi_contour: list
    :param roi_hierarchy: numpy.ndarray
    :param marker: str
    :param objcolor: str
    :param thresh_channel: str
    :param thresh: int
    :return: analysis_images: list
    """

    params.device += 1
    # Make a copy of the reference image
    ref_img = np.copy(img)
    # If the reference image is grayscale convert it to color
    if len(np.shape(ref_img)) == 2:
        ref_img = cv2.cvtColor(ref_img, cv2.COLOR_GRAY2BGR)

    # Marker components
    # If the marker type is "defined" then the marker_mask and marker_contours are equal to the input ROI
    # Initialize a binary image
    roi_mask = np.zeros(np.shape(img)[:2], dtype=np.uint8)
    # Draw the filled ROI on the mask
    cv2.drawContours(roi_mask, roi_contour, -1, (255), -1)
    marker_mask = []
    marker_contour = []

    # If the marker type is "detect" then we will use the ROI to isolate marker contours from the input image
    if marker.upper() == 'DETECT':
        # We need to convert the input image into an one of the HSV channels and then threshold it
        if thresh_channel is not None and thresh is not None:
            # Mask the input image
            masked = apply_mask(rgb_img=ref_img, mask=roi_mask, mask_color="black")
            # Convert the masked image to hue, saturation, or value
            marker_hsv = rgb2gray_hsv(rgb_img=masked, channel=thresh_channel)
            # Threshold the HSV image
            marker_bin = binary_threshold(gray_img=marker_hsv, threshold=thresh, max_value=255, object_type=objcolor)
            # Identify contours in the masked image
            contours, hierarchy = find_objects(img=ref_img, mask=marker_bin)
            # Filter marker contours using the input ROI
            kept_contours, kept_hierarchy, kept_mask, obj_area = roi_objects(img=ref_img, object_contour=contours,
                                                                             obj_hierarchy=hierarchy,
                                                                             roi_contour=roi_contour,
                                                                             roi_hierarchy=roi_hierarchy,
                                                                             roi_type="partial")
            # If there are more than one contour detected, combine them into one
            # These become the marker contour and mask
            marker_contour, marker_mask = object_composition(img=ref_img, contours=kept_contours,
                                                             hierarchy=kept_hierarchy)
        else:
            fatal_error('thresh_channel and thresh must be defined in detect mode')
    elif marker.upper() == "DEFINE":
        # Identify contours in the masked image
        contours, hierarchy = find_objects(img=ref_img, mask=roi_mask)
        # If there are more than one contour detected, combine them into one
        # These become the marker contour and mask
        marker_contour, marker_mask = object_composition(img=ref_img, contours=contours, hierarchy=hierarchy)
    else:
        fatal_error("marker must be either 'define' or 'detect' but {0} was provided.".format(marker))

    # Calculate the moments of the defined marker region
    m = cv2.moments(marker_mask, binaryImage=True)
    # Calculate the marker area
    marker_area = m['m00']

    # Fit a bounding ellipse to the marker
    center, axes, angle = cv2.fitEllipse(marker_contour)
    major_axis = np.argmax(axes)
    minor_axis = 1 - major_axis
    major_axis_length = axes[major_axis]
    minor_axis_length = axes[minor_axis]
    # Calculate the bounding ellipse eccentricity
    eccentricity = np.sqrt(1 - (axes[minor_axis] / axes[major_axis]) ** 2)

    # Make a list to store output images
    analysis_image = []
    cv2.drawContours(ref_img, marker_contour, -1, (255, 0, 0), 5)
    # out_file = os.path.splitext(filename)[0] + '_sizemarker.jpg'
    # print_image(ref_img, out_file)
    analysis_image.append(ref_img)
    if params.debug is 'print':
        print_image(ref_img, os.path.join(params.debug_outdir, str(params.device) + '_marker_shape.png'))
    elif params.debug is 'plot':
        plot_image(ref_img)

    outputs.add_observation(variable='marker_area', trait='marker area',
                            method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int,
                            value=marker_area, label='pixels')
    outputs.add_observation(variable='marker_ellipse_major_axis', trait='marker ellipse major axis length',
                            method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int,
                            value=major_axis_length, label='pixels')
    outputs.add_observation(variable='marker_ellipse_minor_axis', trait='marker ellipse minor axis length',
                            method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int,
                            value=minor_axis_length, label='pixels')
    outputs.add_observation(variable='marker_ellipse_eccentricity', trait='marker ellipse eccentricity',
                            method='plantcv.plantcv.report_size_marker_area', scale='none', datatype=float,
                            value=eccentricity, label='none')

    # Store images
    outputs.images.append(analysis_image)

    return analysis_image
    def get_coordinates(self):
        if self.debug:
            pcv.params.debug = 'print'  # set debug mode
            pcv.params.debug_outdir = './images/DracaenaVision/'  # set output directory

        blue_threshold = pcv.threshold.binary(gray_img=self.blue,
                                              threshold=50,
                                              max_value=255,
                                              object_type='dark')
        pcv.apply_mask(self.colour_image,
                       mask=blue_threshold,
                       mask_color='white')

        # Calculate moments of binary image
        moments = cv.moments(blue_threshold)

        # Calculate x,y coordinate of center
        self.centre_x = int(moments["m10"] / moments["m00"])
        self.centre_y = int(moments["m01"] / moments["m00"])

        # Put text and highlight the center
        cv.circle(self.colour_image, (self.centre_x, self.centre_y), 5,
                  (255, 255, 255), -1)
        cv.putText(self.colour_image, "Centre",
                   (self.centre_x - 25, self.centre_y - 25),
                   cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)

        red_threshold = pcv.threshold.binary(gray_img=self.red,
                                             threshold=70,
                                             max_value=255,
                                             object_type='light')

        # Erode/Dilate the red threshold to remove noise
        red_threshold = pcv.erode(red_threshold, ksize=5, i=1)
        red_threshold = pcv.dilate(red_threshold, ksize=5, i=2)

        # Create mask of area that is of interest
        mask_pts = np.array(
            [[576, 415], [800, 420], [1105, 630], [720, 685], [285, 590]],
            np.int32).reshape((-1, 1, 2))
        area_of_interest = np.zeros(self.colour_image.shape[:2], np.uint8)
        cv.fillPoly(area_of_interest, [mask_pts], (255, 255, 255))

        red_threshold_in_area_of_interest = pcv.logical_and(
            area_of_interest, red_threshold)
        pcv.apply_mask(img=self.colour_image,
                       mask=red_threshold_in_area_of_interest,
                       mask_color='black')

        params = cv.SimpleBlobDetector_Params()
        # Unused filters
        params.filterByCircularity = False
        params.filterByConvexity = False
        params.filterByInertia = False
        # Area filter
        params.filterByArea = True
        params.maxArea = 50000
        params.minArea = 100
        # Colour filter
        params.filterByColor = True
        params.blobColor = 255
        # Misc options
        params.minDistBetweenBlobs = 100

        blob_detector = cv.SimpleBlobDetector_create(params)

        keypoints = blob_detector.detect(red_threshold, mask=area_of_interest)
        keypoints.sort(reverse=True, key=lambda kp: kp.size)

        now = datetime.now().strftime('%d-%m %H %M %S')

        im_with_keypoints = cv.drawKeypoints(
            self.colour_image, keypoints, np.array([]), (0, 0, 255),
            cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

        self.detection_image = im_with_keypoints.copy()
        if self.full_screen:
            self.detection_image = cv.resize(self.detection_image,
                                             (1920, 1080))
        cv.imshow(self.window_name, self.detection_image)
        cv.waitKey(1)

        cv.polylines(im_with_keypoints, [mask_pts],
                     True, (0, 255, 0),
                     thickness=3)
        cv.imwrite('./images/DracaenaVision/{} detections.png'.format(now),
                   im_with_keypoints)

        coordinates = []

        for keypoint in keypoints:
            x = keypoint.pt[0]
            y = keypoint.pt[1]

            # For each X and Y determine depth (z-coordinate)
            depth = -1
            step_size_to_centre_x = (self.centre_x - x) / 100
            step_size_to_centre_y = (self.centre_y - y) / 100

            for i in range(21):
                delta_x = step_size_to_centre_x * i
                delta_y = step_size_to_centre_y * i
                new_x = round(x + delta_x)
                new_y = round(y + delta_y)
                depth = self.depth_array[new_y, new_x]
                if depth < 0.55:
                    print('Depth found with delta ({},{})'.format(
                        delta_x, delta_y))
                    print('Depth of: {} at {} X, {} Y'.format(
                        depth, new_x, new_y))
                    break

            z = depth if depth < 0.55 or depth <= 0 else 0.55

            # Determine the angle at which the tool should be held towards the plant
            angle = 180 - math.degrees(
                math.atan2(y - self.centre_y, x - self.centre_x))

            coordinate = [x, y, z, angle]
            coordinates.append(coordinate)

        return coordinates
def report_size_marker_area(img,
                            roi_contour,
                            roi_hierarchy,
                            marker='define',
                            objcolor='dark',
                            thresh_channel=None,
                            thresh=None):
    """Detects a size marker in a specified region and reports its size and eccentricity

    Inputs:
    img             = An RGB or grayscale image to plot the marker object on
    roi_contour     = A region of interest contour (e.g. output from pcv.roi.rectangle or other methods)
    roi_hierarchy   = A region of interest contour hierarchy (e.g. output from pcv.roi.rectangle or other methods)
    marker          = 'define' or 'detect'. If define it means you set an area, if detect it means you want to
                      detect within an area
    objcolor        = Object color is 'dark' or 'light' (is the marker darker or lighter than the background)
    thresh_channel  = 'h', 's', or 'v' for hue, saturation or value
    thresh          = Binary threshold value (integer)

    Returns:
    analysis_images = List of output images

    :param img: numpy.ndarray
    :param roi_contour: list
    :param roi_hierarchy: numpy.ndarray
    :param marker: str
    :param objcolor: str
    :param thresh_channel: str
    :param thresh: int
    :return: analysis_images: list
    """
    # Store debug
    debug = params.debug
    params.debug = None

    params.device += 1
    # Make a copy of the reference image
    ref_img = np.copy(img)
    # If the reference image is grayscale convert it to color
    if len(np.shape(ref_img)) == 2:
        ref_img = cv2.cvtColor(ref_img, cv2.COLOR_GRAY2BGR)

    # Marker components
    # If the marker type is "defined" then the marker_mask and marker_contours are equal to the input ROI
    # Initialize a binary image
    roi_mask = np.zeros(np.shape(img)[:2], dtype=np.uint8)
    # Draw the filled ROI on the mask
    cv2.drawContours(roi_mask, roi_contour, -1, (255), -1)
    marker_mask = []
    marker_contour = []

    # If the marker type is "detect" then we will use the ROI to isolate marker contours from the input image
    if marker.upper() == 'DETECT':
        # We need to convert the input image into an one of the HSV channels and then threshold it
        if thresh_channel is not None and thresh is not None:
            # Mask the input image
            masked = apply_mask(rgb_img=ref_img,
                                mask=roi_mask,
                                mask_color="black")
            # Convert the masked image to hue, saturation, or value
            marker_hsv = rgb2gray_hsv(rgb_img=masked, channel=thresh_channel)
            # Threshold the HSV image
            marker_bin = binary_threshold(gray_img=marker_hsv,
                                          threshold=thresh,
                                          max_value=255,
                                          object_type=objcolor)
            # Identify contours in the masked image
            contours, hierarchy = find_objects(img=ref_img, mask=marker_bin)
            # Filter marker contours using the input ROI
            kept_contours, kept_hierarchy, kept_mask, obj_area = roi_objects(
                img=ref_img,
                object_contour=contours,
                obj_hierarchy=hierarchy,
                roi_contour=roi_contour,
                roi_hierarchy=roi_hierarchy,
                roi_type="partial")
            # If there are more than one contour detected, combine them into one
            # These become the marker contour and mask
            marker_contour, marker_mask = object_composition(
                img=ref_img, contours=kept_contours, hierarchy=kept_hierarchy)
        else:
            fatal_error(
                'thresh_channel and thresh must be defined in detect mode')
    elif marker.upper() == "DEFINE":
        # Identify contours in the masked image
        contours, hierarchy = find_objects(img=ref_img, mask=roi_mask)
        # If there are more than one contour detected, combine them into one
        # These become the marker contour and mask
        marker_contour, marker_mask = object_composition(img=ref_img,
                                                         contours=contours,
                                                         hierarchy=hierarchy)
    else:
        fatal_error(
            "marker must be either 'define' or 'detect' but {0} was provided.".
            format(marker))

    # Calculate the moments of the defined marker region
    m = cv2.moments(marker_mask, binaryImage=True)
    # Calculate the marker area
    marker_area = m['m00']

    # Fit a bounding ellipse to the marker
    center, axes, angle = cv2.fitEllipse(marker_contour)
    major_axis = np.argmax(axes)
    minor_axis = 1 - major_axis
    major_axis_length = axes[major_axis]
    minor_axis_length = axes[minor_axis]
    # Calculate the bounding ellipse eccentricity
    eccentricity = np.sqrt(1 - (axes[minor_axis] / axes[major_axis])**2)

    cv2.drawContours(ref_img, marker_contour, -1, (255, 0, 0), 5)
    analysis_image = ref_img

    # Reset debug mode
    params.debug = debug

    if params.debug is 'print':
        print_image(
            ref_img,
            os.path.join(params.debug_outdir,
                         str(params.device) + '_marker_shape.png'))
    elif params.debug is 'plot':
        plot_image(ref_img)

    outputs.add_observation(variable='marker_area',
                            trait='marker area',
                            method='plantcv.plantcv.report_size_marker_area',
                            scale='pixels',
                            datatype=int,
                            value=marker_area,
                            label='pixels')
    outputs.add_observation(variable='marker_ellipse_major_axis',
                            trait='marker ellipse major axis length',
                            method='plantcv.plantcv.report_size_marker_area',
                            scale='pixels',
                            datatype=int,
                            value=major_axis_length,
                            label='pixels')
    outputs.add_observation(variable='marker_ellipse_minor_axis',
                            trait='marker ellipse minor axis length',
                            method='plantcv.plantcv.report_size_marker_area',
                            scale='pixels',
                            datatype=int,
                            value=minor_axis_length,
                            label='pixels')
    outputs.add_observation(variable='marker_ellipse_eccentricity',
                            trait='marker ellipse eccentricity',
                            method='plantcv.plantcv.report_size_marker_area',
                            scale='none',
                            datatype=float,
                            value=eccentricity,
                            label='none')

    # Store images
    outputs.images.append(analysis_image)

    return analysis_image
def cluster_contour_splitimg(rgb_img, grouped_contour_indexes, contours, hierarchy, outdir=None, file=None,
                             filenames=None):

    """
    This function takes clustered contours and splits them into multiple images, also does a check to make sure that
    the number of inputted filenames matches the number of clustered contours.

    Inputs:
    rgb_img                 = RGB image data
    grouped_contour_indexes = output of cluster_contours, indexes of clusters of contours
    contours                = contours to cluster, output of cluster_contours
    hierarchy               = hierarchy of contours, output of find_objects
    outdir                  = out directory for output images
    file                    = the name of the input image to use as a plantcv name,
                              output of filename from read_image function
    filenames               = input txt file with list of filenames in order from top to bottom left to right
                              (likely list of genotypes)

    Returns:
    output_path             = array of paths to output images

    :param rgb_img: numpy.ndarray
    :param grouped_contour_indexes: list
    :param contours: list
    :param hierarchy: numpy.ndarray
    :param outdir: str
    :param file: str
    :param filenames: str
    :return output_path: str
    """

    params.device += 1

    sys.stderr.write(
        'This function has been updated to include object hierarchy so object holes can be included\n')

    # get names to split also to check the target number of objects

    i = datetime.now()
    timenow = i.strftime('%m-%d-%Y_%H:%M:%S')

    if file is None:
        filebase = timenow
    else:
        filebase = os.path.splitext(file)[0]

    if filenames is None:
        l = len(grouped_contour_indexes)
        namelist = []
        for x in range(0, l):
            namelist.append(x)
    else:
        with open(filenames, 'r') as n:
            namelist = n.read().splitlines()
        n.close()

    # make sure the number of objects matches the namelist, and if not, remove the smallest grouped countor
    # removing contours is not ideal but the lists don't match there is a warning to check output

    if len(namelist) == len(grouped_contour_indexes):
        corrected_contour_indexes = grouped_contour_indexes
    elif len(namelist) < len(grouped_contour_indexes):
        print("Warning number of names is less than number of grouped contours, attempting to fix, to double check "
              "output")
        diff = len(grouped_contour_indexes) - len(namelist)
        size = []
        for i, x in enumerate(grouped_contour_indexes):
            totallen = []
            for a in x:
                g = i
                la = len(contours[a])
                totallen.append(la)
            sumlen = np.sum(totallen)
            size.append((sumlen, g, i))

        dtype = [('len', int), ('group', list), ('index', int)]
        lencontour = np.array(size, dtype=dtype)
        lencontour = np.sort(lencontour, order='len')

        rm_contour = lencontour[diff:]
        rm_contour = np.sort(rm_contour, order='group')
        corrected_contour_indexes = []

        for x in rm_contour:
            index = x[2]
            corrected_contour_indexes.append(grouped_contour_indexes[index])

    elif len(namelist) > len(grouped_contour_indexes):
        print("Warning number of names is more than number of  grouped contours, double check output")
        diff = len(namelist) - len(grouped_contour_indexes)
        namelist = namelist[0:-diff]
        corrected_contour_indexes = grouped_contour_indexes

    # create filenames

    group_names = []
    group_names1 = []
    for i, x in enumerate(namelist):
        plantname = str(filebase) + '_' + str(x) + '_p' + str(i) + '.jpg'
        maskname = str(filebase) + '_' + str(x) + '_p' + str(i) + '_mask.jpg'
        group_names.append(plantname)
        group_names1.append(maskname)

    # split image

    output_path = []
    output_imgs = []
    output_masks = []

    for y, x in enumerate(corrected_contour_indexes):
        if outdir is not None:
            savename = os.path.join(str(outdir), group_names[y])
            savename1 = os.path.join(str(outdir), group_names1[y])
        else:
            savename = os.path.join(".", group_names[y])
            savename1 = os.path.join(".", group_names1[y])
        iy, ix, iz = np.shape(rgb_img)
        mask = np.zeros((iy, ix, 3), dtype=np.uint8)
        masked_img = np.copy(rgb_img)
        for a in x:
            if hierarchy[0][a][3] > -1:
                cv2.drawContours(mask, contours, a, (0, 0, 0), -1, lineType=8, hierarchy=hierarchy)
            else:
                cv2.drawContours(mask, contours, a, (255, 255, 255), -1, lineType=8, hierarchy=hierarchy)

        mask_binary = mask[:, :, 0]

        if np.sum(mask_binary) == 0:
            pass
        else:
            retval, mask_binary = cv2.threshold(mask_binary, 254, 255, cv2.THRESH_BINARY)
            masked1 = apply_mask(masked_img, mask_binary, 'white')
            output_imgs.append(masked1)
            output_masks.append(mask_binary)
            if outdir is not None:
                print_image(masked1, savename)
                print_image(mask_binary, savename1)
            output_path.append(savename)

            if params.debug == 'print':
                print_image(masked1, os.path.join(params.debug_outdir, str(params.device) + '_clusters.png'))
                print_image(mask_binary, os.path.join(params.debug_outdir, str(params.device) + '_clusters_mask.png'))
            elif params.debug == 'plot':
                plot_image(masked1)
                plot_image(mask_binary, cmap='gray')

    return output_path, output_imgs, output_masks
Beispiel #31
0
def segmentation(imgW, imgNIR, shape):
    # VIS example from PlantCV with few modifications

    # Higher value = more strict selection
    s_threshold = 165
    b_threshold = 200

    # Read image
    img = imread(imgW)
    #img = cvtColor(img, COLOR_BGR2RGB)
    imgNIR = imread(imgNIR)
    #imgNIR = cvtColor(imgNIR, COLOR_BGR2RGB)
    #img, path, img_filename = pcv.readimage(filename=imgW, mode="native")
    #imgNIR, pathNIR, imgNIR_filename = pcv.readimage(filename=imgNIR, mode="native")

    # Convert RGB to HSV and extract the saturation channel
    s = pcv.rgb2gray_hsv(rgb_img=img, channel='s')

    # Threshold the saturation image
    s_thresh = pcv.threshold.binary(gray_img=s, threshold=s_threshold, max_value=255, object_type='light')

    # Median Blur
    s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5)
    s_cnt = pcv.median_blur(gray_img=s_thresh, ksize=5)

    # Convert RGB to LAB and extract the Blue channel
    b = pcv.rgb2gray_lab(rgb_img=img, channel='b')

    # Threshold the blue image ORIGINAL 160
    b_thresh = pcv.threshold.binary(gray_img=b, threshold=b_threshold, max_value=255, object_type='light')
    b_cnt = pcv.threshold.binary(gray_img=b, threshold=b_threshold, max_value=255, object_type='light')

    # Join the thresholded saturation and blue-yellow images
    bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_cnt)

    # Apply Mask (for VIS images, mask_color=white)
    masked = pcv.apply_mask(img=img, mask=bs, mask_color='white')

    # Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels
    masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')

    # Threshold the green-magenta and blue images
    # 115
    # 135
    # 128
    maskeda_thresh = pcv.threshold.binary(gray_img=masked_a, threshold=115, max_value=255, object_type='dark')
    maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a, threshold=135, max_value=255, object_type='light')
    maskedb_thresh = pcv.threshold.binary(gray_img=masked_b, threshold=128, max_value=255, object_type='light')

    # Join the thresholded saturation and blue-yellow images (OR)
    ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)

    # Fill small objects
    ab_fill = pcv.fill(bin_img=ab, size=200)

    # Apply mask (for VIS images, mask_color=white)
    masked2 = pcv.apply_mask(img=masked, mask=ab_fill, mask_color='white')

    # Identify objects
    id_objects, obj_hierarchy = pcv.find_objects(img=masked2, mask=ab_fill)

    # Define ROI
    height = shape[0]
    width = shape[1]
    roi1, roi_hierarchy= pcv.roi.rectangle(img=masked2, x=0, y=0, h=height, w=width)

    # Decide which objects to keep
    roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(img=img, roi_contour=roi1, 
                                                               roi_hierarchy=roi_hierarchy, 
                                                               object_contour=id_objects, 
                                                               obj_hierarchy=obj_hierarchy,
                                                               roi_type='partial')

    # Object combine kept objects
    obj, mask = pcv.object_composition(img=img, contours=roi_objects, hierarchy=hierarchy3)
    
    # Filling holes in the mask, works great for alive plants, not so good for dead plants
    filled_mask = pcv.fill_holes(mask)

    final = pcv.apply_mask(img=imgNIR, mask=mask, mask_color='white')
    pcv.print_image(final, "./segment/segment-temp.png")
def cluster_contour_splitimg(rgb_img,
                             grouped_contour_indexes,
                             contours,
                             hierarchy,
                             outdir=None,
                             file=None,
                             filenames=None):
    """
    This function takes clustered contours and splits them into multiple images, also does a check to make sure that
    the number of inputted filenames matches the number of clustered contours.

    Inputs:
    rgb_img                 = RGB image data
    grouped_contour_indexes = output of cluster_contours, indexes of clusters of contours
    contours                = contours to cluster, output of cluster_contours
    hierarchy               = hierarchy of contours, output of find_objects
    outdir                  = out directory for output images
    file                    = the name of the input image to use as a plantcv name,
                              output of filename from read_image function
    filenames               = input txt file with list of filenames in order from top to bottom left to right
                              (likely list of genotypes)

    Returns:
    output_path             = array of paths to output images

    :param rgb_img: numpy.ndarray
    :param grouped_contour_indexes: list
    :param contours: list
    :param hierarchy: numpy.ndarray
    :param outdir: str
    :param file: str
    :param filenames: str
    :return output_path: str
    """

    params.device += 1

    sys.stderr.write(
        'This function has been updated to include object hierarchy so object holes can be included\n'
    )

    # get names to split also to check the target number of objects

    i = datetime.now()
    timenow = i.strftime('%m-%d-%Y_%H:%M:%S')

    if file is None:
        filebase = timenow
    else:
        filebase = os.path.splitext(file)[0]

    if filenames is None:
        l = len(grouped_contour_indexes)
        namelist = []
        for x in range(0, l):
            namelist.append(x)
    else:
        with open(filenames, 'r') as n:
            namelist = n.read().splitlines()
        n.close()

    # make sure the number of objects matches the namelist, and if not, remove the smallest grouped countor
    # removing contours is not ideal but the lists don't match there is a warning to check output

    if len(namelist) == len(grouped_contour_indexes):
        corrected_contour_indexes = grouped_contour_indexes
    elif len(namelist) < len(grouped_contour_indexes):
        print(
            "Warning number of names is less than number of grouped contours, attempting to fix, to double check "
            "output")
        diff = len(grouped_contour_indexes) - len(namelist)
        size = []
        for i, x in enumerate(grouped_contour_indexes):
            totallen = []
            for a in x:
                g = i
                la = len(contours[a])
                totallen.append(la)
            sumlen = np.sum(totallen)
            size.append((sumlen, g, i))

        dtype = [('len', int), ('group', list), ('index', int)]
        lencontour = np.array(size, dtype=dtype)
        lencontour = np.sort(lencontour, order='len')

        rm_contour = lencontour[diff:]
        rm_contour = np.sort(rm_contour, order='group')
        corrected_contour_indexes = []

        for x in rm_contour:
            index = x[2]
            corrected_contour_indexes.append(grouped_contour_indexes[index])

    elif len(namelist) > len(grouped_contour_indexes):
        print(
            "Warning number of names is more than number of  grouped contours, double check output"
        )
        diff = len(namelist) - len(grouped_contour_indexes)
        namelist = namelist[0:-diff]
        corrected_contour_indexes = grouped_contour_indexes

    # create filenames

    group_names = []
    group_names1 = []
    for i, x in enumerate(namelist):
        plantname = str(filebase) + '_' + str(x) + '_p' + str(i) + '.jpg'
        maskname = str(filebase) + '_' + str(x) + '_p' + str(i) + '_mask.jpg'
        group_names.append(plantname)
        group_names1.append(maskname)

    # split image

    output_path = []
    output_imgs = []
    output_masks = []

    for y, x in enumerate(corrected_contour_indexes):
        if outdir is not None:
            savename = os.path.join(str(outdir), group_names[y])
            savename1 = os.path.join(str(outdir), group_names1[y])
        else:
            savename = os.path.join(".", group_names[y])
            savename1 = os.path.join(".", group_names1[y])
        iy, ix, iz = np.shape(rgb_img)
        mask = np.zeros((iy, ix, 3), dtype=np.uint8)
        masked_img = np.copy(rgb_img)
        for a in x:
            if hierarchy[0][a][3] > -1:
                cv2.drawContours(mask,
                                 contours,
                                 a, (0, 0, 0),
                                 -1,
                                 lineType=8,
                                 hierarchy=hierarchy)
            else:
                cv2.drawContours(mask,
                                 contours,
                                 a, (255, 255, 255),
                                 -1,
                                 lineType=8,
                                 hierarchy=hierarchy)

        mask_binary = mask[:, :, 0]

        if np.sum(mask_binary) == 0:
            pass
        else:
            retval, mask_binary = cv2.threshold(mask_binary, 254, 255,
                                                cv2.THRESH_BINARY)
            masked1 = apply_mask(masked_img, mask_binary, 'white')
            output_imgs.append(masked1)
            output_masks.append(mask_binary)
            if outdir is not None:
                print_image(masked1, savename)
                print_image(mask_binary, savename1)
            output_path.append(savename)

            if params.debug == 'print':
                print_image(
                    masked1,
                    os.path.join(params.debug_outdir,
                                 str(params.device) + '_clusters.png'))
                print_image(
                    mask_binary,
                    os.path.join(params.debug_outdir,
                                 str(params.device) + '_clusters_mask.png'))
            elif params.debug == 'plot':
                plot_image(masked1)
                plot_image(mask_binary, cmap='gray')

    return output_path, output_imgs, output_masks
Beispiel #33
0
def main():
    args = options()

    os.chdir(args.outdir)

    # Read RGB image
    img, path, filename = pcv.readimage(args.image, mode="native")

    # Get metadata from file name
    geno_name = filename.split("}{")
    geno_name = geno_name[5]
    geno_name = geno_name.split("_")
    geno_name = geno_name[1]

    day = filename.split("}{")
    day = day[7]
    day = day.split("_")
    day = day[1]
    day = day.split("}")
    day = day[0]

    plot = filename.split("}{")
    plot = plot[0]
    plot = plot.split("_")
    plot = plot[1]

    exp_name = filename.split("}{")
    exp_name = exp_name[1]
    exp_name = exp_name.split("_")
    exp_name = exp_name[1]

    treat_name = filename.split("}{")
    treat_name = treat_name[6]

    # Create masks using Naive Bayes Classifier and PDFs file
    masks = pcv.naive_bayes_classifier(img, args.pdfs)

    # The following code will identify the racks in the image, find the top edge, and choose a line along the edge to pick a y coordinate to trim any soil/pot pixels identified as plant material.

    # Convert RGB to HSV and extract the Value channel
    v = pcv.rgb2gray_hsv(img, 'v')

    # Threshold the Value image
    v_thresh = pcv.threshold.binary(v, 98, 255, 'light')

    # Dilate mask to fill holes
    dilate_racks = pcv.dilate(v_thresh, 2, 1)

    # Fill in small objects
    mask = np.copy(dilate_racks)
    fill_racks = pcv.fill(mask, 100000)

    #edge detection
    edges = cv2.Canny(fill_racks, 60, 180)

    #write all the straight lines from edge detection
    lines = cv2.HoughLinesP(edges,
                            rho=1,
                            theta=1 * np.pi / 180,
                            threshold=150,
                            minLineLength=50,
                            maxLineGap=15)
    N = lines.shape[0]
    for i in range(N):
        x1 = lines[i][0][0]
        y1 = lines[i][0][1]
        x2 = lines[i][0][2]
        y2 = lines[i][0][3]
        cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 2)

    # keep only horizontal lines
    N = lines.shape[0]
    tokeep = []
    for i in range(N):
        want = (abs(lines[i][0][1] - lines[i][0][3])) <= 10
        tokeep.append(want)

    lines = lines[tokeep]

    # keep only lines in lower half of image
    N = lines.shape[0]
    tokeep = []
    for i in range(N):
        want = 3100 > lines[i][0][1] > 2300
        tokeep.append(want)

    lines = lines[tokeep]

    # assign lines to positions around plants
    N = lines.shape[0]
    tokeep = []
    left = []
    mid = []
    right = []

    for i in range(N):
        leftones = lines[i][0][2] <= 2000
        left.append(leftones)

        midones = 3000 > lines[i][0][2] > 2000
        mid.append(midones)

        rightones = lines[i][0][0] >= 3300
        right.append(rightones)

    right = lines[right]
    left = lines[left]
    mid = lines[mid]

    # choose y values for right left mid adding some pixels to go about the pot (subtract because of orientation of axis)
    y_left = left[0][0][3] - 50
    y_mid = mid[0][0][3] - 50
    y_right = right[0][0][3] - 50

    # reload original image to write new lines on
    img, path, filename = pcv.readimage(args.image)

    # write horizontal lines on image
    cv2.line(img, (left[0][0][0], left[0][0][1]),
             (left[0][0][2], left[0][0][3]), (255, 255, 51), 2)
    cv2.line(img, (mid[0][0][0], mid[0][0][1]), (mid[0][0][2], mid[0][0][3]),
             (255, 255, 51), 2)
    cv2.line(img, (right[0][0][0], right[0][0][1]),
             (right[0][0][2], right[0][0][3]), (255, 255, 51), 2)

    # Add masks together
    added = masks["healthy"] + masks["necrosis"] + masks["stem"]

    # Dilate mask to fill holes
    dilate_img = pcv.dilate(added, 2, 1)

    # Fill in small objects
    mask = np.copy(dilate_img)
    fill_img = pcv.fill(mask, 400)

    ret, inverted = cv2.threshold(fill_img, 75, 255, cv2.THRESH_BINARY_INV)

    # Dilate mask to fill holes of plant
    dilate_inv = pcv.dilate(inverted, 2, 1)

    # Fill in small objects of plant
    mask2 = np.copy(dilate_inv)
    fill_plant = pcv.fill(mask2, 20)

    inverted_img = pcv.invert(fill_plant)

    # Identify objects
    id_objects, obj_hierarchy = pcv.find_objects(img, inverted_img)

    # Define ROIs
    roi_left, roi_hierarchy_left = pcv.roi.rectangle(280, 1280, 1275, 1200,
                                                     img)
    roi_mid, roi_hierarchy_mid = pcv.roi.rectangle(1900, 1280, 1275, 1200, img)
    roi_right, roi_hierarchy_right = pcv.roi.rectangle(3600, 1280, 1275, 1200,
                                                       img)

    # Decide which objects to keep
    roi_objects_left, roi_obj_hierarchy_left, kept_mask_left, obj_area_left = pcv.roi_objects(
        img, 'partial', roi_left, roi_hierarchy_left, id_objects,
        obj_hierarchy)
    roi_objects_mid, roi_obj_hierarchy_mid, kept_mask_mid, obj_area_mid = pcv.roi_objects(
        img, 'partial', roi_mid, roi_hierarchy_mid, id_objects, obj_hierarchy)
    roi_objects_right, roi_obj_hierarchy_right, kept_mask_right, obj_area_right = pcv.roi_objects(
        img, 'partial', roi_right, roi_hierarchy_right, id_objects,
        obj_hierarchy)

    # Combine objects
    obj_r, mask_r = pcv.object_composition(img, roi_objects_right,
                                           roi_obj_hierarchy_right)
    obj_m, mask_m = pcv.object_composition(img, roi_objects_mid,
                                           roi_obj_hierarchy_mid)
    obj_l, mask_l = pcv.object_composition(img, roi_objects_left,
                                           roi_obj_hierarchy_left)

    def analyze_bound_horizontal2(img,
                                  obj,
                                  mask,
                                  line_position,
                                  filename=False):

        ori_img = np.copy(img)

        # Draw line horizontal line through bottom of image, that is adjusted to user input height
        if len(np.shape(ori_img)) == 3:
            iy, ix, iz = np.shape(ori_img)
        else:
            iy, ix = np.shape(ori_img)
        size = (iy, ix)
        size1 = (iy, ix, 3)
        background = np.zeros(size, dtype=np.uint8)
        wback = np.zeros(size1, dtype=np.uint8)
        x_coor = int(ix)
        y_coor = int(iy) - int(line_position)
        rec_corner = int(iy - 2)
        rec_point1 = (1, rec_corner)
        rec_point2 = (x_coor - 2, y_coor - 2)
        cv2.rectangle(background, rec_point1, rec_point2, (255), 1)
        below_contour, below_hierarchy = cv2.findContours(
            background, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2:]

        below = []
        above = []
        mask_nonzerox, mask_nonzeroy = np.nonzero(mask)
        obj_points = np.vstack((mask_nonzeroy, mask_nonzerox))
        obj_points1 = np.transpose(obj_points)

        for i, c in enumerate(obj_points1):
            xy = tuple(c)
            pptest = cv2.pointPolygonTest(below_contour[0],
                                          xy,
                                          measureDist=False)
            if pptest == 1:
                below.append(xy)
                cv2.circle(ori_img, xy, 1, (0, 0, 255))
                cv2.circle(wback, xy, 1, (0, 0, 0))
            else:
                above.append(xy)
                cv2.circle(ori_img, xy, 1, (0, 255, 0))
                cv2.circle(wback, xy, 1, (255, 255, 255))

        return wback

    ori_img = np.copy(img)

    # Draw line horizontal line through bottom of image, that is adjusted to user input height
    if len(np.shape(ori_img)) == 3:
        iy, ix, iz = np.shape(ori_img)
    else:
        iy, ix = np.shape(ori_img)

    if obj_r is not None:
        wback_r = analyze_bound_horizontal2(img, obj_r, mask_r, iy - y_right)
    if obj_m is not None:
        wback_m = analyze_bound_horizontal2(img, obj_m, mask_m, iy - y_mid)
    if obj_l is not None:
        wback_l = analyze_bound_horizontal2(img, obj_l, mask_l, iy - y_left)

    threshold_light = pcv.threshold.binary(img, 1, 1, 'dark')

    if obj_r is not None:
        fgmask_r = pcv.background_subtraction(wback_r, threshold_light)
    if obj_m is not None:
        fgmask_m = pcv.background_subtraction(wback_m, threshold_light)
    if obj_l is not None:
        fgmask_l = pcv.background_subtraction(wback_l, threshold_light)

    if obj_l is not None:
        id_objects_left, obj_hierarchy_left = pcv.find_objects(img, fgmask_l)
    if obj_m is not None:
        id_objects_mid, obj_hierarchy_mid = pcv.find_objects(img, fgmask_m)
    if obj_r is not None:
        id_objects_right, obj_hierarchy_right = pcv.find_objects(img, fgmask_r)

    # Combine objects
    if obj_r is not None:
        obj_r2, mask_r2 = pcv.object_composition(img, id_objects_right,
                                                 obj_hierarchy_right)
    if obj_m is not None:
        obj_m2, mask_m2 = pcv.object_composition(img, id_objects_mid,
                                                 obj_hierarchy_mid)
    if obj_l is not None:
        obj_l2, mask_l2 = pcv.object_composition(img, id_objects_left,
                                                 obj_hierarchy_left)

    # Shape measurements
    if obj_l is not None:
        shape_header_left, shape_data_left, shape_img_left = pcv.analyze_object(
            img, obj_l2, fgmask_l,
            geno_name + '_' + plot + '_' + 'A' + '_' + day + '_' + 'shape.jpg')

    if obj_r is not None:
        shape_header_right, shape_data_right, shape_img_right = pcv.analyze_object(
            img, obj_r2, fgmask_r,
            geno_name + '_' + plot + '_' + 'C' + '_' + day + '_' + 'shape.jpg')

    if obj_m is not None:
        shape_header_mid, shape_data_mid, shape_img_mid = pcv.analyze_object(
            img, obj_m2, fgmask_m,
            geno_name + '_' + plot + '_' + 'B' + '_' + day + '_' + 'shape.jpg')

    # Color data
    if obj_r is not None:
        color_header_right, color_data_right, norm_slice_right = pcv.analyze_color(
            img, fgmask_r, 256, None, 'v', 'img',
            geno_name + '_' + plot + '_' + 'C' + '_' + day + '_' + 'color.jpg')

    if obj_m is not None:
        color_header_mid, color_data_mid, norm_slice_mid = pcv.analyze_color(
            img, fgmask_m, 256, None, 'v', 'img',
            geno_name + '_' + plot + '_' + 'B' + '_' + day + '_' + 'color.jpg')

    if obj_l is not None:
        color_header_left, color_data_left, norm_slice_left = pcv.analyze_color(
            img, fgmask_l, 256, None, 'v', 'img',
            geno_name + '_' + plot + '_' + 'A' + '_' + day + '_' + 'color.jpg')

    new_header = [
        'experiment', 'day', 'genotype', 'treatment', 'plot', 'plant',
        'percent.necrosis', 'area', 'hull-area', 'solidity', 'perimeter',
        'width', 'height', 'longest_axis', 'center-of-mass-x',
        'center-of-mass-y', 'hull_vertices', 'in_bounds', 'ellipse_center_x',
        'ellipse_center_y', 'ellipse_major_axis', 'ellipse_minor_axis',
        'ellipse_angle', 'ellipse_eccentricity', 'bin-number', 'bin-values',
        'blue', 'green', 'red', 'lightness', 'green-magenta', 'blue-yellow',
        'hue', 'saturation', 'value'
    ]
    table = []
    table.append(new_header)

    added2 = masks["healthy"] + masks["stem"]

    # Object combine kept objects
    if obj_l is not None:
        masked_image_healthy_left = pcv.apply_mask(added2, fgmask_l, 'black')
        masked_image_necrosis_left = pcv.apply_mask(masks["necrosis"],
                                                    fgmask_l, 'black')
        added_obj_left = masked_image_healthy_left + masked_image_necrosis_left

        sample = "A"

        # Calculations
        necrosis_left = np.sum(masked_image_necrosis_left)
        necrosis_percent_left = float(necrosis_left) / np.sum(added_obj_left)

        table.append([
            exp_name, day, geno_name, treat_name, plot, sample,
            round(necrosis_percent_left, 5), shape_data_left[1],
            shape_data_left[2], shape_data_left[3], shape_data_left[4],
            shape_data_left[5], shape_data_left[6], shape_data_left[7],
            shape_data_left[8], shape_data_left[9], shape_data_left[10],
            shape_data_left[11], shape_data_left[12], shape_data_left[13],
            shape_data_left[14], shape_data_left[15], shape_data_left[16],
            shape_data_left[17], '"{}"'.format(color_data_left[1]),
            '"{}"'.format(color_data_left[2]), '"{}"'.format(
                color_data_left[3]), '"{}"'.format(color_data_left[4]),
            '"{}"'.format(color_data_left[5]), '"{}"'.format(
                color_data_left[6]), '"{}"'.format(color_data_left[7]),
            '"{}"'.format(color_data_left[8]), '"{}"'.format(
                color_data_left[9]), '"{}"'.format(color_data_left[10]),
            '"{}"'.format(color_data_left[11])
        ])

    # Object combine kept objects
    if obj_m is not None:
        masked_image_healthy_mid = pcv.apply_mask(added2, fgmask_m, 'black')
        masked_image_necrosis_mid = pcv.apply_mask(masks["necrosis"], fgmask_m,
                                                   'black')
        added_obj_mid = masked_image_healthy_mid + masked_image_necrosis_mid

        sample = "B"

        # Calculations
        necrosis_mid = np.sum(masked_image_necrosis_mid)
        necrosis_percent_mid = float(necrosis_mid) / np.sum(added_obj_mid)

        table.append([
            exp_name, day, geno_name, treat_name, plot, sample,
            round(necrosis_percent_mid,
                  5), shape_data_mid[1], shape_data_mid[2], shape_data_mid[3],
            shape_data_mid[4], shape_data_mid[5], shape_data_mid[6],
            shape_data_mid[7], shape_data_mid[8], shape_data_mid[9],
            shape_data_mid[10], shape_data_mid[11], shape_data_mid[12],
            shape_data_mid[13], shape_data_mid[14], shape_data_mid[15],
            shape_data_mid[16], shape_data_mid[17],
            '"{}"'.format(color_data_mid[1]), '"{}"'.format(color_data_mid[2]),
            '"{}"'.format(color_data_mid[3]), '"{}"'.format(color_data_mid[4]),
            '"{}"'.format(color_data_mid[5]), '"{}"'.format(color_data_mid[6]),
            '"{}"'.format(color_data_mid[7]), '"{}"'.format(color_data_mid[8]),
            '"{}"'.format(color_data_mid[9]), '"{}"'.format(
                color_data_mid[10]), '"{}"'.format(color_data_mid[11])
        ])

    # Object combine kept objects
    if obj_r is not None:
        masked_image_healthy_right = pcv.apply_mask(added2, fgmask_r, 'black')
        masked_image_necrosis_right = pcv.apply_mask(masks["necrosis"],
                                                     fgmask_r, 'black')
        added_obj_right = masked_image_healthy_right + masked_image_necrosis_right

        sample = "C"

        # Calculations
        necrosis_right = np.sum(masked_image_necrosis_right)
        necrosis_percent_right = float(necrosis_right) / np.sum(
            added_obj_right)

        table.append([
            exp_name, day, geno_name, treat_name, plot, sample,
            round(necrosis_percent_right, 5), shape_data_right[1],
            shape_data_right[2], shape_data_right[3], shape_data_right[4],
            shape_data_right[5], shape_data_right[6], shape_data_right[7],
            shape_data_right[8], shape_data_right[9], shape_data_right[10],
            shape_data_right[11], shape_data_right[12], shape_data_right[13],
            shape_data_right[14], shape_data_right[15], shape_data_right[16],
            shape_data_right[17], '"{}"'.format(color_data_right[1]),
            '"{}"'.format(color_data_right[2]), '"{}"'.format(
                color_data_right[3]), '"{}"'.format(color_data_right[4]),
            '"{}"'.format(color_data_right[5]), '"{}"'.format(
                color_data_right[6]), '"{}"'.format(color_data_right[7]),
            '"{}"'.format(color_data_right[8]), '"{}"'.format(
                color_data_right[9]), '"{}"'.format(color_data_right[10]),
            '"{}"'.format(color_data_right[11])
        ])

    if obj_l is not None:
        merged2 = cv2.merge([
            masked_image_healthy_left,
            np.zeros(np.shape(masks["healthy"]), dtype=np.uint8),
            masked_image_necrosis_left
        ])  #blue, green, red
        pcv.print_image(
            merged2, geno_name + '_' + plot + '_' + 'A' + '_' + day + '_' +
            'merged.jpg')
    if obj_m is not None:
        merged3 = cv2.merge([
            masked_image_healthy_mid,
            np.zeros(np.shape(masks["healthy"]), dtype=np.uint8),
            masked_image_necrosis_mid
        ])  #blue, green, red
        pcv.print_image(
            merged3, geno_name + '_' + plot + '_' + 'B' + '_' + day + '_' +
            'merged.jpg')
    if obj_r is not None:
        merged4 = cv2.merge([
            masked_image_healthy_right,
            np.zeros(np.shape(masks["healthy"]), dtype=np.uint8),
            masked_image_necrosis_right
        ])  #blue, green, red
        pcv.print_image(
            merged4, geno_name + '_' + plot + '_' + 'C' + '_' + day + '_' +
            'merged.jpg')

    # Save area results to file (individual csv files for one image...)
    file_name = filename.split("}{")
    file_name = file_name[0] + "}{" + file_name[5] + "}{" + file_name[7]

    outfile = str(file_name[:-4]) + 'csv'
    with open(outfile, 'w') as f:
        for row in table:
            f.write(','.join(map(str, row)) + '\n')

    print(filename)
def test(true_positive_file, test_parameters):
    hue_lower_tresh = test_parameters[0]
    hue_higher_tresh = test_parameters[1]
    saturation_lower_tresh = test_parameters[2]
    saturation_higher_tresh = test_parameters[3]
    value_lower_tresh = test_parameters[4]
    value_higher_tresh = test_parameters[5]
    green_lower_tresh = test_parameters[6]
    green_higher_tresh = test_parameters[7]
    red_lower_tresh = test_parameters[8]
    red_higher_thresh = test_parameters[9]
    blue_lower_tresh = test_parameters[10]
    blue_higher_tresh = test_parameters[11]
    blur_k = test_parameters[12]
    fill_k = test_parameters[13]
    
    class args:
            #image = "C:\\Users\\RensD\\OneDrive\\studie\\Master\\The_big_project\\top_perspective\\0214_2018-03-07 08.55 - 26_cam9.png"
            image = true_positive_file
            outdir = "C:\\Users\\RensD\\OneDrive\\studie\\Master\\The_big_project\\top_perspective\\output"
            debug = debug_setting
            result = "results.txt"
    # Get options
    pcv.params.debug=args.debug #set debug mode
    pcv.params.debug_outdir=args.outdir #set output directory

    # Read image (readimage mode defaults to native but if image is RGBA then specify mode='rgb')
    # Inputs:
    #   filename - Image file to be read in 
    #   mode - Return mode of image; either 'native' (default), 'rgb', 'gray', or 'csv'
    img, path, filename = pcv.readimage(filename=args.image, mode='rgb')
    
    s = pcv.rgb2gray_hsv(rgb_img=img, channel='h')
    mask, masked_image = pcv.threshold.custom_range(rgb_img=s, lower_thresh=[hue_lower_tresh], upper_thresh=[hue_higher_tresh], channel='gray')
    masked = pcv.apply_mask(rgb_img=img, mask = mask, mask_color = 'white')
    #print("filtered on hue")
    s = pcv.rgb2gray_hsv(rgb_img=masked, channel='s')
    mask, masked_image = pcv.threshold.custom_range(rgb_img=s, lower_thresh=[saturation_lower_tresh], upper_thresh=[saturation_higher_tresh], channel='gray')
    masked = pcv.apply_mask(rgb_img=masked, mask = mask, mask_color = 'white')
    #print("filtered on saturation")
    s = pcv.rgb2gray_hsv(rgb_img=masked, channel='v')
    mask, masked_image = pcv.threshold.custom_range(rgb_img=s, lower_thresh=[value_lower_tresh], upper_thresh=[value_higher_tresh], channel='gray')
    masked = pcv.apply_mask(rgb_img=masked, mask = mask, mask_color = 'white')
    #print("filtered on value")
    mask, masked = pcv.threshold.custom_range(rgb_img=masked, lower_thresh=[0,green_lower_tresh,0], upper_thresh=[255,green_higher_tresh,255], channel='RGB')
    masked = pcv.apply_mask(rgb_img=masked, mask = mask, mask_color = 'white')
    #print("filtered on green")
    mask, masked = pcv.threshold.custom_range(rgb_img=masked, lower_thresh=[red_lower_tresh,0,0], upper_thresh=[red_higher_thresh,255,255], channel='RGB')
    masked = pcv.apply_mask(rgb_img=masked, mask = mask, mask_color = 'white')
    #print("filtered on red")
    mask_old, masked_old = pcv.threshold.custom_range(rgb_img=masked, lower_thresh=[0,0,blue_lower_tresh], upper_thresh=[255,255,blue_higher_tresh], channel='RGB')
    masked = pcv.apply_mask(rgb_img=masked_old, mask = mask_old, mask_color = 'white')
    #print("filtered on blue")
    ###____________________________________ Blur to minimize 
    try:
        s_mblur = pcv.median_blur(gray_img = masked_old, ksize = blur_k)
        s = pcv.rgb2gray_hsv(rgb_img=s_mblur, channel='v')
        mask, masked_image = pcv.threshold.custom_range(rgb_img=s, lower_thresh=[0], upper_thresh=[254], channel='gray')
    except:
        print("failed blur step")
    try:
        mask = pcv.fill(mask, fill_k)
    except:
        pass
    masked = pcv.apply_mask(rgb_img=masked, mask = mask, mask_color = 'white')


    ###_____________________________________ Now to identify objects
    masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a')
    masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b')
    
     # Threshold the green-magenta and blue images
    maskeda_thresh = pcv.threshold.binary(gray_img=masked_a, threshold=115, 
                                      max_value=255, object_type='dark')
    maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a, threshold=135, 
                                           max_value=255, object_type='light')
    maskedb_thresh = pcv.threshold.binary(gray_img=masked_b, threshold=128, 
                                          max_value=255, object_type='light')
    
    ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh)
    ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1)
    
    # Fill small objects
    # Inputs: 
    #   bin_img - Binary image data 
    #   size - Minimum object area size in pixels (must be an integer), and smaller objects will be filled
    ab_fill = pcv.fill(bin_img=ab, size=200)
    #print("filled")
    # Apply mask (for VIS images, mask_color=white)
    masked2 = pcv.apply_mask(rgb_img=masked, mask=ab_fill, mask_color='white')
    
    id_objects, obj_hierarchy = pcv.find_objects(masked, ab_fill)
    # Let's just take the largest
    roi1, roi_hierarchy= pcv.roi.rectangle(img=masked, x=0, y=0, h=960, w=1280)  # Currently hardcoded
    with HiddenPrints():
        roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(img=img, roi_contour=roi1, 
                                                                       roi_hierarchy=roi_hierarchy, 
                                                                       object_contour=id_objects, 
                                                                       obj_hierarchy=obj_hierarchy,
                                                                       roi_type=roi_type)
    obj, mask = pcv.object_composition(img=img, contours=roi_objects, hierarchy=hierarchy3)
    
    if use_mask == True:
        return(mask)
    else:
        masked2 = pcv.apply_mask(rgb_img=masked, mask=mask, mask_color='white')
        return(masked2)