/// <summary> /// returns oscillations using the DCT. /// </summary> public static void GetOscillationUsingDct(double[] array, double framesPerSecond, double[,] cosines, out double oscilFreq, out double period, out double intenisty) { var modifiedArray = DataTools.SubtractMean(array); var dctCoeff = MFCCStuff.DCT(modifiedArray, cosines); // convert to absolute values because not interested in negative values due to phase. for (int i = 0; i < dctCoeff.Length; i++) { dctCoeff[i] = Math.Abs(dctCoeff[i]); } // remove low freq oscillations from consideration int thresholdIndex = dctCoeff.Length / 5; for (int i = 0; i < thresholdIndex; i++) { dctCoeff[i] = 0.0; } dctCoeff = DataTools.normalise2UnitLength(dctCoeff); //dct = DataTools.NormaliseMatrixValues(dctCoeff); //another option to NormaliseMatrixValues int indexOfMaxValue = DataTools.GetMaxIndex(dctCoeff); //recalculate DCT duration in seconds double dctDuration = dctCoeff.Length / framesPerSecond; oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi period = 2 * dctCoeff.Length / (double)indexOfMaxValue / framesPerSecond; //convert maxID to period in seconds intenisty = dctCoeff[indexOfMaxValue]; }
public static double[] DoDct(double[] vector, double[,] cosines, int lowerDctBound) { //var dctArray = DataTools.Vector2Zscores(dctArray); var dctArray = DataTools.SubtractMean(vector); int dctLength = dctArray.Length; double[] dctCoeff = MFCCStuff.DCT(dctArray, cosines); // convert to absolute values because not interested in negative values due to phase. for (int i = 0; i < dctLength; i++) { dctCoeff[i] = Math.Abs(dctCoeff[i]); } // remove lower coefficients from consideration because they dominate for (int i = 0; i < lowerDctBound; i++) { dctCoeff[i] = 0.0; } dctCoeff = DataTools.normalise2UnitLength(dctCoeff); return(dctCoeff); }
/// <summary> /// THIS METHOD NO LONGER IN USE. /// NOT USEFUL FOR ANIMAL CALLS. /// Tried this but it is suitable only when there is guarantee of numerous spectral tracks as in the vowels of human speech. /// It yields SPURIOUS RESULTS where there is only one whistle track. /// </summary> public static double[,] DetectHarmonicsUsingDCT(double[,] matrix, int minBin, int maxBin, int hzWidth, bool normaliseDCT, int minPeriod, int maxPeriod, double dctThreshold) { int dctLength = maxBin - minBin + 1; //DCT spans N freq bins int minIndex = (int)(hzWidth / (double)maxPeriod * 2); //Times 0.5 because index = Pi and not 2Pi int maxIndex = (int)(hzWidth / (double)minPeriod * 2); //Times 0.5 because index = Pi and not 2Pi //double period = hzWidth / (double)indexOfMaxValue * 2; //Times 2 because index = Pi and not 2Pi if (maxIndex > dctLength) { maxIndex = dctLength; //safety check in case of future changes to code. } int rows = matrix.GetLength(0); int cols = matrix.GetLength(1); double[,] hits = new double[rows, cols]; double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //set up the cosine coefficients for (int r = 0; r < rows - dctLength; r++) { //for (int c = minBin; c <= minBin; c++)//traverse columns - skip DC column //{ var array = new double[dctLength]; //accumulate J rows of values for (int i = 0; i < dctLength; i++) { for (int j = 0; j < 5; j++) { array[i] += matrix[r + j, minBin + i]; } } array = DataTools.SubtractMean(array); // DataTools.writeBarGraph(array); double[] dct = MFCCStuff.DCT(array, cosines); for (int i = 0; i < dctLength; i++) { dct[i] = Math.Abs(dct[i]); //convert to absolute values } for (int i = 0; i < 5; i++) { dct[i] = 0.0; //remove low freq values from consideration } if (normaliseDCT) { dct = DataTools.normalise2UnitLength(dct); } int indexOfMaxValue = DataTools.GetMaxIndex(dct); //DataTools.writeBarGraph(dct); double period = hzWidth / (double)indexOfMaxValue * 2; //Times 2 because index = Pi and not 2Pi //mark DCT location with harmonic freq, only if harmonic freq is in correct range and amplitude if (indexOfMaxValue >= minIndex && indexOfMaxValue <= maxIndex && dct[indexOfMaxValue] > dctThreshold) { for (int i = 0; i < dctLength; i++) { hits[r, minBin + i] = period; } for (int i = 0; i < dctLength; i++) { hits[r + 1, minBin + i] = period; //alternate row } } //c += 5; //skip columns //} r++; //do alternate row } return(hits); }
/// <summary> /// Currently this method is called by only one species recognizer - LitoriaCaerulea. /// </summary> /// <param name="ipArray">an array of decibel values.</param> /// <param name="framesPerSecond">the frame rate.</param> /// <param name="decibelThreshold">Ignore frames below this threshold.</param> /// <param name="dctDuration">Duration in seconds of the required DCT.</param> /// <param name="minOscFreq">minimum oscillation frequency.</param> /// <param name="maxOscFreq">maximum oscillation frequency.</param> /// <param name="dctThreshold">Threshold for the maximum DCT coefficient.</param> /// <param name="dctScores">an array of dct scores.</param> /// <param name="oscFreq">an array of oscillation frequencies.</param> public static void DetectOscillations( double[] ipArray, double framesPerSecond, double decibelThreshold, double dctDuration, double minOscFreq, double maxOscFreq, double dctThreshold, out double[] dctScores, out double[] oscFreq) { int dctLength = (int)Math.Round(framesPerSecond * dctDuration); int minIndex = (int)(minOscFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi int maxIndex = (int)(maxOscFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi if (maxIndex > dctLength) { LoggedConsole.WriteWarnLine("MaxIndex > DCT length. Therefore set maxIndex = DCT length."); maxIndex = dctLength; } int length = ipArray.Length; dctScores = new double[length]; oscFreq = new double[length]; //set up the cosine coefficients double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //following two lines write bmp image of cosine matrix values for checking. //string bmpPath = @"C:\SensorNetworks\Output\cosines.png"; //ImageTools.DrawMatrix(cosines, bmpPath, true); for (int r = 1; r < length - dctLength; r++) { // only stop if current location is a peak if (ipArray[r] < ipArray[r - 1] || ipArray[r] < ipArray[r + 1]) { continue; } // only stop if current location is a peak if (ipArray[r] < decibelThreshold) { continue; } // extract array and ready for DCT var dctArray = DataTools.Subarray(ipArray, r, dctLength); dctArray = DataTools.SubtractMean(dctArray); double[] dctCoefficient = MFCCStuff.DCT(dctArray, cosines); // convert to absolute values because not interested in negative values due to phase. for (int i = 0; i < dctLength; i++) { dctCoefficient[i] = Math.Abs(dctCoefficient[i]); } // remove low freq oscillations from consideration int thresholdIndex = minIndex / 4; for (int i = 0; i < thresholdIndex; i++) { dctCoefficient[i] = 0.0; } dctCoefficient = DataTools.normalise2UnitLength(dctCoefficient); int indexOfMaxValue = DataTools.GetMaxIndex(dctCoefficient); //mark DCT location with oscillation freq, only if oscillation freq is in correct range and amplitude if (indexOfMaxValue >= minIndex && indexOfMaxValue <= maxIndex && dctCoefficient[indexOfMaxValue] > dctThreshold) { for (int i = 0; i < dctLength; i++) { if (dctScores[r + i] < dctCoefficient[indexOfMaxValue]) { dctScores[r + i] = dctCoefficient[indexOfMaxValue]; oscFreq[r + i] = indexOfMaxValue / dctDuration / 2; } } } } }
} // end method ConvertODScores2Events() /* * public static double PeakEntropy(double[] array) * { * bool[] peaks = DataTools.GetPeaks(array); * int peakCount = DataTools.CountTrues(peaks); * //set up histogram of peak energies * double[] histogram = new double[peakCount]; * int count = 0; * for (int k = 0; k < array.Length; k++) * { * if (peaks[k]) * { * histogram[count] = array[k]; * count++; * } * } * histogram = DataTools.NormaliseMatrixValues(histogram); * histogram = DataTools.Normalise2Probabilites(histogram); * double normFactor = Math.Log(histogram.Length) / DataTools.ln2; //normalize for length of the array * double entropy = DataTools.Entropy(histogram) / normFactor; * return entropy; * } * */ /// <summary> /// returns the periodicity in an array of values. /// </summary> public static double[] PeriodicityAnalysis(double[] array) { //DataTools.writeBarGraph(array); var A = AutoAndCrossCorrelation.MyCrossCorrelation(array, array); // do 2/3rds of maximum possible lag int dctLength = A.Length; A = DataTools.SubtractMean(A); //DataTools.writeBarGraph(A); double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //set up the cosine coefficients double[] dct = MFCCStuff.DCT(A, cosines); for (int i = 0; i < dctLength; i++) { dct[i] = Math.Abs(dct[i]); //convert to absolute values } //DataTools.writeBarGraph(dct); for (int i = 0; i < 3; i++) { dct[i] = 0.0; //remove low freq oscillations from consideration } dct = DataTools.normalise2UnitLength(dct); var peaks = DataTools.GetPeaks(dct); // remove non-peak values and low values for (int i = 0; i < dctLength; i++) { if (!peaks[i] || dct[i] < 0.2) { dct[i] = 0.0; } } DataTools.writeBarGraph(dct); //get periodicity of highest three values int peakCount = 3; var period = new double[peakCount]; var maxIndex = new double[peakCount]; for (int i = 0; i < peakCount; i++) { int indexOfMaxValue = DataTools.GetMaxIndex(dct); maxIndex[i] = indexOfMaxValue; //double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi if ((double)indexOfMaxValue == 0) { period[i] = 0.0; } else { period[i] = dctLength / (double)indexOfMaxValue * 2; } dct[indexOfMaxValue] = 0.0; // remove value for next iteration } LoggedConsole.WriteLine("Max indices = {0:f0}, {1:f0}, {2:f0}.", maxIndex[0], maxIndex[1], maxIndex[2]); return(period); }
/* * * /// <summary> * /// Detects oscillations in a given freq bin. * /// there are several important parameters for tuning. * /// a) DCTLength: Good values are 0.25 to 0.50 sec. Do not want too long because DCT requires stationarity. * /// Do not want too short because too small a range of oscillations * /// b) DCTindex: Sets lower bound for oscillations of interest. Index refers to array of coeff returned by DCT. * /// Array has same length as the length of the DCT. Low freq oscillations occur more often by chance. Want to exclude them. * /// c) MinAmplitude: minimum acceptable value of a DCT coefficient if hit is to be accepted. * /// The algorithm is sensitive to this value. A lower value results in more oscillation hits being returned. * /// </summary> * /// <param name="minBin">min freq bin of search band</param> * /// <param name="maxBin">max freq bin of search band</param> * /// <param name="dctLength">number of values</param> * /// <param name="DCTindex">Sets lower bound for oscillations of interest.</param> * /// <param name="minAmplitude">threshold - do not accept a DCT value if its amplitude is less than this threshold</param> * public static Double[,] DetectOscillations(SpectrogramStandard sonogram, int minHz, int maxHz, * double dctDuration, int minOscilFreq, int maxOscilFreq, double minAmplitude) * { * int minBin = (int)(minHz / sonogram.FBinWidth); * int maxBin = (int)(maxHz / sonogram.FBinWidth); * * int dctLength = (int)Math.Round(sonogram.FramesPerSecond * dctDuration); * int minIndex = (int)(minOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi * int maxIndex = (int)(maxOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi * if (maxIndex > dctLength) maxIndex = dctLength; //safety check in case of future changes to code. * * int rows = sonogram.Data.GetLength(0); * int cols = sonogram.Data.GetLength(1); * Double[,] hits = new Double[rows, cols]; * Double[,] matrix = sonogram.Data; * //matrix = ImageTools.WienerFilter(sonogram.Data, 3);// DO NOT USE - SMUDGES EVERYTHING * * * double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //set up the cosine coefficients * //following two lines write matrix of cos values for checking. * //string fPath = @"C:\SensorNetworks\Sonograms\cosines.txt"; * //FileTools.WriteMatrix2File_Formatted(cosines, fPath, "F3"); * * //following two lines write bmp image of cos values for checking. * //string fPath = @"C:\SensorNetworks\Output\cosines.bmp"; * //ImageTools.DrawMatrix(cosines, fPath); * * * * // traverse columns - skip DC column * * * for (int c = minBin; c <= maxBin; c++) { * for (int r = 0; r < rows - dctLength; r++) * { * var array = new double[dctLength]; * //accumulate J columns of values * for (int i = 0; i < dctLength; i++) * for (int j = 0; j < 5; j++) array[i] += matrix[r + i, c + j]; * * array = DataTools.SubtractMean(array); * // DataTools.writeBarGraph(array); * * double[] dct = MFCCStuff.DCT(array, cosines); * for (int i = 0; i < dctLength; i++) dct[i] = Math.Abs(dct[i]);//convert to absolute values * dct[0] = 0.0; dct[1] = 0.0; dct[2] = 0.0; dct[3] = 0.0; dct[4] = 0.0;//remove low freq oscillations from consideration * dct = DataTools.normalise2UnitLength(dct); * //dct = DataTools.NormaliseMatrixValues(dct); //another option to NormaliseMatrixValues * int indexOfMaxValue = DataTools.GetMaxIndex(dct); * double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi * * //DataTools.MinMax(dct, out min, out max); * // DataTools.writeBarGraph(dct); * * //mark DCT location with oscillation freq, only if oscillation freq is in correct range and amplitude * if ((indexOfMaxValue >= minIndex) && (indexOfMaxValue <= maxIndex) && (dct[indexOfMaxValue] > minAmplitude)) * { * for (int i = 0; i < dctLength; i++) hits[r + i, c] = oscilFreq; * } * r += 5; //skip rows * } * c++; //do alternate columns * } * return hits; * } */ public static double[] DetectOscillationsInScoreArray(double[] scoreArray, double dctDuration, double timeScale, double dctThreshold, bool normaliseDCT, int minOscilFreq, int maxOscilFreq) { int dctLength = (int)Math.Round(timeScale * dctDuration); int minIndex = (int)(minOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi int maxIndex = (int)(maxOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi if (maxIndex > dctLength) { maxIndex = dctLength; //safety check in case of future changes to code. } int length = scoreArray.Length; double[] hits = new double[length]; double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //set up the cosine coefficients //following two lines write matrix of cos values for checking. //string fPath = @"C:\SensorNetworks\Sonograms\cosines.txt"; //FileTools.WriteMatrix2File_Formatted(cosines, fPath, "F3"); //following two lines write bmp image of cos values for checking. //string fPath = @"C:\SensorNetworks\Output\cosines.bmp"; //ImageTools.DrawMatrix(cosines, fPath); for (int r = 0; r < length - dctLength; r++) { var array = new double[dctLength]; //transfer values for (int i = 0; i < dctLength; i++) { array[i] = scoreArray[r + i]; } array = DataTools.SubtractMean(array); // DataTools.writeBarGraph(array); double[] dct = MFCCStuff.DCT(array, cosines); for (int i = 0; i < dctLength; i++) { dct[i] = Math.Abs(dct[i]); //convert to absolute values } for (int i = 0; i < 5; i++) { dct[i] = 0.0; //remove low freq oscillations from consideration } if (normaliseDCT) { dct = DataTools.normalise2UnitLength(dct); } int indexOfMaxValue = DataTools.GetMaxIndex(dct); double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi // DataTools.writeBarGraph(dct); //LoggedConsole.WriteLine("oscilFreq = " + oscilFreq); //mark DCT location with oscillation freq, only if oscillation freq is in correct range and amplitude if (indexOfMaxValue >= minIndex && indexOfMaxValue <= maxIndex && dct[indexOfMaxValue] > dctThreshold) { hits[r] = dct[indexOfMaxValue]; hits[r + 1] = dct[indexOfMaxValue]; // because skipping rows. //for (int i = 0; i < dctLength; i++) if (hits[r + i] < dct[indexOfMaxValue]) hits[r + i] = dct[indexOfMaxValue]; } r += 1; //skip rows } return(hits); }
/* * /// <summary> * /// FINDS OSCILLATIONS IN A SONOGRAM * /// SAME METHOD AS ABOVE BUT ..... * /// 1) WITHOUT CALCULATING THE COMPUTATION TIME * /// 2) WITHOUT DOING SEGMENTATION * /// </summary> * /// <param name="sonogram">sonogram derived from the recording</param> * /// <param name="minHz">min bound freq band to search</param> * /// <param name="maxHz">max bound freq band to search</param> * /// <param name="dctDuration">duration of DCT in seconds</param> * /// <param name="dctThreshold">minimum amplitude of DCT </param> * /// <param name="minOscilFreq">ignore oscillation frequencies below this threshold</param> * /// <param name="maxOscilFreq">ignore oscillation frequencies greater than this </param> * /// <param name="scoreThreshold">used for FP/FN</param> * /// <param name="minDuration">ignore hits whose duration is shorter than this</param> * /// <param name="maxDuration">ignore hits whose duration is longer than this</param> * /// <param name="scores">return an array of scores over the entire recording</param> * /// <param name="events">return a list of acoustic events</param> * /// <param name="hits">a matrix to be superimposed over the final sonogram which shows where the DCT coefficients exceeded the threshold</param> * public static void Execute(SpectrogramStandard sonogram, int minHz, int maxHz, * double dctDuration, double dctThreshold, bool normaliseDCT, double minOscilFreq, double maxOscilFreq, * double scoreThreshold, double minDuration, double maxDuration, * out double[] scores, out List<AcousticEvent> events, out Double[,] hits, out double[] oscFreq) * { * //convert the entire recording to an acoustic event - this is the legacy of previous experimentation!!!!!!!!! * List<AcousticEvent> segmentEvents = new List<AcousticEvent>(); * var ae = new AcousticEvent(0.0, sonogram.Duration.TotalSeconds, minHz, maxHz); * ae.SetTimeAndFreqScales(sonogram.FramesPerSecond, sonogram.FBinWidth); * segmentEvents.Add(ae); * * //DETECT OSCILLATIONS * hits = DetectOscillationsInSonogram(sonogram, minHz, maxHz, dctDuration, dctThreshold, normaliseDCT, minOscilFreq, maxOscilFreq, segmentEvents); * hits = RemoveIsolatedOscillations(hits); * * //EXTRACT SCORES AND ACOUSTIC EVENTS * scores = GetOscillationScores(hits, minHz, maxHz, sonogram.FBinWidth);//scores = fraction of BW bins in each row that have an oscilation hit. * scores = DataTools.filterMovingAverage(scores, 3); * oscFreq = GetOscillationFrequency(hits, minHz, maxHz, sonogram.FBinWidth); * events = ConvertODScores2Events(scores, oscFreq, minHz, maxHz, sonogram.FramesPerSecond, sonogram.FBinWidth, sonogram.Configuration.FreqBinCount, scoreThreshold, * minDuration, maxDuration, sonogram.Configuration.SourceFName); * } * */ /// <summary> /// Detects oscillations in a given freq bin. /// there are several important parameters for tuning. /// a) dctDuration: Good values are 0.25 to 0.50 sec. Do not want too long because DCT requires stationarity. /// Do not want too short because too small a range of oscillations /// b) dctThreshold: minimum acceptable value of a DCT coefficient if hit is to be accepted. /// The algorithm is sensitive to this value. A lower value results in more oscillation hits being returned. /// c) Min and Max Oscillaitons: Sets lower & upper bound for oscillations of interest. /// Array has same length as the length of the DCT. Low freq oscillations occur more often by chance. Want to exclude them. /// </summary> /// <param name="minHz">min freq bin of search band.</param> /// <param name="maxHz">max freq bin of search band.</param> public static double[,] DetectOscillationsInSonogram(SpectrogramStandard sonogram, int minHz, int maxHz, double dctDuration, double dctThreshold, bool normaliseDCT, double minOscilFreq, double maxOscilFreq, List <AcousticEvent> events) { if (events == null) { return(null); } int minBin = (int)(minHz / sonogram.FBinWidth); int maxBin = (int)(maxHz / sonogram.FBinWidth); int dctLength = (int)Math.Round(sonogram.FramesPerSecond * dctDuration); int minIndex = (int)(minOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi int maxIndex = (int)(maxOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi if (maxIndex > dctLength) { maxIndex = dctLength; //safety check in case of future changes to code. } int rows = sonogram.Data.GetLength(0); int cols = sonogram.Data.GetLength(1); double[,] hits = new double[rows, cols]; double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //set up the cosine coefficients //following two lines write matrix of cos values for checking. //string fPath = @"C:\SensorNetworks\Sonograms\cosines.txt"; //FileTools.WriteMatrix2File_Formatted(cosines, fPath, "F3"); //following two lines write bmp image of cos values for checking. string fPath = @"C:\SensorNetworks\Output\cosines.bmp"; ImageTools.DrawMatrix(cosines, fPath, true); foreach (AcousticEvent av in events) { int startRow = (int)Math.Round(av.TimeStart * sonogram.FramesPerSecond); int endRow = (int)Math.Round(av.TimeEnd * sonogram.FramesPerSecond); if (endRow >= sonogram.FrameCount) { endRow = sonogram.FrameCount - 1; } endRow -= dctLength; if (endRow <= startRow) { endRow = startRow + 1; //want minimum of one row } // traverse columns for (int c = minBin; c <= maxBin; c++) { for (int r = startRow; r < endRow; r++) { var array = new double[dctLength]; //accumulate J columns of values int N = 5; //average five rows for (int i = 0; i < dctLength; i++) { for (int j = 0; j < N; j++) { array[i] += sonogram.Data[r + i, c + j]; } array[i] /= N; } array = DataTools.SubtractMean(array); // DataTools.writeBarGraph(array); //double entropy = PeakEntropy(array); //if (entropy < 0.85) //{ // r += 6; //skip rows // continue; //} int lowFreqBuffer = 5; double[] dct = MFCCStuff.DCT(array, cosines); for (int i = 0; i < dctLength; i++) { dct[i] = Math.Abs(dct[i]); //convert to absolute values } for (int i = 0; i < lowFreqBuffer; i++) { dct[i] = 0.0; //remove low freq oscillations from consideration } if (normaliseDCT) { dct = DataTools.normalise2UnitLength(dct); } int indexOfMaxValue = DataTools.GetMaxIndex(dct); double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi //DataTools.writeBarGraph(dct); //LoggedConsole.WriteLine("oscilFreq ={0:f2} (max index={1}) Amp={2:f2}", oscilFreq, indexOfMaxValue, dct[indexOfMaxValue]); //calculate specificity i.e. what other oscillations are present. //double offMaxAmplitude = 0.0; //for (int i = lowFreqBuffer; i < dctLength; i++) offMaxAmplitude += dct[i]; //offMaxAmplitude -= (dct[indexOfMaxValue-1] + dct[indexOfMaxValue] + dct[indexOfMaxValue+1]); //offMaxAmplitude /= (dctLength - lowFreqBuffer - 3); //get average //double specificity = 2 * (0.5 - (offMaxAmplitude / dct[indexOfMaxValue])); ////LoggedConsole.WriteLine("avOffAmp={0:f2} specificity ={1:f2}", offMaxAmplitude, specificity); //double threshold = dctThreshold + dctThreshold; //mark DCT location with oscillation freq, only if oscillation freq is in correct range and amplitude if (indexOfMaxValue >= minIndex && indexOfMaxValue <= maxIndex && dct[indexOfMaxValue] > dctThreshold) //if ((indexOfMaxValue >= minIndex) && (indexOfMaxValue <= maxIndex) && ((dct[indexOfMaxValue] * specificity) > threshold)) { for (int i = 0; i < dctLength; i++) { hits[r + i, c] = oscilFreq; } for (int i = 0; i < dctLength; i++) { hits[r + i, c + 1] = oscilFreq; //write alternate column - MUST DO THIS BECAUSE doing alternate columns } } r += 6; //skip rows i.e. frames of the sonogram. } c++; //do alternate columns } } //foreach (AcousticEvent av in events) return(hits); }
public static double[] DetectOscillations(double[] ipArray, double framesPerSecond, double dctDuration, double minOscilFreq, double maxOscilFreq, double dctThreshold) { int dctLength = (int)Math.Round(framesPerSecond * dctDuration); int minIndex = (int)(minOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi int maxIndex = (int)(maxOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi //double midOscilFreq = minOscilFreq + ((maxOscilFreq - minOscilFreq) / 2); if (maxIndex > dctLength) { return(null); //safety check } int length = ipArray.Length; var dctScores = new double[length]; //var hits = new double[length]; double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //set up the cosine coefficients //following two lines write bmp image of cosine matrix values for checking. //string bmpPath = @"C:\SensorNetworks\Output\cosines.png"; //ImageTools.DrawMatrix(cosines, bmpPath, true); for (int r = 1; r < length - dctLength; r++) { // only stop if current location is a peak if (ipArray[r] < ipArray[r - 1] || ipArray[r] < ipArray[r + 1]) { continue; } // extract array and ready for DCT //for (int i = 0; i < dctLength; i++) dctArray[i] = ipArray[r + i]; var dctArray = DataTools.Subarray(ipArray, r, dctLength); dctArray = DataTools.SubtractMean(dctArray); //dctArray = DataTools.Vector2Zscores(dctArray); double[] dctCoeff = MFCCStuff.DCT(dctArray, cosines); // convert to absolute values because not interested in negative values due to phase. for (int i = 0; i < dctLength; i++) { dctCoeff[i] = Math.Abs(dctCoeff[i]); } // remove low freq oscillations from consideration int thresholdIndex = minIndex / 4; for (int i = 0; i < thresholdIndex; i++) { dctCoeff[i] = 0.0; } dctCoeff = DataTools.normalise2UnitLength(dctCoeff); //dct = DataTools.NormaliseMatrixValues(dct); //another option to NormaliseMatrixValues int indexOfMaxValue = DataTools.GetMaxIndex(dctCoeff); //double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi // #### Tried this option for scoring oscillation hits but did not work well. // #### Requires very fine tuning of thresholds //dctCoeff = DataTools.Normalise2Probabilites(dctCoeff); //// sum area under curve where looking for oscillations //double sum = 0.0; //for (int i = minIndex; i <= maxIndex; i++) // sum += dctCoeff[i]; //if (sum > dctThreshold) //{ // for (int i = 0; i < dctLength; i++) hits[r + i, c] = midOscilFreq; //} // DEBUGGING // DataTools.MinMax(dctCoeff, out min, out max); //DataTools.writeBarGraph(dctArray); //DataTools.writeBarGraph(dctCoeff); //mark DCT location with oscillation freq, only if oscillation freq is in correct range and amplitude if (indexOfMaxValue >= minIndex && indexOfMaxValue <= maxIndex && dctCoeff[indexOfMaxValue] > dctThreshold) { //for (int i = 0; i < dctLength; i++) dctScores[r + i] = midOscilFreq; for (int i = 0; i < dctLength; i++) { if (dctScores[r + i] < dctCoeff[indexOfMaxValue]) { dctScores[r + i] = dctCoeff[indexOfMaxValue]; } } } } //return hits; //dctArray return(dctScores); }
/// <summary> /// Detects oscillations in a given freq bin. /// there are several important parameters for tuning. /// a) DCTLength: Good values are 0.25 to 0.50 sec. Do not want too long because DCT requires stationarity. /// Do not want too short because too small a range of oscillations /// b) DCTindex: Sets lower bound for oscillations of interest. Index refers to array of coeff returned by DCT. /// Array has same length as the length of the DCT. Low freq oscillations occur more often by chance. Want to exclude them. /// c) MinAmplitude: minimum acceptable value of a DCT coefficient if hit is to be accepted. /// The algorithm is sensitive to this value. A lower value results in more oscillation hits being returned. /// </summary> /// <param name="sonogram"></param> /// <param name="minHz">min freq bin of search band</param> /// <param name="maxHz">max freq bin of search band</param> /// <param name="dctDuration">number of values</param> /// <param name="maxOscilFreq"></param> /// <param name="dctThreshold">threshold - do not accept a DCT coefficient if its value is less than this threshold</param> /// <param name="minOscilFreq"></param> /// <returns></returns> public static double[,] DetectOscillations(SpectrogramStandard sonogram, int minHz, int maxHz, double dctDuration, int minOscilFreq, int maxOscilFreq, double dctThreshold) { int minBin = (int)(minHz / sonogram.FBinWidth); int maxBin = (int)(maxHz / sonogram.FBinWidth); int dctLength = (int)Math.Round(sonogram.FramesPerSecond * dctDuration); int minIndex = (int)(minOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi int maxIndex = (int)(maxOscilFreq * dctDuration * 2); //multiply by 2 because index = Pi and not 2Pi int midOscilFreq = minOscilFreq + ((maxOscilFreq - minOscilFreq) / 2); if (maxIndex > dctLength) { return(null); //safety check } int rows = sonogram.Data.GetLength(0); int cols = sonogram.Data.GetLength(1); double[,] hits = new double[rows, cols]; double[,] matrix = sonogram.Data; double[,] cosines = MFCCStuff.Cosines(dctLength, dctLength); //set up the cosine coefficients //following two lines write matrix of cos values for checking. //string txtPath = @"C:\SensorNetworks\Output\cosines.txt"; //FileTools.WriteMatrix2File_Formatted(cosines, txtPath, "F3"); //following two lines write bmp image of cos values for checking. //string bmpPath = @"C:\SensorNetworks\Output\cosines.png"; //ImageTools.DrawMatrix(cosines, bmpPath, true); for (int c = minBin; c <= maxBin; c++) //traverse columns - skip DC column { var dctArray = new double[dctLength]; for (int r = 0; r < rows - dctLength; r++) { // extract array and ready for DCT for (int i = 0; i < dctLength; i++) { dctArray[i] = matrix[r + i, c]; } dctArray = DataTools.SubtractMean(dctArray); //dctArray = DataTools.Vector2Zscores(dctArray); double[] dctCoeff = MFCCStuff.DCT(dctArray, cosines); // convert to absolute values because not interested in negative values due to phase. for (int i = 0; i < dctLength; i++) { dctCoeff[i] = Math.Abs(dctCoeff[i]); } // remove low freq oscillations from consideration int thresholdIndex = minIndex / 4; for (int i = 0; i < thresholdIndex; i++) { dctCoeff[i] = 0.0; } dctCoeff = DataTools.normalise2UnitLength(dctCoeff); //dct = DataTools.NormaliseMatrixValues(dct); //another option to NormaliseMatrixValues int indexOfMaxValue = DataTools.GetMaxIndex(dctCoeff); //double oscilFreq = indexOfMaxValue / dctDuration * 0.5; //Times 0.5 because index = Pi and not 2Pi // #### Tried this option for scoring oscillation hits but did not work well. // #### Requires very fine tuning of thresholds //dctCoeff = DataTools.Normalise2Probabilites(dctCoeff); //// sum area under curve where looking for oscillations //double sum = 0.0; //for (int i = minIndex; i <= maxIndex; i++) // sum += dctCoeff[i]; //if (sum > dctThreshold) //{ // for (int i = 0; i < dctLength; i++) hits[r + i, c] = midOscilFreq; //} // DEBUGGING // DataTools.MinMax(dctCoeff, out min, out max); //DataTools.writeBarGraph(dctArray); //DataTools.writeBarGraph(dctCoeff); //mark DCT location with oscillation freq, only if oscillation freq is in correct range and amplitude if (indexOfMaxValue >= minIndex && indexOfMaxValue <= maxIndex && dctCoeff[indexOfMaxValue] > dctThreshold) { for (int i = 0; i < dctLength; i++) { hits[r + i, c] = midOscilFreq; } } r += 5; //skip rows } c++; //do alternate columns } return(hits); }