/// <summary> /// FFTの結果を格納させ、検査を実施させる /// </summary> /// <param name="result">WAVEファイルをFFT処理したデータ</param> public void Add(FFTresult result) { //for (int i = 0; i < this._coreUnit.Count; i++) Parallel.For(0, this._coreUnit.Count, i => // 可能なら並列で処理 { this._coreUnit[i].Set(result); // 発声検出器にデータを格納して発声部分の認識を行う if (this._coreUnit[i].Ready) { Monitor.Enter(this._generatedFeatures); Result feature = this._coreUnit[i].GetFeature(); // 生成された特徴ベクトルを取得 if (this.PcaAnalysis) // 主成分分析を使ったベクトルのスリム化が必要なら処理する { var copyVector = feature.FeatureVector; double[] neoVector = PCA.Convert(copyVector.ToArray()); var neoFeature = new Result(feature.FilterName, feature.Time, feature.TimeWidth, feature.SN, new PatternRecognition.Feature(neoVector)); this._generatedFeatures.Add(neoFeature); // 特徴データを取得可能ならもらって登録する } else { this._generatedFeatures.Add(feature); // 特徴データを取得可能ならもらって登録する } Monitor.Exit(this._generatedFeatures); } if (this.Log) { this._coreUnit[i].SaveConditionOfDetector("sound detection log " + this._coreUnit[i].FileterName + ".csv"); // 必要ならファイルへ検出器のコンディションを出力 } }); return; }
/// <summary> /// WAVEファイルをFFTにかけた結果をセットする /// </summary> /// <param name="result">WaveファイルをFFT処理したデータ</param> public virtual void Set(FFTresult result) { if (this.IsSetuped) { this._time += 1.0 / this._frequencyOfFFT; // 既セット分から経過時刻を計算する this._sensor.Set(result); // 検出器は内部でフィルタリングするため、下のフィルタリング後のデータを渡さなくともよい FFTresult filteredResult = result.Filter(this._band); // フィルタをかけたオブジェクトを作る if (this._sensor.Detection) // 鳴き声があればtrue(になって欲しい) { this._genetator.Add(filteredResult); // フィルタリングしたFFT結果を使った方が明らかにノイズを拾わない } else { if (this._genetator.Ready) { this._genetator.Add(filteredResult); // 最後の閉め double timeWidth = this._genetator.TimeOfStock; double startTime = this._time - timeWidth; // 検出開始時刻を計算 var feature = this._genetator.GetFeature(); // 準備が完了しているようなら特徴ベクトルを作らせる if (feature != null) { this._generatedFeatures.Add(new Result(this.FileterName, startTime, timeWidth, this._genetator.SN, feature)); // nullでなければ、特徴ベクトルとして追加する } } this._genetator.Init(filteredResult); // 特徴ベクトル生成器をリセット } } return; }
// ---- メソッド --------------------------------------- /// <summary> /// 特徴ベクトルを返す /// </summary> /// <returns>特徴ベクトル</returns> public override PatternRecognition.Feature GetFeature() { if (this.Ready) { // 最大のパワーとなった帯域を探すとその最大値を求める double maxPSDofBand = this._sumOfPSDbyBand.Max(); // 最大値を取得 int indexOfMax = -1; // -1としておくことで、↓2行のアルゴリズムにより最大となる帯域のインデックス-1を得る for (int i = 0; i < this._sumOfPSDbyBand.Length && this._sumOfPSDbyBand[i] != maxPSDofBand; i++) { indexOfMax = i; // 最大となった帯域のインデックス-1を取得する } indexOfMax++; // 等しいと代入の前にループを抜けるので、抜けた後で加算してインデックスとする // SN比を求める。求まればよいが。 double noiseLevelPSD = this._timeSeriesPSD[0][indexOfMax]; // フレームの初めはノイズレベルに近いはず double maxPSDofFlame = double.MinValue; for (int i = 0; i < this._timeSeriesPSD.Count; i++) { if (maxPSDofFlame < this._timeSeriesPSD[i][indexOfMax]) { maxPSDofFlame = this._timeSeriesPSD[i][indexOfMax]; } } this._SNratio = 10.0 * Math.Log10(maxPSDofFlame / noiseLevelPSD); // 帯域パワーの最大値が1になるように正規化 var fea1 = new PatternRecognition.Feature(this._sumOfPSDbyBand); fea1.Normalizing(); // 変調スペクトルをもとめる double[] modulatedSongAtUniBand = new double[this._timeSeriesPSD.Count]; for (int i = 0; i < modulatedSongAtUniBand.Length; i++) { modulatedSongAtUniBand[i] = this._timeSeriesPSD[i][indexOfMax]; // 最大であった帯域のパワーを取得する(変調されたパワー) } DFT fft = new DFT(modulatedSongAtUniBand.Length, Window.WindowKind.Hanning); // FFTを実施する準備 FFTresult modulationSpectrum = fft.FFT(modulatedSongAtUniBand, this._condition.FrequencyOfFFT); double[] spectrum = new double[this._condition.MaxModulationSpectrumFrequency * 10]; //for (int i = 0; i < spectrum.Length; i++) Parallel.For(0, spectrum.Length, i => { double minFrequency = (double)i * 0.1; double maxFrequency = minFrequency + 0.1; spectrum[i] = modulationSpectrum.GetPSD(minFrequency, maxFrequency); // 帯域毎にコピー }); // コピーした変調スペクトルを正規化 var fea2 = new PatternRecognition.Feature(spectrum); fea2.Normalizing(); // 特徴ベクトルとしてまとめる var ans = new PatternRecognition.Feature(fea1.ToArray()); ans.Add(fea2.ToArray()); ans.Normalizing(); return(ans); } else { return(null); } }
/// <summary> /// バッファをクリア /// <para>未検出データを常に先頭に格納する必要がありますので、データを引き渡してください。</para> /// <param name="data">FFT済みのデータ</param> /// </summary> public void Init(FFTresult data) { for (int i = 0; i < this._sumOfPSDbyBand.Length; i++) { this._sumOfPSDbyBand[i] = 0.0; // 帯域パワー積算値を初期化 } this._timeSeriesPSD.Clear(); this.Add(data); // データを投げて処理・登録させる this._SNratio = double.NaN; return; }
/// <summary> /// FFT後のデータを入れてください /// </summary> /// <param name="data">FFT済みのデータ</param> public void Add(FFTresult data) { double[] psdByBand = new double[_bandNum]; // 帯域毎の周波数帯域パワーを格納する //for (int i = 0; i < psdByBand.Length; i++) // 帯域パワーの計算と、積算を同時に実施する Parallel.For(0, psdByBand.Length, i => { psdByBand[i] = data.GetPSD(this._band[i], Window.WindowKind.Triangle); // 窓付きで帯域パワーを計算 this._sumOfPSDbyBand[i] += psdByBand[i]; // 帯域パワーを積算する }); this._timeSeriesPSD.Add(psdByBand); return; }
/******************* メソッド *************************************/ /// <summary> /// FFT処理後のデータをセットする /// </summary> /// <param name="data">FFT処理後のデータ</param> /// <exception cref="SystemException">演算条件がセットされていない場合にスロー</exception> public void Set(FFTresult data) { Boolean detection = false; if (this.IsSetuped == false) { throw new SystemException("演算条件がセットされていません。Setup()を行ってください。"); } for (int i = 0; i < this.sensor.Length; i++) { //double psd = Math.Log10(data.GetPSD(this.band[i])); // FFTの結果から、パワー密度を取得 double psd = data.GetPSD(this.band[i]); // FFTの結果から、パワー密度を取得 this.sensor[i].Set(psd); // 検出器に掛ける if (this.sensor[i].FrontDetection) { detection = true; // 検出結果をチェック(一つでも検出していたらture) } this._edge = this.sensor[i].DetectedEdge; } this._detection = detection; // 結果をバックアップ return; }
// ---- メソッド ------------------------------------------------------------- /// <summary> /// WAVEファイルをFFTにかけた結果をセットする /// <para>KMCustomUnit2c1dクラスとの違いは、連続して鳴く鳥に対応しているという点です。</para> /// <para>背景ノイズが人いときはノイズと鳴き声がいっしょくたにされますけど、運が悪かったということで諦めるという方針です。</para> /// </summary> /// <param name="result">WaveファイルをFFT処理したデータ</param> public override void Set(FFTresult result) { if (this.IsSetuped) { this._time += 1.0 / this._frequencyOfFFT; // 既セット分から経過時刻を計算する this._sensor.Set(result); // 検出器は内部でフィルタリングするため、下のフィルタリング後のデータを渡さなくともよい FFTresult filteredResult = result.Filter(this._band); // フィルタをかけたオブジェクトを作る if (this._sensor.Detection) // 鳴き声があればtrue(になって欲しい) { this._genetator.Add(filteredResult); this.nonDetectionTime = 0.0; } else { if (this.nonDetectionTime < this.Threshold) { this._genetator.Add(filteredResult); } if ((this.nonDetectionTime > this.Threshold || this._genetator.TimeOfStock > 10.0) && this._genetator.Ready) { double timeWidth = this._genetator.TimeOfStock; double startTime = this._time - timeWidth; // 検出開始時刻を計算 var feature = this._genetator.GetFeature(); // 準備が完了しているようなら特徴ベクトルを作らせる if (feature != null) { this._generatedFeatures.Add(new Result(this.FileterName, startTime, timeWidth, this._genetator.SN, feature)); // nullでなければ、特徴ベクトルとして追加する } this._genetator.Init(filteredResult); // 特徴ベクトル生成器をリセット } if (this._genetator.Ready == false) { this._genetator.Init(filteredResult); // 特徴ベクトル生成器をリセット } this.nonDetectionTime += 1.0 / this._frequencyOfFFT; } } return; }
/// <summary> /// WAVEファイルをFFTにかけた結果をセットする /// </summary> /// <param name="result">WaveファイルをFFT処理したデータ</param> public virtual void Set(FFTresult result) { if (this.IsSetuped) { this._time += 1.0 / this._frequencyOfFFT; // 既セット分から経過時刻を計算する this._sensor.Set(result); // 検出器は内部でフィルタリングするため、下のフィルタリング後のデータを渡さなくともよい FFTresult filteredResult = result.Filter(this._band); // フィルタをかけたオブジェクトを作る if (this._sensor.Detection) // 鳴き声があればtrue(になって欲しい) { this._genetator.Add(filteredResult); // フィルタリングしたFFT結果を使った方が明らかにノイズを拾わない } else { if (this._genetator.Ready) { this._genetator.Add(filteredResult); // 最後の閉め double timeWidth = this._genetator.TimeOfStock; double startTime = this._time - timeWidth; // 検出開始時刻を計算 var feature = this._genetator.GetFeature(); // 準備が完了しているようなら特徴ベクトルを作らせる if (feature != null) this._generatedFeatures.Add(new Result(this.FileterName, startTime, timeWidth, this._genetator.SN, feature)); // nullでなければ、特徴ベクトルとして追加する } this._genetator.Init(filteredResult); // 特徴ベクトル生成器をリセット } } return; }
/// <summary> /// 音声ファイルを開き、生成された特徴ベクトルのリストを返す /// </summary> /// <param name="waveFilePath">WAVEファイルのフルパス</param> /// <param name="discriminator">識別器<para>識別させない場合は省略して下さい。</para></param> /// <param name="threshold">棄却レベル<para>識別のさせる場合、この値を下回ると識別結果をUnknownと判定します。</para></param> /// <returns> /// 生成された特徴ベクトルのリスト /// <para>識別器を指定している場合は、識別結果を添付します。</para> /// </returns> public List <Result> GenerateFeatureList(string waveFilePath, Discriminator discriminator = null, double threshold = 0.0) { var ans = new List <Result>(0); // 結果を格納する if (System.IO.File.Exists(waveFilePath)) { WaveReader wave_file = new WaveReader(); wave_file.Open(waveFilePath); // waveファイルを開く if (wave_file.IsOpen) { try { this.Lock = true; this.SetGenerator(wave_file); this._generator.Log = this.Log; DFT fft = new DFT(this.ReadAmount, Window.WindowKind.Hanning); // FFTを実施する準備 while (wave_file.IsOpen && wave_file.ReadLimit == false) // 読めるなら、全て読むまでループ { MusicData data = wave_file.Read(this.ReadAmount); // ファイルからread_size_sec秒間読み込み。 double[] sound; if (data.UsableCH == MusicData.UsableChannel.Left) // データの入っている方を格納する { sound = data.GetData(MusicData.Channel.Left); // } else { sound = data.GetData(MusicData.Channel.Right); } FFTresult result = fft.FFT(sound, (double)wave_file.SamplingRate); // FFTを実行. FFTサイズが音声データよりも小さい場合、始めの方しか処理されないので注意. this._generator.Add(result); if (this._generator.Ready) { //Console.WriteLine("検出したようです。"); // デバッグモードで動かしながら確認するためのコンソール出力 Result feature = this._generator.GetFeature(); // 特徴ベクトル生成 if (discriminator == null) { ans.Add(feature); // 答えを格納 } else { Discriminator.IDandLikelihood id = discriminator.Discriminate(feature.FeatureVector); string name = id.ID; if (id.Likelihood < threshold) { name = "Unknown"; } feature.DiscriminationResult = name; if (!(this.MatchOnlyAtDiscrimination == true && name != feature.FilterName)) { ans.Add(feature); } } } } } catch (SystemException e) { throw new Exception(e.Message); } } else { throw new Exception("Waveファイルを開くことができませんでした。\nWaveReaderクラスエラーメッセージ:" + wave_file.ErrorMessage); } wave_file.Close(); // waveファイルを閉じる } this.Lock = false; return(ans); }
/// <summary> /// バッファをクリア /// <para>未検出データを常に先頭に格納する必要がありますので、データを引き渡してください。</para> /// <param name="data">FFT済みのデータ</param> /// </summary> public void Init(FFTresult data) { for (int i = 0; i < this._sumOfPSDbyBand.Length; i++) this._sumOfPSDbyBand[i] = 0.0; // 帯域パワー積算値を初期化 this._timeSeriesPSD.Clear(); this.Add(data); // データを投げて処理・登録させる this._SNratio = double.NaN; return; }
// ---- メソッド ------------------------------------------------------------- /// <summary> /// WAVEファイルをFFTにかけた結果をセットする /// <para>KMCustomUnit2c1dクラスとの違いは、連続して鳴く鳥に対応しているという点です。</para> /// <para>背景ノイズが人いときはノイズと鳴き声がいっしょくたにされますけど、運が悪かったということで諦めるという方針です。</para> /// </summary> /// <param name="result">WaveファイルをFFT処理したデータ</param> public override void Set(FFTresult result) { if (this.IsSetuped) { this._time += 1.0 / this._frequencyOfFFT; // 既セット分から経過時刻を計算する this._sensor.Set(result); // 検出器は内部でフィルタリングするため、下のフィルタリング後のデータを渡さなくともよい FFTresult filteredResult = result.Filter(this._band); // フィルタをかけたオブジェクトを作る if (this._sensor.Detection) // 鳴き声があればtrue(になって欲しい) { this._genetator.Add(filteredResult); this.nonDetectionTime = 0.0; } else { if (this.nonDetectionTime < this.Threshold) this._genetator.Add(filteredResult); if ((this.nonDetectionTime > this.Threshold || this._genetator.TimeOfStock > 10.0) && this._genetator.Ready) { double timeWidth = this._genetator.TimeOfStock; double startTime = this._time - timeWidth; // 検出開始時刻を計算 var feature = this._genetator.GetFeature(); // 準備が完了しているようなら特徴ベクトルを作らせる if (feature != null) this._generatedFeatures.Add(new Result(this.FileterName, startTime, timeWidth, this._genetator.SN, feature)); // nullでなければ、特徴ベクトルとして追加する this._genetator.Init(filteredResult); // 特徴ベクトル生成器をリセット } if (this._genetator.Ready == false) this._genetator.Init(filteredResult); // 特徴ベクトル生成器をリセット this.nonDetectionTime += 1.0 / this._frequencyOfFFT; } } return; }
/******************* メソッド *************************************/ /// <summary> /// FFT処理後のデータをセットする /// </summary> /// <param name="data">FFT処理後のデータ</param> /// <exception cref="SystemException">演算条件がセットされていない場合にスロー</exception> public void Set(FFTresult data) { Boolean detection = false; if (this.IsSetuped == false) throw new SystemException("演算条件がセットされていません。Setup()を行ってください。"); for (int i = 0; i < this.sensor.Length; i++) { //double psd = Math.Log10(data.GetPSD(this.band[i])); // FFTの結果から、パワー密度を取得 double psd = data.GetPSD(this.band[i]); // FFTの結果から、パワー密度を取得 this.sensor[i].Set(psd); // 検出器に掛ける if(this.sensor[i].FrontDetection)detection = true; // 検出結果をチェック(一つでも検出していたらture) this._edge = this.sensor[i].DetectedEdge; } this._detection = detection; // 結果をバックアップ return; }