taigatakano commited on
Commit
1a2008f
·
1 Parent(s): bf435ab

Support High/Low Pass Fillter

Browse files
.gitignore CHANGED
@@ -6,3 +6,4 @@
6
  __pycache__
7
  flagged
8
  *.DS_Store
 
 
6
  __pycache__
7
  flagged
8
  *.DS_Store
9
+ *.mp4
Dockerfile CHANGED
@@ -10,3 +10,8 @@ WORKDIR /app
10
  COPY pyproject.toml* poetry.lock* /app/
11
  RUN poetry install
12
  RUN rm -rf /app/pyproject.toml* /app/poetry.lock*
 
 
 
 
 
 
10
  COPY pyproject.toml* poetry.lock* /app/
11
  RUN poetry install
12
  RUN rm -rf /app/pyproject.toml* /app/poetry.lock*
13
+
14
+ # For Huggingface
15
+ # COPY . /app/
16
+
17
+ # CMD [ "python3", "lab_tool_webui.py" ]
lab_tool_webui.py → app.py RENAMED
@@ -22,6 +22,16 @@ with gr.Blocks() as main_ui:
22
  column_dropdown = gr.Dropdown(["Fp1", "Fp2", "T7", "T8", "O1", "O2"], value="Fp2", label="使用する信号データ", allow_custom_value=True, info="使用する信号データを選んでください。デフォルトはFp2です。")
23
  start_time = gr.Slider(minimum=0, maximum=60, value=0.0, step=0.5, label="Start Time (sec)")
24
  end_time = gr.Slider(minimum=0, maximum=60, value=60.0, step=0.5, label="End Time (sec)")
 
 
 
 
 
 
 
 
 
 
25
  submit_button = gr.Button("計算開始")
26
 
27
  file_input.change(
@@ -34,7 +44,11 @@ with gr.Blocks() as main_ui:
34
  wavelet_image = gr.Image(type="filepath", label="Wavelet")
35
  signal_image = gr.Image(type="filepath", label="Signal")
36
 
37
- submit_button.click(wavelet.wavelet_ui, inputs=[file_input, fs_slider, fmax_slider, column_dropdown, start_time, end_time], outputs=[wavelet_image, signal_image])
 
 
 
 
38
 
39
  with gr.Tab("1f Noise Search"):
40
  with gr.Row():
@@ -50,4 +64,4 @@ with gr.Blocks() as main_ui:
50
 
51
 
52
  if __name__ == "__main__":
53
- main_ui.queue().launch(server_name="0.0.0.0")
 
22
  column_dropdown = gr.Dropdown(["Fp1", "Fp2", "T7", "T8", "O1", "O2"], value="Fp2", label="使用する信号データ", allow_custom_value=True, info="使用する信号データを選んでください。デフォルトはFp2です。")
23
  start_time = gr.Slider(minimum=0, maximum=60, value=0.0, step=0.5, label="Start Time (sec)")
24
  end_time = gr.Slider(minimum=0, maximum=60, value=60.0, step=0.5, label="End Time (sec)")
25
+ filter_setting = gr.Radio(
26
+ ["No Filter", "High PASS", "Low PASS"],
27
+ label="フィルター設定",
28
+ value="High PASS",
29
+ )
30
+ fp_hp = gr.Slider(minimum=0, maximum=20, value=3, step=0.1, label="通過域端周波数 [Hz]")
31
+ fs_hp = gr.Slider(minimum=0, maximum=20, value=1, step=0.1, label="阻止域端周波数 [Hz]")
32
+ gpass = gr.Slider(minimum=0, maximum=100, value=3, step=1, label="通過域端最大損失 [dB]")
33
+ gstop = gr.Slider(minimum=0, maximum=100, value=40, step=1, label="阻止域端最小損失 [dB]")
34
+
35
  submit_button = gr.Button("計算開始")
36
 
37
  file_input.change(
 
44
  wavelet_image = gr.Image(type="filepath", label="Wavelet")
45
  signal_image = gr.Image(type="filepath", label="Signal")
46
 
47
+ submit_button.click(wavelet.wavelet_ui, inputs=[
48
+ file_input,
49
+ fs_slider, fmax_slider, column_dropdown, start_time, end_time,
50
+ filter_setting, fp_hp, fs_hp, gpass, gstop],
51
+ outputs=[wavelet_image, signal_image])
52
 
53
  with gr.Tab("1f Noise Search"):
54
  with gr.Row():
 
64
 
65
 
66
  if __name__ == "__main__":
67
+ main_ui.queue().launch(server_name="0.0.0.0", server_port=7860)
lab_tools/filter.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import scipy.signal as signal
2
+
3
+
4
+ def apply_filter(signal_data, sample_rate, pass_freq, stop_freq, pass_gain, stop_gain, filter_type):
5
+ nyquist_freq = sample_rate / 2 # ナイキスト周波数
6
+ normalized_pass_freq = pass_freq / nyquist_freq # 通過域端周波数を正規化
7
+ normalized_stop_freq = stop_freq / nyquist_freq # 阻止域端周波数を正規化
8
+ filter_order, cutoff_freq = signal.buttord(
9
+ normalized_pass_freq, normalized_stop_freq, pass_gain, stop_gain
10
+ ) # フィルタのオーダーと正規化周波数を計算
11
+ b, a = signal.butter(filter_order, cutoff_freq, filter_type) # フィルタの伝達関数を計算
12
+ filtered_signal = signal.filtfilt(b, a, signal_data) # 信号にフィルタを適用
13
+ return filtered_signal
14
+
15
+
16
+ def lowpass(signal_data, sample_rate, pass_freq, stop_freq, pass_gain, stop_gain):
17
+ return apply_filter(signal_data, sample_rate, pass_freq, stop_freq, pass_gain, stop_gain, "low")
18
+
19
+
20
+ def highpass(signal_data, sample_rate, pass_freq, stop_freq, pass_gain, stop_gain):
21
+ return apply_filter(signal_data, sample_rate, pass_freq, stop_freq, pass_gain, stop_gain, "high")
lab_tools/highpass.py DELETED
@@ -1,62 +0,0 @@
1
- import numpy as np
2
- from scipy import signal
3
- from scipy import fftpack
4
- from typing import Tuple
5
-
6
-
7
- def highpass_filter(signal_data: np.ndarray, samplerate: float, fp: float, fs: float, gpass: float, gstop: float) -> np.ndarray:
8
- fn = samplerate / 2
9
- wp = fp / fn
10
- ws = fs / fn
11
-
12
- N, Wn = signal.buttord(wp, ws, gpass, gstop)
13
- b, a = signal.butter(N, Wn, "high")
14
-
15
- filtered_signal = signal.filtfilt(b, a, signal_data)
16
-
17
- return filtered_signal
18
-
19
-
20
- def overlap_frames(signal_data: np.ndarray, samplerate: float, frame_size: int, overlap: float) -> Tuple[np.ndarray, int]:
21
- total_duration = len(signal_data) / samplerate
22
- frame_duration = frame_size / samplerate
23
- step_size = frame_size * (1 - overlap / 100)
24
-
25
- num_frames = int((total_duration - (frame_duration * overlap / 100)) / (frame_duration * (1 - overlap / 100)))
26
-
27
- frames = []
28
-
29
- for i in range(num_frames):
30
- start_idx = int(step_size * i)
31
- frames.append(signal_data[start_idx:start_idx + frame_size])
32
-
33
- return np.array(frames), num_frames
34
-
35
-
36
- def hanning(signal_data: np.ndarray, frame_size: int, num_frames: int) -> Tuple[np.ndarray, float]:
37
- han = signal.get_window('hann', frame_size)
38
- acf = 1 / (sum(han) / frame_size)
39
-
40
- for i in range(num_frames):
41
- signal_data[i] *= han
42
-
43
- return signal_data, acf
44
-
45
-
46
- def fft_ave(signal_data: np.ndarray, samplerate: float, frame_size: int, num_frames: int, acf: float):
47
- fft_array = []
48
- for i in range(num_frames):
49
- fft_result = fftpack.fft(signal_data[i]) / frame_size
50
- fft_array.append(acf * np.abs(fft_result))
51
-
52
- fft_axis = np.linspace(0, samplerate / 2, frame_size // 2)
53
- fft_array = np.array(fft_array)[:, :frame_size // 2]
54
- fft_mean = np.mean(fft_array, axis=0)
55
-
56
- return fft_array, fft_mean, fft_axis
57
-
58
-
59
- def linear_to_db(x: float, y: float) -> float:
60
- if y == 0:
61
- raise ValueError("y cannot be zero in logarithmic conversion")
62
- return 20 * np.log10(x / y)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
lab_tools/wavelet.py CHANGED
@@ -4,6 +4,7 @@ import math
4
  import tempfile
5
 
6
  from lab_tools import labutils
 
7
 
8
 
9
  # モルレーウェーブレット関数
@@ -42,13 +43,24 @@ def plot_cwt(cwt_result, time_data, fmax):
42
 
43
 
44
  # グラフ描画とCWTの処理を行う関数
45
- def wavelet_ui(uploaded_file, Fs, fmax, column_name, start_time, end_time):
 
 
 
46
  filepath = uploaded_file.name
47
  signal = labutils.load_signal(filepath, column_name)
48
-
49
  if len(signal) == 0:
50
  return None, None
51
 
 
 
 
 
 
 
 
 
 
52
  # 時間データを計算
53
  t_data = np.arange(0, len(signal) / Fs, 1 / Fs)
54
 
 
4
  import tempfile
5
 
6
  from lab_tools import labutils
7
+ from lab_tools import filter
8
 
9
 
10
  # モルレーウェーブレット関数
 
43
 
44
 
45
  # グラフ描画とCWTの処理を行う関数
46
+ def wavelet_ui(
47
+ uploaded_file,
48
+ Fs, fmax, column_name, start_time, end_time,
49
+ filter_setting, fp_hp, fs_hp, gpass, gstop):
50
  filepath = uploaded_file.name
51
  signal = labutils.load_signal(filepath, column_name)
 
52
  if len(signal) == 0:
53
  return None, None
54
 
55
+ # Filter
56
+ timestamps = labutils.load_signal(filepath, "Timestamp")
57
+ dt = (timestamps[1] - timestamps[0])
58
+ samplerate = 1.0 / dt
59
+ if filter_setting == "High PASS":
60
+ signal = filter.highpass(signal, samplerate, fp_hp, fs_hp, gpass, gstop)
61
+ elif filter_setting == "Low PASS":
62
+ signal = filter.lowpass(signal, samplerate, fp_hp, fs_hp, gpass, gstop)
63
+
64
  # 時間データを計算
65
  t_data = np.arange(0, len(signal) / Fs, 1 / Fs)
66
 
run.py DELETED
@@ -1,71 +0,0 @@
1
- import numpy as np
2
- import matplotlib.pyplot as plt
3
- from pydub import AudioSegment
4
- from scipy.fftpack import fft
5
- import yt_dlp
6
- import os
7
-
8
- url = "https://youtu.be/Ci_zad39Uhw?si=AhB9ArgrWUvbPiv5"
9
-
10
- ydl_opts = {
11
- 'postprocessors': [
12
- {
13
- 'key': 'FFmpegExtractAudio',
14
- 'preferredcodec': 'mp3',
15
- 'preferredquality': '128',
16
- }
17
- ],
18
- 'outtmpl': '%(title)s.%(ext)s' # ファイル名のテンプレート
19
- }
20
-
21
- with yt_dlp.YoutubeDL(ydl_opts) as ydl:
22
- info_dict = ydl.extract_info(url, download=True)
23
- file_path = ydl.prepare_filename(info_dict)
24
- filename, ext = os.path.splitext(file_path)
25
- filename += ".mp3"
26
- print(f"Downloaded file path: {filename}")
27
-
28
- # MP3ファイルの読み込みとWAV形式への変換
29
- audio = AudioSegment.from_mp3(filename)
30
- os.remove(filename)
31
- data = np.array(audio.get_array_of_samples())
32
- sample_rate = audio.frame_rate
33
-
34
- # モノラル変換(ステレオの場合)
35
- if audio.channels > 1:
36
- data = data.reshape((-1, audio.channels)).mean(axis=1)
37
-
38
- # フーリエ変換の実行
39
- N = len(data)
40
- T = 1.0 / sample_rate
41
- yf = fft(data)
42
- xf = np.fft.fftfreq(N, T)[:N//2]
43
-
44
- # パワースペクトルの計算
45
- power_spectrum = 2.0/N * np.abs(yf[:N//2])
46
-
47
- # プロット用に周波数とパワーを制限
48
- xf_log = xf[1:] # 0Hzを除去 (ログスケールでは0が扱えないため)
49
- power_spectrum_log = power_spectrum[1:]
50
-
51
- # 縦軸の範囲を指定(例: 0から100まで)
52
- y_min = 0
53
- y_max = 1000
54
-
55
- # グラフの描画
56
- plt.figure(figsize=(10, 6))
57
- plt.plot(xf_log, power_spectrum_log)
58
- plt.xscale('log')
59
- plt.yscale('log')
60
-
61
- plt.title('Power Spectrum (Log Scale)')
62
- plt.xlabel('Frequency (Hz)')
63
- plt.ylabel('Power')
64
- plt.grid(True, which="both", ls="--")
65
- plt.xlim([1, sample_rate // 2]) # 1Hz から Nyquist周波数 (sample_rate/2) まで
66
-
67
- # 縦軸の範囲を指定
68
- # plt.ylim([y_min, y_max])
69
-
70
-
71
- plt.show()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
run.sh → run_dev_env.sh RENAMED
File without changes
test.py DELETED
@@ -1,93 +0,0 @@
1
- import pandas as pd
2
- import sys
3
- import matplotlib.pyplot as plt
4
- from lab_tools import highpass
5
-
6
-
7
- def load_signal(file_path, column_name):
8
- try:
9
- with open(file_path, 'r') as file:
10
- # データ部分が始まる行を見つける
11
- for i, line in enumerate(file):
12
- if 'Timestamp' in line:
13
- header_line = i
14
- break
15
-
16
- # 見つけたヘッダー行からデータを読み込む
17
- df = pd.read_csv(file_path, skiprows=header_line)
18
- signal = df[column_name].values
19
- return signal
20
- except FileNotFoundError as e:
21
- print(f"Error: {e}", file=sys.stderr)
22
- return []
23
- except KeyError as e:
24
- print(f"Column '{column_name}' not found in the file. ({e})", file=sys.stderr)
25
- return []
26
-
27
-
28
- samplerate = 1000
29
- time_data = load_signal("./test2_143809.csv", "Timestamp")
30
- signal_data = load_signal("./test2_143809.csv", "Fp1")
31
- fp = 22 # 通過域端周波数[Hz]※ベクトル
32
- fs = 10 # 阻止域端周波数[Hz]※ベクトル
33
- gpass = 5 # 通過域端最大損失[dB]
34
- gstop = 40 # 阻止域端最小損失[dB]
35
- Fs = 4096 # フレームサイズ
36
- overlap = 90
37
-
38
- data_filt = highpass.highpass_filter(signal_data, samplerate, fp, fs, gpass, gstop)
39
-
40
- t_array_org, N_ave_org = highpass.overlap_frames(signal_data, samplerate, Fs, overlap)
41
- t_array_filt, N_ave_filt = highpass.overlap_frames(signal_data, samplerate, Fs, overlap)
42
-
43
- t_array_org, acf_org = highpass.hanning(t_array_org, Fs, N_ave_org)
44
- t_array_filt, acf_filt = highpass.hanning(t_array_filt, Fs, N_ave_filt)
45
-
46
- fft_array_org, fft_mean_org, fft_axis_org = highpass.fft_ave(t_array_org, samplerate, Fs, N_ave_org, acf_org)
47
- fft_array_filt, fft_mean_filt, fft_axis_filt = highpass.fft_ave(t_array_filt, samplerate, Fs, N_ave_filt, acf_filt)
48
-
49
- fft_mean_org = highpass.linear_to_db(fft_mean_org, 2e-5)
50
- fft_mean_filt = highpass.linear_to_db(fft_mean_filt, 2e-5)
51
-
52
- # フォントの種類とサイズを設定する。
53
- # plt.rcParams['font.size'] = 14
54
- # plt.rcParams['font.family'] = 'Times New Roman'
55
-
56
- # 目盛を内側にする。
57
- plt.rcParams['xtick.direction'] = 'in'
58
- plt.rcParams['ytick.direction'] = 'in'
59
-
60
- # グラフの上下左右に目盛線を付ける。
61
- fig = plt.figure(figsize=(20, 10))
62
- ax1 = fig.add_subplot(211)
63
- ax1.yaxis.set_ticks_position('both')
64
- ax1.xaxis.set_ticks_position('both')
65
- ax2 = fig.add_subplot(212)
66
- ax2.yaxis.set_ticks_position('both')
67
- ax2.xaxis.set_ticks_position('both')
68
-
69
- # 軸のラベルを設定する。
70
- ax1.set_xlabel('Time [s]')
71
- ax1.set_ylabel('V[μV]')
72
- ax2.set_xlabel('Frequency [Hz]')
73
- ax2.set_ylabel('Amp[dB]')
74
-
75
- # データプロットの準備とともに、ラベルと線の太さ、凡例の設置を行う。
76
- ax1.plot(time_data, signal_data, label='original', lw=1)
77
- ax1.plot(time_data, data_filt, label='filtered', lw=1)
78
- ax2.plot(fft_axis_org, fft_mean_org, label='original', lw=1)
79
- ax2.plot(fft_axis_filt, fft_mean_filt, label='filtered', lw=1)
80
- plt.legend()
81
-
82
- # 軸のリミットを設定する。
83
- # ax1.set_xlim(0,1200)
84
- # ax1.set_xticks(np.arange(0,1201,100))
85
- # ax2.set_xlim(0, max(fft_axis_org)/2)
86
- # ax2.set_xticks(np.arange(0,501,10))
87
- # ax2.set_ylim(-50, 150)
88
-
89
- # レイアウト設定
90
- fig.tight_layout()
91
-
92
- # グラフを表示する。
93
- plt.savefig("./out.png")