class_docstr: Method 1: CrossCorrelation algorithm.
  Ideas come from Paul Bourke(http://paulbourke.net/miscellaneous/correlate/).
docstr: Detect correlation by computing correlation coefficients for all allowed shift steps,
  then take the maximum.
code: def _detect_correlation(self):
      """
      Detect correlation by computing correlation coefficients for all allowed shift steps,
      then take the maximum.
      """
      correlations = []
      shifted_correlations = []
      self.time_series_a.normalize()
      self.time_series_b.normalize()
      a, b = self.time_series_a.align(self.time_series_b)
      a_values, b_values = a.values, b.values
      a_avg, b_avg = a.average(), b.average()
      a_stdev, b_stdev = a.stdev(), b.stdev()
      n = len(a)
      denom = a_stdev * b_stdev * n
      # Find the maximal shift steps according to the maximal shift seconds.
      allowed_shift_step = self._find_allowed_shift(a.timestamps)
      if allowed_shift_step:
          shift_upper_bound = allowed_shift_step
          shift_lower_bound = -allowed_shift_step
      else:
          shift_upper_bound = 1
          shift_lower_bound = 0
      for delay in range(shift_lower_bound, shift_upper_bound):
          delay_in_seconds = a.timestamps[abs(delay)] - a.timestamps[0]
          if delay < 0:
              delay_in_seconds = -delay_in_seconds
          s = 0
          for i in range(n):
              j = i + delay
              if j < 0 or j >= n:
                  continue
              else:
                  s += (a_values[i] - a_avg) * (b_values[j] - b_avg)
          r = s / denom if denom != 0 else s
          correlations.append([delay_in_seconds, r])
          # Take shift into account to create a "shifted correlation coefficient".
          if self.max_shift_milliseconds:
              shifted_correlations.append(
                  r
                  * (
                      1
                      + float(delay_in_seconds)
                      / self.max_shift_milliseconds
                      * self.shift_impact
                  )
              )
          else:
              shifted_correlations.append(r)
      max_correlation = list(max(correlations, key=lambda k: k[1]))
      max_shifted_correlation = max(shifted_correlations)
      max_correlation.append(max_shifted_correlation)
      self.correlation_result = CorrelationResult(*max_correlation)
  
file_path: luminol/src/luminol/algorithms/correlator_algorithms/cross_correlator.py
imports: from luminol.algorithms.correlator_algorithms import CorrelatorAlgorithm
  from luminol.modules.correlation_result import CorrelationResult
  from luminol.constants import DEFAULT_SHIFT_IMPACT, DEFAULT_ALLOWED_SHIFT_SECONDS
label_desc: Use image node_id 2 for calling the CrossCorrelator obj's underlying member method code with example usage: obj._detect_correlation() without return types