1+ import io
12import os
23
34import matplotlib .patches as patches
@@ -26,9 +27,14 @@ def createMipPlot(
2627 ) -> None :
2728 '''
2829 Create a MIP projection in the frontal and side plane with
29- the calcication overlayed
30+ the calcication overlayed. The text box is generated seperately
31+ and then resampled to the MIP
3032 '''
3133
34+
35+ '''
36+ Generate MIP image
37+ '''
3238 # Create transparent hot cmap
3339 hot = plt .get_cmap ('hot' , 256 )
3440 hot_colors = hot (np .linspace (0 , 1 , 256 ))
@@ -87,38 +93,24 @@ def createMipPlot(
8793 axx .set_ylabel ('Slice number' , color = 'white' , fontsize = 10 )
8894 axx .tick_params (axis = 'y' , colors = 'white' , labelsize = 10 )
8995
90- # colorbar
91- # cbar = fig.colorbar(calc_im, ax=axx, fraction=0.026, pad=0.01, orientation='horizontal')
92- # cbar.set_label('Calcified Pixels') # Optional label
93-
9496 # extend black background
95- axx .set_xlim (0 , ct_proj_all .shape [1 ] + round (ct_proj_all .shape [1 ]* 1 / 3 ))
96-
97- rect = patches .Rectangle (
98- (ct_proj_all .shape [1 ], 0 ),
99- width = round (ct_proj_all .shape [1 ]* 1 / 3 ),
100- height = ct_proj_all .shape [0 ],
101- linewidth = 1 ,
102- edgecolor = "black" ,
103- facecolor = "black" ,
104- # zorder=text_obj.get_zorder() - 1,
105- )
106- axx .add_patch (rect )
107-
108- axx .set_ylim (ct_proj_all .shape [0 ] + 7 , 0 )
109-
110- rect_y = patches .Rectangle (
111- (0 , ct_proj_all .shape [0 ]),
112- width = ct_proj_all .shape [1 ] + round (ct_proj_all .shape [1 ]* 1 / 3 ),
113- height = 7 ,
114- linewidth = 1 ,
115- edgecolor = "black" ,
116- facecolor = "black" ,
117- # zorder=text_obj.get_zorder() - 1,
118- )
119- axx .add_patch (rect )
97+ axx .set_xlim (0 , ct_proj_all .shape [1 ])
12098
121- # Report printing on the plot
99+ # wrap plot in Image
100+ tight_bbox = fig .get_tightbbox (fig .canvas .get_renderer ())
101+ # Create a new bounding box with padding only on the left
102+ # The bbox coordinates are [left, bottom, right, top]
103+ custom_bbox = Bbox ([[tight_bbox .x0 - 0.05 , tight_bbox .y0 - 0.07 ], # Add 0.5 inches to the left only
104+ [tight_bbox .x1 , tight_bbox .y1 ]])
105+ buf_mip = io .BytesIO ()
106+ fig .savefig (buf_mip , bbox_inches = custom_bbox , pad_inches = 0 , dpi = 300 , format = 'png' )
107+ plt .close (fig )
108+ buf_mip .seek (0 )
109+ image_mip = Image .open (buf_mip )
110+
111+ '''
112+ Generate the text box
113+ '''
122114 spacing = 23
123115 indent = 1
124116 text_box_x_offset = 20
@@ -142,36 +134,59 @@ def createMipPlot(
142134
143135 report_text .append ('{:<{}}{:<{}}{}' .format ('' ,indent , 'Threshold (HU):' , spacing , HU_val ))
144136
137+ fig_t , axx_t = plt .subplots (figsize = (5.85 ,5.85 ), dpi = 300 )
138+ fig_t .patch .set_facecolor ('black' )
139+ axx_t .set_facecolor ('black' )
140+
141+ axx_t .imshow (np .ones ((100 ,65 )), cmap = 'gray' )
145142 bbox_props = dict (boxstyle = "round" , facecolor = "gray" , alpha = 0.5 )
146143
147- text_obj = axx .text (
148- x = ct_proj_all . shape [ 1 ] + text_box_x_offset ,
149- y = 10 ,
144+ text_obj = axx_t .text (
145+ x = 0.3 ,
146+ y = 1 ,
150147 s = '\n ' .join (report_text ),
151148 color = "white" ,
152- fontsize = 8 ,
149+ # fontsize=12.12 * font_scale,
150+ fontsize = 15.7 ,
153151 family = "monospace" ,
154152 bbox = bbox_props ,
155153 va = "top" ,
156154 ha = "left" ,
157155 )
158156
157+ axx_t .set_aspect (0.69 )
158+ axx_t .axis ('off' )
159+ fig .canvas .draw_idle ()
159160 plt .tight_layout ()
161+
162+ # wrap text box in Image
163+ tight_bbox_text = fig_t .get_tightbbox (fig_t .canvas .get_renderer ())
164+ custom_bbox_text = Bbox ([[tight_bbox_text .x0 - 0.05 , tight_bbox_text .y0 - 0.14 ], # Add 0.5 inches to the left only
165+ [tight_bbox_text .x1 + 0.15 , tight_bbox_text .y1 + 0.05 ]])
166+ buf_text = io .BytesIO ()
167+ fig_t .savefig (buf_text , bbox_inches = custom_bbox_text , pad_inches = 0 , dpi = 300 , format = 'png' )
168+ plt .close (fig_t )
169+ buf_text .seek (0 )
170+ image_text = Image .open (buf_text )
160171
161- tight_bbox = fig .get_tightbbox (fig .canvas .get_renderer ())
172+ # Match the width to the projection image
173+ aspect_ratio = image_text .height / image_text .width
174+ adjusted_width = int (image_mip .height / aspect_ratio )
162175
163- # Create a new bounding box with padding only on the left
164- # The bbox coordinates are [left, bottom, right, top]
165- custom_bbox = Bbox ([[tight_bbox .x0 - 0.05 , tight_bbox .y0 ], # Add 0.5 inches to the left only
166- [tight_bbox .x1 , tight_bbox .y1 ]])
176+ image_text_resample = image_text .resize ([adjusted_width , image_mip .height ], Image .LANCZOS )
167177
178+ # Merge into one image
179+ result = Image .new ("RGB" , (image_mip .width + image_text_resample .width , image_mip .height ))
180+ result .paste (im = image_mip , box = (0 , 0 ))
181+ result .paste (im = image_text_resample , box = (image_mip .width , 0 ))
168182
183+ # create path and save
169184 path = os .path .join (save_root , 'sub_figures' )
170185 os .makedirs (path , exist_ok = True )
171- # Save with the custom bounding box
172- fig .savefig (os .path .join (path ,'projection.png' ), bbox_inches = custom_bbox , pad_inches = 0 , dpi = 300 )
186+ result .save (os .path .join (path ,'projection.png' ), dpi = (300 , 300 ))
187+
188+
173189
174- plt .close (fig )
175190
176191def createCalciumMosaic (
177192 ct : NDArray ,
@@ -444,7 +459,7 @@ def createCalciumMosaicVertebrae(
444459 for i , name in enumerate (vertebra_names ):
445460 x_ = crop_size * (i % per_row ) + 4 # crop_size//2
446461 y_ = crop_size * (i // per_row ) + 4
447- axx .text (x_ , y_ , s = name , color = 'white' , fontsize = 10 , va = 'top' , ha = 'left' ,
462+ axx .text (x_ , y_ , s = name , color = 'white' , fontsize = 18 , va = 'top' , ha = 'left' ,
448463 bbox = dict (facecolor = 'black' , edgecolor = 'black' , boxstyle = 'round,pad=0.1' ))
449464
450465 axx .set_xticks ([])
0 commit comments