/// <summary> /// returns an oscillation array for a single frequency bin. /// </summary> /// <param name="xCorrByTimeMatrix">derived from single frequency bin.</param> /// <param name="sensitivity">a threshold used to ignore low ascillation intensities.</param> /// <returns>vector of oscillation values.</returns> public static double[] GetOscillationArrayUsingFft(double[,] xCorrByTimeMatrix, double sensitivity) { int xCorrLength = xCorrByTimeMatrix.GetLength(0); int sampleCount = xCorrByTimeMatrix.GetLength(1); // set up vector to contain fft output var oscillationsVector = new double[xCorrLength / 2]; // loop over all the Auto-correlation vectors and do FFT for (int e = 0; e < sampleCount; e++) { double[] autocor = MatrixTools.GetColumn(xCorrByTimeMatrix, e); // zero mean the auto-correlation vector before doing FFT autocor = DataTools.DiffFromMean(autocor); FFT.WindowFunc wf = FFT.Hamming; var fft = new FFT(autocor.Length, wf); var spectrum = fft.Invoke(autocor); // skip spectrum[0] because it is DC or zero oscillations/sec spectrum = DataTools.Subarray(spectrum, 1, spectrum.Length - 2); // reduce the power in the low coeff because these can dominate. // This is a hack! spectrum[0] *= 0.33; // convert to energy and calculate total power in spectrum spectrum = DataTools.SquareValues(spectrum); double sumOfSquares = spectrum.Sum(); // get combined relative power in the three bins centred on max. int maxIndex = DataTools.GetMaxIndex(spectrum); double powerAtMax = spectrum[maxIndex]; if (maxIndex == 0) { powerAtMax += spectrum[1] + spectrum[2]; } else if (maxIndex >= spectrum.Length - 1) { powerAtMax += spectrum[maxIndex - 1] + spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex - 1] + spectrum[maxIndex + 1]; } double relativePower = powerAtMax / sumOfSquares; // if the relative power of the max oscillation is large enough, // then accumulate its power into the oscillations Vector if (relativePower > sensitivity) { oscillationsVector[maxIndex] += powerAtMax; } } return(LogTransformOscillationVector(oscillationsVector, sampleCount)); }
public static double[] GetOscillationArrayUsingWpd(double[,] xCorrByTimeMatrix, double sensitivity, int binNumber) { int xCorrLength = xCorrByTimeMatrix.GetLength(0); int sampleCount = xCorrByTimeMatrix.GetLength(1); double[] oscillationsVector = new double[xCorrLength / 2]; for (int e = 0; e < sampleCount; e++) { var autocor = MatrixTools.GetColumn(xCorrByTimeMatrix, e); autocor = DataTools.DiffFromMean(autocor); var wpd = new WaveletPacketDecomposition(autocor); double[] spectrum = wpd.GetWPDEnergySpectrumWithoutDC(); // reduce the power in first coeff because it can dominate - this is a hack! spectrum[0] *= 0.5; spectrum = DataTools.SquareValues(spectrum); // get relative power in the three bins around max. double sumOfSquares = spectrum.Sum(); //double avPower = spectrum.Sum() / spectrum.Length; int maxIndex = DataTools.GetMaxIndex(spectrum); double powerAtMax = spectrum[maxIndex]; if (maxIndex == 0) { powerAtMax += spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex - 1]; } if (maxIndex >= spectrum.Length - 1) { powerAtMax += spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex + 1]; } double relativePower1 = powerAtMax / sumOfSquares; if (relativePower1 > sensitivity) { // check for boundary overrun if (maxIndex < oscillationsVector.Length) { // add in a new oscillation oscillationsVector[maxIndex] += powerAtMax; } } } return(LogTransformOscillationVector(oscillationsVector, sampleCount)); }
/// <summary> /// returns the fft spectrum of a cross-correlation function. /// </summary> /// <param name="v1"></param> /// <param name="v2"></param> /// <returns></returns> public static double[] CrossCorr(double[] v1, double[] v2) { int n = v1.Length; // assume both vectors of same length double[] r; alglib.corrr1d(v1, n, v2, n, out r); // alglib.complex[] f; // alglib.fftr1d(newOp, out f); // System.LoggedConsole.WriteLine("{0}", alglib.ap.format(f, 3)); // for (int i = 0; i < op.Length; i++) LoggedConsole.WriteLine("{0} {1:f2}", i, op[i]); // rearrange corr output and NormaliseMatrixValues int xcorrLength = 2 * n; double[] xCorr = new double[xcorrLength]; // for (int i = 0; i < n - 1; i++) newOp[i] = r[i + n]; //rearrange corr output // for (int i = n - 1; i < opLength-1; i++) newOp[i] = r[i - n + 1]; for (int i = 0; i < n - 1; i++) { xCorr[i] = r[i + n] / (i + 1); // rearrange and NormaliseMatrixValues } for (int i = n - 1; i < xcorrLength - 1; i++) { xCorr[i] = r[i - n + 1] / (xcorrLength - i - 1); } // add extra value at end so have length = power of 2 for FFT // xCorr[xCorr.Length - 1] = xCorr[xCorr.Length - 2]; // LoggedConsole.WriteLine("xCorr length = " + xCorr.Length); // for (int i = 0; i < xCorr.Length; i++) LoggedConsole.WriteLine("{0} {1:f2}", i, xCorr[i]); // DataTools.writeBarGraph(xCorr); xCorr = DataTools.DiffFromMean(xCorr); FFT.WindowFunc wf = FFT.Hamming; var fft = new FFT(xCorr.Length, wf); var spectrum = fft.Invoke(xCorr); return(spectrum); }// CrossCorrelation()
/// <summary> /// This method assume the matrix is derived from a spectrogram rotated so that the matrix rows are spectral columns of sonogram. /// /// </summary> /// <param name="m"></param> /// <param name="amplitudeThreshold"></param> /// <returns></returns> public static Tuple <double[], double[]> DetectBarsInTheRowsOfaMatrix(double[,] m, double threshold, int zeroBinCount) { int rowCount = m.GetLength(0); int colCount = m.GetLength(1); var intensity = new double[rowCount]; //an array of period intensity var periodicity = new double[rowCount]; //an array of the periodicity values double[] prevRow = MatrixTools.GetRow(m, 0); prevRow = DataTools.DiffFromMean(prevRow); for (int r = 1; r < rowCount; r++) { double[] thisRow = MatrixTools.GetRow(m, r); thisRow = DataTools.DiffFromMean(thisRow); var spectrum = AutoAndCrossCorrelation.CrossCorr(prevRow, thisRow); for (int s = 0; s < zeroBinCount; s++) { spectrum[s] = 0.0; //in real data these bins are dominant and hide other frequency content } spectrum = DataTools.NormaliseArea(spectrum); int maxId = DataTools.GetMaxIndex(spectrum); double intensityValue = spectrum[maxId]; intensity[r] = intensityValue; double period = 0.0; if (maxId != 0) { period = 2 * colCount / (double)maxId; } periodicity[r] = period; prevRow = thisRow; }// rows return(Tuple.Create(intensity, periodicity)); } //DetectBarsInTheRowsOfaMatrix()
} //DetectBarsInTheRowsOfaMatrix() /// A METHOD TO DETECT HARMONICS IN THE ROWS of the passed portion of a sonogram. /// This method assume the matrix is derived from a spectrogram rotated so that the matrix rows are spectral columns of sonogram. /// Was first developed for crow calls. /// First looks for a decibel profile that matches the passed call duration and decibel loudness /// Then samples the centre portion for the correct harmonic period. /// </summary> /// <param name="m"></param> /// <param name="amplitudeThreshold"></param> /// <returns></returns> public static Tuple <double[], double[], double[]> DetectHarmonicsInSonogramMatrix(double[,] m, double dBThreshold, int callSpan) { int zeroBinCount = 3; //to remove low freq content which dominates the spectrum int halfspan = callSpan / 2; double[] dBArray = MatrixTools.GetRowAverages(m); dBArray = DataTools.filterMovingAverage(dBArray, 3); bool doNoiseRemoval = true; if (doNoiseRemoval) { double StandardDeviationCount = 0.1; // number of noise SDs to calculate noise threshold - determines severity of noise reduction SNR.BackgroundNoise bgn = SNR.SubtractBackgroundNoiseFromSignal(dBArray, StandardDeviationCount); dBArray = bgn.NoiseReducedSignal; } bool[] peaks = DataTools.GetPeaks(dBArray); int rowCount = m.GetLength(0); int colCount = m.GetLength(1); var intensity = new double[rowCount]; //an array of period intensity var periodicity = new double[rowCount]; //an array of the periodicity values for (int r = halfspan; r < rowCount - halfspan; r++) { //APPLY A FILTER: must satisfy the following conditions for a call. if (!peaks[r]) { continue; } if (dBArray[r] < dBThreshold) { continue; } double lowerDiff = dBArray[r] - dBArray[r - halfspan]; double upperDiff = dBArray[r] - dBArray[r + halfspan]; if (lowerDiff < dBThreshold || upperDiff < dBThreshold) { continue; } double[] prevRow = DataTools.DiffFromMean(MatrixTools.GetRow(m, r - 1)); double[] thisRow = DataTools.DiffFromMean(MatrixTools.GetRow(m, r)); var spectrum = AutoAndCrossCorrelation.CrossCorr(prevRow, thisRow); for (int s = 0; s < zeroBinCount; s++) { spectrum[s] = 0.0; //in real data these bins are dominant and hide other frequency content } spectrum = DataTools.NormaliseArea(spectrum); int maxId = DataTools.GetMaxIndex(spectrum); double intensityValue = spectrum[maxId]; intensity[r] = intensityValue; double period = 0.0; if (maxId != 0) { period = 2 * colCount / (double)maxId; } periodicity[r] = period; prevRow = thisRow; } // rows return(Tuple.Create(dBArray, intensity, periodicity)); } //DetectHarmonicsInSonogramMatrix()
public static double[] GetOscillationArrayUsingCwt(double[,] xCorrByTimeMatrix, double framesPerSecond, int binNumber) { int xCorrLength = xCorrByTimeMatrix.GetLength(0); //int sampleCount = xCorrByTimeMatrix.GetLength(1); // loop over the singular values and // transfer data from SVD.UMatrix to a single vector of oscilation values var oscillationsVector = new double[xCorrLength / 2]; for (int e = 0; e < 10; e++) { var autocor = new double[xCorrLength]; // the sign of the left singular vectors are usually negative. if (autocor[0] < 0) { for (int i = 0; i < autocor.Length; i++) { autocor[i] *= -1.0; } } // ##########################################################\ autocor = DataTools.DiffFromMean(autocor); FFT.WindowFunc wf = FFT.Hamming; var fft = new FFT(autocor.Length, wf); var spectrum = fft.Invoke(autocor); // skip spectrum[0] because it is DC or zero oscillations/sec spectrum = DataTools.Subarray(spectrum, 1, spectrum.Length - 2); // reduce the power in first coeff because it can dominate - this is a hack! spectrum[0] *= 0.5; spectrum = DataTools.SquareValues(spectrum); double avPower = spectrum.Sum() / spectrum.Length; int maxIndex = DataTools.GetMaxIndex(spectrum); double powerAtMax = spectrum[maxIndex]; //double relativePower1 = powerAtMax / sumOfSquares; double relativePower2 = powerAtMax / avPower; //if (relativePower1 > 0.05) if (relativePower2 > 10.0) { // check for boundary overrun if (maxIndex < oscillationsVector.Length) { // add in a new oscillation //oscillationsVector[maxIndex] += powerAtMax; oscillationsVector[maxIndex] += relativePower2; } } } for (int i = 0; i < oscillationsVector.Length; i++) { // NormaliseMatrixValues by sample count //oscillationsVector[i] /= sampleCount; // do log transform if (oscillationsVector[i] < 1.0) { oscillationsVector[i] = 0.0; } else { oscillationsVector[i] = Math.Log10(1 + oscillationsVector[i]); } } return(oscillationsVector); }
/// <summary> /// reduces the sequence of Xcorrelation vectors to a single summary vector. /// Does this by: /// (1) do SVD on the collection of XCORRELATION vectors /// (2) select the dominant ones based on the eigen values - 90% threshold /// Typically there are 1 to 10 eigen values depending on how busy the bin is. /// (3) Do an FFT on each of the returned SVD vectors to pick the dominant oscillation rate. /// (4) Accumulate the oscillations in a freq by oscillation rate matrix. /// The amplitude value for the oscillation is the eigenvalue. /// # /// NOTE: There should only be one dominant oscillation in any one freq band at one time. /// Birds with oscillating calls do call simultaneously, but this technique will only pick up the dominant call. /// #. /// </summary> /// <param name="xCorrByTimeMatrix">double[,] xCorrelationsByTime = new double[sampleLength, sampleCount]. </param> /// <param name="sensitivity">can't remember what this does.</param> /// <param name="binNumber">only used when debugging.</param> public static double[] GetOscillationArrayUsingSvdAndFft(double[,] xCorrByTimeMatrix, double sensitivity, int binNumber) { int xCorrLength = xCorrByTimeMatrix.GetLength(0); // int sampleCount = xCorrByTimeMatrix.GetLength(1); // do singular value decomp on the xcorrelation vectors. // we want to compute the U and V matrices of singular vectors. //var svd = DenseMatrix.OfArray(xCorrByTimeMatrix).Svd(true); var svd = DenseMatrix.OfArray(xCorrByTimeMatrix).Svd(); // svd.S returns the singular values in a vector Vector <double> singularValues = svd.S; // get total energy in first singular values double energySum = 0.0; foreach (double v in singularValues) { energySum += v * v; } // get the 90% most significant ####### THis is a significant parameter but not critical. 90% is OK double significanceThreshold = 0.9; double energy = 0.0; int countOfSignificantSingularValues = 0; for (int n = 0; n < singularValues.Count; n++) { energy += singularValues[n] * singularValues[n]; double fraction = energy / energySum; if (fraction > significanceThreshold) { countOfSignificantSingularValues = n + 1; break; } } //foreach (double d in singularValues) // Console.WriteLine("singular value = {0}", d); //Console.WriteLine("Freq bin:{0} Count Of Significant SingularValues = {1}", binNumber, countOfSignificantSingularValues); // svd.U returns the LEFT singular vectors in matrix Matrix <double> uMatrix = svd.U; //Matrix<double> relevantU = UMatrix.SubMatrix(0, UMatrix.RowCount-1, 0, eigenVectorCount); //Console.WriteLine("\n\n"); //MatrixTools.writeMatrix(UMatrix.ToArray()); //string pathUmatrix1 = @"C:\SensorNetworks\Output\Sonograms\testMatrixSVD_U1.png"; //ImageTools.DrawReversedMDNMatrix(UMatrix, pathUmatrix1); //string pathUmatrix2 = @"C:\SensorNetworks\Output\Sonograms\testMatrixSVD_U2.png"; //ImageTools.DrawReversedMDNMatrix(relevantU, pathUmatrix2); // loop over the singular values and // transfer data from SVD.UMatrix to a single vector of oscilation values double[] oscillationsVector = new double[xCorrLength / 2]; for (int e = 0; e < countOfSignificantSingularValues; e++) { double[] autocor = uMatrix.Column(e).ToArray(); // the sign of the left singular vectors are usually negative. if (autocor[0] < 0) { for (int i = 0; i < autocor.Length; i++) { autocor[i] *= -1.0; } } // ##########################################################\ autocor = DataTools.DiffFromMean(autocor); FFT.WindowFunc wf = FFT.Hamming; var fft = new FFT(autocor.Length, wf); var spectrum = fft.Invoke(autocor); // skip spectrum[0] because it is DC or zero oscillations/sec spectrum = DataTools.Subarray(spectrum, 1, spectrum.Length - 2); // reduce the power in first coeff because it can dominate - this is a hack! spectrum[0] *= 0.5; spectrum = DataTools.SquareValues(spectrum); // get relative power in the three bins around max. double sumOfSquares = spectrum.Sum(); int maxIndex = DataTools.GetMaxIndex(spectrum); double powerAtMax = spectrum[maxIndex]; if (maxIndex == 0) { powerAtMax += spectrum[1] + spectrum[2]; } else if (maxIndex >= spectrum.Length - 1) { powerAtMax += spectrum[maxIndex - 1] + spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex - 1] + spectrum[maxIndex + 1]; } double relativePower = powerAtMax / sumOfSquares; // if the relative power of the max oscillation is large enough, // then accumulate its power into the oscillationsVector if (relativePower > sensitivity) { oscillationsVector[maxIndex] += powerAtMax; } } return(LogTransformOscillationVector(oscillationsVector, countOfSignificantSingularValues)); }
public static double[] GetOscillationArrayUsingWpd(double[,] xCorrByTimeMatrix, double sensitivity, int binNumber) { int xCorrLength = xCorrByTimeMatrix.GetLength(0); int sampleCount = xCorrByTimeMatrix.GetLength(1); double[] oscillationsVector = new double[xCorrLength / 2]; for (int e = 0; e < sampleCount; e++) { double[] autocor = MatrixTools.GetColumn(xCorrByTimeMatrix, e); // ##########################################################\ autocor = DataTools.DiffFromMean(autocor); WaveletPacketDecomposition wpd = new WaveletPacketDecomposition(autocor); double[] spectrum = wpd.GetWPDEnergySpectrumWithoutDC(); // reduce the power in first coeff because it can dominate - this is a hack! spectrum[0] *= 0.66; spectrum = DataTools.SquareValues(spectrum); // get relative power in the three bins around max. double sumOfSquares = spectrum.Sum(); //double avPower = spectrum.Sum() / spectrum.Length; int maxIndex = DataTools.GetMaxIndex(spectrum); double powerAtMax = spectrum[maxIndex]; if (maxIndex == 0) { powerAtMax += spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex - 1]; } if (maxIndex >= spectrum.Length - 1) { powerAtMax += spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex + 1]; } double relativePower1 = powerAtMax / sumOfSquares; //double relativePower2 = powerAtMax / avPower; if (relativePower1 > sensitivity) //if (relativePower2 > 10.0) { // check for boundary overrun if (maxIndex < oscillationsVector.Length) { // add in a new oscillation oscillationsVector[maxIndex] += powerAtMax; //oscillationsVector[maxIndex] += relativePower; } } } for (int i = 0; i < oscillationsVector.Length; i++) { // NormaliseMatrixValues by sample count oscillationsVector[i] /= sampleCount; // do log transform if (oscillationsVector[i] < 1.0) { oscillationsVector[i] = 0.0; } else { oscillationsVector[i] = Math.Log10(1 + oscillationsVector[i]); } } return(oscillationsVector); }
public static double[] GetOscillationArrayUsingFft(double[,] xCorrByTimeMatrix, double sensitivity, int binNumber) { int xCorrLength = xCorrByTimeMatrix.GetLength(0); int sampleCount = xCorrByTimeMatrix.GetLength(1); // loop over the Auto-correlation vectors and do FFT double[] oscillationsVector = new double[xCorrLength / 2]; for (int e = 0; e < sampleCount; e++) { double[] autocor = MatrixTools.GetColumn(xCorrByTimeMatrix, e); //DataTools.writeBarGraph(autocor); // ##########################################################\ autocor = DataTools.DiffFromMean(autocor); FFT.WindowFunc wf = FFT.Hamming; var fft = new FFT(autocor.Length, wf); var spectrum = fft.Invoke(autocor); // skip spectrum[0] because it is DC or zero oscillations/sec spectrum = DataTools.Subarray(spectrum, 1, spectrum.Length - 2); // reduce the power in first coeff because it can dominate - this is a hack! spectrum[0] *= 0.66; spectrum = DataTools.SquareValues(spectrum); // get relative power in the three bins around max. double sumOfSquares = spectrum.Sum(); //double avPower = spectrum.Sum() / spectrum.Length; int maxIndex = DataTools.GetMaxIndex(spectrum); double powerAtMax = spectrum[maxIndex]; if (maxIndex == 0) { powerAtMax += spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex - 1]; } if (maxIndex >= spectrum.Length - 1) { powerAtMax += spectrum[maxIndex]; } else { powerAtMax += spectrum[maxIndex + 1]; } double relativePower1 = powerAtMax / sumOfSquares; //double relativePower2 = powerAtMax / avPower; if (relativePower1 > sensitivity) //if (relativePower2 > 10.0) { // check for boundary overrun if (maxIndex < oscillationsVector.Length) { // add in a new oscillation oscillationsVector[maxIndex] += powerAtMax; //oscillationsVector[maxIndex] += relativePower; } } } for (int i = 0; i < oscillationsVector.Length; i++) { // NormaliseMatrixValues by sample count oscillationsVector[i] /= sampleCount; // do log transform if (oscillationsVector[i] < 1.0) { oscillationsVector[i] = 0.0; } else { oscillationsVector[i] = Math.Log10(1 + oscillationsVector[i]); } } return(oscillationsVector); }