Example #1
0
def calibrate(raw_data, white_reference, dark_reference):
    """This function allows you calibrate raw hyperspectral image data with white and dark reference data.

    Inputs:
    raw_data        = Raw image 'Spectral_data' class instance
    white_reference = White reference 'Spectral_data' class instance
    dark_reference  = Dark reference 'Spectral_data' class instance

    Returns:
    calibrated      = Calibrated hyperspectral image

    :param raw_data: __main__.Spectral_data
    :param white_reference: __main__.Spectral_data
    :param dark_reference: __main__.Spectral_data
    :return calibrated: __main__.Spectral_data
    """
    # Auto-increment device
    params.device += 1

    # Collect the number of wavelengths present
    num_bands = len(white_reference.wavelength_dict)
    den = white_reference.array_data - dark_reference.array_data

    # Calibrate using reflectance = (raw data - dark reference) / (white reference - dark reference)
    output_num = []
    for i in range(0, raw_data.lines):
        ans = raw_data.array_data[i,].astype(np.float16) - dark_reference.array_data
        output_num.append(ans)
    num = np.stack(output_num, axis=2)
    output_calibrated = []
    for i in range(0, raw_data.lines):
        ans1 = raw_data.array_data[i,] / den
        output_calibrated.append(ans1)

    # Reshape into hyperspectral datacube
    scalibrated = np.stack(output_calibrated, axis=2)
    calibrated_array = np.transpose(scalibrated[0], (1, 0, 2))

    # Make a new class instance with the calibrated hyperspectral image
    calibrated = Spectral_data(array_data=calibrated_array, max_wavelength=raw_data.max_wavelength,
                               min_wavelength=raw_data.min_wavelength, d_type=raw_data.d_type,
                               wavelength_dict=raw_data.wavelength_dict, samples=raw_data.samples,
                               lines=raw_data.lines, interleave=raw_data.interleave,
                               wavelength_units=raw_data.wavelength_units, array_type=raw_data.array_type,
                               pseudo_rgb=None, filename=None, default_bands=raw_data.default_bands)

    # Make pseudo-rgb image for the calibrated image
    pseudo_rgb = _make_pseudo_rgb(spectral_array=calibrated)
    calibrated.pseudo_rgb = pseudo_rgb

    if params.debug == "plot":
        # Gamma correct pseudo_rgb image
        plot_image(pseudo_rgb)
    elif params.debug == "print":
        print_image(pseudo_rgb, os.path.join(params.debug_outdir, str(params.device) + "_calibrated_rgb.png"))

    return calibrated
Example #2
0
def egi(rgb_img):
    """Excess Green Index.

    r = R / (R + G + B)
    g = G / (R + G + B)
    b = B / (R + G + B)
    EGI = 2g - r - b

    The theoretical range for EGI is (-1, 2).

    Inputs:
    rgb_img      = Color image (np.array)

    Returns:
    index_array    = Index data as a Spectral_data instance

    :param rgb_img: np.array
    :return index_array: np.array
    """

    # Split the RGB image into component channels
    blue, green, red = cv2.split(rgb_img)
    # Calculate float32 sum of all channels
    total = red.astype(np.float32) + green.astype(np.float32) + blue.astype(np.float32)
    # Calculate normalized channels
    r = red.astype(np.float32) / total
    g = green.astype(np.float32) / total
    b = blue.astype(np.float32) / total
    index_array_raw = (2 * g) - r - b

    hsi = Spectral_data(array_data=None, max_wavelength=0, min_wavelength=0, max_value=255, min_value=0,
                        d_type=np.uint8, wavelength_dict={}, samples=None, lines=None, interleave=None,
                        wavelength_units=None, array_type=None, pseudo_rgb=None, filename=None, default_bands=None)

    return _package_index(hsi=hsi, raw_index=index_array_raw, method="EGI")
Example #3
0
def extract_wavelength(spectral_data, wavelength):
    """Find index of a target wavelength band in a hyperspectral data instance.

        Inputs:
            spectral_array = Hyperspectral data instance
            wavelength     = Target wavelength value

        Returns:
            index_array    = Data instance of request wavelength band

        :param spectral_array: __main__.Spectral_data
        :param target: float
        :return index_array: __main__.Spectral_data
        """
    # Make a list of all keys which are the wavelengths
    all_wavelengths = spectral_data.wavelength_dict.keys()

    # Find index of the band with the closest wavelengths
    band_index = _find_closest(np.array([float(i) for i in all_wavelengths]), wavelength)

    # Print which wavelength will be used
    wl_dict = spectral_data.wavelength_dict
    print("The closest band found to " + str(wavelength) + spectral_data.wavelength_units + " is: " +
          str(list(wl_dict.keys())[band_index]))

    # Reshape
    index_array_raw = spectral_data.array_data[:, :, [band_index]]
    index_array_raw = np.transpose(np.transpose(index_array_raw)[0])

    # Resulting array is float 32 from -1 to 1, transform into uint8 for plotting
    all_positive = np.add(index_array_raw, np.ones(np.shape(index_array_raw)))
    normalized = all_positive.astype(np.float64) / 2  # normalize the data to 0 - 1
    index_array = (255 * normalized).astype(np.uint8)  # scale to 255

    # Plot out grayscale image
    if params.debug == "plot":
        plot_image(index_array)
    elif params.debug == "print":
        print_image(index_array,
                    os.path.join(params.debug_outdir, str(params.device) + str(wavelength) + "_index.png"))

    # Find array min and max values
    max_pixel = float(np.amax(index_array_raw))
    min_pixel = float(np.amin(index_array_raw))

    # Make a spectral data instance
    index_array = Spectral_data(array_data=index_array_raw, max_wavelength=wavelength,
                                min_wavelength=wavelength, max_value=max_pixel, min_value=min_pixel, d_type=np.uint8,
                                wavelength_dict={}, samples=spectral_data.samples,
                                lines=spectral_data.lines, interleave=spectral_data.interleave,
                                wavelength_units=spectral_data.wavelength_units,
                                array_type="index_" + str(wavelength),
                                pseudo_rgb=None, filename=spectral_data.filename, default_bands=None)

    return index_array
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
Example #5
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
Example #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 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
Example #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, "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
Example #8
0
def read_data(filename):
    """Read hyperspectral image data from file.
    Inputs:
    filename          = Name of image file

    Returns:
    spectral_array    = Hyperspectral data instance

    :param filename: str
    :return spectral_array: __main__.Spectral_data
        """

    # Initialize dictionary
    header_dict = {}

    headername = filename + ".hdr"

    with open(headername, "r") as f:
        # Replace characters for easier parsing
        hdata = f.read()
        hdata = hdata.replace(",\n", ",")
        hdata = hdata.replace("\n,", ",")
        hdata = hdata.replace("{\n", "{")
        hdata = hdata.replace("\n}", "}")
        hdata = hdata.replace(" \n ", "")
        hdata = hdata.replace(";", "")
    hdata = hdata.split("\n")

    # Loop through and create a dictionary from the header file
    for i, string in enumerate(hdata):
        if ' = ' in string:
            header_data = string.split(" = ")
            header_dict.update(
                {header_data[0].rstrip(): header_data[1].rstrip()})
        elif ' : ' in string:
            header_data = string.split(" : ")
            header_dict.update(
                {header_data[0].rstrip(): header_data[1].rstrip()})

    # Reformat wavelengths
    header_dict["wavelength"] = header_dict["wavelength"].replace("{", "")
    header_dict["wavelength"] = header_dict["wavelength"].replace("}", "")
    header_dict["wavelength"] = header_dict["wavelength"].replace(" ", "")
    header_dict["wavelength"] = header_dict["wavelength"].split(",")

    # Create dictionary of wavelengths
    wavelength_dict = {}
    for j, wavelength in enumerate(header_dict["wavelength"]):
        wavelength_dict.update({float(wavelength): float(j)})

    # Replace datatype ID number with the numpy datatype
    dtype_dict = {
        "1": np.uint8,
        "2": np.int16,
        "3": np.int32,
        "4": np.float32,
        "5": np.float64,
        "6": np.complex64,
        "9": np.complex128,
        "12": np.uint16,
        "13": np.uint32,
        "14": np.uint64,
        "15": np.uint64
    }
    header_dict["data type"] = dtype_dict[header_dict["data type"]]

    # Read in the data from the file
    raw_data = np.fromfile(filename, header_dict["data type"], -1)

    # Reshape the raw data into a datacube array
    array_data = raw_data.reshape(int(header_dict["lines"]),
                                  int(header_dict["bands"]),
                                  int(header_dict["samples"])).transpose(
                                      (0, 2, 1))

    # Check for default bands (that get used to make pseudo_rgb image)
    default_bands = None
    if "default bands" in header_dict:
        header_dict["default bands"] = header_dict["default bands"].replace(
            "{", "")
        header_dict["default bands"] = header_dict["default bands"].replace(
            "}", "")
        default_bands = header_dict["default bands"].split(",")

    # Find array min and max values
    max_pixel = float(np.amax(array_data))
    min_pixel = float(np.amin(array_data))

    # Create an instance of the spectral_data class
    spectral_array = Spectral_data(
        array_data=array_data,
        max_wavelength=float(str(header_dict["wavelength"][-1]).rstrip()),
        min_wavelength=float(str(header_dict["wavelength"][0]).rstrip()),
        max_value=max_pixel,
        min_value=min_pixel,
        d_type=header_dict["data type"],
        wavelength_dict=wavelength_dict,
        samples=int(header_dict["samples"]),
        lines=int(header_dict["lines"]),
        interleave=header_dict["interleave"],
        wavelength_units=header_dict["wavelength units"],
        array_type="datacube",
        pseudo_rgb=None,
        filename=filename,
        default_bands=default_bands)

    # Make pseudo-rgb image and replace it inside the class instance object
    pseudo_rgb = _make_pseudo_rgb(spectral_array)
    spectral_array.pseudo_rgb = pseudo_rgb

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

    return spectral_array