docstr: Finds the Costes Automatic Threshold for colocalization using a linear algorithm.
  Candiate thresholds are gradually decreased until Pearson R falls below 0.
  If "Fast" mode is enabled the "steps" between tested thresholds will be increased
  when Pearson R is much greater than 0.
docstr_tok: ["Finds","the","Costes","Automatic","Threshold","for","colocalization","using","a","linear","algorithm",".","Candiate","thresholds","are","gradually","decreased","until","Pearson","R","falls","below","0",".","If","``","Fast","''","mode","is","enabled","the","``","steps","''","between","tested","thresholds","will","be","increased","when","Pearson","R","is","much","greater","than","0","."]
code: def linear_costes(self, fi, si, scale_max=255):
      """
      Finds the Costes Automatic Threshold for colocalization using a linear algorithm.
      Candiate thresholds are gradually decreased until Pearson R falls below 0.
      If "Fast" mode is enabled the "steps" between tested thresholds will be increased
      when Pearson R is much greater than 0.
      """
      i_step = 1 / scale_max
      non_zero = (fi > 0) | (si > 0)
      xvar = numpy.var(fi[non_zero], axis=0, ddof=1)
      yvar = numpy.var(si[non_zero], axis=0, ddof=1)
  
      xmean = numpy.mean(fi[non_zero], axis=0)
      ymean = numpy.mean(si[non_zero], axis=0)
  
      z = fi[non_zero] + si[non_zero]
      zvar = numpy.var(z, axis=0, ddof=1)
  
      covar = 0.5 * (zvar - (xvar + yvar))
  
      denom = 2 * covar
      num = (yvar - xvar) + numpy.sqrt(
          (yvar - xvar) * (yvar - xvar) + 4 * (covar * covar)
      )
      a = num / denom
      b = ymean - a * xmean
  
      # Start at 1 step above the maximum value
      img_max = max(fi.max(), si.max())
      i = i_step * ((img_max // i_step) + 1)
  
      num_true = None
      fi_max = fi.max()
      si_max = si.max()
  
      # Initialise without a threshold
      costReg, _ = scipy.stats.pearsonr(fi, si)
      thr_fi_c = i
      thr_si_c = (a * i) + b
      while i > fi_max and (a * i) + b > si_max:
          i -= i_step
      while i > i_step:
          thr_fi_c = i
          thr_si_c = (a * i) + b
          combt = (fi < thr_fi_c) | (si < thr_si_c)
          try:
              # Only run pearsonr if the input has changed.
              if (positives := numpy.count_nonzero(combt)) != num_true:
                  costReg, _ = scipy.stats.pearsonr(
                      fi[combt], si[combt]
                  )
                  num_true = positives
  
              if costReg <= 0:
                  break
              elif (
                  self.fast_costes.value == M_ACCURATE
                  or i < i_step * 10
              ):
                  i -= i_step
              elif costReg > 0.45:
                  # We're way off, step down 10x
                  i -= i_step * 10
              elif costReg > 0.35:
                  # Still far from 0, step 5x
                  i -= i_step * 5
              elif costReg > 0.25:
                  # Step 2x
                  i -= i_step * 2
              else:
                  i -= i_step
          except ValueError:
              break
      return thr_fi_c, thr_si_c
  
imports: import numpy
  import scipy.ndimage
  import scipy.stats
  from cellprofiler_core.constants.measurement import COLTYPE_FLOAT
  from cellprofiler_core.module import Module
  from cellprofiler_core.setting import Divider, Binary, ValidationError
  from cellprofiler_core.setting.choice import Choice
  from cellprofiler_core.setting.subscriber import LabelListSubscriber, ImageListSubscriber
  from cellprofiler_core.setting.text import Float
  from cellprofiler_core.utilities.core.object import size_similarly
  from centrosome.cpmorphology import fixup_scipy_ndimage_result
  from scipy.linalg import lstsq
label_desc: Use image node_id 12 for calling the MeasureColocalization obj's underlying member method code with example usage: obj.linear_costes(fi, si, scale_max) and returns: thr_fi_c,  thr_si_c