Nicha1234 commited on
Commit
b8fb806
·
verified ·
1 Parent(s): 80b96b4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +127 -9
app.py CHANGED
@@ -6,7 +6,7 @@ import os
6
  import io
7
  from PIL import Image
8
 
9
- # สำคัญมาก! บังคับให้ Matplotlib ทำงานแบบ Background เพื่อแก้ปัญหาภาพและตัวอักษรสั่น
10
  import matplotlib
11
  matplotlib.use('Agg')
12
  import matplotlib.pyplot as plt
@@ -36,6 +36,18 @@ st.markdown("""
36
  color: #1565C0;
37
  margin-top: 20px;
38
  }
 
 
 
 
 
 
 
 
 
 
 
 
39
  </style>
40
  """, unsafe_allow_html=True)
41
 
@@ -70,10 +82,10 @@ def format_kspace_display(k_data):
70
 
71
  kspace_bg_image = format_kspace_display(kspace_raw)
72
 
73
- # --- ฟังก์ชันแปลงรูปภาพ (คืนค่าเป็น PIL Image แ้ปัญหภาพสั่น) ---
74
  def get_image_from_plot(fig):
75
  buf = io.BytesIO()
76
- plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0, dpi=100)
77
  plt.close(fig)
78
  buf.seek(0)
79
  return Image.open(buf)
@@ -127,11 +139,74 @@ def draw_mri(mri_result):
127
  ax.axis('off')
128
  return get_image_from_plot(fig)
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  # ==========================================================
131
  # --- 3. ส่วนแสดงผลเว็บ (ใช้ Container บีบขอบซ้าย-ขวา) ---
132
  # ==========================================================
133
 
134
- # แบ่งเป็น 3 คอลัมน์ โดยให้คอลัม��์กลางใหญ่สุด (สัดส่วน 1 : 6 : 1) เพื่อเว้นขอบเนื้อหาหลัก
135
  _, col_main, _ = st.columns([1, 6, 1])
136
 
137
  with col_main:
@@ -149,7 +224,6 @@ with col_main:
149
  ข้อมูลใน k-space มักจะถูกนำมาแสดงผลในรูปแบบตารางสี่เหลี่ยม (Grid) โดยมีแกนหลักคือ **kx (แนวนอน - Frequency)** และ **ky (แนวตั้ง - Phase)** จุดสำคัญคือ **แกน kx และ ky เหล่านี้ ไม่ได้บอกตำแหน่งพิกัดในภาพอวัยวะ** แต่มันคือแกนที่บอกถึงลักษณะของ **"ความถี่เชิงพื้นที่"** ที่เป็นคลื่น (Sinusoidal wave) ด้วยเหตุนี้ **จุดแต่ละจุดบน K-space จึงไม่ได้จับคู่แบบ 1 ต่อ 1 กับพิกเซลบนภาพ MRI** (เช่น จุดมุมซ้ายบนของ K-space ไม่ได้สร้างภาพมุมซ้ายบนของอวัยวะ)
150
  """)
151
 
152
- # จัดให้รูปเดี่ยวองค์ประกอบ K-Space อยู่กึ่งกลางหน้าจอพอดี
153
  _, col_img_center, _ = st.columns([1.5, 3, 1.5])
154
  with col_img_center:
155
  if os.path.exists("Screenshot 2026-05-07 205051.png"):
@@ -159,6 +233,54 @@ with col_main:
159
  else:
160
  st.info("กรุณาอัปโหลดรูปภาพ Screenshot 2026-05-07 205051 (เป็น .png หรือ .jpg ก็ได้)")
161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  st.markdown("## 📍 1 จุดบน k-space")
163
 
164
  st.markdown("""
@@ -175,10 +297,8 @@ with col_main:
175
  - **จุดมืดหรือจาง:** ภาพ MRI แทบจะไม่มีลวดลายชนิดนี้ประกอบอยู่เลย
176
  """)
177
 
178
- # --- ส่วน Interactive 1 ---
179
  st.markdown("### 🎛️ ลองปรับตำแหน่งของจุด K-Space เพื่อดูคลื่นความถี่")
180
 
181
- # บีบให้แถบสไลเดอร์และคู่รูปภาพขยับมาอยู่ตรงกลางพร้อมกัน
182
  _, col_slide1, col_slide2, _ = st.columns([0.5, 2.5, 2.5, 0.5])
183
  with col_slide1:
184
  kx_val = st.slider("พิกัด kx (แนวนอน)", -112, 111, 15)
@@ -224,7 +344,6 @@ with col_main:
224
  อนุญาตให้เฉพาะข้อมูล **ขอบนอก (ความถี่สูง)** ผ่านไปได้ ข้อมูลตรงกลางทิ้งไป ผลลัพธ์ที่ได้คือภาพจะสูญเสียคอนทราสต์จนเกือบมืดสนิท แต่จะปรากฏ **"เส้นขอบร่าง" (Outline)** ขึ้นมาอย่างคมชัด
225
  """)
226
 
227
- # --- ส่วน Interactive 2 ---
228
  st.markdown("### 🎛️ ลองปรับตัวกรอง (Interactive Filter)")
229
 
230
  _, col_radio_center, _ = st.columns([2, 3, 2])
@@ -240,7 +359,6 @@ with col_main:
240
 
241
  filtered_k, mri_result = apply_filter(kspace_raw, mode, radius)
242
 
243
- # จัดรูปภาพคู่ของพาร์ทฟิลเตอร์ให้อยู่กึ่งกลางจออย่างสมดุล
244
  _, col_fimg1, col_fimg2, _ = st.columns([0.5, 2.5, 2.5, 0.5])
245
  with col_fimg1:
246
  st.image(draw_filtered_kspace(filtered_k), caption="ภาพ K-Space ที่ถูกตัวกรอง", use_container_width=True)
 
6
  import io
7
  from PIL import Image
8
 
9
+ # บังคับให้ Matplotlib ทำงานแบบ Background เพื่อแก้ปัญหาภาพสั่น
10
  import matplotlib
11
  matplotlib.use('Agg')
12
  import matplotlib.pyplot as plt
 
36
  color: #1565C0;
37
  margin-top: 20px;
38
  }
39
+ /* ปรับแต่งปุ่มให้ดูสวยงาม */
40
+ div.stButton > button:first-child {
41
+ background-color: #f0f2f6;
42
+ color: #0D47A1;
43
+ border-radius: 8px;
44
+ border: 1px solid #1E88E5;
45
+ font-weight: bold;
46
+ }
47
+ div.stButton > button:first-child:hover {
48
+ background-color: #1E88E5;
49
+ color: white;
50
+ }
51
  </style>
52
  """, unsafe_allow_html=True)
53
 
 
82
 
83
  kspace_bg_image = format_kspace_display(kspace_raw)
84
 
85
+ # --- ฟังก์ชันแปลงรูปภาพ (ลดกาสั่น) ---
86
  def get_image_from_plot(fig):
87
  buf = io.BytesIO()
88
+ plt.savefig(buf, format='png', bbox_inches='tight', pad_inches=0.1, dpi=100)
89
  plt.close(fig)
90
  buf.seek(0)
91
  return Image.open(buf)
 
139
  ax.axis('off')
140
  return get_image_from_plot(fig)
141
 
142
+ # --- ฟังก์ชันวาดระบบเติม K-Space ทีละบรรทัด (Interactive 3) ---
143
+ def draw_gy_sequence(current_step, total_steps):
144
+ # กราฟแสดง Phase Encoding (Gy) ด้านซ้าย
145
+ fig, ax = plt.subplots(figsize=(3, 4))
146
+
147
+ # วาดแกน
148
+ ax.axhline(0, color='black', lw=1.5)
149
+ ax.axvline(0, color='black', lw=1.5)
150
+
151
+ y_vals = np.linspace(1, -1, total_steps)
152
+
153
+ for i, y in enumerate(y_vals):
154
+ # ไฮไลต์บรรทัดที่กำลังทำงานให้เป็นสีแดงและหนาขึ้น
155
+ if i == current_step:
156
+ ax.plot([0.1, 0.9], [y, y], color='red', lw=4)
157
+ elif i < current_step:
158
+ ax.plot([0.1, 0.9], [y, y], color='gray', lw=2, alpha=0.5)
159
+ else:
160
+ ax.plot([0.1, 0.9], [y, y], color='blue', lw=2)
161
+
162
+ ax.set_xlim(-0.1, 1.1)
163
+ ax.set_ylim(-1.2, 1.2)
164
+ ax.set_title("Gy (Phase Encoding)", fontweight='bold')
165
+ ax.axis('off')
166
+ return get_image_from_plot(fig)
167
+
168
+ def draw_kspace_filling(current_step, total_steps, mode_type, real_bg):
169
+ # กราฟแสดง K-Space ที่ค่อยๆ ถูกเติม
170
+ fig, ax = plt.subplots(figsize=(4, 4))
171
+
172
+ # สร้างภาพเปล่าสีดำ
173
+ display_img = np.zeros((224, 224))
174
+
175
+ # คำนวณขอบเขตบรรทัดที่จะเติม
176
+ lines_per_step = 224 // total_steps
177
+ current_line_idx = current_step * lines_per_step
178
+
179
+ if mode_type == "ข้อมูลจำลอง (Simulated)":
180
+ # สร้างภาพเส้นจำลองสีเหลืองแนวนอน
181
+ Y, X = np.ogrid[:224, :224]
182
+ # จำลองสัญญาณให้ตรงกลางสว่าง ขอบมืด
183
+ intensity = np.exp(-((Y - 112)**2) / (2 * 50**2))
184
+ for i in range(current_line_idx + lines_per_step):
185
+ display_img[i, :] = intensity[i, 0] * 255
186
+
187
+ ax.imshow(display_img, cmap='inferno', extent=[-112, 112, -112, 112], vmin=0, vmax=255)
188
+ else:
189
+ # ใช้ข้อมูล kspace.mat จริง
190
+ if current_step >= total_steps - 1:
191
+ display_img = real_bg
192
+ else:
193
+ display_img[:current_line_idx + lines_per_step, :] = real_bg[:current_line_idx + lines_per_step, :]
194
+
195
+ ax.imshow(display_img, cmap='gray', extent=[-112, 112, -112, 112], vmin=0, vmax=1)
196
+
197
+ ax.set_xlim(-112, 112)
198
+ ax.set_ylim(112, -112) # Invert Y axis ให้เติมจากบนลงล่างตามกราฟ Gy
199
+ ax.axhline(0, color='white', linewidth=0.5, linestyle='--')
200
+ ax.axvline(0, color='white', linewidth=0.5, linestyle='--')
201
+ ax.set_title("K-Space Filling", fontweight='bold')
202
+ ax.axis('off')
203
+ return get_image_from_plot(fig)
204
+
205
+
206
  # ==========================================================
207
  # --- 3. ส่วนแสดงผลเว็บ (ใช้ Container บีบขอบซ้าย-ขวา) ---
208
  # ==========================================================
209
 
 
210
  _, col_main, _ = st.columns([1, 6, 1])
211
 
212
  with col_main:
 
224
  ข้อมูลใน k-space มักจะถูกนำมาแสดงผลในรูปแบบตารางสี่เหลี่ยม (Grid) โดยมีแกนหลักคือ **kx (แนวนอน - Frequency)** และ **ky (แนวตั้ง - Phase)** จุดสำคัญคือ **แกน kx และ ky เหล่านี้ ไม่ได้บอกตำแหน่งพิกัดในภาพอวัยวะ** แต่มันคือแกนที่บอกถึงลักษณะของ **"ความถี่เชิงพื้นที่"** ที่เป็นคลื่น (Sinusoidal wave) ด้วยเหตุนี้ **จุดแต่ละจุดบน K-space จึงไม่ได้จับคู่แบบ 1 ต่อ 1 กับพิกเซลบนภาพ MRI** (เช่น จุดมุมซ้ายบนของ K-space ไม่ได้สร้างภาพมุมซ้ายบนของอวัยวะ)
225
  """)
226
 
 
227
  _, col_img_center, _ = st.columns([1.5, 3, 1.5])
228
  with col_img_center:
229
  if os.path.exists("Screenshot 2026-05-07 205051.png"):
 
233
  else:
234
  st.info("กรุณาอัปโหลดรูปภาพ Screenshot 2026-05-07 205051 (เป็น .png หรือ .jpg ก็ได้)")
235
 
236
+ # ---------------------------------------------------------
237
+ # NEW INTERACTIVE: การเติม K-Space ทีละบรรทัด (Line-by-Line Filling)
238
+ # ---------------------------------------------------------
239
+ st.markdown("### 🎛️ จำลองการเติมข้อมูล K-Space ตามรอบของ Phase Encoding (Gy)")
240
+ st.markdown("สังเกตการทำงานของเกรเดียนท์ Gy ด้านซ้าย ว่าในแต่ละรอบการกระตุ้น (TR) จะดึงข้อมูลมาเติมใน K-Space ทีละบรรทัดอย่างไร")
241
+
242
+ # State Management สำหรับการควบคุมแอนิเมชัน
243
+ total_anim_steps = 15
244
+ if 'fill_step' not in st.session_state:
245
+ st.session_state.fill_step = 0
246
+
247
+ def step_forward():
248
+ if st.session_state.fill_step < total_anim_steps - 1:
249
+ st.session_state.fill_step += 1
250
+
251
+ def step_backward():
252
+ if st.session_state.fill_step > 0:
253
+ st.session_state.fill_step -= 1
254
+
255
+ def run_all():
256
+ st.session_state.fill_step = total_anim_steps - 1
257
+
258
+ def reset_anim():
259
+ st.session_state.fill_step = 0
260
+
261
+ # แถบควบคุมบน
262
+ _, col_ctrl1, col_ctrl2, _ = st.columns([1, 2, 2, 1])
263
+ with col_ctrl1:
264
+ data_mode = st.radio("เลือกรูปแบบข้อมูลแสดงผล:", ["ข้อมูลจำลอง (Simulated)", "ข้อมูลจริง (kspace.mat)"])
265
+ with col_ctrl2:
266
+ st.write("ควบคุมการเติมบรรทัด:")
267
+ c1, c2, c3, c4 = st.columns(4)
268
+ c1.button("⏮ Reset", on_click=reset_anim)
269
+ c2.button("◀ ก่อนหน้า", on_click=step_backward)
270
+ c3.button("ถัดไป ▶", on_click=step_forward)
271
+ c4.button("⏭ รันจนจบ", on_click=run_all)
272
+
273
+ st.progress((st.session_state.fill_step + 1) / total_anim_steps)
274
+
275
+ # แสดงผลรูปภาพ
276
+ _, col_anim1, col_anim2, _ = st.columns([1.5, 2, 3, 1.5])
277
+ with col_anim1:
278
+ st.image(draw_gy_sequence(st.session_state.fill_step, total_anim_steps), use_container_width=True)
279
+ with col_anim2:
280
+ st.image(draw_kspace_filling(st.session_state.fill_step, total_anim_steps, data_mode, kspace_bg_image), use_container_width=True)
281
+
282
+
283
+ st.markdown("---")
284
  st.markdown("## 📍 1 จุดบน k-space")
285
 
286
  st.markdown("""
 
297
  - **จุดมืดหรือจาง:** ภาพ MRI แทบจะไม่มีลวดลายชนิดนี้ประกอบอยู่เลย
298
  """)
299
 
 
300
  st.markdown("### 🎛️ ลองปรับตำแหน่งของจุด K-Space เพื่อดูคลื่นความถี่")
301
 
 
302
  _, col_slide1, col_slide2, _ = st.columns([0.5, 2.5, 2.5, 0.5])
303
  with col_slide1:
304
  kx_val = st.slider("พิกัด kx (แนวนอน)", -112, 111, 15)
 
344
  อนุญาตให้เฉพาะข้อมูล **ขอบนอก (ความถี่สูง)** ผ่านไปได้ ข้อมูลตรงกลางทิ้งไป ผลลัพธ์ที่ได้คือภาพจะสูญเสียคอนทราสต์จนเกือบมืดสนิท แต่จะปรากฏ **"เส้นขอบร่าง" (Outline)** ขึ้นมาอย่างคมชัด
345
  """)
346
 
 
347
  st.markdown("### 🎛️ ลองปรับตัวกรอง (Interactive Filter)")
348
 
349
  _, col_radio_center, _ = st.columns([2, 3, 2])
 
359
 
360
  filtered_k, mri_result = apply_filter(kspace_raw, mode, radius)
361
 
 
362
  _, col_fimg1, col_fimg2, _ = st.columns([0.5, 2.5, 2.5, 0.5])
363
  with col_fimg1:
364
  st.image(draw_filtered_kspace(filtered_k), caption="ภาพ K-Space ที่ถูกตัวกรอง", use_container_width=True)