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
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
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
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
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
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