danforthcenter / plantcv

Plant phenotyping with image analysis
Mozilla Public License 2.0
637 stars 263 forks source link

Photosynthesis analysis quality filtering #636

Open nfahlgren opened 3 years ago

nfahlgren commented 3 years ago

Is your feature request related to a problem? Please describe. When we calculate a metric like (Fm - F0) / Fm, there can be some quality control issues. One that came up is that when F0 = 0 then Fv/Fm = 1. I think this is not a productive result both because it lacks biological relevance and because our lack of ability to detect signal in F0 could just be a detection limit issue.

Describe the solution you'd like I propose that we only calculate Fv/Fm where F0 > 0 within the plantcv.photosynthesis.analyze_fvfm function.

Describe alternatives you've considered Alternatively, we do not change anything and leave it to the user to filter the results. I worry a little about creating a non-transparent process (though we would document it) but I think this is a non-controversial filter to add?

cc: @annacasto, @dschneiderch

Additional context If we do add quality filtering, this could also apply to other photosynthesis functions being developed in #426.

dschneiderch commented 3 years ago

In #426 I effectively did this filtering by 1. initializing the output array with 0s and 2. only computing Fv and YII, NPQ when it made sense to i.e. Fo < Fm and Fv > 0, Fm > 0

    # To calculate the divisions properly we need to change from unit16 to float64 data types
    out_flt = np.zeros_like(mask, dtype='float64')
    # Calculate Fvariable, where Fv = Fmax - Fmin (masked)
    fv = np.subtract(fmax_mask, fmin_mask, out=out_flt.copy(),
                     where=fmin_mask < fmax_mask)

    # make sure to initialize with out=. using where= provides random values at False pixels. you will get a strange result.
    # this was a problem when (because?) mask comes from Fm instead of Fm' so the plant pixels can be different
    #mask<0, fmax>0 = FALSE: not part of plant but fluorescence detected.
    #mask>0, fmax<=0 = FALSE: part of plant in Fm but no fluorescence detected(!!!, plant movement?)
    yii = np.divide(fv, fmax_mask, out=out_flt.copy(),
                    where=np.logical_and(fv > 0, fmax_mask > 0))

overall it will be (and was for me) more confusing to get negative results in YII and NPQ! If you don't initialize with 0 you get a lot of pixels with values like 1e-16 which is also confusing and non sensical.

nfahlgren commented 3 years ago

Awesome, thanks @dschneiderch!