TETSU0701 commited on
Commit
e230a96
·
verified ·
1 Parent(s): dfdd00e

Upload 3 files

Browse files
Files changed (3) hide show
  1. ctranspath.pth +3 -0
  2. utils_color_norm.py +270 -0
  3. utils_preprocessing.py +335 -0
ctranspath.pth ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7c998680060c8743551a412583fac689db43cec07053b72dfec6dcd810113539
3
+ size 111292151
utils_color_norm.py ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Stain normalization based on the method of:
3
+
4
+ M. Macenko et al., ‘A method for normalizing histology slides for quantitative analysis’,
5
+ in 2009 IEEE International Symposium on Biomedical Imaging: From Nano to Macro, 2009, pp. 1107–1110.
6
+
7
+ Uses the spams package:
8
+
9
+ http://spams-devel.gforge.inria.fr/index.html
10
+ (https://anaconda.org/conda-forge/python-spams
11
+ https://pypi.org/project/spams/)
12
+
13
+ """
14
+
15
+ from __future__ import division
16
+
17
+ import numpy as np
18
+ import cv2 as cv
19
+ import spams
20
+ import matplotlib.pyplot as plt
21
+
22
+ ##================================================================================================
23
+
24
+ def read_image(path):
25
+ """
26
+ Read an image to RGB uint8
27
+ :param path:
28
+ :return:
29
+ """
30
+ im = cv.imread(path)
31
+ im = cv.cvtColor(im, cv.COLOR_BGR2RGB)
32
+ return im
33
+
34
+ def show_colors(C):
35
+ """
36
+ Shows rows of C as colors (RGB)
37
+ :param C:
38
+ :return:
39
+ """
40
+ n = C.shape[0]
41
+ for i in range(n):
42
+ if C[i].max() > 1.0:
43
+ plt.plot([0, 1], [n - 1 - i, n - 1 - i], c=C[i] / 255, linewidth=20)
44
+ else:
45
+ plt.plot([0, 1], [n - 1 - i, n - 1 - i], c=C[i], linewidth=20)
46
+ plt.axis('off')
47
+ plt.axis([0, 1, -1, n])
48
+
49
+
50
+ def show(image, now=True, fig_size=(10, 10)):
51
+ """
52
+ Show an image (np.array).
53
+ Caution! Rescales image to be in range [0,1].
54
+ :param image:
55
+ :param now:
56
+ :param fig_size:
57
+ :return:
58
+ """
59
+ image = image.astype(np.float32)
60
+ m, M = image.min(), image.max()
61
+ if fig_size != None:
62
+ plt.rcParams['figure.figsize'] = (fig_size[0], fig_size[1])
63
+ plt.imshow((image - m) / (M - m), cmap='gray')
64
+ plt.axis('off')
65
+ if now == True:
66
+ plt.show()
67
+
68
+
69
+ def build_stack(tup):
70
+ """
71
+ Build a stack of images from a tuple of images
72
+ :param tup:
73
+ :return:
74
+ """
75
+ N = len(tup)
76
+ if len(tup[0].shape) == 3:
77
+ h, w, c = tup[0].shape
78
+ stack = np.zeros((N, h, w, c))
79
+ if len(tup[0].shape) == 2:
80
+ h, w = tup[0].shape
81
+ stack = np.zeros((N, h, w))
82
+ for i in range(N):
83
+ stack[i] = tup[i]
84
+ return stack
85
+
86
+
87
+ def patch_grid(ims, width=5, sub_sample=None, rand=False, save_name=None):
88
+ """
89
+ Display a grid of patches
90
+ :param ims:
91
+ :param width:
92
+ :param sub_sample:
93
+ :param rand:
94
+ :return:
95
+ """
96
+ N0 = np.shape(ims)[0]
97
+ if sub_sample is None:
98
+ N = N0
99
+ stack = ims
100
+ elif sub_sample != None and rand == False:
101
+ N = sub_sample
102
+ stack = ims[:N]
103
+ elif sub_sample != None and rand == True:
104
+ N = sub_sample
105
+ idx = np.random.choice(range(N), sub_sample, replace=False)
106
+ stack = ims[idx]
107
+ height = np.ceil(float(N) / width).astype(np.uint16)
108
+ plt.rcParams['figure.figsize'] = (18, (18 / width) * height)
109
+ plt.figure()
110
+ for i in range(N):
111
+ plt.subplot(height, width, i + 1)
112
+ im = stack[i]
113
+ show(im, now=False, fig_size=None)
114
+ if save_name != None:
115
+ plt.savefig(save_name)
116
+ plt.show()
117
+
118
+ ######################################
119
+
120
+ def standardize_brightness(I):
121
+ """
122
+ :param I:
123
+ :return:
124
+ """
125
+ p = np.percentile(I, 95)
126
+ return np.clip(I * 255.0 / p, 0, 255).astype(np.uint8)
127
+
128
+
129
+ def remove_zeros(I):
130
+ """
131
+ Remove zeros, replace with 1's.
132
+ :param I: uint8 array
133
+ :return:
134
+ """
135
+ mask = (I == 0)
136
+ I[mask] = 1
137
+ return I
138
+
139
+
140
+ def RGB_to_OD(I):
141
+ """
142
+ Convert from RGB to optical density
143
+ :param I:
144
+ :return:
145
+ """
146
+ I = remove_zeros(I)
147
+ return -1 * np.log(I / 255)
148
+
149
+
150
+ def OD_to_RGB(OD):
151
+ """
152
+ Convert from optical density to RGB
153
+ :param OD:
154
+ :return:
155
+ """
156
+ return (255 * np.exp(-1 * OD)).astype(np.uint8)
157
+
158
+
159
+ def normalize_rows(A):
160
+ """
161
+ Normalize rows of an array
162
+ :param A:
163
+ :return:
164
+ """
165
+ return A / np.linalg.norm(A, axis=1)[:, None]
166
+
167
+
168
+ def notwhite_mask(I, thresh=0.8):
169
+ """
170
+ Get a binary mask where true denotes 'not white'
171
+ :param I:
172
+ :param thresh:
173
+ :return:
174
+ """
175
+ I_LAB = cv.cvtColor(I, cv.COLOR_RGB2LAB)
176
+ L = I_LAB[:, :, 0] / 255.0
177
+ return (L < thresh)
178
+
179
+
180
+ def sign(x):
181
+ """
182
+ Returns the sign of x
183
+ :param x:
184
+ :return:
185
+ """
186
+ if x > 0:
187
+ return +1
188
+ elif x < 0:
189
+ return -1
190
+ elif x == 0:
191
+ return 0
192
+
193
+ def get_concentrations(I, stain_matrix, lamda=0.01):
194
+ """
195
+ Get concentrations, a npix x 2 matrix
196
+ :param I:
197
+ :param stain_matrix: a 2x3 stain matrix
198
+ :return:
199
+ """
200
+ OD = RGB_to_OD(I).reshape((-1, 3))
201
+ return spams.lasso(OD.T, D=stain_matrix.T, mode=2, lambda1=lamda, pos=True).toarray().T
202
+
203
+ ##================================================================================================
204
+ def get_stain_matrix(I, beta=0.15, alpha=1):
205
+ """
206
+ Get stain matrix (2x3)
207
+ :param I:
208
+ :param beta:
209
+ :param alpha:
210
+ :return:
211
+ """
212
+ OD = RGB_to_OD(I).reshape((-1, 3))
213
+ OD = OD[(OD > beta).any(axis=1), :]
214
+ _, V = np.linalg.eigh(np.cov(OD, rowvar=False))
215
+ V = V[:, [2, 1]]
216
+ if V[0, 0] < 0:
217
+ V[:, 0] *= -1
218
+ if V[0, 1] < 0:
219
+ V[:, 1] *= -1
220
+ That = np.dot(OD, V)
221
+ phi = np.arctan2(That[:, 1], That[:, 0])
222
+ minPhi = np.percentile(phi, alpha)
223
+ maxPhi = np.percentile(phi, 100 - alpha)
224
+ v1 = np.dot(V, np.array([np.cos(minPhi), np.sin(minPhi)]))
225
+ v2 = np.dot(V, np.array([np.cos(maxPhi), np.sin(maxPhi)]))
226
+ if v1[0] > v2[0]:
227
+ HE = np.array([v1, v2])
228
+ else:
229
+ HE = np.array([v2, v1])
230
+ return normalize_rows(HE)
231
+
232
+ ##================================================================================================
233
+
234
+ class macenko_normalizer(object):
235
+ """
236
+ A stain normalization object
237
+ """
238
+
239
+ def __init__(self):
240
+ self.stain_matrix_target = np.array(
241
+ [[0.5626, 0.2159], [0.7201, 0.8012], [0.4062, 0.5581]], dtype=np.float32).T
242
+ self.target_concentrations = None
243
+
244
+ def fit(self, target):
245
+ target = standardize_brightness(target)
246
+ self.stain_matrix_target = get_stain_matrix(target)
247
+ self.target_concentrations = get_concentrations(target, self.stain_matrix_target)
248
+
249
+ def target_stains(self):
250
+ return OD_to_RGB(self.stain_matrix_target)
251
+
252
+ def transform(self, I):
253
+ I = standardize_brightness(I)
254
+ stain_matrix_source = get_stain_matrix(I)
255
+ source_concentrations = get_concentrations(I, stain_matrix_source)
256
+ maxC_source = np.percentile(source_concentrations, 99, axis=0).reshape((1, 2))
257
+ maxC_target = np.array([1.9705, 1.0308], dtype=float).reshape((1, 2))
258
+ source_concentrations *= maxC_target / maxC_source
259
+ return (255* np.exp(-1*np.dot(source_concentrations, self.stain_matrix_target).reshape(I.shape))).astype(np.uint8)
260
+
261
+ def hematoxylin(self, I):
262
+ I = standardize_brightness(I)
263
+ h, w, c = I.shape
264
+ stain_matrix_source = get_stain_matrix(I)
265
+ source_concentrations = get_concentrations(I, stain_matrix_source)
266
+ H = source_concentrations[:, 0].reshape(h, w)
267
+ H = np.exp(-1 * H)
268
+ return H
269
+
270
+
utils_preprocessing.py ADDED
@@ -0,0 +1,335 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import pandas as pd
3
+ import matplotlib.pyplot as plt
4
+ import openslide
5
+ from PIL import Image
6
+
7
+ import cv2
8
+ import torch
9
+ from torch import nn
10
+ import torchvision
11
+ #from torchvision.models import resnet50
12
+ import torchvision.transforms as transforms
13
+ # from transformers import ViTImageProcessor, ViTModel
14
+ # from timm.models.vision_transformer import VisionTransformer
15
+ # import timm
16
+ from ctrans_model import CTransPath
17
+
18
+ import utils_color_norm
19
+ color_norm = utils_color_norm.macenko_normalizer()
20
+
21
+ ## check available device
22
+ device = (torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu'))
23
+ print("device:", device)
24
+ ##======================================================================================================
25
+ class resnet50_feature_extraction(nn.Module):
26
+ def __init__(self, model_type="load_from_saved_file"):
27
+ super().__init__()
28
+
29
+ if model_type == "load_from_internet":
30
+ self.resnet = resnet50(weights=torchvision.models.ResNet50_Weights.IMAGENET1K_V2)
31
+ elif model_type == "load_from_saved_file":
32
+ self.resnet = resnet50(weights=None)
33
+ else:
34
+ print("cannot find model_type can only be load_from_internet or load_from_saved_file")
35
+
36
+
37
+ def forward(self, x):
38
+ x = self.resnet.conv1(x)
39
+ x = self.resnet.bn1(x)
40
+ x = self.resnet.relu(x)
41
+ x = self.resnet.maxpool(x)
42
+
43
+ x = self.resnet.layer1(x)
44
+ x = self.resnet.layer2(x)
45
+ x = self.resnet.layer3(x)
46
+ x = self.resnet.layer4(x)
47
+
48
+ x = self.resnet.avgpool(x)
49
+ x = torch.flatten(x, 1)
50
+ return x
51
+
52
+ ##======================================================================================================
53
+ def evaluate_tile_edge(img_np, edge_mag_thrsh, edge_fraction_thrsh):
54
+
55
+ select = 1 ## initial value
56
+
57
+ #img_np = np.array(img_RGB)
58
+ tile_size = img_np.shape[0]
59
+
60
+ ##---------------------------------------
61
+ ## 0) exclude if edge_mag > 0.5
62
+ img_gray=cv2.cvtColor(img_np, cv2.COLOR_RGB2GRAY)
63
+
64
+ # Remove noise using a Gaussian filter
65
+ #img_gray = cv2.GaussianBlur(img_gray, (5,5), 0)
66
+
67
+ sobelx = cv2.Sobel(img_gray, cv2.CV_32F, 1, 0)
68
+ sobely = cv2.Sobel(img_gray, cv2.CV_32F, 0, 1)
69
+
70
+ sobelx1 = cv2.convertScaleAbs(sobelx)
71
+ sobely1 = cv2.convertScaleAbs(sobely)
72
+
73
+ mag = cv2.addWeighted(sobelx1, 0.5, sobely1, 0.5, 0)
74
+
75
+ unique, counts = np.unique(mag, return_counts=True)
76
+
77
+ edge_mag = counts[np.argwhere(unique < edge_mag_thrsh)].sum()/(tile_size*tile_size)
78
+
79
+ if edge_mag > edge_fraction_thrsh:
80
+ select = 0
81
+
82
+ return select
83
+
84
+ ##======================================================================================================
85
+ def evaluate_tile_color(img_np,black_thrsh,black_pct_thrsh,blue_level_thrsh,red_level_thrsh,
86
+ H_min,H_max,S_min,S_max,V_min,V_max,select):
87
+
88
+ #img_np = np.array(img_RGB)
89
+
90
+ L, A, B = cv2.split(cv2.cvtColor((img_np), cv2.COLOR_RGB2LAB))
91
+
92
+ ##---------------------------------------
93
+ ## 1) remove if percentage of black spot > 0.01
94
+ black_pct = np.mean(L < black_thrsh)
95
+ if black_pct > black_pct_thrsh:
96
+ select = 0
97
+ return select
98
+ ##---------------------------------------
99
+ ## 2) remove if too blue (heavy mark), or too red (blood)
100
+ red,green,blue = np.mean(img_np[:,:,0]),np.mean(img_np[:,:,1]),np.mean(img_np[:,:,2])
101
+ blue_level = blue/(red + green)
102
+ blue_level2 = blue*blue_level
103
+
104
+ if blue_level2 > blue_level_thrsh:
105
+ select = 0
106
+ return select
107
+
108
+ ##---
109
+ red_level = red/(green + blue)
110
+ red_level2 = red*red_level
111
+
112
+ if red_level2 > red_level_thrsh:
113
+ select = 0
114
+ return select
115
+
116
+ ##---------------------------------------
117
+ ## 3) remove if tile has the same color suggested (using color detection)
118
+ H,S,V = cv2.split(cv2.cvtColor(img_np, cv2.COLOR_RGB2HSV))
119
+ H,S,V = np.mean(H),np.mean(S),np.mean(V)
120
+
121
+ if (H_min <= H and H <= H_max and S_min <= S and S <= S_max and V_min <= V and V <= V_max):
122
+ select = 0
123
+ return select
124
+
125
+ return select
126
+
127
+ ##================================================================================================
128
+ def slide2tiles(path2slide, slide_name, slide_file_name, mag_assumed, mag_selected, tile_size,
129
+ mask_downsampling,edge_mag_thrsh,edge_fraction_thrsh,save_tile_file,
130
+ path2mask,path2coordinates):
131
+
132
+ ## open slide
133
+ slide = openslide.OpenSlide(f"{path2slide}{slide_file_name}")
134
+
135
+ ## magnification max
136
+ if openslide.PROPERTY_NAME_OBJECTIVE_POWER in slide.properties:
137
+ mag_max = slide.properties[openslide.PROPERTY_NAME_OBJECTIVE_POWER]
138
+ print("mag_max:", mag_max)
139
+ mag_original = mag_max
140
+ else:
141
+ print("[WARNING] mag not found, assuming: {mag_assumed}")
142
+ mag_max = mag_assumed
143
+ mag_original = 0
144
+
145
+ ## downsample_level
146
+ downsampling = int(int(mag_max)/mag_selected)
147
+ print(f"downsampling: {downsampling}")
148
+
149
+
150
+ mask_tile_size = int(np.ceil(tile_size/mask_downsampling))
151
+ #print("mask_tile_size:", mask_tile_size)
152
+
153
+ ##------------------------------------------------------------------
154
+ ## slide partitioning
155
+ ## slide size at largest level (level=0)
156
+ px0, py0 = slide.level_dimensions[0]
157
+ tile_size0 = int(tile_size*downsampling)
158
+ print(f"px0: {px0}, py0: {py0}, tile_size0: {tile_size0}")
159
+
160
+ n_rows,n_cols = int(py0/tile_size0), int(px0/tile_size0)
161
+ print(f"n_rows: {n_rows}, n_cols: {n_cols}")
162
+
163
+ n_tiles_total = n_rows*n_cols
164
+ print(f"n_tiles_total: {n_tiles_total}")
165
+
166
+ ##-----------------------
167
+ img_mask = np.full((int((n_rows)*mask_tile_size),int((n_cols)*mask_tile_size),3),255).astype(np.uint8)
168
+ mask = np.full((int((n_rows)*mask_tile_size),int((n_cols)*mask_tile_size),3),255).astype(np.uint8)
169
+
170
+ i_tile = 0
171
+ tiles_list = []
172
+
173
+ col_list = []
174
+ row_list = []
175
+ i_tile_list = []
176
+ for row in range(n_rows):
177
+ print(f"row: {row}/{n_rows}")
178
+ for col in range(n_cols):
179
+
180
+ tile = slide.read_region((col*tile_size0, row*tile_size0),\
181
+ level=0, size=[tile_size0, tile_size0]).convert("RGB") ## RGBA image --> RGB
182
+
183
+ if tile.size[0] == tile_size0 and tile.size[1] == tile_size0:
184
+ # downsample to target tile size
185
+ tile = tile.resize((tile_size, tile_size))
186
+
187
+ mask_tile = np.array(tile.resize((mask_tile_size, mask_tile_size)))
188
+
189
+ img_mask[int(row*mask_tile_size):int((row+1)*mask_tile_size),\
190
+ int(col*mask_tile_size):int((col+1)*mask_tile_size),:] = mask_tile
191
+
192
+ tile = np.array(tile)
193
+ #print(tile.shape)
194
+
195
+ ## evaluate tile
196
+ select = evaluate_tile_edge(tile, edge_mag_thrsh, edge_fraction_thrsh)
197
+
198
+ if select == 1:
199
+ ## 2022.09.08: color normalization:
200
+ tile_norm = Image.fromarray(color_norm.transform(tile))
201
+
202
+ mask_tile_norm = np.array(tile_norm.resize((mask_tile_size, mask_tile_size)))
203
+
204
+ mask[int(row*mask_tile_size):int((row+1)*mask_tile_size),\
205
+ int(col*mask_tile_size):int((col+1)*mask_tile_size),:] = mask_tile_norm
206
+
207
+ #tiles_list.append(np.array(tile_norm).astype(np.uint8))
208
+ tiles_list.append(tile_norm)
209
+
210
+ if save_tile_file:
211
+ tile_name = "tile_" + str(row).zfill(5)+"_" + str(col).zfill(5) + "_" \
212
+ + str(i_tile).zfill(5) + "_" + str(downsampling).zfill(3)
213
+
214
+ tile_norm.save(f"{tile_folder}/{tile_name}.png")
215
+
216
+ ## 2023.05.27: tile information
217
+ col_list.append(col)
218
+ row_list.append(row)
219
+ i_tile_list.append(i_tile)
220
+
221
+ i_tile += 1
222
+
223
+ ## 2023.05.27: save tile coordinates:
224
+ downsampling_list = [downsampling]*len(row_list)
225
+ df_coordinates = pd.DataFrame({"row": row_list, "col": col_list, "i_tile": i_tile_list, "downsampling": downsampling})
226
+ df_coordinates.to_csv(f"{path2coordinates}{slide_name}.csv", index_label="tile_idx")
227
+
228
+ ##======================================================================================================
229
+ ## plot: draw color lines on the mask
230
+ line_color = [0,255,0]
231
+
232
+ n_tiles = len(tiles_list)
233
+
234
+ img_mask[:,::mask_tile_size,:] = line_color
235
+ img_mask[::mask_tile_size,:,:] = line_color
236
+ mask[:,::mask_tile_size,:] = line_color
237
+ mask[::mask_tile_size,:,:] = line_color
238
+
239
+ fig, ax = plt.subplots(1,2,figsize=(30,15))
240
+ ax[0].imshow(img_mask)
241
+ ax[1].imshow(mask)
242
+
243
+ ax[0].set_title(f"{slide_name}, mag_original: {mag_original}, mag_assumed: {mag_assumed}")
244
+ ax[1].set_title(f"n_rows: {n_rows}, n_cols: {n_cols}, n_tiles_total: {n_tiles_total}, n_tiles_selected: {n_tiles}")
245
+
246
+ plt.tight_layout(h_pad=0.4, w_pad=0.5)
247
+ plt.savefig(f"{path2mask}{slide_name}.pdf", format="pdf", dpi=50)
248
+ plt.close()
249
+
250
+ img_mask = 0 ; mask = 0
251
+
252
+ print("completed cleaning")
253
+
254
+ return tiles_list
255
+
256
+ ##======================================================================================================
257
+ def tile_transform(tiles_list, data_mean, data_std):
258
+ data_transform = transforms.Compose([transforms.Resize(224),
259
+ transforms.ToTensor(),
260
+ transforms.Normalize(mean=data_mean, std=data_std)])
261
+
262
+ ## data transform:
263
+ n_tiles = len(tiles_list)
264
+ print("n_tiles:", n_tiles)
265
+
266
+ tiles = []
267
+ for i in range(n_tiles):
268
+ tiles.append(data_transform(tiles_list[i]).unsqueeze(0))
269
+ tiles = torch.cat(tiles, dim=0)
270
+ print("tiles.shape:", tiles.shape)
271
+ tiles_list = 0
272
+
273
+ return tiles ## [n_tiles,3,224,224]
274
+
275
+ ##================================================================================================
276
+ def tiles2features(tiles_list, model_name, batch_size):
277
+
278
+ ##----------------------------------------
279
+ ## model config
280
+ if model_name == "vit":
281
+ path2model = "../vit-base-patch16-224-in21k"
282
+ model = ViTModel.from_pretrained(path2model)
283
+ model.to(device)
284
+ data_mean=[0.5, 0.5, 0.5] ; data_std = [0.5, 0.5, 0.5]
285
+
286
+ if model_name == "dino":
287
+ path2model = "../dino_vit_small_patch16_ep200.pt"
288
+ model = VisionTransformer(img_size=224, patch_size=16,
289
+ embed_dim=384, num_heads=6, num_classes=0)
290
+ model.to(device)
291
+ model.load_state_dict(torch.load(path2model,map_location=device))
292
+ data_mean=[0.485, 0.456, 0.406] ; data_std = [0.229, 0.224, 0.225]
293
+
294
+ if model_name == "ctrans":
295
+ path2model = "../ctranspath.pth"
296
+ model = CTransPath(num_classes=0)
297
+ model.to(device)
298
+ model.load_state_dict(torch.load(path2model)['model'])
299
+ model = model.cpu()
300
+ data_mean=[0.485, 0.456, 0.406] ; data_std = [0.229, 0.224, 0.225]
301
+
302
+ model.eval()
303
+
304
+ ## tile transform
305
+ tiles = tile_transform(tiles_list, data_mean, data_std)
306
+
307
+ ## extract features from tiles
308
+ n_tiles = tiles.shape[0]
309
+ features = []
310
+ for idx_start in range(0, n_tiles, batch_size):
311
+ idx_end = idx_start + min(batch_size, n_tiles - idx_start)
312
+
313
+ with torch.no_grad():
314
+ y = model(tiles[idx_start:idx_end])
315
+
316
+ if model_name == "vit":
317
+ y = y.last_hidden_state[:, 0]
318
+
319
+ features.append(y.detach().cpu().numpy())
320
+
321
+ features = np.concatenate(features)
322
+ print("features.shape:", features.shape)
323
+
324
+ return features
325
+ ##================================================================================================
326
+ def init_random_seed(random_seed=42):
327
+ # Python RNG
328
+ np.random.seed(random_seed)
329
+
330
+ # Torch RNG
331
+ torch.manual_seed(random_seed)
332
+ torch.cuda.manual_seed(random_seed)
333
+ torch.cuda.manual_seed_all(random_seed)
334
+ torch.backends.cudnn.deterministic = True
335
+ torch.backends.cudnn.benchmark = False