def _hist_gray(gray_img, bins, lower_bound, upper_bound, mask=None): """ Prepare the ready to plot histogram data Inputs: gray_img = grayscale image to analyze bins = divide the data into n evenly spaced bins lower_bound = the lower bound of the bins (x-axis min value) upper_bound = the upper bound of the bins (x-axis max value) mask = binary mask, calculate histogram from masked area only (default=None) Returns: bin_labels = an array of histogram bin labels hist_percent = an array of histogram represented by percent values hist_gray_data = an array of histogram (original values) :param gray_img: numpy.ndarray :param bins: int :param lower_bound: int :param upper_bound: int :param mask: numpy.ndarray :return bin_labels: numpy.ndarray :return hist_percent: numpy.ndarray :return hist_gray_data: numpy.ndarray """ params.device += 1 debug = params.debug # Apply mask if one is supplied if mask is not None: min_val = np.min(gray_img) pixels = len(np.where(mask > 0)[0]) # apply plant shaped mask to image params.debug = None mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) masked = np.where(mask1 != 0, gray_img, min_val - 5000) else: pixels = gray_img.shape[0] * gray_img.shape[1] masked = gray_img params.debug = debug # Store histogram data hist_gray_data, hist_bins = np.histogram(masked, bins, (lower_bound, upper_bound)) # make hist percentage for plotting hist_percent = (hist_gray_data / float(pixels)) * 100 # use middle value of every bin as bin label bin_labels = np.array([ np.average([hist_bins[i], hist_bins[i + 1]]) for i in range(0, len(hist_bins) - 1) ]) return bin_labels, hist_percent, hist_gray_data
def report_size_marker_area(img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, thresh=None): """Detects a size marker in a specified region and reports its size and eccentricity Inputs: img = An RGB or grayscale image to plot the marker object on roi_contour = A region of interest contour (e.g. output from pcv.roi.rectangle or other methods) roi_hierarchy = A region of interest contour hierarchy (e.g. output from pcv.roi.rectangle or other methods) marker = 'define' or 'detect'. If define it means you set an area, if detect it means you want to detect within an area objcolor = Object color is 'dark' or 'light' (is the marker darker or lighter than the background) thresh_channel = 'h', 's', or 'v' for hue, saturation or value thresh = Binary threshold value (integer) Returns: analysis_images = List of output images :param img: numpy.ndarray :param roi_contour: list :param roi_hierarchy: numpy.ndarray :param marker: str :param objcolor: str :param thresh_channel: str :param thresh: int :return: analysis_images: list """ # Store debug debug = params.debug params.debug = None params.device += 1 # Make a copy of the reference image ref_img = np.copy(img) # If the reference image is grayscale convert it to color if len(np.shape(ref_img)) == 2: ref_img = cv2.cvtColor(ref_img, cv2.COLOR_GRAY2BGR) # Marker components # If the marker type is "defined" then the marker_mask and marker_contours are equal to the input ROI # Initialize a binary image roi_mask = np.zeros(np.shape(img)[:2], dtype=np.uint8) # Draw the filled ROI on the mask cv2.drawContours(roi_mask, roi_contour, -1, (255), -1) marker_mask = [] marker_contour = [] # If the marker type is "detect" then we will use the ROI to isolate marker contours from the input image if marker.upper() == 'DETECT': # We need to convert the input image into an one of the HSV channels and then threshold it if thresh_channel is not None and thresh is not None: # Mask the input image masked = apply_mask(rgb_img=ref_img, mask=roi_mask, mask_color="black") # Convert the masked image to hue, saturation, or value marker_hsv = rgb2gray_hsv(rgb_img=masked, channel=thresh_channel) # Threshold the HSV image marker_bin = binary_threshold(gray_img=marker_hsv, threshold=thresh, max_value=255, object_type=objcolor) # Identify contours in the masked image contours, hierarchy = find_objects(img=ref_img, mask=marker_bin) # Filter marker contours using the input ROI kept_contours, kept_hierarchy, kept_mask, obj_area = roi_objects( img=ref_img, object_contour=contours, obj_hierarchy=hierarchy, roi_contour=roi_contour, roi_hierarchy=roi_hierarchy, roi_type="partial") # If there are more than one contour detected, combine them into one # These become the marker contour and mask marker_contour, marker_mask = object_composition( img=ref_img, contours=kept_contours, hierarchy=kept_hierarchy) else: fatal_error( 'thresh_channel and thresh must be defined in detect mode') elif marker.upper() == "DEFINE": # Identify contours in the masked image contours, hierarchy = find_objects(img=ref_img, mask=roi_mask) # If there are more than one contour detected, combine them into one # These become the marker contour and mask marker_contour, marker_mask = object_composition(img=ref_img, contours=contours, hierarchy=hierarchy) else: fatal_error( "marker must be either 'define' or 'detect' but {0} was provided.". format(marker)) # Calculate the moments of the defined marker region m = cv2.moments(marker_mask, binaryImage=True) # Calculate the marker area marker_area = m['m00'] # Fit a bounding ellipse to the marker center, axes, angle = cv2.fitEllipse(marker_contour) major_axis = np.argmax(axes) minor_axis = 1 - major_axis major_axis_length = axes[major_axis] minor_axis_length = axes[minor_axis] # Calculate the bounding ellipse eccentricity eccentricity = np.sqrt(1 - (axes[minor_axis] / axes[major_axis])**2) cv2.drawContours(ref_img, marker_contour, -1, (255, 0, 0), 5) analysis_image = ref_img # Reset debug mode params.debug = debug if params.debug is 'print': print_image( ref_img, os.path.join(params.debug_outdir, str(params.device) + '_marker_shape.png')) elif params.debug is 'plot': plot_image(ref_img) outputs.add_observation(variable='marker_area', trait='marker area', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=marker_area, label='pixels') outputs.add_observation(variable='marker_ellipse_major_axis', trait='marker ellipse major axis length', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=major_axis_length, label='pixels') outputs.add_observation(variable='marker_ellipse_minor_axis', trait='marker ellipse minor axis length', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=minor_axis_length, label='pixels') outputs.add_observation(variable='marker_ellipse_eccentricity', trait='marker ellipse eccentricity', method='plantcv.plantcv.report_size_marker_area', scale='none', datatype=float, value=eccentricity, label='none') # Store images outputs.images.append(analysis_image) return analysis_image
def report_size_marker_area(img, roi_contour, roi_hierarchy, marker='define', objcolor='dark', thresh_channel=None, thresh=None): """Detects a size marker in a specified region and reports its size and eccentricity Inputs: img = An RGB or grayscale image to plot the marker object on roi_contour = A region of interest contour (e.g. output from pcv.roi.rectangle or other methods) roi_hierarchy = A region of interest contour hierarchy (e.g. output from pcv.roi.rectangle or other methods) marker = 'define' or 'detect'. If define it means you set an area, if detect it means you want to detect within an area objcolor = Object color is 'dark' or 'light' (is the marker darker or lighter than the background) thresh_channel = 'h', 's', or 'v' for hue, saturation or value thresh = Binary threshold value (integer) Returns: analysis_images = List of output images :param img: numpy.ndarray :param roi_contour: list :param roi_hierarchy: numpy.ndarray :param marker: str :param objcolor: str :param thresh_channel: str :param thresh: int :return: analysis_images: list """ params.device += 1 # Make a copy of the reference image ref_img = np.copy(img) # If the reference image is grayscale convert it to color if len(np.shape(ref_img)) == 2: ref_img = cv2.cvtColor(ref_img, cv2.COLOR_GRAY2BGR) # Marker components # If the marker type is "defined" then the marker_mask and marker_contours are equal to the input ROI # Initialize a binary image roi_mask = np.zeros(np.shape(img)[:2], dtype=np.uint8) # Draw the filled ROI on the mask cv2.drawContours(roi_mask, roi_contour, -1, (255), -1) marker_mask = [] marker_contour = [] # If the marker type is "detect" then we will use the ROI to isolate marker contours from the input image if marker.upper() == 'DETECT': # We need to convert the input image into an one of the HSV channels and then threshold it if thresh_channel is not None and thresh is not None: # Mask the input image masked = apply_mask(rgb_img=ref_img, mask=roi_mask, mask_color="black") # Convert the masked image to hue, saturation, or value marker_hsv = rgb2gray_hsv(rgb_img=masked, channel=thresh_channel) # Threshold the HSV image marker_bin = binary_threshold(gray_img=marker_hsv, threshold=thresh, max_value=255, object_type=objcolor) # Identify contours in the masked image contours, hierarchy = find_objects(img=ref_img, mask=marker_bin) # Filter marker contours using the input ROI kept_contours, kept_hierarchy, kept_mask, obj_area = roi_objects(img=ref_img, object_contour=contours, obj_hierarchy=hierarchy, roi_contour=roi_contour, roi_hierarchy=roi_hierarchy, roi_type="partial") # If there are more than one contour detected, combine them into one # These become the marker contour and mask marker_contour, marker_mask = object_composition(img=ref_img, contours=kept_contours, hierarchy=kept_hierarchy) else: fatal_error('thresh_channel and thresh must be defined in detect mode') elif marker.upper() == "DEFINE": # Identify contours in the masked image contours, hierarchy = find_objects(img=ref_img, mask=roi_mask) # If there are more than one contour detected, combine them into one # These become the marker contour and mask marker_contour, marker_mask = object_composition(img=ref_img, contours=contours, hierarchy=hierarchy) else: fatal_error("marker must be either 'define' or 'detect' but {0} was provided.".format(marker)) # Calculate the moments of the defined marker region m = cv2.moments(marker_mask, binaryImage=True) # Calculate the marker area marker_area = m['m00'] # Fit a bounding ellipse to the marker center, axes, angle = cv2.fitEllipse(marker_contour) major_axis = np.argmax(axes) minor_axis = 1 - major_axis major_axis_length = axes[major_axis] minor_axis_length = axes[minor_axis] # Calculate the bounding ellipse eccentricity eccentricity = np.sqrt(1 - (axes[minor_axis] / axes[major_axis]) ** 2) # Make a list to store output images analysis_image = [] cv2.drawContours(ref_img, marker_contour, -1, (255, 0, 0), 5) # out_file = os.path.splitext(filename)[0] + '_sizemarker.jpg' # print_image(ref_img, out_file) analysis_image.append(ref_img) if params.debug is 'print': print_image(ref_img, os.path.join(params.debug_outdir, str(params.device) + '_marker_shape.png')) elif params.debug is 'plot': plot_image(ref_img) outputs.add_observation(variable='marker_area', trait='marker area', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=marker_area, label='pixels') outputs.add_observation(variable='marker_ellipse_major_axis', trait='marker ellipse major axis length', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=major_axis_length, label='pixels') outputs.add_observation(variable='marker_ellipse_minor_axis', trait='marker ellipse minor axis length', method='plantcv.plantcv.report_size_marker_area', scale='pixels', datatype=int, value=minor_axis_length, label='pixels') outputs.add_observation(variable='marker_ellipse_eccentricity', trait='marker ellipse eccentricity', method='plantcv.plantcv.report_size_marker_area', scale='none', datatype=float, value=eccentricity, label='none') # Store images outputs.images.append(analysis_image) return analysis_image
def analyze_nir_intensity(gray_img, mask, bins, histplot=False): """This function calculates the intensity of each pixel associated with the plant and writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. Inputs: gray_img = 8- or 16-bit grayscale image data mask = Binary mask made from selected contours bins = number of classes to divide spectrum into histplot = if True plots histogram of intensity values Returns: hist_header = NIR histogram data table headers hist_data = NIR histogram data table values nir_hist = NIR histogram image :param gray_img: numpy array :param mask: numpy array :param bins: int :param histplot: bool :return hist_header: list :return hist_data: list :return nir_hist: str """ import matplotlib matplotlib.use('Agg', warn=False) from plotnine import ggplot, aes, geom_line, scale_x_continuous # from matplotlib import pyplot as plt params.device += 1 # apply plant shaped mask to image mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) masked = np.multiply(gray_img, mask1) # calculate histogram if gray_img.dtype == 'uint16': maxval = 65536 else: maxval = 256 # Make a pseudo-RGB image rgbimg = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR) hist_nir, hist_bins = np.histogram(masked, bins, (1, maxval), False, None, None) hist_bins1 = hist_bins[:-1] hist_bins2 = [l for l in hist_bins1] hist_nir1 = [l for l in hist_nir] # make hist percentage for plotting pixels = cv2.countNonZero(mask1) hist_percent = (hist_nir / float(pixels)) * 100 # report histogram data hist_header = ['HEADER_HISTOGRAM', 'bin-number', 'bin-values', 'nir'] hist_data = ['HISTOGRAM_DATA', bins, hist_bins2, hist_nir1] # No longer returning a pseudocolored image # make mask to select the background # mask_inv = cv2.bitwise_not(mask) # img_back = cv2.bitwise_and(rgbimg, rgbimg, mask=mask_inv) # img_back1 = cv2.applyColorMap(img_back, colormap=1) # mask the background and color the plant with color scheme 'jet' # cplant = cv2.applyColorMap(rgbimg, colormap=2) # masked1 = apply_mask(cplant, mask, 'black') masked1 = cv2.bitwise_and(rgbimg, rgbimg, mask=mask) # cplant_back = cv2.add(masked1, img_back1) if params.debug is not None: if params.debug == "print": print_image( masked1, os.path.join(params.debug_outdir, str(params.device) + "_masked_nir_plant.jpg")) if params.debug == "plot": plot_image(masked1) nir_hist = [] if histplot is True: hist_x = hist_percent bin_labels = np.arange(0, bins) dataset = pd.DataFrame({ 'Grayscale pixel intensity': bin_labels, 'Proportion of pixels (%)': hist_x }) fig_hist = (ggplot(data=dataset, mapping=aes(x='Grayscale pixel intensity', y='Proportion of pixels (%)')) + geom_line(color='red') + scale_x_continuous(breaks=list(range(0, bins, 25)))) # plot hist percent # plt.plot(hist_percent, color='green', label='Signal Intensity') # plt.xlim([0, (bins - 1)]) # plt.xlabel(('Grayscale pixel intensity (0-' + str(bins) + ")")) # plt.ylabel('Proportion of pixels (%)') # fig_name_hist = (os.path.splitext(filename)[0] + '_nir_hist.svg') # plt.savefig(fig_name_hist) nir_hist.append(fig_hist) if params.debug is not None: if params.debug == "print": fig_hist.save( os.path.join(params.debug_outdir, str(params.device) + '_nir_hist.png')) if params.debug == "plot": print(fig_hist) return hist_header, hist_data, nir_hist
def histogram(gray_img, mask=None, bins=256, color='red', title=None): """Plot a histogram using ggplot. Inputs: gray_img = grayscale image to analyze mask = binary mask made from selected contours bins = number of classes to divide spectrum into color = color of the line drawn title = custom title for the plot gets drawn if title is not None :param gray_img: numpy.ndarray :param mask: numpy.ndarray :param bins: int :param color: str :param title: str :return fig_hist: ggplot """ params.device += 1 debug = params.debug # Apply mask if one is supplied if mask is not None: # apply plant shaped mask to image params.debug = None mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) masked = np.multiply(gray_img, mask1) else: masked = gray_img params.debug = debug if gray_img.dtype == 'uint16': maxval = 65536 else: maxval = 256 # Store histogram data hist_gray_data, hist_bins = np.histogram(masked, bins, (1, maxval)) # make hist percentage for plotting pixels = cv2.countNonZero(masked) hist_percent = (hist_gray_data / float(pixels)) * 100 hist_x = hist_percent bin_labels = np.arange(0, bins) dataset = pd.DataFrame({ 'Grayscale pixel intensity': bin_labels, 'Proportion of pixels (%)': hist_x }) if title is None: fig_hist = (ggplot(data=dataset, mapping=aes(x='Grayscale pixel intensity', y='Proportion of pixels (%)')) + geom_line(color=color) + scale_x_continuous(breaks=list(range(0, bins, 25)))) elif title is not None: fig_hist = (ggplot(data=dataset, mapping=aes(x='Grayscale pixel intensity', y='Proportion of pixels (%)')) + geom_line(color=color) + scale_x_continuous(breaks=list(range(0, bins, 25))) + labels.ggtitle(title)) if params.debug is not None: if params.debug == "print": fig_hist.save( os.path.join(params.debug_outdir, str(params.device) + '_hist.png')) if params.debug == "plot": print(fig_hist) return fig_hist
def plot_hist(gray_img, mask=None, bins=256): """Plot a histogram using ggplot. Inputs: gray_img = grayscale image to analyze mask = binary mask made from selected contours bins = number of classes to divide spectrum into :param gray_img: numpy.ndarray :param mask: numpy.ndarray :param bins: int :return bins: list :return hist: list :return fig_hist: ggplot """ params.device += 1 debug = params.debug # Apply mask if one is supplied if mask is not None: # apply plant shaped mask to image params.debug=None mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) masked = np.multiply(gray_img, mask1) else: masked = gray_img if gray_img.dtype == 'uint16': maxval = 65536 else: maxval = 256 # Store histogram data hist_gray_data, hist_bins = np.histogram(masked, bins, (1, maxval), False, None, None) hist_bins1 = hist_bins[:-1] hist_bins2 = [l for l in hist_bins1] hist_gray = [l for l in hist_gray_data] # make hist percentage for plotting pixels = cv2.countNonZero(masked) hist_percent = (hist_gray_data / float(pixels)) * 100 # report histogram data hist_header = [ 'HEADER_HISTOGRAM', 'bin-number', 'bin-values', 'gray_img' ] hist_data = [ 'HISTOGRAM_DATA', bins, hist_bins2, hist_gray ] hist_x = hist_percent bin_labels = np.arange(0, bins) dataset = pd.DataFrame({'Grayscale pixel intensity': bin_labels, 'Proportion of pixels (%)': hist_x}) fig_hist = (ggplot(data=dataset, mapping=aes(x='Grayscale pixel intensity', y='Proportion of pixels (%)')) + geom_line(color='red') + scale_x_continuous(breaks=list(range(0, bins, 25)))) params.debug=debug if params.debug is not None: if params.debug == "print": fig_hist.save(os.path.join(params.debug_outdir, str(params.device) + '_hist.png')) if params.debug == "plot": print(fig_hist) return hist_header, hist_data, fig_hist
def analyze_nir_intensity(gray_img, mask, bins=256, histplot=False): """This function calculates the intensity of each pixel associated with the plant and writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. Inputs: gray_img = 8- or 16-bit grayscale image data mask = Binary mask made from selected contours bins = number of classes to divide spectrum into histplot = if True plots histogram of intensity values Returns: analysis_images = NIR histogram image :param gray_img: numpy array :param mask: numpy array :param bins: int :param histplot: bool :return analysis_images: list """ params.device += 1 # apply plant shaped mask to image mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) masked = np.multiply(gray_img, mask1) # calculate histogram if gray_img.dtype == 'uint16': maxval = 65536 else: maxval = 256 # Make a pseudo-RGB image rgbimg = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR) hist_nir, hist_bins = np.histogram(masked, bins, (1, maxval)) hist_bins1 = hist_bins[:-1] hist_bins2 = [float(round(l, 2)) for l in hist_bins1] hist_nir1 = [float(l) for l in hist_nir] # make hist percentage for plotting pixels = cv2.countNonZero(mask1) hist_percent = (hist_nir / float(pixels)) * 100 # No longer returning a pseudocolored image # make mask to select the background # mask_inv = cv2.bitwise_not(mask) # img_back = cv2.bitwise_and(rgbimg, rgbimg, mask=mask_inv) # img_back1 = cv2.applyColorMap(img_back, colormap=1) # mask the background and color the plant with color scheme 'jet' # cplant = cv2.applyColorMap(rgbimg, colormap=2) # masked1 = apply_mask(cplant, mask, 'black') masked1 = cv2.bitwise_and(rgbimg, rgbimg, mask=mask) # cplant_back = cv2.add(masked1, img_back1) if params.debug is not None: if params.debug == "print": print_image(masked1, os.path.join(params.debug_outdir, str(params.device) + "_masked_nir_plant.jpg")) if params.debug == "plot": plot_image(masked1) analysis_images = [] if histplot is True: hist_x = hist_percent bin_labels = np.arange(0, bins) dataset = pd.DataFrame({'Grayscale pixel intensity': bin_labels, 'Proportion of pixels (%)': hist_x}) fig_hist = (ggplot(data=dataset, mapping=aes(x='Grayscale pixel intensity', y='Proportion of pixels (%)')) + geom_line(color='red') + scale_x_continuous(breaks=list(range(0, bins, 25)))) analysis_images.append(fig_hist) if params.debug == "print": fig_hist.save(os.path.join(params.debug_outdir, str(params.device) + '_nir_hist.png')) elif params.debug == "plot": print(fig_hist) outputs.add_observation(variable='nir_frequencies', trait='near-infrared frequencies', method='plantcv.plantcv.analyze_nir_intensity', scale='frequency', datatype=list, value=hist_nir1, label=hist_bins2) # Store images outputs.images.append(analysis_images) return analysis_images
def analyze_nir_intensity(gray_img, mask, bins=256, histplot=False): """This function calculates the intensity of each pixel associated with the plant and writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. Inputs: gray_img = 8- or 16-bit grayscale image data mask = Binary mask made from selected contours bins = number of classes to divide spectrum into histplot = if True plots histogram of intensity values Returns: analysis_images = NIR histogram image :param gray_img: numpy array :param mask: numpy array :param bins: int :param histplot: bool :return analysis_images: plotnine ggplot """ # apply plant shaped mask to image mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) # masked = np.multiply(gray_img, mask1) # calculate histogram if gray_img.dtype == 'uint16': maxval = 65536 else: maxval = 256 masked_array = gray_img[np.where(mask > 0)] masked_nir_mean = np.average(masked_array) masked_nir_median = np.median(masked_array) masked_nir_std = np.std(masked_array) # Make a pseudo-RGB image rgbimg = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR) # Calculate histogram hist_nir = [ float(i[0]) for i in cv2.calcHist([gray_img], [0], mask, [bins], [0, maxval]) ] # Create list of bin labels bin_width = maxval / float(bins) b = 0 bin_labels = [float(b)] for i in range(bins - 1): b += bin_width bin_labels.append(b) # make hist percentage for plotting pixels = cv2.countNonZero(mask1) hist_percent = [(p / float(pixels)) * 100 for p in hist_nir] masked1 = cv2.bitwise_and(rgbimg, rgbimg, mask=mask) if params.debug is not None: params.device += 1 if params.debug == "print": print_image( masked1, os.path.join(params.debug_outdir, str(params.device) + "_masked_nir_plant.png")) if params.debug == "plot": plot_image(masked1) analysis_image = None if histplot is True: hist_x = hist_percent # bin_labels = np.arange(0, bins) dataset = pd.DataFrame({ 'Grayscale pixel intensity': bin_labels, 'Proportion of pixels (%)': hist_x }) fig_hist = (ggplot(data=dataset, mapping=aes(x='Grayscale pixel intensity', y='Proportion of pixels (%)')) + geom_line(color='red') + scale_x_continuous(breaks=list(range(0, maxval, 25)))) analysis_image = fig_hist if params.debug == "print": fig_hist.save(os.path.join(params.debug_outdir, str(params.device) + '_nir_hist.png'), verbose=False) elif params.debug == "plot": print(fig_hist) outputs.add_observation(variable='nir_frequencies', trait='near-infrared frequencies', method='plantcv.plantcv.analyze_nir_intensity', scale='frequency', datatype=list, value=hist_nir, label=bin_labels) outputs.add_observation(variable='nir_mean', trait='near-infrared mean', method='plantcv.plantcv.analyze_nir_intensity', scale='none', datatype=float, value=masked_nir_mean, label='none') outputs.add_observation(variable='nir_median', trait='near-infrared median', method='plantcv.plantcv.analyze_nir_intensity', scale='none', datatype=float, value=masked_nir_median, label='none') outputs.add_observation(variable='nir_stdev', trait='near-infrared standard deviation', method='plantcv.plantcv.analyze_nir_intensity', scale='none', datatype=float, value=masked_nir_std, label='none') # Store images outputs.images.append(analysis_image) return analysis_image
def analyze_nir_intensity(gray_img, mask, bins, histplot=False, filename=False): """This function calculates the intensity of each pixel associated with the plant and writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. Inputs: gray_img = 8- or 16-bit grayscale image data mask = Binary mask made from selected contours bins = number of classes to divide spectrum into histplot = if True plots histogram of intensity values filename = False or image name. If defined print image Returns: hist_header = NIR histogram data table headers hist_data = NIR histogram data table values analysis_img = output image :param gray_img: numpy array :param mask: numpy array :param bins: int :param histplot: bool :param filename: str :return hist_header: list :return hist_data: list :return analysis_img: str """ params.device += 1 # apply plant shaped mask to image mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) masked = np.multiply(gray_img, mask1) # calculate histogram if gray_img.dtype == 'uint16': maxval = 65536 else: maxval = 256 # Make a pseudo-RGB image rgbimg = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR) hist_nir, hist_bins = np.histogram(masked, bins, (1, maxval), False, None, None) hist_bins1 = hist_bins[:-1] hist_bins2 = [l for l in hist_bins1] hist_nir1 = [l for l in hist_nir] # make hist percentage for plotting pixels = cv2.countNonZero(mask1) hist_percent = (hist_nir / float(pixels)) * 100 # report histogram data hist_header = ['HEADER_HISTOGRAM', 'bin-number', 'bin-values', 'nir'] hist_data = ['HISTOGRAM_DATA', bins, hist_bins2, hist_nir1] analysis_img = [] # make mask to select the background mask_inv = cv2.bitwise_not(mask) img_back = cv2.bitwise_and(rgbimg, rgbimg, mask=mask_inv) img_back1 = cv2.applyColorMap(img_back, colormap=1) # mask the background and color the plant with color scheme 'jet' cplant = cv2.applyColorMap(rgbimg, colormap=2) masked1 = apply_mask(cplant, mask, 'black') cplant_back = cv2.add(masked1, img_back1) if filename: path = os.path.dirname(filename) fig_name = 'NIR_pseudocolor_colorbar.svg' if not os.path.isfile(path + '/' + fig_name): plot_colorbar(path, fig_name, bins) fig_name_pseudo = (os.path.splitext(filename)[0] + '_nir_pseudo_col.jpg') print_image(cplant_back, fig_name_pseudo) analysis_img.append(['IMAGE', 'pseudo', fig_name_pseudo]) if params.debug is not None: if params.debug == "print": print_image( masked1, os.path.join(params.debug_outdir, str(params.device) + "_nir_pseudo_plant.jpg")) print_image( cplant_back, os.path.join(params.debug_outdir, str(params.device) + "_nir_pseudo_plant_back.jpg")) if params.debug == "plot": plot_image(masked1) plot_image(cplant_back) if histplot is True: import matplotlib matplotlib.use('Agg', warn=False) from matplotlib import pyplot as plt # plot hist percent plt.plot(hist_percent, color='green', label='Signal Intensity') plt.xlim([0, (bins - 1)]) plt.xlabel(('Grayscale pixel intensity (0-' + str(bins) + ")")) plt.ylabel('Proportion of pixels (%)') if filename: fig_name_hist = (os.path.splitext(filename)[0] + '_nir_hist.svg') plt.savefig(fig_name_hist) analysis_img.append(['IMAGE', 'hist', fig_name_hist]) if params.debug == "print": plt.savefig( os.path.join(params.debug_outdir, str(params.device) + "_nir_histogram.png")) if params.debug == "plot": plt.figure() plt.clf() return hist_header, hist_data, analysis_img
def analyze_thermal_values(thermal_array, mask, histplot=False): """This extracts the thermal values of each pixel writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. Inputs: array = numpy array of thermal values mask = Binary mask made from selected contours histplot = if True plots histogram of intensity values Returns: analysis_img = output image :param thermal_array: numpy.ndarray :param mask: numpy.ndarray :param histplot: bool :return analysis_img: ggplot """ max_value = np.amax(thermal_array) # Calculate histogram hist_thermal = [ float(i[0]) for i in cv2.calcHist([np.float32(thermal_array)], [0], mask, [256], [0, max_value]) ] bin_width = max_value / 256. b = 0 bin_labels = [float(b)] for i in range(255): b += bin_width bin_labels.append(b) # Store debug mode debug = params.debug params.debug = None # apply plant shaped mask to image mask1 = binary_threshold(mask, 0, 255, 'light') params.debug = debug mask1 = (mask1 / 255) masked_thermal = thermal_array[np.where(mask > 0)] pixels = cv2.countNonZero(mask1) hist_percent = [(p / float(pixels)) * 100 for p in hist_thermal] maxtemp = np.amax(masked_thermal) mintemp = np.amin(masked_thermal) avgtemp = np.average(masked_thermal) mediantemp = np.median(masked_thermal) # Store data into outputs class outputs.add_observation(variable='max_temp', trait='maximum temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=maxtemp, label='degrees') outputs.add_observation(variable='min_temp', trait='minimum temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=mintemp, label='degrees') outputs.add_observation(variable='mean_temp', trait='mean temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=avgtemp, label='degrees') outputs.add_observation(variable='median_temp', trait='median temperature', method='plantcv.plantcv.analyze_thermal_values', scale='degrees', datatype=float, value=mediantemp, label='degrees') outputs.add_observation(variable='thermal_frequencies', trait='thermal frequencies', method='plantcv.plantcv.analyze_thermal_values', scale='frequency', datatype=list, value=hist_percent, label=bin_labels) analysis_img = None if histplot is True: params.device += 1 dataset = pd.DataFrame({ 'Temperature C': bin_labels, 'Proportion of pixels (%)': hist_percent }) fig_hist = (ggplot(data=dataset, mapping=aes(x='Temperature C', y='Proportion of pixels (%)')) + geom_line(color='green')) analysis_img = fig_hist if params.debug == "print": fig_hist.save(os.path.join( params.debug_outdir, str(params.device) + '_therm_histogram.png'), verbose=False) elif params.debug == "plot": print(fig_hist) return analysis_img
def analyze_nir_intensity(gray_img, mask, bins, histplot=False): """This function calculates the intensity of each pixel associated with the plant and writes the values out to a file. It can also print out a histogram plot of pixel intensity and a pseudocolor image of the plant. Inputs: gray_img = 8- or 16-bit grayscale image data mask = Binary mask made from selected contours bins = number of classes to divide spectrum into histplot = if True plots histogram of intensity values Returns: hist_header = NIR histogram data table headers hist_data = NIR histogram data table values analysis_images = NIR histogram image :param gray_img: numpy array :param mask: numpy array :param bins: int :param histplot: bool :return hist_header: list :return hist_data: list :return analysis_images: list """ params.device += 1 # apply plant shaped mask to image mask1 = binary_threshold(mask, 0, 255, 'light') mask1 = (mask1 / 255) masked = np.multiply(gray_img, mask1) # calculate histogram if gray_img.dtype == 'uint16': maxval = 65536 else: maxval = 256 # Make a pseudo-RGB image rgbimg = cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR) hist_nir, hist_bins = np.histogram(masked, bins, (1, maxval)) hist_bins1 = hist_bins[:-1] hist_bins2 = [round(l, 2) for l in hist_bins1] hist_nir1 = [l for l in hist_nir] # make hist percentage for plotting pixels = cv2.countNonZero(mask1) hist_percent = (hist_nir / float(pixels)) * 100 # report histogram data hist_header = ['HEADER_HISTOGRAM', 'bin-number', 'bin-values', 'nir'] hist_data = ['HISTOGRAM_DATA', bins, hist_bins2, hist_nir1] # No longer returning a pseudocolored image # make mask to select the background # mask_inv = cv2.bitwise_not(mask) # img_back = cv2.bitwise_and(rgbimg, rgbimg, mask=mask_inv) # img_back1 = cv2.applyColorMap(img_back, colormap=1) # mask the background and color the plant with color scheme 'jet' # cplant = cv2.applyColorMap(rgbimg, colormap=2) # masked1 = apply_mask(cplant, mask, 'black') masked1 = cv2.bitwise_and(rgbimg, rgbimg, mask=mask) # cplant_back = cv2.add(masked1, img_back1) if params.debug is not None: if params.debug == "print": print_image( masked1, os.path.join(params.debug_outdir, str(params.device) + "_masked_nir_plant.jpg")) if params.debug == "plot": plot_image(masked1) analysis_images = [] if histplot is True: hist_x = hist_percent bin_labels = np.arange(0, bins) dataset = pd.DataFrame({ 'Grayscale pixel intensity': bin_labels, 'Proportion of pixels (%)': hist_x }) fig_hist = (ggplot(data=dataset, mapping=aes(x='Grayscale pixel intensity', y='Proportion of pixels (%)')) + geom_line(color='red') + scale_x_continuous(breaks=list(range(0, bins, 25)))) analysis_images.append(fig_hist) if params.debug == "print": fig_hist.save( os.path.join(params.debug_outdir, str(params.device) + '_nir_hist.png')) elif params.debug == "plot": print(fig_hist) # Store into global measurements if not 'nir_histogram' in outputs.measurements: outputs.measurements['nir_histogram'] = {} outputs.measurements['nir_histogram']['signal_values'] = hist_bins2 outputs.measurements['nir_histogram']['frequency'] = hist_nir1 # Store images outputs.images.append(analysis_images) return hist_header, hist_data, analysis_images