Update app.py
Browse files
app.py
CHANGED
|
@@ -72,14 +72,15 @@ def load_kspace_data():
|
|
| 72 |
|
| 73 |
kspace_raw = load_kspace_data()
|
| 74 |
|
| 75 |
-
# ปรับแก้ความสว่างด้วย Log Transformation
|
| 76 |
def format_kspace_display(k_data):
|
| 77 |
k_mag = np.abs(k_data)
|
| 78 |
-
|
|
|
|
| 79 |
return np.zeros_like(k_mag, dtype=np.uint8)
|
| 80 |
|
| 81 |
-
# สูตร Log Transformation เพื่อดึงความสว่าง
|
| 82 |
-
c = 255.0 / np.log(1 +
|
| 83 |
log_img = c * np.log(1 + k_mag)
|
| 84 |
return log_img.astype(np.uint8)
|
| 85 |
|
|
@@ -92,64 +93,40 @@ def get_image_from_plot(fig):
|
|
| 92 |
buf.seek(0)
|
| 93 |
return Image.open(buf)
|
| 94 |
|
| 95 |
-
# สร้างภาพ
|
| 96 |
def draw_kspace_diagram():
|
| 97 |
-
fig, ax = plt.subplots(figsize=(
|
|
|
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
|
| 103 |
-
# วาด
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
ax.add_patch(plt.Rectangle((0.6, 0.2), 0.1, 0.6, facecolor=c_lr, edgecolor='black', zorder=2))
|
| 107 |
-
ax.add_patch(plt.Rectangle((0.1, 0.8), 0.6, 0.1, facecolor=c_tb, edgecolor='black', zorder=2))
|
| 108 |
-
ax.add_patch(plt.Rectangle((0.1, 0.1), 0.6, 0.1, facecolor=c_tb, edgecolor='black', zorder=2))
|
| 109 |
|
| 110 |
-
#
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
for i in range(1, 7):
|
| 114 |
-
ax.plot([0.1 + i*0.1, 0.1 + i*0.1], [0.1, 0.9], color='black', lw=0.5, alpha=0.5, zorder=3)
|
| 115 |
-
|
| 116 |
-
# ตัวหนังสืออธิบาย
|
| 117 |
-
ax.text(0.4, 0.95, "k-space data", ha='center', va='bottom', fontsize=16, fontweight='bold')
|
| 118 |
-
ax.text(0.4, 0.85, "HIGH $K_y$ FREQUENCY ZONE\nCAPTURES HORIZONTAL EDGES", ha='center', va='center', fontsize=9, fontweight='bold')
|
| 119 |
-
ax.text(0.4, 0.5, "LOW SPATIAL\nFREQUENCY ZONE\nCAPTURES BASIC CONTRAST", ha='center', va='center', fontsize=11, fontweight='bold')
|
| 120 |
-
|
| 121 |
-
# แกน X, Y
|
| 122 |
-
ax.annotate('', xy=(0.7, 0.05), xytext=(0.1, 0.05), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=2))
|
| 123 |
-
ax.text(0.4, 0.0, 'Frequency Encoding (horizontal axis) $K_x$', ha='center', va='top', fontsize=14)
|
| 124 |
-
ax.annotate('', xy=(0.05, 0.9), xytext=(0.05, 0.1), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=2))
|
| 125 |
-
ax.text(0.0, 0.5, 'Phase Encoding\n(vertical axis) $K_y$', ha='center', va='center', rotation=90, fontsize=14)
|
| 126 |
-
|
| 127 |
-
# วาดคลื่น 3 รูปแบบ
|
| 128 |
-
x_w = np.linspace(0, 1, 300)
|
| 129 |
|
| 130 |
-
# คลื่น
|
| 131 |
-
|
| 132 |
-
ax.plot(
|
| 133 |
-
ax.annotate('', xy=(0.78, 0.8), xytext=(0.55, 0.6), arrowprops=dict(arrowstyle='fancy', color=c_center, lw=2, connectionstyle="arc3,rad=-0.2"))
|
| 134 |
-
|
| 135 |
-
# คลื่นกลาง (Blue - High freq y)
|
| 136 |
-
y_w2 = np.sin(2 * np.pi * 12 * x_w) * 0.1 + 0.5
|
| 137 |
-
ax.plot(x_w*0.2 + 0.8, y_w2, color=c_tb, lw=3)
|
| 138 |
-
ax.annotate('', xy=(0.78, 0.55), xytext=(0.65, 0.85), arrowprops=dict(arrowstyle='fancy', color=c_tb, lw=2, connectionstyle="arc3,rad=0.2"))
|
| 139 |
|
| 140 |
-
#
|
| 141 |
-
|
| 142 |
-
ax.
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
ax.
|
| 146 |
-
ax.set_ylim(-0.05, 1.05)
|
| 147 |
ax.axis('off')
|
|
|
|
| 148 |
return get_image_from_plot(fig)
|
| 149 |
|
| 150 |
def draw_kspace_point(kx, ky, bg_image):
|
| 151 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 152 |
-
# vmin=0, vmax=255
|
| 153 |
ax.imshow(bg_image, cmap='gray', extent=[-112, 112, -112, 112], vmin=0, vmax=255)
|
| 154 |
ax.plot(kx, ky, 'ro', markersize=6)
|
| 155 |
ax.annotate('', xy=(kx, ky), xytext=(0, 0), arrowprops=dict(arrowstyle='->', color='yellow', lw=2))
|
|
@@ -186,6 +163,7 @@ def apply_filter(k_data, mode, radius):
|
|
| 186 |
|
| 187 |
def draw_filtered_kspace(filtered_k):
|
| 188 |
fig, ax = plt.subplots(figsize=(4, 4))
|
|
|
|
| 189 |
ax.imshow(format_kspace_display(filtered_k), cmap='gray', vmin=0, vmax=255)
|
| 190 |
ax.axis('off')
|
| 191 |
return get_image_from_plot(fig)
|
|
@@ -193,7 +171,7 @@ def draw_filtered_kspace(filtered_k):
|
|
| 193 |
def draw_mri(mri_result):
|
| 194 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 195 |
m_disp = mri_result / (np.max(mri_result) + 1e-8)
|
| 196 |
-
ax.imshow(np.flipud(m_disp), cmap='gray')
|
| 197 |
ax.axis('off')
|
| 198 |
return get_image_from_plot(fig)
|
| 199 |
|
|
@@ -245,7 +223,6 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 245 |
plt.tight_layout()
|
| 246 |
return get_image_from_plot(fig)
|
| 247 |
|
| 248 |
-
# วาด Trajectory แบบลูกศรซ้ายไปขวา (เฉพาะโหมดจำลอง)
|
| 249 |
def draw_kspace_filling(current_step, total_steps):
|
| 250 |
fig, ax = plt.subplots(figsize=(5, 6))
|
| 251 |
|
|
@@ -295,19 +272,13 @@ with col_main:
|
|
| 295 |
ข้อมูลใน k-space มักจะถูกนำมาแสดงผลในรูปแบบตารางสี่เหลี่ยม (Grid) โดยมีแกนหลักคือ **kx (แนวนอน - Frequency)** และ **ky (แนวตั้ง - Phase)** จุดสำคัญคือ **แกน kx และ ky เหล่านี้ ไม่ได้บอกตำแหน่งพิกัดในภาพอวัยวะ** แต่มันคือแกนที่บอกถึงลักษณะของ **"ความถี่เชิงพื้นที่"** ที่เป็นคลื่น (Sinusoidal wave) ด้วยเหตุนี้ **จุดแต่ละจุดบน K-space จึงไม่ได้จับคู่แบบ 1 ต่อ 1 กับพิกเซลบนภาพ MRI** (เช่น จุดมุมซ้ายบนของ K-space ไม่ได้สร้างภาพมุมซ้ายบนของอวัยวะ)
|
| 296 |
""")
|
| 297 |
|
| 298 |
-
_, col_img_center, _ = st.columns([
|
| 299 |
with col_img_center:
|
| 300 |
-
# ใช้กราฟ
|
| 301 |
st.image(draw_kspace_diagram(), use_container_width=True)
|
| 302 |
-
# คำอ้างอิง
|
| 303 |
-
st.markdown(
|
| 304 |
-
"""<p class="reference-text">ภาพจำลองอ้างอิงจาก: Clover Learning. (2024). <i>MRI k-space made easy - MRI physics explained</i> [Video]. YouTube.
|
| 305 |
-
<a href="https://youtu.be/cfE6SL2Xj6o?t=68" target="_blank">https://youtu.be/cfE6SL2Xj6o?t=68</a></p>""",
|
| 306 |
-
unsafe_allow_html=True
|
| 307 |
-
)
|
| 308 |
|
| 309 |
# ---------------------------------------------------------
|
| 310 |
-
#
|
| 311 |
# ---------------------------------------------------------
|
| 312 |
st.markdown("## 🛤️ K-Space Trajectories")
|
| 313 |
st.markdown("""
|
|
@@ -398,7 +369,6 @@ with col_main:
|
|
| 398 |
with col_img2:
|
| 399 |
st.image(draw_wave(kx_val, ky_val), caption="แผ่นลวดลายคลื่น (2D Sinusoidal Wave)", use_container_width=True)
|
| 400 |
|
| 401 |
-
# ส่วน Toggle สำหรับสังเกตคลื่น
|
| 402 |
with st.expander("🔍 สังเกตลักษณะคลื่น (คลิกเพื่อดูคำอธิบายเพิ่มเติม)"):
|
| 403 |
st.markdown("""
|
| 404 |
**ลองปรับเลื่อนพิกัดเพื่อสังเกตความเปลี่ยนแปลง:**
|
|
|
|
| 72 |
|
| 73 |
kspace_raw = load_kspace_data()
|
| 74 |
|
| 75 |
+
# ปรับแก้ความสว่างด้วย Log Transformation ตามสูตรของเพื่อน ให้สว่างแบบ Original
|
| 76 |
def format_kspace_display(k_data):
|
| 77 |
k_mag = np.abs(k_data)
|
| 78 |
+
max_val = np.max(k_mag)
|
| 79 |
+
if max_val == 0:
|
| 80 |
return np.zeros_like(k_mag, dtype=np.uint8)
|
| 81 |
|
| 82 |
+
# สูตร Log Transformation เพื่อดึงความสว่างให้เห็นรายละเอียดเส้น K-space ชัดเจน
|
| 83 |
+
c = 255.0 / np.log(1 + max_val)
|
| 84 |
log_img = c * np.log(1 + k_mag)
|
| 85 |
return log_img.astype(np.uint8)
|
| 86 |
|
|
|
|
| 93 |
buf.seek(0)
|
| 94 |
return Image.open(buf)
|
| 95 |
|
| 96 |
+
# สร้างภาพองค์ประกอบ K-Space แบบกราฟเส้นตัดกัน (พื้นดำ)
|
| 97 |
def draw_kspace_diagram():
|
| 98 |
+
fig, ax = plt.subplots(figsize=(5, 5))
|
| 99 |
+
ax.set_facecolor('black')
|
| 100 |
|
| 101 |
+
# แกนเส้นตรงสีขาว
|
| 102 |
+
ax.axhline(0, color='white', lw=2)
|
| 103 |
+
ax.axvline(0, color='white', lw=2)
|
| 104 |
|
| 105 |
+
# วาดคลื่นจำลอง
|
| 106 |
+
x = np.linspace(-1, 1, 100)
|
| 107 |
+
y = np.linspace(-1, 1, 100)
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
+
# คลื่นแดง (kx)
|
| 110 |
+
y_red = 0.2 * np.sin(10 * x)
|
| 111 |
+
ax.plot(x, y_red, color='red', alpha=0.8, lw=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
+
# คลื่นน้ำเงิน (ky)
|
| 114 |
+
x_blue = 0.2 * np.sin(10 * y)
|
| 115 |
+
ax.plot(x_blue, y, color='blue', alpha=0.8, lw=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
+
# ใส่ข้อความบอกแกน
|
| 118 |
+
ax.text(0.95, 0.05, 'kx\n(Frequency)', transform=ax.transAxes, ha='right', va='bottom', fontsize=14, fontweight='bold', color='red')
|
| 119 |
+
ax.text(0.05, 0.95, 'ky\n(Phase)', transform=ax.transAxes, ha='left', va='top', fontsize=14, fontweight='bold', color='#4B8BDE')
|
| 120 |
+
|
| 121 |
+
ax.set_xlim(-1, 1)
|
| 122 |
+
ax.set_ylim(-1, 1)
|
|
|
|
| 123 |
ax.axis('off')
|
| 124 |
+
fig.patch.set_facecolor('black')
|
| 125 |
return get_image_from_plot(fig)
|
| 126 |
|
| 127 |
def draw_kspace_point(kx, ky, bg_image):
|
| 128 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 129 |
+
# vmin=0, vmax=255 เพื่อรักษาความสว่าง
|
| 130 |
ax.imshow(bg_image, cmap='gray', extent=[-112, 112, -112, 112], vmin=0, vmax=255)
|
| 131 |
ax.plot(kx, ky, 'ro', markersize=6)
|
| 132 |
ax.annotate('', xy=(kx, ky), xytext=(0, 0), arrowprops=dict(arrowstyle='->', color='yellow', lw=2))
|
|
|
|
| 163 |
|
| 164 |
def draw_filtered_kspace(filtered_k):
|
| 165 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 166 |
+
# ปรับภาพ Filter ให้สว่างและอัปเดตตามแบบ Real-time
|
| 167 |
ax.imshow(format_kspace_display(filtered_k), cmap='gray', vmin=0, vmax=255)
|
| 168 |
ax.axis('off')
|
| 169 |
return get_image_from_plot(fig)
|
|
|
|
| 171 |
def draw_mri(mri_result):
|
| 172 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 173 |
m_disp = mri_result / (np.max(mri_result) + 1e-8)
|
| 174 |
+
ax.imshow(np.flipud(m_disp), cmap='gray') # แก้ภาพกลับหัว
|
| 175 |
ax.axis('off')
|
| 176 |
return get_image_from_plot(fig)
|
| 177 |
|
|
|
|
| 223 |
plt.tight_layout()
|
| 224 |
return get_image_from_plot(fig)
|
| 225 |
|
|
|
|
| 226 |
def draw_kspace_filling(current_step, total_steps):
|
| 227 |
fig, ax = plt.subplots(figsize=(5, 6))
|
| 228 |
|
|
|
|
| 272 |
ข้อมูลใน k-space มักจะถูกนำมาแสดงผลในรูปแบบตารางสี่เหลี่ยม (Grid) โดยมีแกนหลักคือ **kx (แนวนอน - Frequency)** และ **ky (แนวตั้ง - Phase)** จุดสำคัญคือ **แกน kx และ ky เหล่านี้ ไม่ได้บอกตำแหน่งพิกัดในภาพอวัยวะ** แต่มันคือแกนที่บอกถึงลักษณะของ **"ความถี่เชิงพื้นที่"** ที่เป็นคลื่น (Sinusoidal wave) ด้วยเหตุนี้ **จุดแต่ละจุดบน K-space จึงไม่ได้จับคู่แบบ 1 ต่อ 1 กับพิกเซลบนภาพ MRI** (เช่น จุดมุมซ้ายบนของ K-space ไม่ได้สร้างภาพมุมซ้ายบนของอวัยวะ)
|
| 273 |
""")
|
| 274 |
|
| 275 |
+
_, col_img_center, _ = st.columns([2.5, 3, 2.5])
|
| 276 |
with col_img_center:
|
| 277 |
+
# ใช้กราฟเส้นแทนการใช้รูปภาพ
|
| 278 |
st.image(draw_kspace_diagram(), use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
|
| 280 |
# ---------------------------------------------------------
|
| 281 |
+
# K-Space Trajectories
|
| 282 |
# ---------------------------------------------------------
|
| 283 |
st.markdown("## 🛤️ K-Space Trajectories")
|
| 284 |
st.markdown("""
|
|
|
|
| 369 |
with col_img2:
|
| 370 |
st.image(draw_wave(kx_val, ky_val), caption="แผ่นลวดลายคลื่น (2D Sinusoidal Wave)", use_container_width=True)
|
| 371 |
|
|
|
|
| 372 |
with st.expander("🔍 สังเกตลักษณะคลื่น (คลิกเพื่อดูคำอธิบายเพิ่มเติม)"):
|
| 373 |
st.markdown("""
|
| 374 |
**ลองปรับเลื่อนพิกัดเพื่อสังเกตความเปลี่ยนแปลง:**
|