<jupyter_start><jupyter_text>PSII Tutorial PSII images (3 in a set; F0, Fmin, and Fmax) are captured directly following a saturating fluorescence pulse (red light; 630 nm). These three PSII images can be used to calculate Fv/Fm (efficiency of photosystem II) for each pixel of the plant. Unfortunately, our PSII imaging cabinet has a design flaw when capturing images of plants with vertical architecture. You can read more about how we validated this flaw using our PSII analysis workflows in the [PlantCV Paper](http://dx.doi.org/10.1016/j.molp.2015.06.005).To run a PSII workflow over a single PSII image set (3 images) there are 4 required inputs:1.  **Image 1:** F0 (a.k.a Fdark/null) image.2.  **Image 2:** Fmin image.3.  **Image 3:** Fmax image. 5.  **Output directory:** If debug mode is set to 'print' output images from each step are produced.<jupyter_code>from plantcv import plantcv as pcv
import numpy as np
import cv2

class options:
    def __init__(self):
        self.image = "img/tutorial_images/psII/Fmax.jpg"
        self.debug = "plot"
        self.writeimg= False 
        self.result = "./psII_tutorial_results"
        self.outdir = "."
# Get options
args = options()

# Set debug to the global parameter 
pcv.params.debug = args.debug

# Read image (converting fmax and track to 8 bit just to create a mask, use 16-bit for all the math)

# Inputs:
#   filename - Image file to be read in 
#   mode - Return mode of image; either 'native' (default), 'rgb', 'gray', or 'csv' 
mask, path, filename = pcv.readimage(filename=args.image)

# Read in track mask 
track = cv2.imread("img/tutorial_images/psII/mask.jpg")

# Make a copy of our image 
mask1 = np.copy(mask)<jupyter_output><empty_output><jupyter_text>We use a premade mask for the screw of the car that consistently give background signal, but this is not required.<jupyter_code># Mask pesky track autofluor

# Inputs:
#   rgb_img - RGB image data 
#   channel - Split 'h' (hue), 's' (saturation), or 'v' (value) channel 
track1 = pcv.rgb2gray_hsv(rgb_img=track, channel='v')

# Inputs:
#   gray_img - Grayscale image data 
#   threshold- Threshold value (between 0-255)
#   max_value - Value to apply above threshold (255 = white) 
#   object_type - 'light' (default) or 'dark'. If the object is lighter than the background then standard threshold is done.
#                 If the object is darker than the background then inverse thresholding is done. 
track_thresh = pcv.threshold.binary(gray_img=track1, threshold=40, max_value=255, object_type='light')

# Inputs:
#   gray_img - Grayscale image data 
track_inv = pcv.invert(gray_img=track_thresh)

# Inputs: 
#   rgb_img - RGB image data 
#   mask - Binary mask image data 
#   mask_color - 'black' or 'white'
track_masked = pcv.apply_mask(rgb_img=mask1, mask=track_inv, mask_color='black')

# Threshold the image

# Inputs: 
#   gray_img - Grayscale image data 
#   threshold - Threshold value (0-255)
#   max_value - Value to apply above the threshold (255 = white)
#   object_type - 'light' (default) or 'dark'. If the object is lighter than 
#                 the background then standard thresholding is done. If the 
#                 object is darker than the background then inverse thresholding. 
fmax_thresh = pcv.threshold.binary(gray_img=track_masked, threshold=20, max_value=255, 
                                   object_type='light')

# Median Filter

# Inputs:
#   gray_img - Grayscale image data 
#   ksize - Kernel size. Integer or tuple; (ksize, ksize) box if integer is input, 
#           (n, m) size box if tuple is given.
s_mblur = pcv.median_blur(gray_img=fmax_thresh, ksize=5)

# Fill small objects

# Inputs:
#   bin_img - Binary image data 
#   size - Minimum object area size in pixels (integer), smaller objects get filled in. 
sfill_cnt = pcv.fill(bin_img=s_mblur, size=110)

# Identify objects

# Inputs:
#   img - RGB or grayscale image data for plotting
#   mask - Binary mask used for detecting contours
id_objects,obj_hierarchy = pcv.find_objects(img=mask, mask=sfill_cnt)

# Define ROI

# Inputs: 
#   img - RGB or grayscale image to plot the ROI on 
#   x - The x-coordinate of the upper left corner of the rectangle 
#   y - The y-coordinate of the upper left corner of the rectangle 
#   h - The height of the rectangle 
#   w - The width of the rectangle 
roi1, roi_hierarchy = pcv.roi.rectangle(img=mask, x=100, y=100, h=150, w=150)

# Decide which objects to keep

# Inputs:
#    img            = img to display kept objects
#    roi_contour    = contour of roi, output from any ROI function
#    roi_hierarchy  = contour of roi, output from any ROI function
#    object_contour = contours of objects, output from pcv.find_objects function
#    obj_hierarchy  = hierarchy of objects, output from pcv.find_objects function
#    roi_type       = 'partial' (default, for partially inside), 'cutto', or 
#    'largest' (keep only largest contour)
roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(img=mask, roi_contour=roi1, 
                                                               roi_hierarchy=roi_hierarchy, 
                                                               object_contour=id_objects, 
                                                               obj_hierarchy=obj_hierarchy, 
                                                               roi_type='partial')

# Object combine kept objects

# Use the object_composition function to outline the plant 
# Inputs:
#   img - RGB or grayscale image data for plotting 
#   contours - Contour list 
#   hierarchy - Contour hierarchy array 
obj, masked = pcv.object_composition(img=mask, contours=roi_objects, hierarchy=hierarchy3)<jupyter_output><empty_output><jupyter_text>Now we can analyze the plant objects for traits such as shape, or PSII signal.<jupyter_code>################ Analysis ################  

# Find shape properties, output shape image (optional)

# Inputs:
#   img - RGB or grayscale image data 
#   obj- Single or grouped contour object
#   mask - Binary image mask to use as mask for moments analysis 
shape_img = pcv.analyze_object(img=mask, obj=obj, mask=masked)


# Fluorescence Measurement (read in 16-bit images)
fdark, path_dark, file_dark = pcv.readimage(filename="img/tutorial_images/psII/Fdark.jpg")
fmin, path_min, file_min = pcv.readimage(filename="img/tutorial_images/psII/Fmin.jpg")
fmax, path_max, file_max = pcv.readimage(filename="img/tutorial_images/psII/Fmax.jpg")

# Extract Fv/Fm data of objects and produce pseudocolored images 

# Inputs:
#   fdark - Grayscale image 
#   fmin - Grayscale image 
#   fmax - Grayscale image 
#   mask - Binary mask of selected contours 
#   bins - Number of grayscale bins (0-256 for 8-bit img, 0-65536 for 16-bit). Default bins = 256
fvfm_images = pcv.fluor_fvfm(fdark=fdark, fmin=fmin, fmax=fmax, mask=kept_mask, bins=256)

# Store the two images\n
fv_img = fvfm_images[0]
fvfm_hist = fvfm_images[1]
    
# Print out the histogram 
pcv.print_image(fvfm_hist, filename="tutorial_fvfm_hist.jpg")


# Use pcv.visualize.pseudocolor to pseudocolor the image. This function gives 
# the user more control of how the pseudocolored image looks. 

# Inputs:
#     gray_img - Grayscale image data
#     obj - Single or grouped contour object (optional), if provided the pseudocolored image gets cropped down to the region of interest.
#     mask - Binary mask (optional) 
#     background - Background color/type. Options are "image" (gray_img), "white", or "black". A mask must be supplied.
#     cmap - Colormap (https://matplotlib.org/tutorials/colors/colormaps.html)
#     min_value - Minimum value for range of interest
#     max_value - Maximum value for range of interest
#     dpi - Dots per inch for image if printed out (optional, if dpi=None then the default is set to 100 dpi).
#     axes - If False then the title, x-axis, and y-axis won't be displayed (default axes=True).
#     colorbar - If False then the colorbar won't be displayed (default colorbar=True)


# Pseudocolor the Fv/Fm grayscale image that is calculated inside the fluor_fvfm function 
pseudocolored_img = pcv.visualize.pseudocolor(gray_img=fv_img, obj=None, mask=kept_mask, cmap='jet')

# Plot with the option background='image'
simple_pseudo_img = pcv.visualize.pseudocolor(gray_img=fv_img, obj=None, mask=kept_mask, background="image", 
                                              axes=False, colorbar=False, cmap='jet')

# The print_results function will take the measurements stored when running any (or all) of these functions, format, 
# and print an output text file for data analysis. The Outputs class stores data whenever any of the following functions
# are ran: analyze_bound_horizontal, analyze_bound_vertical, analyze_color, analyze_nir_intensity, analyze_object, 
# fluor_fvfm, report_size_marker_area, watershed. If no functions have been run, it will print an empty text file 
pcv.print_results(filename='psII_tutorial_results.txt')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08755145177439093, 'fraction_numerical': 0.01223717877405718, 'mean_word_length': 3.3960880195599024, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 11, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '165950', 'license': 'MIT', 'n_tokens_mistral': 2953, 'n_tokens_neox': 2753, 'n_words': 974, 'path': 'notebooks/psII_tutorial.ipynb', 'repo_name': 'bigFin/PlantCV_CannTx_1', 'size': '8989'}