Update app.py
Browse files
app.py
CHANGED
|
@@ -45,6 +45,13 @@ st.markdown("""
|
|
| 45 |
background-color: #1E88E5;
|
| 46 |
color: white;
|
| 47 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
</style>
|
| 49 |
""", unsafe_allow_html=True)
|
| 50 |
|
|
@@ -149,7 +156,7 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 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
|
| 153 |
ax = axes[1]
|
| 154 |
gy_vals = np.linspace(1, -1, total_steps)
|
| 155 |
for i, gy_amp in enumerate(gy_vals):
|
|
@@ -162,7 +169,7 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 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
|
| 166 |
ax = axes[2]
|
| 167 |
gx = np.zeros_like(t)
|
| 168 |
gx[(t > 2.5) & (t < 3.5)] = -0.5
|
|
@@ -183,62 +190,46 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 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 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 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 |
-
|
| 214 |
-
|
| 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,10 +256,17 @@ with col_main:
|
|
| 265 |
|
| 266 |
_, col_img_center, _ = st.columns([1.5, 3, 1.5])
|
| 267 |
with col_img_center:
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
|
| 273 |
# ---------------------------------------------------------
|
| 274 |
# NEW: K-Space Trajectories
|
|
@@ -278,10 +276,10 @@ with col_main:
|
|
| 278 |
**การบันทึกข้อมูลลงใน K-space สามารถทำได้หลายรูปแบบ** โดยจะยกตัวอย่างให้การเก็บข้อมูลจะดำเนินไปทีละบรรทัด โดยสัญญาณจะเกิดขึ้นหลังจากกระตุ้นด้วย RF Pulse โดยมีลำดับดังนี้
|
| 279 |
|
| 280 |
1. กระบวนการเริ่มต้นขึ้นเมื่อสนามแม่เหล็กเกรเดียนท์ **Gx** และ **Gy** เริ่มทำงานพร้อมกันเพื่อสร้างพิกัดอ้างอิง
|
| 281 |
-
2. ระบบจะเริ่มบันทึกข้อมูลจุดแรกของเฟสในแถวที่ 1 โดยจะเริ่มต้นที่ค่าแอมปลิจูดของ **Gy ทางฝั่งบวกก่อน
|
| 282 |
-
3. สัญญาณจะถูกบันทึกไล่ไปตามแกน
|
| 283 |
4. เมื่อจบแถวแรก ระบบจะเริ่มกระบวนการใหม่เพื่อบันทึกข้อมูลในเฟสแถวที่ 2, 3, 4 ต่อไปเรื่อยๆ
|
| 284 |
-
5. การวนรอบนี้จะดำเนินต่อไปพร้อมกับการเปลี่ยนค่า **Gy ให้ลดลงจนไปถึงค่าทางฝั่งลบ
|
| 285 |
""")
|
| 286 |
|
| 287 |
total_anim_steps = 15
|
|
@@ -323,7 +321,6 @@ with col_main:
|
|
| 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 |
|
|
|
|
| 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 |
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 |
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 |
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 |
+
x_array = np.linspace(-10, 10, 224)
|
| 201 |
+
for step in range(current_step + 1):
|
| 202 |
+
y_center = int(step * step_size + step_size / 2)
|
| 203 |
+
y_start = max(0, y_center - 1)
|
| 204 |
+
y_end = min(224, y_center + 1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
+
signal = np.abs(np.sinc(x_array))
|
| 207 |
+
noise = np.random.uniform(0.1, 0.4, 224)
|
| 208 |
+
line_detail = signal + noise
|
| 209 |
+
|
| 210 |
+
ky_dist = abs(step - (total_steps - 1)/2) / ((total_steps - 1)/2)
|
| 211 |
+
line_detail = line_detail * (1.0 - 0.7 * ky_dist)
|
| 212 |
+
|
| 213 |
+
display_img[y_start:y_end, :] = np.clip(line_detail, 0, 1)
|
| 214 |
+
|
| 215 |
+
ax.imshow(display_img, cmap='gray', extent=[-112, 112, -112, 112], vmin=0, vmax=1)
|
| 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 |
+
ax.imshow(display_img, cmap='gray', extent=[-112, 112, -112, 112], vmin=0, vmax=1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|
| 257 |
_, col_img_center, _ = st.columns([1.5, 3, 1.5])
|
| 258 |
with col_img_center:
|
| 259 |
+
# เช็คไฟล์รูปภาพใหม่
|
| 260 |
+
if os.path.exists("image_729a00.jpg"):
|
| 261 |
+
st.image("image_729a00.jpg", use_container_width=True)
|
| 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 |
**การบันทึกข้อมูลลงใน 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 |
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 |
|