Skip to content

Commit cd7a4fc

Browse files
committed
Add setting the objective magnification when patching mahmoodlab#287
2 parents f1e9394 + 77befdc commit cd7a4fc

3 files changed

Lines changed: 74 additions & 9 deletions

File tree

create_patches_fp.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ def patching(WSI_object, **kwargs):
3737
start_time = time.time()
3838

3939
# Patch
40-
file_path = WSI_object.process_contours(**kwargs)
40+
file_path, attr_dict = WSI_object.process_contours(**kwargs)
4141

4242

4343
### Stop Patch Timer
4444
patch_time_elapsed = time.time() - start_time
45-
return file_path, patch_time_elapsed
45+
return file_path, patch_time_elapsed, attr_dict
4646

4747

4848
def seg_and_patch(source, save_dir, patch_save_dir, mask_save_dir, stitch_save_dir,
@@ -52,7 +52,8 @@ def seg_and_patch(source, save_dir, patch_save_dir, mask_save_dir, stitch_save_d
5252
filter_params = {'a_t':100, 'a_h': 16, 'max_n_holes':8},
5353
vis_params = {'vis_level': -1, 'line_thickness': 500},
5454
patch_params = {'use_padding': True, 'contour_fn': 'four_pt'},
55-
patch_level = 0,
55+
magnification = None,
56+
patch_level = 0, custom_downsample = 1,
5657
use_default_params = False,
5758
seg = False, save_mask = True,
5859
stitch= False,
@@ -195,9 +196,23 @@ def seg_and_patch(source, save_dir, patch_save_dir, mask_save_dir, stitch_save_d
195196

196197
patch_time_elapsed = -1 # Default time
197198
if patch:
198-
current_patch_params.update({'patch_level': patch_level, 'patch_size': patch_size, 'step_size': step_size,
199+
if magnification:
200+
if WSI_object.max_objective_magnification is None:
201+
df.loc[idx, 'status'] = 'WSI do not has the attribute of objective magnification.'
202+
print('WSI do not has the attribute of objective magnification.')
203+
continue
204+
patch_level, custom_downsample = WSI_object.getPatchLevel(magnification)
205+
if isinstance(patch_level, dict):
206+
df.loc[idx, 'status'] = patch_level['error']
207+
print(patch_level['error'])
208+
continue
209+
current_patch_params.update({'patch_level': patch_level, 'patch_size': patch_size,
210+
'step_size': step_size, 'custom_downsample': custom_downsample,
199211
'save_path': patch_save_dir})
200-
file_path, patch_time_elapsed = patching(WSI_object = WSI_object, **current_patch_params,)
212+
file_path, patch_time_elapsed, attr_dict = patching(WSI_object = WSI_object, **current_patch_params)
213+
print(attr_dict)
214+
for key in ['patch_level', 'target_patch_size', 'target_step_size','patch_size', 'step_size', 'custom_downsample']:
215+
df.loc[i, key] = attr_dict[key]
201216

202217
stitch_time_elapsed = -1
203218
if stitch:
@@ -242,6 +257,8 @@ def seg_and_patch(source, save_dir, patch_save_dir, mask_save_dir, stitch_save_d
242257
help='directory to save processed data')
243258
parser.add_argument('--preset', default=None, type=str,
244259
help='predefined profile of default segmentation and filter parameters (.csv)')
260+
parser.add_argument('--magnification', type=int, default=None,
261+
help='objective magnification at which to patch. When it is not None, the patch_level does not work.')
245262
parser.add_argument('--patch_level', type=int, default=0,
246263
help='downsample level at which to patch')
247264
parser.add_argument('--process_list', type = str, default=None,
@@ -306,6 +323,6 @@ def seg_and_patch(source, save_dir, patch_save_dir, mask_save_dir, stitch_save_d
306323
seg_times, patch_times = seg_and_patch(**directories, **parameters,
307324
patch_size = args.patch_size, step_size=args.step_size,
308325
seg = args.seg, use_default_params=False, save_mask = True,
309-
stitch= args.stitch,
326+
stitch= args.stitch, magnification = args.magnification,
310327
patch_level=args.patch_level, patch = args.patch,
311328
process_list = process_list, auto_skip=args.no_auto_skip)

dataset_modules/dataset_h5.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ def __init__(self,
6464
dset = f['coords']
6565
self.patch_level = f['coords'].attrs['patch_level']
6666
self.patch_size = f['coords'].attrs['patch_size']
67+
self.target_patch_size = f['coords'].attrs['target_patch_size']
68+
self.custom_downsample = f['coords'].attrs['custom_downsample']
6769
self.length = len(dset)
6870

6971
self.summary()
@@ -84,7 +86,8 @@ def __getitem__(self, idx):
8486
with h5py.File(self.file_path,'r') as hdf5_file:
8587
coord = hdf5_file['coords'][idx]
8688
img = self.wsi.read_region(coord, self.patch_level, (self.patch_size, self.patch_size)).convert('RGB')
87-
89+
if self.custom_downsample > 1:
90+
img = img.resize((self.target_patch_size, self.target_patch_size))
8891
img = self.roi_transforms(img)
8992
return {'img': img, 'coord': coord}
9093

wsi_core/WholeSlideImage.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,50 @@ def __init__(self, path):
3232
self.wsi = openslide.open_slide(path)
3333
self.level_downsamples = self._assertLevelDownsamples()
3434
self.level_dim = self.wsi.level_dimensions
35+
self.max_objective_magnification = self.getMaxObjectivteMagnification()
3536

3637
self.contours_tissue = None
3738
self.contours_tumor = None
3839
self.hdf5_file = None
3940

4041
def getOpenSlide(self):
4142
return self.wsi
43+
44+
def getMaxObjectivteMagnification(self):
45+
if 'aperio.AppMag' in self.wsi.properties:
46+
return int(self.wsi.properties['aperio.AppMag'])
47+
elif 'openslide.mpp-x' in self.wsi.properties:
48+
if abs(float(self.wsi.properties['openslide.mpp-x'])-0.25) < 0.1:
49+
return 40
50+
elif abs(float(self.wsi.properties['openslide.mpp-x'])-0.5) < 0.1:
51+
return 20
52+
elif 'tiff.XResolution' in self.wsi.properties:
53+
# um per pixel
54+
mpp = 1.0/(float(self.wsi.properties['tiff.XResolution'])/10000)
55+
if abs(mpp-0.25) < 0.1:
56+
return 40
57+
elif abs(mpp-0.5) < 0.1:
58+
return 20
59+
return None
60+
61+
def getPatchLevel(self,magnification):
62+
if magnification > self.max_objective_magnification:
63+
return {'error':'The specified magnification exceeds the maximum available magnification.'}, None
64+
if self.max_objective_magnification % magnification != 0:
65+
return {'error':'The specified magnification must be divisible by the maximum magnification.'}, None
66+
scale = self.max_objective_magnification / magnification
67+
patch_level, custom_downsample = None, None
68+
for i, level_downsample in enumerate(self.level_downsamples):
69+
downsample = int(level_downsample[0])
70+
if downsample == scale:
71+
patch_level = i
72+
custom_downsample = 1
73+
elif downsample>scale:
74+
break
75+
if not patch_level:
76+
patch_level = 0
77+
custom_downsample = scale
78+
return patch_level, custom_downsample
4279

4380
def initXML(self, xml_path):
4481
def _createContour(coord_list):
@@ -368,7 +405,9 @@ def _assertLevelDownsamples(self):
368405

369406
return level_downsamples
370407

371-
def process_contours(self, save_path, patch_level=0, patch_size=256, step_size=256, **kwargs):
408+
def process_contours(self, save_path, patch_level=0, patch_size=256, step_size=256, custom_downsample=1, **kwargs):
409+
target_patch_size, target_step_size = patch_size, step_size
410+
patch_size, step_size = int(target_patch_size * custom_downsample), int(target_step_size * custom_downsample)
372411
save_path_hdf5 = os.path.join(save_path, str(self.name) + '.h5')
373412
print("Creating patches for: ", self.name, "...",)
374413
elapsed = time.time()
@@ -381,14 +420,19 @@ def process_contours(self, save_path, patch_level=0, patch_size=256, step_size=2
381420
print('Processing contour {}/{}'.format(idx, n_contours))
382421

383422
asset_dict, attr_dict = self.process_contour(cont, self.holes_tissue[idx], patch_level, save_path, patch_size, step_size, **kwargs)
423+
attr_dict['coords'].update({
424+
'target_patch_size': target_patch_size,
425+
'target_step_size': target_step_size,
426+
'custom_downsample': custom_downsample
427+
})
384428
if len(asset_dict) > 0:
385429
if init:
386430
save_hdf5(save_path_hdf5, asset_dict, attr_dict, mode='w')
387431
init = False
388432
else:
389433
save_hdf5(save_path_hdf5, asset_dict, mode='a')
390434

391-
return self.hdf5_file
435+
return self.hdf5_file, attr_dict['coords']
392436

393437

394438
def process_contour(self, cont, contour_holes, patch_level, save_path, patch_size = 256, step_size = 256,
@@ -464,6 +508,7 @@ def process_contour(self, cont, contour_holes, patch_level, save_path, patch_siz
464508
asset_dict = {'coords' : results}
465509

466510
attr = {'patch_size' : patch_size, # To be considered...
511+
'step_size' : step_size,
467512
'patch_level' : patch_level,
468513
'downsample': self.level_downsamples[patch_level],
469514
'downsampled_level_dim' : tuple(np.array(self.level_dim[patch_level])),

0 commit comments

Comments
 (0)