Update app.py
Browse files
app.py
CHANGED
|
@@ -45,13 +45,6 @@ st.markdown("""
|
|
| 45 |
background-color: #1E88E5;
|
| 46 |
color: white;
|
| 47 |
}
|
| 48 |
-
/* ปรับแต่งส่วนอ้างอิง */
|
| 49 |
-
.reference-text {
|
| 50 |
-
font-size: 14px;
|
| 51 |
-
color: #666;
|
| 52 |
-
text-align: center;
|
| 53 |
-
margin-top: 5px;
|
| 54 |
-
}
|
| 55 |
</style>
|
| 56 |
""", unsafe_allow_html=True)
|
| 57 |
|
|
@@ -156,7 +149,7 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 156 |
ax.text(0, 0.5, 'RF', fontsize=14, fontweight='bold', va='center', ha='right', transform=ax.get_yaxis_transform())
|
| 157 |
ax.axis('off')
|
| 158 |
|
| 159 |
-
# 2. Gy
|
| 160 |
ax = axes[1]
|
| 161 |
gy_vals = np.linspace(1, -1, total_steps)
|
| 162 |
for i, gy_amp in enumerate(gy_vals):
|
|
@@ -169,7 +162,7 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 169 |
ax.text(0, 0, 'Gy', fontsize=14, fontweight='bold', va='center', ha='right', transform=ax.get_yaxis_transform())
|
| 170 |
ax.axis('off')
|
| 171 |
|
| 172 |
-
# 3. Gx
|
| 173 |
ax = axes[2]
|
| 174 |
gx = np.zeros_like(t)
|
| 175 |
gx[(t > 2.5) & (t < 3.5)] = -0.5
|
|
@@ -190,46 +183,62 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 190 |
plt.tight_layout()
|
| 191 |
return get_image_from_plot(fig)
|
| 192 |
|
|
|
|
| 193 |
def draw_kspace_filling(current_step, total_steps, mode_type, real_bg):
|
| 194 |
fig, ax = plt.subplots(figsize=(5, 6))
|
| 195 |
-
display_img = np.zeros((224, 224))
|
| 196 |
-
|
| 197 |
-
step_size = 224 / total_steps
|
| 198 |
|
| 199 |
if mode_type == "ข้อมูลจำลอง (Simulated)":
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
|
| 215 |
-
ax.
|
|
|
|
|
|
|
| 216 |
else:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
if current_step >= total_steps - 1:
|
| 218 |
display_img = real_bg
|
| 219 |
else:
|
| 220 |
current_line_idx = int(current_step * step_size)
|
|
|
|
| 221 |
display_img[:current_line_idx + int(step_size), :] = real_bg[:current_line_idx + int(step_size), :]
|
| 222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
|
| 224 |
-
ax.set_xlim(-112, 112)
|
| 225 |
-
ax.set_ylim(112, -112)
|
| 226 |
-
|
| 227 |
-
current_y_center = 112 - int(current_step * step_size + step_size / 2)
|
| 228 |
-
ax.annotate('', xy=(-70, current_y_center), xytext=(-110, current_y_center), arrowprops=dict(arrowstyle='->', color='red', lw=2))
|
| 229 |
-
|
| 230 |
-
ax.axhline(0, color='white', linewidth=0.5, linestyle='--')
|
| 231 |
-
ax.axvline(0, color='white', linewidth=0.5, linestyle='--')
|
| 232 |
-
ax.set_title("K-Space Filling", fontweight='bold')
|
| 233 |
ax.axis('off')
|
| 234 |
return get_image_from_plot(fig)
|
| 235 |
|
|
@@ -256,17 +265,10 @@ with col_main:
|
|
| 256 |
|
| 257 |
_, col_img_center, _ = st.columns([1.5, 3, 1.5])
|
| 258 |
with col_img_center:
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
st.markdown(
|
| 264 |
-
"""<p class="reference-text">ภาพจาก: Clover Learning. (2024). <i>MRI k-space made easy - MRI physics explained</i> [Video]. YouTube.
|
| 265 |
-
<a href="https://youtu.be/cfE6SL2Xj6o?t=68" target="_blank">https://youtu.be/cfE6SL2Xj6o?t=68</a></p>""",
|
| 266 |
-
unsafe_allow_html=True
|
| 267 |
-
)
|
| 268 |
-
else:
|
| 269 |
-
st.info("กรุณาอัปโหลดรูปภาพ image_729a00.jpg เข้าระบบ")
|
| 270 |
|
| 271 |
# ---------------------------------------------------------
|
| 272 |
# NEW: K-Space Trajectories
|
|
@@ -276,10 +278,10 @@ with col_main:
|
|
| 276 |
**การบันทึกข้อมูลลงใน K-space สามารถทำได้หลายรูปแบบ** โดยจะยกตัวอย่างให้การเก็บข้อมูลจะดำเนินไปทีละบรรทัด โดยสัญญาณจะเกิดขึ้นหลังจากกระตุ้นด้วย RF Pulse โดยมีลำดับดังนี้
|
| 277 |
|
| 278 |
1. กระบวนการเริ่มต้นขึ้นเมื่อสนามแม่เหล็กเกรเดียนท์ **Gx** และ **Gy** เริ่มทำงานพร้อมกันเพื่อสร้างพิกัดอ้างอิง
|
| 279 |
-
2. ระบบจะเริ่มบันทึกข้อมูลจุดแรกของเฟสในแถวที่ 1 โดยจะเริ่มต้นที่ค่าแอมปลิจูดของ **Gy ทางฝั่งบวกก่อน**
|
| 280 |
-
3. สัญญาณจะถูกบันทึกไล่ไปตามแกน Gx จนได้ข้อมูลครบถ้วนเต็ม 1 แถว
|
| 281 |
4. เมื่อจบแถวแรก ระบบจะเริ่มกระบวนการใหม่เพื่อบันทึกข้อมูลในเฟสแถวที่ 2, 3, 4 ต่อไปเรื่อยๆ
|
| 282 |
-
5. การวนรอบนี้จะดำเนินต่อไปพร้อมกับการเปลี่ยนค่า **Gy ให้ลดลงจนไปถึงค่าทางฝั่งลบ** เมื่อบันทึกครบทุกแถวตามจำนวนความละเอียดที่ตั้งไว้ จะถือว่าสิ้นสุดกระบวนการเก็บข้อมูล MRI แบบสองมิติลงบน K-space
|
| 283 |
""")
|
| 284 |
|
| 285 |
total_anim_steps = 15
|
|
@@ -321,6 +323,7 @@ with col_main:
|
|
| 321 |
with col_anim2:
|
| 322 |
st.image(draw_kspace_filling(st.session_state.fill_step, total_anim_steps, data_mode, kspace_bg_image), use_container_width=True)
|
| 323 |
|
|
|
|
| 324 |
st.markdown("---")
|
| 325 |
st.markdown("## 📍 1 จุดบน k-space")
|
| 326 |
|
|
|
|
| 45 |
background-color: #1E88E5;
|
| 46 |
color: white;
|
| 47 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
</style>
|
| 49 |
""", unsafe_allow_html=True)
|
| 50 |
|
|
|
|
| 149 |
ax.text(0, 0.5, 'RF', fontsize=14, fontweight='bold', va='center', ha='right', transform=ax.get_yaxis_transform())
|
| 150 |
ax.axis('off')
|
| 151 |
|
| 152 |
+
# 2. Gy (Phase Encoding)
|
| 153 |
ax = axes[1]
|
| 154 |
gy_vals = np.linspace(1, -1, total_steps)
|
| 155 |
for i, gy_amp in enumerate(gy_vals):
|
|
|
|
| 162 |
ax.text(0, 0, 'Gy', fontsize=14, fontweight='bold', va='center', ha='right', transform=ax.get_yaxis_transform())
|
| 163 |
ax.axis('off')
|
| 164 |
|
| 165 |
+
# 3. Gx (Frequency Encoding)
|
| 166 |
ax = axes[2]
|
| 167 |
gx = np.zeros_like(t)
|
| 168 |
gx[(t > 2.5) & (t < 3.5)] = -0.5
|
|
|
|
| 183 |
plt.tight_layout()
|
| 184 |
return get_image_from_plot(fig)
|
| 185 |
|
| 186 |
+
# --- ฟังก์ชันวาด Trajectory (เริ่มซ้ายบน วิ่งไปขวา) ---
|
| 187 |
def draw_kspace_filling(current_step, total_steps, mode_type, real_bg):
|
| 188 |
fig, ax = plt.subplots(figsize=(5, 6))
|
|
|
|
|
|
|
|
|
|
| 189 |
|
| 190 |
if mode_type == "ข้อมูลจำลอง (Simulated)":
|
| 191 |
+
# สร้างกราฟดำ
|
| 192 |
+
ax.set_facecolor('black')
|
| 193 |
+
|
| 194 |
+
# แกน kx, ky
|
| 195 |
+
ax.axhline(0, color='gray', lw=1, ls='--')
|
| 196 |
+
ax.axvline(0, color='gray', lw=1, ls='--')
|
| 197 |
+
|
| 198 |
+
# คำนวณพิกัด Y จากบนลงล่าง (+ky ไป -ky)
|
| 199 |
+
y_vals = np.linspace(90, -90, total_steps)
|
| 200 |
+
|
| 201 |
+
for i in range(current_step + 1):
|
| 202 |
+
y = y_vals[i]
|
| 203 |
+
# วาดเส้นลูกศรวิ่งจาก ซ้าย (-kx) ไป ขวา (+kx)
|
| 204 |
+
if i == current_step:
|
| 205 |
+
ax.annotate('', xy=(100, y), xytext=(-100, y), arrowprops=dict(arrowstyle='->', color='red', lw=3))
|
| 206 |
+
else:
|
| 207 |
+
ax.annotate('', xy=(100, y), xytext=(-100, y), arrowprops=dict(arrowstyle='->', color='white', lw=1.5))
|
| 208 |
+
|
| 209 |
+
# วาดเส้นประเฉียงๆ โยงจากจุดจบขวา ไปเริ่มซ้ายในบรรทัดถัดไป
|
| 210 |
+
for i in range(current_step):
|
| 211 |
+
ax.plot([100, -100], [y_vals[i], y_vals[i+1]], color='gray', ls=':', lw=1)
|
| 212 |
|
| 213 |
+
ax.set_xlim(-112, 112)
|
| 214 |
+
ax.set_ylim(-112, 112)
|
| 215 |
+
|
| 216 |
else:
|
| 217 |
+
# โหมดข้อมูลจริง (kspace.mat) เติมจากบนลงล่าง
|
| 218 |
+
display_img = np.zeros((224, 224))
|
| 219 |
+
step_size = 224 / total_steps
|
| 220 |
+
|
| 221 |
if current_step >= total_steps - 1:
|
| 222 |
display_img = real_bg
|
| 223 |
else:
|
| 224 |
current_line_idx = int(current_step * step_size)
|
| 225 |
+
# เติมข้อมูลจากแถวบน (index 0) ลงมา
|
| 226 |
display_img[:current_line_idx + int(step_size), :] = real_bg[:current_line_idx + int(step_size), :]
|
| 227 |
+
|
| 228 |
+
# origin='upper' ควบคุมให้ index 0 อยู่ด้านบนสุด (y=112)
|
| 229 |
+
ax.imshow(display_img, cmap='gray', extent=[-112, 112, -112, 112], origin='upper', vmin=0, vmax=1)
|
| 230 |
+
|
| 231 |
+
# ลูกศรชี้พิกัดที่กำลังทำงาน
|
| 232 |
+
current_y_center = 112 - int(current_step * step_size + step_size / 2)
|
| 233 |
+
ax.annotate('', xy=(112, current_y_center), xytext=(-112, current_y_center), arrowprops=dict(arrowstyle='->', color='red', lw=2))
|
| 234 |
+
|
| 235 |
+
ax.set_xlim(-112, 112)
|
| 236 |
+
ax.set_ylim(-112, 112)
|
| 237 |
+
|
| 238 |
+
ax.set_title("K-Space Trajectory", fontweight='bold', color='white' if mode_type=="ข้อมูลจำลอง (Simulated)" else 'black')
|
| 239 |
+
if mode_type == "ข้อมูลจำลอง (Simulated)":
|
| 240 |
+
fig.patch.set_facecolor('black')
|
| 241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
ax.axis('off')
|
| 243 |
return get_image_from_plot(fig)
|
| 244 |
|
|
|
|
| 265 |
|
| 266 |
_, col_img_center, _ = st.columns([1.5, 3, 1.5])
|
| 267 |
with col_img_center:
|
| 268 |
+
if os.path.exists("Screenshot 2026-05-07 205051.png"):
|
| 269 |
+
st.image("Screenshot 2026-05-07 205051.png", use_container_width=True)
|
| 270 |
+
elif os.path.exists("Screenshot 2026-05-07 205051.jpg"):
|
| 271 |
+
st.image("Screenshot 2026-05-07 205051.jpg", use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
|
| 273 |
# ---------------------------------------------------------
|
| 274 |
# NEW: K-Space Trajectories
|
|
|
|
| 278 |
**การบันทึกข้อมูลลงใน K-space สามารถทำได้หลายรูปแบบ** โดยจะยกตัวอย่างให้การเก็บข้อมูลจะดำเนินไปทีละบรรทัด โดยสัญญาณจะเกิดขึ้นหลังจากกระตุ้นด้วย RF Pulse โดยมีลำดับดังนี้
|
| 279 |
|
| 280 |
1. กระบวนการเริ่มต้นขึ้นเมื่อสนามแม่เหล็กเกรเดียนท์ **Gx** และ **Gy** เริ่มทำงานพร้อมกันเพื่อสร้างพิกัดอ้างอิง
|
| 281 |
+
2. ระบบจะเริ่มบันทึกข้อมูลจุดแรกของเฟสในแถวที่ 1 โดยจะเริ่มต้นที่ค่าแอมปลิจูดของ **Gy ทางฝั่งบวกก่อน (พิกัดด้านบนของ K-Space)**
|
| 282 |
+
3. สัญญาณจะถูกบันทึกไล่ไปตามแกน **Gx (จากซ้าย -kx ไป ขวา +kx)** จนได้ข้อมูลครบถ้วนเต็ม 1 แถว
|
| 283 |
4. เมื่อจบแถวแรก ระบบจะเริ่มกระบวนการใหม่เพื่อบันทึกข้อมูลในเฟสแถวที่ 2, 3, 4 ต่อไปเรื่อยๆ
|
| 284 |
+
5. การวนรอบนี้จะดำเนินต่อไปพร้อมกับการเปลี่ยนค่า **Gy ให้ลดลงจนไปถึงค่าทางฝั่งลบ (พิกัดด้านล่าง)** เมื่อบันทึกครบทุกแถวตามจำนวนความละเอียดที่ตั้งไว้ จะถือว่าสิ้นสุดกระบวนการเก็บข้อมูล MRI แบบสองมิติลงบน K-space
|
| 285 |
""")
|
| 286 |
|
| 287 |
total_anim_steps = 15
|
|
|
|
| 323 |
with col_anim2:
|
| 324 |
st.image(draw_kspace_filling(st.session_state.fill_step, total_anim_steps, data_mode, kspace_bg_image), use_container_width=True)
|
| 325 |
|
| 326 |
+
|
| 327 |
st.markdown("---")
|
| 328 |
st.markdown("## 📍 1 จุดบน k-space")
|
| 329 |
|