Update app.py
Browse files
app.py
CHANGED
|
@@ -14,11 +14,14 @@ st.set_page_config(layout="wide", page_title="K-Space to MRI")
|
|
| 14 |
|
| 15 |
st.markdown("""
|
| 16 |
<style>
|
| 17 |
-
/* CSS แก้ปัญหาการสั่นของหน้าจอ
|
| 18 |
-
|
| 19 |
-
overflow-y: scroll !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
}
|
| 21 |
-
.stApp { transition: none !important; }
|
| 22 |
|
| 23 |
html, body, [class*="st-"] {
|
| 24 |
font-size: 18px;
|
|
@@ -87,7 +90,6 @@ def format_kspace_display(k_data):
|
|
| 87 |
c = 255.0 / np.log(1 + max_val)
|
| 88 |
log_img = c * np.log(1 + k_mag)
|
| 89 |
|
| 90 |
-
# เสริม Gamma correction ดึงความสว่างให้เห็นมิติรอยตัดชัดเจน
|
| 91 |
log_norm = (log_img - np.min(log_img)) / (np.max(log_img) - np.min(log_img) + 1e-8)
|
| 92 |
log_boosted = np.power(log_norm, 0.3)
|
| 93 |
return log_boosted
|
|
@@ -96,7 +98,7 @@ kspace_bg_image = format_kspace_display(kspace_raw)
|
|
| 96 |
|
| 97 |
def get_image_from_plot(fig):
|
| 98 |
buf = io.BytesIO()
|
| 99 |
-
#
|
| 100 |
plt.savefig(buf, format='png', dpi=100)
|
| 101 |
plt.close(fig)
|
| 102 |
buf.seek(0)
|
|
@@ -109,32 +111,32 @@ def draw_kspace_diagram():
|
|
| 109 |
rect = plt.Rectangle((-1, -1), 2, 2, fill=False, edgecolor='black', lw=3)
|
| 110 |
ax.add_patch(rect)
|
| 111 |
|
| 112 |
-
# วาดแกนลูกศรตัดกันตรงกลาง
|
| 113 |
ax.annotate('', xy=(0.95, 0), xytext=(-0.95, 0), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=2))
|
| 114 |
ax.annotate('', xy=(0, 0.95), xytext=(0, -0.95), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=2))
|
| 115 |
|
| 116 |
# ตัวอักษรบอกทิศทาง +kx, -kx, +ky, -ky
|
| 117 |
ax.text(1.1, 0, '+kx', fontsize=20, fontweight='bold', va='center')
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
| 120 |
ax.text(0, 1.1, '+ky', fontsize=20, fontweight='bold', ha='center')
|
| 121 |
ax.text(0, -1.1, '-ky', fontsize=20, fontweight='bold', ha='center', va='top')
|
| 122 |
|
| 123 |
-
# Label บอกชื่อแกน (เอาไว้ข้างนอกกรอบ
|
| 124 |
-
# Frequency axis ด้านล่าง (ขยับเส้นให้ต่ำลง)
|
| 125 |
ax.annotate('', xy=(1, -1.4), xytext=(-1, -1.4), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=4))
|
| 126 |
ax.text(0, -1.5, 'kx (Frequency)', ha='center', va='top', fontsize=16, fontweight='bold')
|
| 127 |
|
| 128 |
-
# Phase axis ด้านซ้าย (ขยับเส้นหนีออกไปทางซ้ายมากขึ้น เพื่อไม่ให้ทับ -kx)
|
| 129 |
ax.annotate('', xy=(-1.5, 1), xytext=(-1.5, -1), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=4))
|
| 130 |
ax.text(-1.6, 0, 'ky (Phase)', ha='right', va='center', rotation=90, fontsize=16, fontweight='bold')
|
| 131 |
|
| 132 |
-
|
| 133 |
-
ax.
|
| 134 |
-
ax.set_ylim(-1.8, 1.4)
|
| 135 |
ax.axis('off')
|
| 136 |
|
| 137 |
-
|
|
|
|
| 138 |
return get_image_from_plot(fig)
|
| 139 |
|
| 140 |
def draw_kspace_point(kx, ky, bg_image):
|
|
@@ -147,6 +149,7 @@ def draw_kspace_point(kx, ky, bg_image):
|
|
| 147 |
ax.set_xlim(-112, 112)
|
| 148 |
ax.set_ylim(-112, 112)
|
| 149 |
ax.axis('off')
|
|
|
|
| 150 |
return get_image_from_plot(fig)
|
| 151 |
|
| 152 |
def draw_wave(kx, ky):
|
|
@@ -159,6 +162,7 @@ def draw_wave(kx, ky):
|
|
| 159 |
wave = np.cos(2 * np.pi * (freq_x * X + freq_y * Y))
|
| 160 |
ax.imshow(wave, cmap='gray', extent=[-112, 112, -112, 112])
|
| 161 |
ax.axis('off')
|
|
|
|
| 162 |
return get_image_from_plot(fig)
|
| 163 |
|
| 164 |
def apply_filter(k_data, mode, radius):
|
|
@@ -177,19 +181,19 @@ def draw_filtered_kspace(filtered_k):
|
|
| 177 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 178 |
ax.imshow(format_kspace_display(filtered_k), cmap='gray', vmin=0, vmax=1)
|
| 179 |
ax.axis('off')
|
|
|
|
| 180 |
return get_image_from_plot(fig)
|
| 181 |
|
| 182 |
-
# ฟังก์ชัน MRI แก้ให้ภาพสว่างใส โดยตัด Noise พิกเซลทิ้ง
|
| 183 |
def draw_mri(mri_result):
|
| 184 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 185 |
if np.max(mri_result) > 0:
|
| 186 |
-
|
| 187 |
-
vmin, vmax = np.percentile(mri_result, (1, 99))
|
| 188 |
else:
|
| 189 |
vmin, vmax = 0, 1
|
| 190 |
|
| 191 |
ax.imshow(np.flipud(mri_result), cmap='gray', vmin=vmin, vmax=vmax)
|
| 192 |
ax.axis('off')
|
|
|
|
| 193 |
return get_image_from_plot(fig)
|
| 194 |
|
| 195 |
def draw_pulse_sequence(current_step, total_steps):
|
|
@@ -237,7 +241,8 @@ def draw_pulse_sequence(current_step, total_steps):
|
|
| 237 |
ax.text(0, 0.5, 'Signal\n(Echo)', fontsize=14, fontweight='bold', va='center', ha='right', transform=ax.get_yaxis_transform())
|
| 238 |
ax.axis('off')
|
| 239 |
|
| 240 |
-
|
|
|
|
| 241 |
return get_image_from_plot(fig)
|
| 242 |
|
| 243 |
def draw_kspace_filling(current_step, total_steps):
|
|
@@ -266,6 +271,8 @@ def draw_kspace_filling(current_step, total_steps):
|
|
| 266 |
fig.patch.set_facecolor('black')
|
| 267 |
|
| 268 |
ax.axis('off')
|
|
|
|
|
|
|
| 269 |
return get_image_from_plot(fig)
|
| 270 |
|
| 271 |
# ==========================================================
|
|
@@ -336,15 +343,12 @@ with col_main:
|
|
| 336 |
|
| 337 |
st.progress((st.session_state.fill_step + 1) / total_anim_steps)
|
| 338 |
|
|
|
|
| 339 |
col_anim1, col_anim2 = st.columns([1, 1])
|
| 340 |
with col_anim1:
|
| 341 |
-
|
| 342 |
-
# นำ use_container_width=True ออกเพื่อล็อกขนาดภาพไม่ให้สั่น
|
| 343 |
-
img1_ph.image(draw_pulse_sequence(st.session_state.fill_step, total_anim_steps))
|
| 344 |
with col_anim2:
|
| 345 |
-
|
| 346 |
-
# นำ use_container_width=True ออกเพื่อล็อกขนาดภาพไม่ให้สั่น
|
| 347 |
-
img2_ph.image(draw_kspace_filling(st.session_state.fill_step, total_anim_steps))
|
| 348 |
|
| 349 |
st.markdown("""
|
| 350 |
---
|
|
|
|
| 14 |
|
| 15 |
st.markdown("""
|
| 16 |
<style>
|
| 17 |
+
/* CSS แก้ปัญหาการสั่นของหน้าจอแบบเด็ดขาด */
|
| 18 |
+
.stApp {
|
| 19 |
+
overflow-y: scroll !important;
|
| 20 |
+
transition: none !important;
|
| 21 |
+
}
|
| 22 |
+
.main .block-container {
|
| 23 |
+
min-height: 101vh !important;
|
| 24 |
}
|
|
|
|
| 25 |
|
| 26 |
html, body, [class*="st-"] {
|
| 27 |
font-size: 18px;
|
|
|
|
| 90 |
c = 255.0 / np.log(1 + max_val)
|
| 91 |
log_img = c * np.log(1 + k_mag)
|
| 92 |
|
|
|
|
| 93 |
log_norm = (log_img - np.min(log_img)) / (np.max(log_img) - np.min(log_img) + 1e-8)
|
| 94 |
log_boosted = np.power(log_norm, 0.3)
|
| 95 |
return log_boosted
|
|
|
|
| 98 |
|
| 99 |
def get_image_from_plot(fig):
|
| 100 |
buf = io.BytesIO()
|
| 101 |
+
# เซฟภาพด้วยขนาดพิกเซลตายตัว ห้ามใช้ bbox_inches='tight' เด็ดขาดเพื่อป้องกันเว็บสั่น
|
| 102 |
plt.savefig(buf, format='png', dpi=100)
|
| 103 |
plt.close(fig)
|
| 104 |
buf.seek(0)
|
|
|
|
| 111 |
rect = plt.Rectangle((-1, -1), 2, 2, fill=False, edgecolor='black', lw=3)
|
| 112 |
ax.add_patch(rect)
|
| 113 |
|
| 114 |
+
# วาดแกนลูกศรตัดกันตรงกลาง (อยู่ภายในกรอบ)
|
| 115 |
ax.annotate('', xy=(0.95, 0), xytext=(-0.95, 0), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=2))
|
| 116 |
ax.annotate('', xy=(0, 0.95), xytext=(0, -0.95), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=2))
|
| 117 |
|
| 118 |
# ตัวอักษรบอกทิศทาง +kx, -kx, +ky, -ky
|
| 119 |
ax.text(1.1, 0, '+kx', fontsize=20, fontweight='bold', va='center')
|
| 120 |
+
|
| 121 |
+
# ขยับ -kx ออกไปทางซ้ายให้มีช่องว่าง (ha='right' ทำให้ขอบขวาของตัวหนังสืออยู่ที่พิกัด -1.1 ซึ่งไม่ทับกรอบ)
|
| 122 |
+
ax.text(-1.1, 0, '-kx', fontsize=20, fontweight='bold', va='center', ha='right')
|
| 123 |
+
|
| 124 |
ax.text(0, 1.1, '+ky', fontsize=20, fontweight='bold', ha='center')
|
| 125 |
ax.text(0, -1.1, '-ky', fontsize=20, fontweight='bold', ha='center', va='top')
|
| 126 |
|
| 127 |
+
# Label บอกชื่อแกน (เอาไว้ข้างนอกกรอบ)
|
|
|
|
| 128 |
ax.annotate('', xy=(1, -1.4), xytext=(-1, -1.4), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=4))
|
| 129 |
ax.text(0, -1.5, 'kx (Frequency)', ha='center', va='top', fontsize=16, fontweight='bold')
|
| 130 |
|
|
|
|
| 131 |
ax.annotate('', xy=(-1.5, 1), xytext=(-1.5, -1), arrowprops=dict(arrowstyle='<|-|>', color='black', lw=4))
|
| 132 |
ax.text(-1.6, 0, 'ky (Phase)', ha='right', va='center', rotation=90, fontsize=16, fontweight='bold')
|
| 133 |
|
| 134 |
+
ax.set_xlim(-2.0, 1.5)
|
| 135 |
+
ax.set_ylim(-1.8, 1.5)
|
|
|
|
| 136 |
ax.axis('off')
|
| 137 |
|
| 138 |
+
# ล็อกระยะขอบให้ตายตัวป้องกันการกระตุก
|
| 139 |
+
fig.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1)
|
| 140 |
return get_image_from_plot(fig)
|
| 141 |
|
| 142 |
def draw_kspace_point(kx, ky, bg_image):
|
|
|
|
| 149 |
ax.set_xlim(-112, 112)
|
| 150 |
ax.set_ylim(-112, 112)
|
| 151 |
ax.axis('off')
|
| 152 |
+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
|
| 153 |
return get_image_from_plot(fig)
|
| 154 |
|
| 155 |
def draw_wave(kx, ky):
|
|
|
|
| 162 |
wave = np.cos(2 * np.pi * (freq_x * X + freq_y * Y))
|
| 163 |
ax.imshow(wave, cmap='gray', extent=[-112, 112, -112, 112])
|
| 164 |
ax.axis('off')
|
| 165 |
+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
|
| 166 |
return get_image_from_plot(fig)
|
| 167 |
|
| 168 |
def apply_filter(k_data, mode, radius):
|
|
|
|
| 181 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 182 |
ax.imshow(format_kspace_display(filtered_k), cmap='gray', vmin=0, vmax=1)
|
| 183 |
ax.axis('off')
|
| 184 |
+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
|
| 185 |
return get_image_from_plot(fig)
|
| 186 |
|
|
|
|
| 187 |
def draw_mri(mri_result):
|
| 188 |
fig, ax = plt.subplots(figsize=(4, 4))
|
| 189 |
if np.max(mri_result) > 0:
|
| 190 |
+
vmin, vmax = np.percentile(mri_result, (1, 99.5))
|
|
|
|
| 191 |
else:
|
| 192 |
vmin, vmax = 0, 1
|
| 193 |
|
| 194 |
ax.imshow(np.flipud(mri_result), cmap='gray', vmin=vmin, vmax=vmax)
|
| 195 |
ax.axis('off')
|
| 196 |
+
fig.subplots_adjust(left=0, right=1, top=1, bottom=0)
|
| 197 |
return get_image_from_plot(fig)
|
| 198 |
|
| 199 |
def draw_pulse_sequence(current_step, total_steps):
|
|
|
|
| 241 |
ax.text(0, 0.5, 'Signal\n(Echo)', fontsize=14, fontweight='bold', va='center', ha='right', transform=ax.get_yaxis_transform())
|
| 242 |
ax.axis('off')
|
| 243 |
|
| 244 |
+
# ล็อกระยะกรอบตายตัวเพื่อหยุดอาการสั่น
|
| 245 |
+
fig.subplots_adjust(left=0.2, right=0.95, top=0.95, bottom=0.05, hspace=0.2)
|
| 246 |
return get_image_from_plot(fig)
|
| 247 |
|
| 248 |
def draw_kspace_filling(current_step, total_steps):
|
|
|
|
| 271 |
fig.patch.set_facecolor('black')
|
| 272 |
|
| 273 |
ax.axis('off')
|
| 274 |
+
# ล็อกระยะกรอบตายตัวเพื่อหยุดอาการสั่น
|
| 275 |
+
fig.subplots_adjust(left=0.05, right=0.95, top=0.9, bottom=0.05)
|
| 276 |
return get_image_from_plot(fig)
|
| 277 |
|
| 278 |
# ==========================================================
|
|
|
|
| 343 |
|
| 344 |
st.progress((st.session_state.fill_step + 1) / total_anim_steps)
|
| 345 |
|
| 346 |
+
# วางภาพแสดงผลโดยตรง ใช้ use_container_width เพื่อให้รูปขยายเต็มคอลัมน์โดยไม่กระตุก
|
| 347 |
col_anim1, col_anim2 = st.columns([1, 1])
|
| 348 |
with col_anim1:
|
| 349 |
+
st.image(draw_pulse_sequence(st.session_state.fill_step, total_anim_steps), use_container_width=True)
|
|
|
|
|
|
|
| 350 |
with col_anim2:
|
| 351 |
+
st.image(draw_kspace_filling(st.session_state.fill_step, total_anim_steps), use_container_width=True)
|
|
|
|
|
|
|
| 352 |
|
| 353 |
st.markdown("""
|
| 354 |
---
|