Nicha1234 commited on
Commit
5b9d5f9
·
verified ·
1 Parent(s): 5a1e208

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +144 -184
app.py CHANGED
@@ -2,101 +2,64 @@ import streamlit as st
2
  import numpy as np
3
  import scipy.io
4
  import matplotlib.pyplot as plt
 
 
5
  import zipfile
6
  import os
7
 
8
- # --- ตั้งค่า็บ Streamlit ---
9
  st.set_page_config(layout="wide", page_title="K-Space to MRI")
10
 
11
- # --- ฟังก์ชันช่วยเหลือ (Helper Functions) ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  @st.cache_data
13
  def load_kspace_data():
14
  try:
15
- # ตรวจสอบว่ามีไฟล์ mat หรือ zip ใน directory หรือไม่
16
  if os.path.exists('kspace.mat'):
17
  mat_data = scipy.io.loadmat('kspace.mat')
18
- else:
19
  with zipfile.ZipFile("kspace.zip", 'r') as zip_ref:
20
  zip_ref.extractall("temp_kspace")
21
  mat_data = scipy.io.loadmat("temp_kspace/kspace.mat")
22
- kspace = mat_data['kspace']
23
- return kspace
24
- except Exception as e:
25
- st.error("ไม่พบไฟล์ข้อมูล kspace.mat หรือ kspace.zip กรุณาอัปโหลดไฟล์เข้าระบบ")
26
- # สร้างข้อมูลจำลองกรณีไม่เจอไฟล์ เพื่อให้แอปทำงานต่อได้
27
- x = np.linspace(-5, 5, 224)
28
- y = np.linspace(-5, 5, 224)
29
- X, Y = np.meshgrid(x, y)
30
- dummy_image = np.sin(X**2 + Y**2) * np.exp(-(X**2 + Y**2)/10)
31
- return np.fft.fftshift(np.fft.fft2(dummy_image))
32
-
33
- kspace_data = load_kspace_data()
34
-
35
- def kspace_to_image(kspace_freq):
36
- # แปลง K-space กลับเป็นภาพ MRI ขนาด 224x224
37
- img = np.fft.ifft2(np.fft.ifftshift(kspace_freq))
38
- img_mag = np.abs(img)
39
- # Normalize ให้ค่าอยู่ระหว่าง 0-1
40
- img_norm = (img_mag - img_mag.min()) / (img_mag.max() - img_mag.min())
41
- return img_norm
42
-
43
- def generate_kspace_axis_image():
44
- # จำลองรูปแกน K-Space ตามที่แนบมา
45
- fig, ax = plt.subplots(figsize=(5, 5))
46
- ax.axhline(0, color='black', linewidth=2)
47
- ax.axvline(0, color='black', linewidth=2)
48
-
49
- ax.text(0.95, 0.05, 'kx\n(Frequency)', transform=ax.transAxes, ha='right', va='bottom', fontsize=12, fontweight='bold', color='red')
50
- ax.text(0.05, 0.95, 'ky\n(Phase)', transform=ax.transAxes, ha='left', va='top', fontsize=12, fontweight='bold', color='red')
51
-
52
- ax.grid(True, linestyle='--', alpha=0.5)
53
- ax.set_xlim(-1, 1)
54
- ax.set_ylim(-1, 1)
55
- ax.set_xticks([])
56
- ax.set_yticks([])
57
- ax.set_title("K-Space", fontweight='bold')
58
-
59
- # วาดคลื่นจำลองในกราฟให้คล้ายกับรูปที่ร่างไว้
60
- x = np.linspace(-1, 1, 100)
61
- y = 0.2 * np.sin(10 * x)
62
- ax.plot(x, y, color='red', alpha=0.5)
63
- ax.plot(y, x, color='blue', alpha=0.5)
64
-
65
- return fig
66
 
67
- def generate_2d_wave(kx, ky, size=224):
68
- x = np.arange(size)
69
- y = np.arange(size)
70
- X, Y = np.meshgrid(x, y)
71
-
72
- X = X - size // 2
73
- Y = Y - size // 2
74
-
75
- # สร้างคลื่น 2D Sinusoidal
76
- wave = np.cos(2 * np.pi * (kx * X + ky * Y) / size)
77
- return wave
78
 
79
- def draw_kspace_point(kx, ky, size=224):
80
- fig, ax = plt.subplots(figsize=(5, 5))
81
- ax.imshow(np.zeros((size, size)), cmap='gray', extent=[-size//2, size//2, -size//2, size//2])
82
-
83
- ax.plot(kx, ky, 'ro', markersize=8)
84
- # วาดเส้นแสดงทิศทาง
85
- ax.annotate('', xy=(kx, ky), xytext=(0, 0),
86
- arrowprops=dict(arrowstyle='->', color='yellow', lw=2))
87
-
88
- ax.axhline(0, color='white', linewidth=0.5, linestyle='--')
89
- ax.axvline(0, color='white', linewidth=0.5, linestyle='--')
90
-
91
- ax.set_xlim(-size//2, size//2)
92
- ax.set_ylim(-size//2, size//2)
93
- ax.set_title(f"พิกัด K-Space (kx={kx}, ky={ky})")
94
- ax.axis('off')
95
- return fig
96
 
97
- # --- ส่วนเนื้อาและ UI ของหน้าเว็บ ---
98
 
99
- st.title("K-Space to MRI image")
100
 
101
  st.markdown("""
102
  **K-space คือ**
@@ -105,11 +68,29 @@ st.markdown("""
105
 
106
  st.header("องค์ประกอบของ K-Space")
107
 
108
- col1, col2 = st.columns([1, 1.5])
109
- with col1:
110
- fig_axis = generate_kspace_axis_image()
111
- st.pyplot(fig_axis)
112
- with col2:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  st.markdown("""
114
  ข้อมูลใน k-space มักจะถูกนำมาแสดงผลในรูปแบบตารางสี่เหลี่ยม (Grid) โดยมีแกนหลักคือ kx (แนวนอน - Frequency) และ ky (แนวตั้ง - Phase) แต่จุดสำคัญคือ แกน kx และ ky เหล่านี้ ไม่ได้บอกตำแหน่งพิกัด ในภาพ แต่มันคือแกนที่บอกถึง ลักษณะของ "ความถี่เชิงพื้นที่ (Spatial Frequencies)" ซึ่งเป็นคลื่นความถี่ Sinusoidal wave ด้วยเหตุนี้ จุดแต่ละจุดบนพิกัด (kx, ky) ใน k-space จึง ไม่ได้จับคู่แบบ 1 ต่อ 1 กับพิกเซล (x, y) บนภาพ MRI (ไม่ได้แปลว่าจุดมุมซ้ายบนใน k-space จะสร้างภาพมุมซ้ายบนของ ภาพอวัยวะ)
115
  """)
@@ -117,37 +98,45 @@ with col2:
117
  st.divider()
118
 
119
  st.header("1 จุดบน k-space")
 
120
 
121
- # --- Interactive Part 1: การเือจุดบน K-Space ---
122
- st.markdown("**(Interactive: ลองเลื่อน Slider เพื่อเลือกตำแหน่งจุด แล้วดูทิศทางและลักษณะของแผ่นลวดลายคลื่น)**")
123
-
124
- if 'kx_val' not in st.session_state:
125
- st.session_state.kx_val = 15
126
- if 'ky_val' not in st.session_state:
127
- st.session_state.ky_val = 20
128
-
129
- def reset_point():
130
- st.session_state.kx_val = 0
131
- st.session_state.ky_val = 0
132
-
133
- col_w1, col_w2, col_w3 = st.columns([1, 1, 1])
134
- with col_w1:
135
- st.slider("ตำแหน่งแกน kx (แนวนอน)", -112, 111, key='kx_val')
136
- st.slider("ตำแหน่งแกน ky (แนวตั้ง)", -112, 111, key='ky_val')
137
- st.button("Reset จุด", on_click=reset_point)
138
-
139
- with col_w2:
140
- fig_pt = draw_kspace_point(st.session_state.kx_val, st.session_state.ky_val)
141
- st.pyplot(fig_pt)
142
-
143
- with col_w3:
144
- wave_img = generate_2d_wave(st.session_state.kx_val, st.session_state.ky_val)
145
- fig_wave, ax_wave = plt.subplots(figsize=(5, 5))
146
- ax_wave.imshow(wave_img, cmap='gray')
147
- ax_wave.set_title("แผ่นลวดลายคลื่น (2D Sinusoidal Wave)")
148
- ax_wave.axis('off')
149
- st.pyplot(fig_wave)
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
  st.markdown("""
153
  **k-space 1 จุด = ข้อมูลของภาพทั้งภาพ และ ภาพ 1 พิกเซล = ผลรวมของ k-space ทุกจุด**
@@ -187,83 +176,54 @@ st.markdown("""
187
  - **ตัวอย่างในภาพ MRI:** ขอบของอวัยวะ (Edges), รอยต่อระหว่างกระดูกกับไขสันหลัง, หรือรายละเอียดเส้นเลือดเส้นเล็กๆ
188
  - **ตำแหน่งใน k-space:** ข้อมูลเหล่านี้จะกระจายตัวอยู่บริเวณ "ขอบนอก"
189
  - **หน้าที่หลัก:** สร้าง "ความคมชัด (Resolution) และรายละเอียดเล็กๆ" ทำให้ภาพไม่เบลอ
190
-
191
- ซึ่งจะให้ผู้เรียนได้ลองปรับหน้าตาของภาพ K-Space แล้วเปรียบเทียบความแตกต่างด้วยการปรับ High-pass filter และ Low-pass filter เพื่อดูลักษณะและความสำคัญของข้อมูลบริเวณกลางและขอบนอกของ K-space
192
- เมื่อเราจำแนกข้อมูลใน k-space ออกเป็นความถี่ต่ำ (ตรงกลาง) และความถี่สูง (ขอบนอก) ได้แล้ว เราสามารถเลือก "หยิบ" หรือ "ทิ้ง" ข้อมูลบางส่วนเพื่อดูผลลัพธ์ได้ เรียกว่าการใช้ตัวกรอง (Filter)
193
  """)
194
 
195
  st.divider()
196
 
197
- # --- Interactive Part 2: Slide bar แบบ High/Low Pass ---
198
- st.subheader("ตัวกง K-Space (Interactive Filters)")
199
 
200
- if 'filter_type' not in st.session_state:
201
- st.session_state.filter_type = 'Low-pass Filter'
202
- if 'filter_radius' not in st.session_state:
203
- st.session_state.filter_radius = 112
204
 
205
- def reset_filter():
206
- st.session_state.filter_type = 'Low-pass Filter'
207
- st.session_state.filter_radius = 112
208
-
209
- col_f1, col_f2 = st.columns([1, 1.5])
210
- with col_f1:
211
- filter_choice = st.radio("เลือกรูปแบบ Filter", ['Low-pass Filter', 'High-pass Filter'], key='filter_type')
212
 
213
- if filter_choice == 'Low-pass Filter':
214
- st.slider("ปรับระดับการให้ข้อมูลผ่าน (จากเต็มไปหาแคบลง)", 0, 112, value=112, key='filter_radius', help="ยิ่งลดค่า ข้อมูลขอบนอกยิ่งหาย เหลือแต่ตรงกลาง")
215
- else:
216
- st.slider("ปรับระดับการตัดข้อมูลตรงกลาง (จาก 0 ไปหาสูงสุด)", 0, 112, value=0, key='filter_radius', help="ยิ่งเพิ่มค่��� ข้อมูลตรงกลยิ่งหาย อแต่เส้ขอบ")
217
-
218
- st.button("Reset ค่า Filter", on_click=reset_filter)
219
-
220
- with col_f2:
221
- if filter_choice == 'Low-pass Filter':
222
- with st.expander("รายละเอียด Low-pass Filter", expanded=True):
223
- st.markdown("""
224
- **Low-pass Filter (ตัวกรองปล่อยความถี่ต่ำผ่าน):**
225
- - **ทำงานอย่างไร:** "อนุญาตให้เฉพาะข้อมูลตรงกลาง (ความถี่ต่ำ) ผ่านไปสร้างภาพได้ ส่วนข้อมูลขอบนอก (ความถี่สูง) ให้ทิ้งไป"
226
- - **ผลลัพธ์ที่ได้:** เราจะได้ภาพที่มี "คอนทราสต์" ดูออกว่าเป็นอวัยวะอะไร แต่ภาพจะ "เบลอ" (Blurry) เพราะข้อมูลเส้นขอบถูกทิ้งไปแล้ว
227
- """)
228
  else:
229
- with st.expander("ราะเียด High-pass Filter", expanded=True):
230
- st.markdown("""
231
- **High-pass Filter (ตัวกรองปล่อยความถี่สูงผ่าน):**
232
- - **ทำงานอย่างไ:** อนุญาตใหเฉพะข้อมูลขอบนอก (ความถี่สู) ผ่านไปได้ ส่วนข้อมูลตรงกลางทิ้ง
233
- - **ผลลัพธ์ที่ได้:** ภาพจะสูญเสียคอนทราสต์ไปจนเกือบมืดสนิท แต่จะปรากฏ "เส้นขอบร่าง" (Outline) ของอวัยวะขึ้นมาอย่างคมชัด
234
- """)
235
-
236
- # คำนวณผลลัพธ์ Filter
237
- size = 224
238
- center = size // 2
239
- Y, X = np.ogrid[:size, :size]
240
- dist_from_center = np.sqrt((X - center)**2 + (Y - center)**2)
241
-
242
- mask = np.ones((size, size))
243
- radius = st.session_state.filter_radius
244
-
245
- if filter_choice == 'Low-pass Filter':
246
- mask[dist_from_center > radius] = 0
247
- elif filter_choice == 'High-pass Filter':
248
- mask[dist_from_center < radius] = 0
249
-
250
- filtered_kspace = kspace_data * mask
251
-
252
- # แสดงผลภาพซ้ายและขวา
253
- col_img1, col_img2 = st.columns(2)
254
-
255
- with col_img1:
256
- fig_k, ax_k = plt.subplots(figsize=(5, 5))
257
- filt_k_mag = np.log(np.abs(filtered_kspace) + 1)
258
- ax_k.imshow(filt_k_mag, cmap='gray')
259
- ax_k.set_title(f"ภาพ K-Space ที่ถูรอง", fontweight='bold')
260
- ax_k.axis('off')
261
- st.pyplot(fig_k)
262
-
263
- with col_img2:
264
- mri_img = kspace_to_image(filtered_kspace)
265
- fig_m, ax_m = plt.subplots(figsize=(5, 5))
266
- ax_m.imshow(mri_img, cmap='gray')
267
- ax_m.set_title("ภาพ MRI ผลลัพธ์ (ขนาด 224x224)", fontweight='bold')
268
- ax_m.axis('off')
269
- st.pyplot(fig_m)
 
2
  import numpy as np
3
  import scipy.io
4
  import matplotlib.pyplot as plt
5
+ import plotly.express as px
6
+ import plotly.graph_objects as go
7
  import zipfile
8
  import os
9
 
10
+ # --- 1. ตั้งค่า CSS เพื่อปรับขนาดตัอักษรและจัดกลาง ---
11
  st.set_page_config(layout="wide", page_title="K-Space to MRI")
12
 
13
+ st.markdown("""
14
+ <style>
15
+ /* ปรับขนาดตัวอักษรหลัก */
16
+ html, body, [class*="st-"] {
17
+ font-size: 18px;
18
+ }
19
+ /* หัวข้อหลักให้อยู่กลาง */
20
+ .main-title {
21
+ text-align: center;
22
+ font-size: 45px !important;
23
+ font-weight: bold;
24
+ color: #1E88E5;
25
+ margin-bottom: 30px;
26
+ }
27
+ /* หัวข้อรอง */
28
+ h1, h2, h3 {
29
+ color: #0D47A1;
30
+ }
31
+ </style>
32
+ """, unsafe_allow_html=True)
33
+
34
+ # --- 2. ฟังก์ชันโหลดข้อมูลและการคำนวณ ---
35
  @st.cache_data
36
  def load_kspace_data():
37
  try:
 
38
  if os.path.exists('kspace.mat'):
39
  mat_data = scipy.io.loadmat('kspace.mat')
40
+ elif os.path.exists('kspace.zip'):
41
  with zipfile.ZipFile("kspace.zip", 'r') as zip_ref:
42
  zip_ref.extractall("temp_kspace")
43
  mat_data = scipy.io.loadmat("temp_kspace/kspace.mat")
44
+ else:
45
+ # สร้างข้อมูลจำลองถ้าไม่เจอไฟล์
46
+ return np.zeros((224, 224))
47
+
48
+ k = mat_data['kspace']
49
+ return k
50
+ except:
51
+ return np.zeros((224, 224))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
+ kspace_raw = load_kspace_data()
 
 
 
 
 
 
 
 
 
 
54
 
55
+ def get_mri_image(k_data):
56
+ # แก้ Artifact สมอง 4 มุม: ต้อง shift ก่อน IFFT และ shift ผลลัพธ์หลัง IFFT อีกรอบ
57
+ img = np.fft.fftshift(np.fft.ifft2(np.fft.ifftshift(k_data)))
58
+ return np.abs(img)
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ # --- 3. ส่วนหน้าเว็บ ---
61
 
62
+ st.markdown('<p class="main-title">K-space to MRI image</p>', unsafe_allow_html=True)
63
 
64
  st.markdown("""
65
  **K-space คือ**
 
68
 
69
  st.header("องค์ประกอบของ K-Space")
70
 
71
+ # ส่วนที่ทำภาพเลียนแบบ "รูปเขียว" (K-space axis)
72
+ col_ax1, col_ax2 = st.columns([1, 1])
73
+ with col_ax1:
74
+ fig_axis = go.Figure()
75
+ # วาด Grid และแกน
76
+ for i in range(-5, 6):
77
+ fig_axis.add_trace(go.Scatter(x=[-5, 5], y=[i, i], mode='lines', line=dict(color='lightgreen', width=1), showlegend=False))
78
+ fig_axis.add_trace(go.Scatter(x=[i, i], y=[-5, 5], mode='lines', line=dict(color='lightgreen', width=1), showlegend=False))
79
+
80
+ # ลูกศรแกน
81
+ fig_axis.add_annotation(x=5.5, y=0, text="kx (Frequency)", showarrow=True, arrowhead=2, ax=-40, ay=0, font=dict(color="red", size=14))
82
+ fig_axis.add_annotation(x=0, y=5.5, text="ky (Phase)", showarrow=True, arrowhead=2, ax=0, ay=40, font=dict(color="red", size=14))
83
+
84
+ fig_axis.update_layout(
85
+ title="K-space axis",
86
+ xaxis=dict(visible=False, range=[-6, 6]),
87
+ yaxis=dict(visible=False, range=[-6, 6]),
88
+ width=400, height=400,
89
+ plot_bgcolor='white'
90
+ )
91
+ st.plotly_chart(fig_axis)
92
+
93
+ with col_ax2:
94
  st.markdown("""
95
  ข้อมูลใน k-space มักจะถูกนำมาแสดงผลในรูปแบบตารางสี่เหลี่ยม (Grid) โดยมีแกนหลักคือ kx (แนวนอน - Frequency) และ ky (แนวตั้ง - Phase) แต่จุดสำคัญคือ แกน kx และ ky เหล่านี้ ไม่ได้บอกตำแหน่งพิกัด ในภาพ แต่มันคือแกนที่บอกถึง ลักษณะของ "ความถี่เชิงพื้นที่ (Spatial Frequencies)" ซึ่งเป็นคลื่นความถี่ Sinusoidal wave ด้วยเหตุนี้ จุดแต่ละจุดบนพิกัด (kx, ky) ใน k-space จึง ไม่ได้จับคู่แบบ 1 ต่อ 1 กับพิกเซล (x, y) บนภาพ MRI (ไม่ได้แปลว่าจุดมุมซ้ายบนใน k-space จะสร้างภาพมุมซ้ายบนของ ภาพอวัยวะ)
96
  """)
 
98
  st.divider()
99
 
100
  st.header("1 จุดบน k-space")
101
+ st.info("💡 วิธีเล่น: กดคลิกที่จุดใดก็ได้บนภาพ K-Space ด้านซ้าย เพื่อดูคลื่นความถี่ที่เกิดขึ้นในจุดนั้น")
102
 
103
+ # --- Interactive Part: กบนภาพ K-Space ---
104
+ col_int1, col_int2 = st.columns([1, 1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
+ with col_int1:
107
+ # แสดงภาพ K-Space แบบ Log scale เพื่อให้เห็นข้อมูลชัด��จน
108
+ k_display = np.log(np.abs(kspace_raw) + 1)
109
+
110
+ # ใช้ Plotly เพื่อให้คลิกได้
111
+ fig_k = px.imshow(k_display, color_continuous_scale='gray', labels=dict(x="kx", y="ky"))
112
+ fig_k.update_layout(coloraxis_showscale=False, margin=dict(l=20, r=20, t=30, b=20), width=450, height=450)
113
+
114
+ # จับเหตุการณ์การคลิก (ใน Streamlit จะใช้การเลือกจุดจาก Plotly)
115
+ selected_point = st.plotly_chart(fig_k, on_select="rerun")
116
+
117
+ # กำหนดจุดเริ่มต้นถ้ายังไม่กด
118
+ kx_idx, ky_idx = 127, 127 # กลางภาพ
119
+ if selected_point and "points" in selected_point and len(selected_point["points"]) > 0:
120
+ kx_idx = selected_point["points"][0]["x"]
121
+ ky_idx = selected_point["points"][0]["y"]
122
+
123
+ with col_int2:
124
+ # คำนวณคลื่น 2D จากจุดที่เลือก
125
+ size = 224
126
+ x = np.linspace(-size//2, size//2, size)
127
+ y = np.linspace(-size//2, size//2, size)
128
+ X, Y = np.meshgrid(x, y)
129
+
130
+ # ความถี่ตามตำแหน่ง kx, ky (relative to center)
131
+ freq_x = (kx_idx - size//2) / size
132
+ freq_y = (ky_idx - size//2) / size
133
+ wave = np.cos(2 * np.pi * (freq_x * X + freq_y * Y))
134
+
135
+ fig_wave = px.imshow(wave, color_continuous_scale='gray', title=f"คลื่นที่พิกัด kx:{kx_idx-112}, ky:{112-ky_idx}")
136
+ fig_wave.update_layout(coloraxis_showscale=False, width=400, height=400)
137
+ st.plotly_chart(fig_wave)
138
+ if st.button("Reset จุด"):
139
+ st.rerun()
140
 
141
  st.markdown("""
142
  **k-space 1 จุด = ข้อมูลของภาพทั้งภาพ และ ภาพ 1 พิกเซล = ผลรวมของ k-space ทุกจุด**
 
176
  - **ตัวอย่างในภาพ MRI:** ขอบของอวัยวะ (Edges), รอยต่อระหว่างกระดูกกับไขสันหลัง, หรือรายละเอียดเส้นเลือดเส้นเล็กๆ
177
  - **ตำแหน่งใน k-space:** ข้อมูลเหล่านี้จะกระจายตัวอยู่บริเวณ "ขอบนอก"
178
  - **หน้าที่หลัก:** สร้าง "ความคมชัด (Resolution) และรายละเอียดเล็กๆ" ทำให้ภาพไม่เบลอ
 
 
 
179
  """)
180
 
181
  st.divider()
182
 
183
+ # --- ส่วน Filter ---
184
+ st.subheader("Interactive Filters: ปับแต่ภาพ MRI จาก K-Space")
185
 
186
+ col_filter_ctrl, col_filter_res = st.columns([1, 2])
 
 
 
187
 
188
+ with col_filter_ctrl:
189
+ mode = st.radio("เลือกตัวกรอง", ["Low-pass Filter", "High-pass Filter"])
 
 
 
 
 
190
 
191
+ # ปรับปรุง Slide bar ตาม Logic ที่ต้องการ
192
+ if mode == "Low-pass Filter":
193
+ radius = st.slider("รัศมีข้อมูลความถี่ต่ำ (กลางภาพ)", 1, 112, 112)
194
+ st.write("ยิ่งลดค่ พจะยิ่งเลอแต่เห็โครงร่างหลัก")
 
 
 
 
 
 
 
 
 
 
 
195
  else:
196
+ radius = st.slider("รัศมีกรตัดข้อมูตรงกลางอก", 0, 112, 0)
197
+ st.write("ยิ่งเพิ่มค่า คอนทราสต์จะหายไปเหลือแต่เส้นขอบ")
198
+
199
+ # ร้าง Mask
200
+ Y_m, X_m = np.ogrid[:224, :224]
201
+ dist = np.sqrt((X_m - 112)**2 + (Y_m - 112)**2)
202
+ mask = np.ones((224, 224))
203
+
204
+ if mode == "Low-pass Filter":
205
+ mask[dist > radius] = 0
206
+ else:
207
+ mask[dist < radius] = 0
208
+
209
+ filtered_k = kspace_raw * mask
210
+ mri_result = get_mri_image(filtered_k)
211
+
212
+ with col_filter_res:
213
+ c1, c2 = st.columns(2)
214
+ with c1:
215
+ st.image(np.log(np.abs(filtered_k)+1), caption="K-Space Filtered", use_container_width=True)
216
+ with c2:
217
+ st.image(mri_result/mri_result.max(), caption="MRI Result (224x224)", use_container_width=True)
218
+
219
+ # Toggle ่อนเนือห
220
+ with st.expander("อ่านคำอธิบายเพิ่มเติมเกี่ยวกับ Filter"):
221
+ st.markdown("""
222
+ **Low-pass Filter (ตัวกรองปล่อยความถี่ต่ำผ่าน):**
223
+ - **ทำงานอย่างไร:** "อนุญาตให้เฉพาะข้อมูลตรงกลาง (ความถี่ต่ำ) ผ่านไปสร้างภาพได้ ส่วนข้อมูลขอบนอก (ความถี่สูง) ให้ทิ้งไป"
224
+ - **ผลลัพธ์ที่ได้:** เราจะได้ภาพที่มี "คอนทราสต์" ดูออกว่าเป็นอวัยวะอะไร แต่ภาพจะ "เบลอ" (Blurry) เพราะข้อมูลเส้นขอบถูกทิ้งไปแล้ว
225
+
226
+ **High-pass Filter (ตัวกรองปล่อยความถี่สูงผ่าน):**
227
+ - **ทำงานอย่างไร:** อนุญาตให้เฉพาะข้อมูลขอบนอก (ความถี่สูง) ผ่านไปได้ ส่วนข้อมูลตรงกลางทิ้ง
228
+ - **ผลลัพธ์ที่ได้:** ภาพจะสูญเสียคอนทราสต์ไปจนเกือบมืดสนิท แต่จะปรากฏ "เส้นขอบร่าง" (Outline) ของอวัยวะขึ้นมาอย่างคมชัด
229
+ """)