Exemplo n.º 1
0
def _make_pseudo_rgb(spectral_array):
    """Create the best pseudo-rgb image possible from a hyperspectral datacube

    Inputs:
        spectral_array = Hyperspectral data instance

    Returns:
        pseudo_rgb     = Pseudo-rgb image

    :param spectral_array: __main__.Spectral_data
    :return pseudo_rgb: numpy.ndarray
    """

    # Make shorter variable names for data from the spectral class instance object
    array_data = spectral_array.array_data
    default_bands = spectral_array.default_bands
    wl_keys = spectral_array.wavelength_dict.keys()


    if default_bands is not None:
        pseudo_rgb = cv2.merge((array_data[:, :, int(default_bands[0])],
                                array_data[:, :, int(default_bands[1])],
                                array_data[:, :, int(default_bands[2])]))

    else:
        max_wavelength = max([float(i) for i in wl_keys])
        min_wavelength = min([float(i) for i in wl_keys])
        # Check range of available wavelength
        if max_wavelength >= 635 and min_wavelength <= 490:
            id_red = _find_closest(spectral_array=np.array([float(i) for i in wl_keys]), target=710)
            id_green = _find_closest(spectral_array=np.array([float(i) for i in wl_keys]), target=540)
            id_blue = _find_closest(spectral_array=np.array([float(i) for i in wl_keys]), target=480)

            pseudo_rgb = cv2.merge((array_data[:, :, [id_blue]],
                                    array_data[:, :, [id_green]],
                                    array_data[:, :, [id_red]]))
        else:
            # Otherwise take 3 wavelengths, first, middle and last available wavelength
            id_red = int(len(spectral_array.wavelength_dict)) - 1
            id_green = int(id_red / 2)
            pseudo_rgb = cv2.merge((array_data[:, :, [0]],
                                    array_data[:, :, [id_green]],
                                    array_data[:, :, [id_red]]))

    # Gamma correct pseudo_rgb image
    pseudo_rgb = pseudo_rgb ** (1 / 2.2)
    # Scale each of the channels up to 255
    debug = params.debug
    params.debug = None
    pseudo_rgb = cv2.merge((rescale(pseudo_rgb[:, :, 0]),
                            rescale(pseudo_rgb[:, :, 1]),
                            rescale(pseudo_rgb[:, :, 2])))

    # Reset debugging mode
    params.debug = debug

    return pseudo_rgb
Exemplo n.º 2
0
def apply_mask(img, mask, mask_color):
    """Apply white image mask to image, with bitwise AND operator bitwise NOT operator and ADD operator.

    Inputs:
    img        = RGB image data
    mask       = Binary mask image data
    mask_color = 'white' or 'black'

    Returns:
    masked_img = masked image data

    :param rgb_img: numpy.ndarray
    :param mask: numpy.ndarray
    :param mask_color: str
    :return masked_img: numpy.ndarray
    """

    params.device += 1

    if mask_color.upper() == "WHITE":
        color_val = 255
    elif mask_color.upper() == "BLACK":
        color_val = 0
    else:
        fatal_error('Mask Color ' + str(mask_color) +
                    ' is not "white" or "black"!')

    array_data = img.copy()

    # Mask the array
    array_data[np.where(mask == 0)] = color_val

    # Check the array data format
    if len(np.shape(array_data)) > 2 and np.shape(array_data)[-1] > 3:
        # Replace this part with _make_pseudo_rgb
        num_bands = np.shape(array_data)[2]
        med_band = int(num_bands / 2)
        pseudo_rgb = cv2.merge(
            (rescale(array_data[:, :, 0]), rescale(array_data[:, :, med_band]),
             rescale(array_data[:, :, num_bands - 1])))
        if params.debug == 'print':
            print_image(
                pseudo_rgb,
                os.path.join(params.debug_outdir,
                             str(params.device) + '_masked.png'))
        elif params.debug == 'plot':
            plot_image(pseudo_rgb)
    else:
        if params.debug == 'print':
            print_image(
                array_data,
                os.path.join(params.debug_outdir,
                             str(params.device) + '_masked.png'))
        elif params.debug == 'plot':
            plot_image(array_data)

    return array_data
Exemplo n.º 3
0
def _preprocess_img_dtype(img):
    """ Transform the input image such that the datatype after transformation is uint8, ready for opencv functions
    :param img: numpy.ndarray
    :return img_: numpy.ndarray
    """
    debug_mode = params.debug
    params.debug = None
    try:
        img_ = rescale(img)
    except RuntimeError:
        img_ = img_as_ubyte(img)
    params.debug = debug_mode
    return img_
def _package_index(hsi, raw_index, method):
    """Private function to package raw index array as a Spectral_data object.
    Inputs:
    hsi       = hyperspectral data (Spectral_data object)
    raw_index = raw index array
    method    = index method (e.g. NDVI)

    Returns:
    index        = index image as a Spectral_data object.

    :params hsi: __main__.Spectral_data
    :params raw_index: np.array
    :params method: str
    :params index: __main__.Spectral_data
    """
    params.device += 1

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

    # Resulting array is float 32 from varying natural ranges, transform into uint8 for plotting
    all_positive = np.add(raw_index, 2 * np.ones(np.shape(raw_index)))
    scaled = rescale(all_positive)

    # Find array min and max values
    obs_max_pixel = float(np.nanmax(raw_index))
    obs_min_pixel = float(np.nanmin(raw_index))

    index = Spectral_data(array_data=raw_index, max_wavelength=0,
                          min_wavelength=0, max_value=obs_max_pixel,
                          min_value=obs_min_pixel, d_type=np.uint8,
                          wavelength_dict={}, samples=hsi.samples,
                          lines=hsi.lines, interleave=hsi.interleave,
                          wavelength_units=hsi.wavelength_units,
                          array_type="index_" + method.lower(),
                          pseudo_rgb=scaled, filename=hsi.filename, default_bands=None)

    # Restore debug mode
    params.debug = debug

    if params.debug == "plot":
        plot_image(index.pseudo_rgb)
    elif params.debug == "print":
        print_image(index.pseudo_rgb,
                    os.path.join(params.debug_outdir, str(params.device) + method + "_index.png"))

    return index
def nonuniform_illumination(img, ksize):
    """Correct for non uniform illumination i.e. spotlight correction.

            Inputs:
            img       = RGB or grayscale image data
            ksize     = (optional) new minimum value for range of interest. default = 0

            Returns:
            corrected_img = rescaled image

            :param img: numpy.ndarray
            :param ksize: int
            :return corrected_img: numpy.ndarray
            """
    if len(np.shape(img)) == 3:
        img = rgb2gray(img)

    # Fill foreground objects
    kernel = np.ones((ksize, ksize), np.uint8)
    opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

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

    # Heavily blurred image acts like a background image
    blurred_img = gaussian_blur(opening, ksize=(ksize, ksize))
    img_mean = np.mean(blurred_img)
    corrected_img = img - blurred_img + img_mean
    corrected_img = rescale(gray_img=corrected_img, min_value=0, max_value=255)

    # Reset debug mode
    params.debug = debug
    # Autoincrement the device counter
    params.device += 1

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

    return corrected_img
Exemplo n.º 6
0
def extract_index(array, index="NDVI", distance=20):
    """Pull out indices of interest from a hyperspectral datacube.

        Inputs:
        array = hyperspectral data instance
        index = index of interest, either "ndvi", "gdvi", or "savi"
        distance = how lenient to be if the required wavelengths are not available

        Returns:
        index_array    = Index data as a Spectral_data instance

        :param array: __main__.Spectral_data
        :param index: str
        :param distance: int
        :return index_array: __main__.Spectral_data
        """
    params.device += 1

    # Min and max available wavelength will be used to determine if an index can be extracted
    max_wavelength = float(array.max_wavelength)
    min_wavelength = float(array.min_wavelength)

    # Dictionary of wavelength and it's index in the list
    wavelength_dict = array.wavelength_dict.copy()
    array_data = array.array_data.copy()

    if index.upper() == "NDVI":
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 670:
            # Obtain index that best represents NIR and red bands
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (nir - red) / (nir + red)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating NDVI. Try increasing fudge factor."
            )

    elif index.upper() == "GDVI":
        # Green Difference Vegetation Index [Sripada et al. (2006)]
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 680:
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 680)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -2 to 2
            index_array_raw = nir - red
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating GDVI. Try increasing fudge factor."
            )

    elif index.upper() == "SAVI":
        # Soil Adjusted Vegetation Index [Huete et al. (1988)]
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 680:
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 680)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -1.2 to 1.2
            index_array_raw = (1.5 * (nir - red)) / (red + nir + 0.5)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating SAVI. Try increasing fudge factor."
            )

    else:
        fatal_error(
            index +
            " is not one of the currently available indices for this function."
        )

    # Reshape array into hyperspectral datacube shape
    index_array_raw = np.transpose(np.transpose(index_array_raw)[0])

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

    # Resulting array is float 32 from varying natural ranges, transform into uint8 for plotting
    all_positive = np.add(index_array_raw,
                          2 * np.ones(np.shape(index_array_raw)))
    scaled = rescale(all_positive)

    index_array = Spectral_data(array_data=index_array_raw,
                                max_wavelength=0,
                                min_wavelength=0,
                                d_type=np.uint8,
                                wavelength_dict={},
                                samples=array.samples,
                                lines=array.lines,
                                interleave=array.interleave,
                                wavelength_units=array.wavelength_units,
                                array_type="index_" + index.lower(),
                                pseudo_rgb=scaled,
                                filename=array.filename,
                                default_bands=None)

    # Restore debug mode
    params.debug = debug

    if params.debug == "plot":
        plot_image(index_array.pseudo_rgb)
    elif params.debug == "print":
        print_image(
            index_array.pseudo_rgb,
            os.path.join(params.debug_outdir,
                         str(params.device) + index + "_index.png"))

    return index_array
Exemplo n.º 7
0
def extract_index(array, index="NDVI", distance=20):
    """Pull out indices of interest from a hyperspectral datacube.

        Inputs:
        array = hyperspectral data instance
        index = index of interest, either "ndvi", "gdvi", or "savi"
        distance = how lenient to be if the required wavelengths are not available

        Returns:
        index_array    = Index data as a Spectral_data instance

        :param array: __main__.Spectral_data
        :param index: str
        :param distance: int
        :return index_array: __main__.Spectral_data
        """
    params.device += 1

    # Min and max available wavelength will be used to determine if an index can be extracted
    max_wavelength = float(array.max_wavelength)
    min_wavelength = float(array.min_wavelength)

    # Dictionary of wavelength and it's index in the list
    wavelength_dict = array.wavelength_dict.copy()
    array_data = array.array_data.copy()

    if index.upper() == "NDVI":
        if (max_wavelength + distance) >= 800 and (min_wavelength - distance) <= 670:
            # Obtain index that best represents NIR and red bands
            nir_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 670)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (nir - red) / (nir + red)
        else:
            fatal_error("Available wavelengths are not suitable for calculating NDVI. Try increasing distance.")

    elif index.upper() == "GDVI":
        # Green Difference Vegetation Index [Sripada et al. (2006)]
        if (max_wavelength + distance) >= 800 and (min_wavelength - distance) <= 680:
            nir_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 680)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -2 to 2
            index_array_raw = nir - red
        else:
            fatal_error("Available wavelengths are not suitable for calculating GDVI. Try increasing distance.")

    elif index.upper() == "SAVI":
        # Soil Adjusted Vegetation Index [Huete et al. (1988)]
        if (max_wavelength + distance) >= 800 and (min_wavelength - distance) <= 680:
            nir_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 680)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -1.2 to 1.2
            index_array_raw = (1.5 * (nir - red)) / (red + nir + 0.5)
        else:
            fatal_error("Available wavelengths are not suitable for calculating SAVI. Try increasing distance.")

    elif index.upper() == "PRI":
        #  Photochemical Reflectance Index (https://doi.org/10.1111/j.1469-8137.1995.tb03064.x)
        if (max_wavelength + distance) >= 570 and (min_wavelength - distance) <= 531:
            # Obtain index that best approximates 570 and 531 nm bands
            pri570_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 570)
            pri531_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 531)
            pri570 = (array_data[:, :, [pri570_index]])
            pri531 = (array_data[:, :, [pri531_index]])
            # PRI = (R531− R570)/(R531+ R570))
            denominator = pri531 + pri570
            # Avoid dividing by zero
            index_array_raw = np.where(denominator == 0, 0, ((pri531 - pri570) / denominator))
        else:
            fatal_error("Available wavelengths are not suitable for calculating PRI. Try increasing distance.")

    elif index.upper() == "ACI":
        #  Van den Berg et al. 2005
        if (max_wavelength + distance) >= 800 and (min_wavelength - distance) <= 560:
            green_index = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 560)
            nir_index   = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 800)
            green = (array_data[:, :, [green_index]])
            nir   = (array_data[:, :, [nir_index]])
            # Naturally ranges from -1.0 to 1.0
            index_array_raw = green/nir
        else:
            fatal_error("Available wavelengths are not suitable for calculating ACI. Try increasing distance.")
    
    elif index.upper() == "ARI":
        # Gitelson et al., 2001
        if (max_wavelength + distance) >= 700 and (min_wavelength - distance) <= 550:
            ari550_indes = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 550)
            ari700_index   = _find_closest(np.array([float(i) for i in wavelength_dict.keys()]), 700)
            ari550 = (array_data[:, :, [ari550_indes]])
            ari700 = (array_data[:, :, [ari700_index]])
            index_array_raw = (1/ari550)-(1/ari700)
        else:
            fatal_error("Available wavelengths are not suitable for calculating ARI. Try increasing distance.")
    else:
        fatal_error(index + " is not one of the currently available indices for this function. Please open an issue " +
                    "on the PlantCV GitHub account so we can add more handy indicies!")

    # Reshape array into hyperspectral datacube shape
    index_array_raw = np.transpose(np.transpose(index_array_raw)[0])

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

    # Resulting array is float 32 from varying natural ranges, transform into uint8 for plotting
    all_positive = np.add(index_array_raw, 2 * np.ones(np.shape(index_array_raw)))
    scaled = rescale(all_positive)

    # Find array min and max values
    obs_max_pixel = float(np.nanmax(index_array_raw))
    obs_min_pixel = float(np.nanmin(index_array_raw))

    index_array = Spectral_data(array_data=index_array_raw, max_wavelength=0,
                                min_wavelength=0, max_value=obs_max_pixel, min_value=obs_min_pixel, d_type=np.uint8,
                                wavelength_dict={}, samples=array.samples,
                                lines=array.lines, interleave=array.interleave,
                                wavelength_units=array.wavelength_units, array_type="index_" + index.lower(),
                                pseudo_rgb=scaled, filename=array.filename, default_bands=None)

    # Restore debug mode
    params.debug = debug

    if params.debug == "plot":
        plot_image(index_array.pseudo_rgb)
    elif params.debug == "print":
        print_image(index_array.pseudo_rgb,
                    os.path.join(params.debug_outdir, str(params.device) + index + "_index.png"))

    return index_array
Exemplo n.º 8
0
def extract_index(array, index="NDVI", distance=20):
    """Pull out indices of interest from a hyperspectral datacube.

        Inputs:
        array = hyperspectral data instance
        index = index of interest, "ndvi", "gdvi", "savi", "pri", "aci", "ari", "cari", "ci_rededge", "cri1", "cri2", "evi",
        "mari", "mcari", "mtci", "ndre", "psnd_chla", "psnd_chlb", "psnd_car", "psri", "pssr1", "pssr2", "pssr3", "rgri", "rvsi", "sipi",
        "sr", "vari", "vi_green", "wbi".
        distance = how lenient to be if the required wavelengths are not available

        Returns:
        index_array    = Index data as a Spectral_data instance

        :param array: __main__.Spectral_data
        :param index: str
        :param distance: int
        :return index_array: __main__.Spectral_data
        """
    params.device += 1

    # Min and max available wavelength will be used to determine if an index can be extracted
    max_wavelength = float(array.max_wavelength)
    min_wavelength = float(array.min_wavelength)

    # Dictionary of wavelength and it's index in the list
    wavelength_dict = array.wavelength_dict
    array_data = array.array_data

    if index.upper() == "NDVI":
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 670:
            # Obtain index that best represents NIR and red bands
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (nir - red) / (nir + red)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating NDVI. Try increasing distance."
            )

    elif index.upper() == "GDVI":
        # Green Difference Vegetation Index [Sripada et al. (2006)]
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 680:
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 680)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -2 to 2
            index_array_raw = nir - red
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating GDVI. Try increasing distance."
            )

    elif index.upper() == "SAVI":
        # Soil Adjusted Vegetation Index [Huete et al. (1988)]
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 680:
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 680)
            nir = (array_data[:, :, [nir_index]])
            red = (array_data[:, :, [red_index]])
            # Naturally ranges from -1.2 to 1.2
            index_array_raw = (1.5 * (nir - red)) / (red + nir + 0.5)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating SAVI. Try increasing distance."
            )

    elif index.upper() == "PRI":
        #  Photochemical Reflectance Index (https://doi.org/10.1111/j.1469-8137.1995.tb03064.x)
        if (max_wavelength + distance) >= 570 and (min_wavelength -
                                                   distance) <= 531:
            # Obtain index that best approximates 570 and 531 nm bands
            pri570_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 570)
            pri531_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 531)
            pri570 = (array_data[:, :, [pri570_index]])
            pri531 = (array_data[:, :, [pri531_index]])
            # PRI = (R531- R570)/(R531+ R570))
            denominator = pri531 + pri570
            # Avoid dividing by zero
            index_array_raw = np.where(denominator == 0, 0,
                                       ((pri531 - pri570) / denominator))
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PRI. Try increasing distance."
            )

    elif index.upper() == "ACI":
        #  Van den Berg et al. 2005
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 560:
            green_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 560)
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            green = (array_data[:, :, [green_index]])
            nir = (array_data[:, :, [nir_index]])
            # Naturally ranges from -1.0 to 1.0
            index_array_raw = green / nir
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating ACI. Try increasing distance."
            )

    elif index.upper() == "ARI":
        # Gitelson et al., 2001
        if (max_wavelength + distance) >= 700 and (min_wavelength -
                                                   distance) <= 550:
            ari550_indes = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 550)
            ari700_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 700)
            ari550 = (array_data[:, :, [ari550_indes]])
            ari700 = (array_data[:, :, [ari700_index]])
            index_array_raw = (1 / ari550) - (1 / ari700)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating ARI. Try increasing distance."
            )

    elif index.upper() == 'CARI':
        # Chlorophyll absorption in reflectance index (Giteson et al., 2003a)
        if (max_wavelength + distance) >= 700 and (min_wavelength -
                                                   distance) <= 550:
            cari550_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 550)
            cari700_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 700)
            cari550 = (array_data[:, :, [cari550_index]])
            cari700 = (array_data[:, :, [cari700_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = (1 / cari550) - (1 / cari700)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating CARI. Try increasing distance."
            )

    elif index.upper() == 'CI_REDEDGE':
        # Chlorophyll index red edge (Giteson et al., 2003a)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 750:
            rededge_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 750)
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            rededge = (array_data[:, :, [rededge_index]])
            nir = (array_data[:, :, [nir_index]])
            # Naturally ranges from -1 to inf
            index_array_raw = nir / rededge - 1
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating CI_rededge. Try increasing distance."
            )

    elif index.upper() == 'CRI1':
        # Carotenoid reflectance index (Gitelson et al., 2002b) (note: part 1 of 2)
        if (max_wavelength + distance) >= 550 and (min_wavelength -
                                                   distance) <= 510:
            cri1510_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 510)
            cri1550_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 550)
            cri1510 = (array_data[:, :, [cri1510_index]])
            cri1550 = (array_data[:, :, [cri1550_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = 1 / cri1510 - 1 / cri1550
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating CRI1. Try increasing distance."
            )

    elif index.upper() == 'CRI2':
        # Carotenoid reflectance index (Gitelson et al., 2002b) (note: part 1 of 2)
        if (max_wavelength + distance) >= 700 and (min_wavelength -
                                                   distance) <= 510:
            cri1510_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 510)
            cri1700_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 700)
            cri1510 = (array_data[:, :, [cri1510_index]])
            cri1700 = (array_data[:, :, [cri1700_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = 1 / cri1510 - 1 / cri1700
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating CRI2. Try increasing distance."
            )

    elif index.upper() == 'EVI':
        # Enhanced Vegetation index (Huete et al., 1997)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 470:
            blue_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 470)
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            blue = (array_data[:, :, [blue_index]])
            red = (array_data[:, :, [red_index]])
            nir = (array_data[:, :, [nir_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = 2.5 * (nir - red) / (nir + 6 * red - 7.5 * blue +
                                                   1)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating EVI. Try increasing distance."
            )

    elif index.upper() == 'MARI':
        # Modified anthocyanin reflectance index (Gitelson et al., 2001)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 550:
            mari550_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 550)
            mari700_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 700)
            nir_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            mari550 = (array_data[:, :, [mari550_index]])
            mari700 = (array_data[:, :, [mari700_index]])
            nir = (array_data[:, :, [nir_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = ((1 / mari550) - (1 / mari700)) * nir
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating MARI. Try increasing distance."
            )

    elif index.upper() == 'MCARI':
        # Modified chlorophyll absorption in reflectance index (Daughtry et al., 2000)
        if (max_wavelength + distance) >= 700 and (min_wavelength -
                                                   distance) <= 550:
            mcari550_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 550)
            mcari670_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            mcari700_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 700)
            mcari550 = (array_data[:, :, [mcari550_index]])
            mcari670 = (array_data[:, :, [mcari670_index]])
            mcari700 = (array_data[:, :, [mcari700_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = ((mcari700 - mcari670) - 0.2 *
                               (mcari700 - mcari550)) * (mcari700 / mcari670)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating MCARI. Try increasing distance."
            )

    elif index.upper() == 'MTCI':
        # MERIS terrestrial chlorophyll index (Dash and Curran, 2004)
        if (max_wavelength + distance) >= 753.75 and (min_wavelength -
                                                      distance) <= 681.25:
            mtci68125_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 681.25)
            mtci70875_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 708.75)
            mtci75375_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 753.75)
            mtci68125 = (array_data[:, :, [mtci68125_index]])
            mtci70875 = (array_data[:, :, [mtci70875_index]])
            mtci75375 = (array_data[:, :, [mtci75375_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = (mtci75375 - mtci70875) / (mtci70875 - mtci68125)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating MTCI. Try increasing distance."
            )

    elif index.upper() == 'NDRE':
        # Normalized difference red edge (Barnes et al., 2000)
        if (max_wavelength + distance) >= 790 and (min_wavelength -
                                                   distance) <= 720:
            ndre720_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 720)
            ndre790_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 790)
            ndre790 = (array_data[:, :, [ndre790_index]])
            ndre720 = (array_data[:, :, [ndre720_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (ndre790 - ndre720) / (ndre790 + ndre720)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating NDRE. Try increasing distance."
            )

    elif index.upper() == 'PSND_CHLA':
        # Pigment sensitive normalized difference (Blackburn, 1998) note: chl_a
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 675:
            psndchla675_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 675)
            psndchla800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            psndchla675 = (array_data[:, :, [psndchla675_index]])
            psndchla800 = (array_data[:, :, [psndchla800_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (psndchla800 - psndchla675) / (psndchla800 +
                                                             psndchla675)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PSND_CHLA. Try increasing distance."
            )

    elif index.upper() == 'PSND_CHLB':
        # Pigment sensitive normalized difference (Blackburn, 1998) note: chl_b (part 1 of 3)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 650:
            psndchlb650_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 650)
            psndchlb800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            psndchlb650 = (array_data[:, :, [psndchlb650_index]])
            psndchlb800 = (array_data[:, :, [psndchlb800_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (psndchlb800 - psndchlb650) / (psndchlb800 +
                                                             psndchlb650)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PSND_CHLB. Try increasing distance."
            )

    elif index.upper() == 'PSND_CAR':
        # Pigment sensitive normalized difference (Blackburn, 1998) note: chl_b (part 1 of 3)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 500:
            psndcar500_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 500)
            psndcar800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            psndcar500 = (array_data[:, :, [psndcar500_index]])
            psndcar800 = (array_data[:, :, [psndcar800_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (psndcar800 - psndcar500) / (psndcar800 +
                                                           psndcar500)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PSND_CAR. Try increasing distance."
            )

    elif index.upper() == 'PSRI':
        # Plant senescence reflectance index (Merzlyak et al., 1999) note: car (part 1 of 3)
        if (max_wavelength + distance) >= 750 and (min_wavelength -
                                                   distance) <= 500:
            psri500_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 500)
            psri678_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 678)
            psri750_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 750)
            psri500 = (array_data[:, :, [psri500_index]])
            psri678 = (array_data[:, :, [psri678_index]])
            psri750 = (array_data[:, :, [psri750_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = (psri678 - psri500) / psri750
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PSRI. Try increasing distance."
            )

    elif index.upper() == 'PSSR1':
        # Pigment-specific spectral ration (Blackburn, 1998) note: part 1 of 3
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 675:
            pssr1_800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            pssr1_675_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 675)
            pssr1_800 = (array_data[:, :, [pssr1_800_index]])
            pssr1_675 = (array_data[:, :, [pssr1_675_index]])
            # Naturally ranges from 0 to inf
            index_array_raw = pssr1_800 / pssr1_675
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PSSR1. Try increasing distance."
            )

    elif index.upper() == 'PSSR2':
        # Pigment-specific spectral ration (Blackburn, 1998) note: part 2 of 3
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 650:
            pssr2_800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            pssr2_650_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 650)
            pssr2_800 = (array_data[:, :, [pssr2_800_index]])
            pssr2_650 = (array_data[:, :, [pssr2_650_index]])
            # Naturally ranges from 0 to inf
            index_array_raw = pssr2_800 / pssr2_650
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PSSR2. Try increasing distance."
            )

    elif index.upper() == 'PSSR3':
        # Pigment-specific spectral ration (Blackburn, 1998) note: part 3 of 3
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 500:
            pssr3_800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            pssr3_500_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 500)
            pssr3_800 = (array_data[:, :, [pssr3_800_index]])
            pssr3_500 = (array_data[:, :, [pssr3_500_index]])
            # Naturally ranges from 0 to inf
            index_array_raw = pssr3_800 / pssr3_500
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating PSSR3. Try increasing distance."
            )

    elif index.upper() == 'RGRI':
        # Red/green ratio index (Gamon and Surfus, 1999)
        if (max_wavelength + distance) >= 670 and (min_wavelength -
                                                   distance) <= 560:
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            green_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 560)
            red = (array_data[:, :, [red_index]])
            green = (array_data[:, :, [green_index]])
            # Naturally ranges from 0 to inf
            index_array_raw = red / green
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating RGRI. Try increasing distance."
            )

    elif index.upper() == 'RVSI':
        # Red-edge vegetation stress index (Metron and Huntington, 1999)
        if (max_wavelength + distance) >= 752 and (min_wavelength -
                                                   distance) <= 714:
            rvsi714_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 714)
            rvsi733_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 733)
            rvsi752_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 752)
            rvsi714 = (array_data[:, :, [rvsi714_index]])
            rvsi733 = (array_data[:, :, [rvsi733_index]])
            rvsi752 = (array_data[:, :, [rvsi752_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (rvsi714 + rvsi752) / 2 - rvsi733
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating RVSI. Try increasing distance."
            )

    elif index.upper() == 'SIPI':
        # Structure-intensitive pigment index (Penuelas et al., 1995)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 445:
            sipi445_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 445)
            sipi680_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 680)
            sipi800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            sipi445 = (array_data[:, :, [sipi445_index]])
            sipi680 = (array_data[:, :, [sipi680_index]])
            sipi800 = (array_data[:, :, [sipi800_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = (sipi800 - sipi445) / (sipi800 - sipi680)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating SIPI. Try increasing distance."
            )

    elif index.upper() == 'SR':
        # Simple ratio (Jordan, 1969)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 675:
            sr675_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 675)
            sr800_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 800)
            sr675 = (array_data[:, :, [sr675_index]])
            sr800 = (array_data[:, :, [sr800_index]])
            # Naturally ranges from 0 to inf
            index_array_raw = sr800 / sr675
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating SR. Try increasing distance."
            )

    elif index.upper() == 'VARI':
        # Visible atmospherically resistant index (Gitelson et al., 2002a)
        if (max_wavelength + distance) >= 670 and (min_wavelength -
                                                   distance) <= 470:
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            green_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 560)
            blue_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 470)
            red = (array_data[:, :, [red_index]])
            green = (array_data[:, :, [green_index]])
            blue = (array_data[:, :, [blue_index]])
            # Naturally ranges from -inf to inf
            index_array_raw = (green - red) / (green + red - blue)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating VARI. Try increasing distance."
            )

    elif index.upper() == 'VI_GREEN':
        # Vegetation index using green band (Gitelson et al., 2002a)
        if (max_wavelength + distance) >= 670 and (min_wavelength -
                                                   distance) <= 560:
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            green_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 560)
            red = (array_data[:, :, [red_index]])
            green = (array_data[:, :, [green_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (green - red) / (green + red)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating VI_green. Try increasing distance."
            )

    elif index.upper() == 'WBI':
        # Water band index (Peñuelas et al., 1997)
        if (max_wavelength + distance) >= 800 and (min_wavelength -
                                                   distance) <= 675:
            red_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 670)
            green_index = _find_closest(
                np.array([float(i) for i in wavelength_dict.keys()]), 560)
            red = (array_data[:, :, [red_index]])
            green = (array_data[:, :, [green_index]])
            # Naturally ranges from -1 to 1
            index_array_raw = (green - red) / (green + red)
        else:
            fatal_error(
                "Available wavelengths are not suitable for calculating VI_green. Try increasing distance."
            )

    else:
        fatal_error(
            index +
            " is not one of the currently available indices for this function. Please open an issue "
            +
            "on the PlantCV GitHub account so we can add more handy indicies!")

    # Reshape array into hyperspectral datacube shape
    index_array_raw = np.transpose(np.transpose(index_array_raw)[0])

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

    # Resulting array is float 32 from varying natural ranges, transform into uint8 for plotting
    all_positive = np.add(index_array_raw,
                          2 * np.ones(np.shape(index_array_raw)))
    scaled = rescale(all_positive)

    # Find array min and max values
    obs_max_pixel = float(np.nanmax(index_array_raw))
    obs_min_pixel = float(np.nanmin(index_array_raw))

    index_array = Spectral_data(array_data=index_array_raw,
                                max_wavelength=0,
                                min_wavelength=0,
                                max_value=obs_max_pixel,
                                min_value=obs_min_pixel,
                                d_type=np.uint8,
                                wavelength_dict={},
                                samples=array.samples,
                                lines=array.lines,
                                interleave=array.interleave,
                                wavelength_units=array.wavelength_units,
                                array_type="index_" + index.lower(),
                                pseudo_rgb=scaled,
                                filename=array.filename,
                                default_bands=None)

    # Restore debug mode
    params.debug = debug

    if params.debug == "plot":
        plot_image(index_array.pseudo_rgb)
    elif params.debug == "print":
        print_image(
            index_array.pseudo_rgb,
            os.path.join(params.debug_outdir,
                         str(params.device) + index + "_index.png"))

    return index_array