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
예제 #2
0
def _pseudocolored_image(histogram, bins, img, mask, background, channel,
                         filename, analysis_images):
    """Pseudocolor image.

    Inputs:
    histogram       = a normalized histogram of color values from one color channel
    bins            = number of color bins the channel is divided into
    img             = input image
    mask            = binary mask image
    background      = what background image?: channel image (img) or white
    channel         = color channel name
    filename        = input image filename
    analysis_images = list of analysis image filenames

    Returns:
    analysis_images = list of analysis image filenames

    :param histogram: list
    :param bins: int
    :param img: numpy array
    :param mask: numpy array
    :param background: str
    :param channel: str
    :param filename: str
    :param analysis_images: list
    :return analysis_images: list
    """
    mask_inv = cv2.bitwise_not(mask)

    cplant = cv2.applyColorMap(histogram, colormap=2)
    cplant1 = cv2.bitwise_and(cplant, cplant, mask=mask)

    output_imgs = {
        "pseudo_on_img": {
            "background": "img",
            "img": None
        },
        "pseudo_on_white": {
            "background": "white",
            "img": None
        }
    }

    if background == 'img' or background == 'both':
        # mask the background and color the plant with color scheme 'jet'
        img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        img_back = cv2.bitwise_and(img_gray, img_gray, mask=mask_inv)
        img_back3 = np.dstack((img_back, img_back, img_back))

        output_imgs["pseudo_on_img"]["img"] = cv2.add(cplant1, img_back3)

    if background == 'white' or background == 'both':
        # Get the image size
        if np.shape(img)[2] == 3:
            ix, iy, iz = np.shape(img)
        else:
            ix, iy = np.shape(img)
        size = ix, iy
        back = np.zeros(size, dtype=np.uint8)
        w_back = back + 255
        w_back3 = np.dstack((w_back, w_back, w_back))
        img_back3 = cv2.bitwise_and(w_back3, w_back3, mask=mask_inv)
        output_imgs["pseudo_on_white"]["img"] = cv2.add(cplant1, img_back3)

    if filename:
        for key in output_imgs:
            if output_imgs[key]["img"] is not None:
                fig_name_pseudo = str(filename[0:-4]) + '_' + str(channel) + '_pseudo_on_' + \
                                  output_imgs[key]["background"] + '.jpg'
                path = os.path.dirname(filename)
                print_image(output_imgs[key]["img"], fig_name_pseudo)
                analysis_images.append(['IMAGE', 'pseudo', fig_name_pseudo])
    else:
        path = "."

    if params.debug is not None:
        if params.debug == 'print':
            for key in output_imgs:
                if output_imgs[key]["img"] is not None:
                    print_image(
                        output_imgs[key]["img"],
                        os.path.join(
                            params.debug_outdir,
                            str(params.device) + "_" +
                            output_imgs[key]["background"] +
                            '_pseudocolor.jpg'))
            fig_name = 'VIS_pseudocolor_colorbar_' + str(
                channel) + '_channel.svg'
            if not os.path.isfile(os.path.join(params.debug_outdir, fig_name)):
                plot_colorbar(path, fig_name, bins)
        elif params.debug == 'plot':
            for key in output_imgs:
                if output_imgs[key]["img"] is not None:
                    plot_image(output_imgs[key]["img"])

    return analysis_images
예제 #3
0
def fluor_fvfm(fdark, fmin, fmax, mask, device, filename, bins=1000, debug=None):
    """Analyze PSII camera images.

    Inputs:
    fdark       = 16-bit grayscale fdark image
    fmin        = 16-bit grayscale fmin image
    fmax        = 16-bit grayscale fmax image
    mask        = mask of plant (binary,single channel)
    device      = counter for debug
    filename    = name of file
    bins        = number of bins from 0 to 65,536 (default is 1000)
    debug       = None, print, or plot. Print = save to file, Plot = print to screen.

    Returns:
    device      = device number
    hist_header = fvfm data table headers
    hist_data   = fvfm data table values

    :param fdark: numpy array
    :param fmin: numpy array
    :param fmax: numpy array
    :param mask: numpy array
    :param device: int
    :param filename: str
    :param bins: int
    :param debug: str
    :return device: int
    :return hist_header: list
    :return hist_data: list
    """

    # Auto-increment the device counter
    device += 1
    # Check that fdark, fmin, and fmax are grayscale (single channel)
    if not all(len(np.shape(i)) == 2 for i in [fdark, fmin, fmax]):
        fatal_error("The fdark, fmin, and fmax images must be grayscale images.")
    # Check that fdark, fmin, and fmax are 16-bit images
    if not all(i.dtype == "uint16" for i in [fdark, fmin, fmax]):
        fatal_error("The fdark, fmin, and fmax images must be 16-bit images.")

    # QC Fdark Image
    fdark_mask = cv2.bitwise_and(fdark, fdark, mask=mask)
    if np.amax(fdark_mask) > 2000:
        qc_fdark = False
    else:
        qc_fdark = True

    # Mask Fmin and Fmax Image
    fmin_mask = cv2.bitwise_and(fmin, fmin, mask=mask)
    fmax_mask = cv2.bitwise_and(fmax, fmax, mask=mask)

    # Calculate Fvariable, where Fv = Fmax - Fmin (masked)
    fv = np.subtract(fmax_mask, fmin_mask)

    # When Fmin is greater than Fmax, a negative value is returned.
    # Because the data type is unsigned integers, negative values roll over, resulting in nonsensical values
    # Wherever Fmin is greater than Fmax, set Fv to zero
    fv[np.where(fmax_mask < fmin_mask)] = 0

    # Calculate Fv/Fm (Fvariable / Fmax) where Fmax is greater than zero
    # By definition above, wherever Fmax is zero, Fvariable will also be zero
    # To calculate the divisions properly we need to change from unit16 to float64 data types
    fvfm = fv.astype(np.float64)
    fmax_flt = fmax_mask.astype(np.float64)
    fvfm[np.where(fmax_mask > 0)] /= fmax_flt[np.where(fmax_mask > 0)]

    # Calculate the median Fv/Fm value for non-zero pixels
    fvfm_median = np.median(fvfm[np.where(fvfm > 0)])

    # Calculate the histogram of Fv/Fm non-zero values
    fvfm_hist, fvfm_bins = np.histogram(fvfm[np.where(fvfm > 0)], bins, range=(0, 1))
    # fvfm_bins is a bins + 1 length list of bin endpoints, so we need to calculate bin midpoints so that
    # the we have a one-to-one list of x (FvFm) and y (frequency) values.
    # To do this we add half the bin width to each lower bin edge x-value
    midpoints = fvfm_bins[:-1] + 0.5 * np.diff(fvfm_bins)

    # Calculate which non-zero bin has the maximum Fv/Fm value
    max_bin = midpoints[np.argmax(fvfm_hist)]

    # Store Fluorescence Histogram Data
    hist_header = (
        'HEADER_HISTOGRAM',
        'bin-number',
        'fvfm_bins',
        'fvfm_hist',
        'fvfm_hist_peak',
        'fvfm_median',
        'fdark_passed_qc'
    )

    hist_data = (
        'FLU_DATA',
        bins,
        np.around(midpoints, decimals=len(str(bins))).tolist(),
        fvfm_hist.tolist(),
        float(max_bin),
        float(np.around(fvfm_median, decimals=4)),
        qc_fdark
    )

    if filename:
        import matplotlib
        matplotlib.use('Agg', warn=False)
        from matplotlib import pyplot as plt
        from matplotlib import cm as cm

        # Print F-variable image
        print_image(fv, (str(filename[0:-4]) + '_fv_img.png'))
        print('\t'.join(map(str, ('IMAGE', 'fv', str(filename[0:-4]) + '_fv_img.png'))))

        # Create Histogram Plot, if you change the bin number you might need to change binx so that it prints
        # an appropriate number of labels
        binx = int(bins / 50)
        plt.plot(midpoints, fvfm_hist, color='green', label='Fv/Fm')
        plt.xticks(list(midpoints[0::binx]), rotation='vertical', size='xx-small')
        plt.legend()
        ax = plt.subplot(111)
        ax.set_ylabel('Plant Pixels')
        ax.text(0.05, 0.95, ('Peak Bin Value: ' + str(max_bin)), transform=ax.transAxes, verticalalignment='top')
        plt.grid()
        plt.title('Fv/Fm of ' + str(filename[0:-4]))
        fig_name = (str(filename[0:-4]) + '_fvfm_hist.svg')
        plt.savefig(fig_name)
        plt.clf()
        print('\t'.join(map(str, ('IMAGE', 'hist', fig_name))))

        # Pseudocolored Fv/Fm image
        fvfm_8bit = fvfm * 255
        fvfm_8bit = fvfm_8bit.astype(np.uint8)
        plt.imshow(fvfm_8bit, vmin=0, vmax=1, cmap=cm.jet_r)
        plt.subplot(111)
        mask_inv = cv2.bitwise_not(mask)
        background = np.dstack((mask, mask, mask, mask_inv))
        my_cmap = plt.get_cmap('binary_r')
        plt.imshow(background, cmap=my_cmap)
        plt.axis('off')
        fig_name = (str(filename[0:-4]) + '_pseudo_fvfm.png')
        plt.savefig(fig_name, dpi=600, bbox_inches='tight')
        plt.clf()
        print('\t'.join(map(str, ('IMAGE', 'pseudo', fig_name))))

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

    if debug == 'print':
        print_image(fmin_mask, (str(device) + '_fmin_mask.png'))
        print_image(fmax_mask, (str(device) + '_fmax_mask.png'))
        print_image(fv, (str(device) + '_fv_convert.png'))
    elif debug == 'plot':
        plot_image(fmin_mask, cmap='gray')
        plot_image(fmax_mask, cmap='gray')
        plot_image(fv, cmap='gray')

    return device, hist_header, hist_data