File size: 4,234 Bytes
e5a19d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import os
import torch
import numpy as np
from PIL import Image
from utils import *
from tqdm import tqdm
from gradcam import GradCAM, GradCAMpp
from overlay_image import overlay_numpy

DISEASES = [ 'Atelectasis',
             'Cardiomegaly',
             'Effusion',
             'Infiltration',
             'Mass',
             'Nodule',
             'Pneumonia',
             'Pneumothorax',
             'Consolidation',
             'Edema',
             'Emphysema',
             'Fibrosis',
             'Pleural Thickening',
             'Hernia' ]

class GradCamGenerator:
    def __init__(self, model_path, layer, overlay=False):
        self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
        self.model = self.load_model(model_path)
        self.layer = layer
        self.overlay = overlay # Overlay GC heatmaps with image
        self.layer_module = self.model.get_submodule(layer)
        self.gc_model2 = GradCAM(self.model, self.layer_module)  # , device_ids=self.device)

    def load_model(self, model_path, print_net=False):
        checkpoint = torch.load(model_path, map_location=self.device)
        model = checkpoint['model']
        self.set_inplace_False(model)
        if print_net:
            print(model)
        return model

    def set_inplace_False(self, module):
        for layer in module._modules.values():
            if isinstance(layer, nn.ReLU):
                layer.inplace = False
            self.set_inplace_False(layer)

    def generate_grad_cam(self, path):
        img = self.pil_loader(path, 3)
        input_image = self.transform_pil_to_tensor(img)
        tclass = self.target_from_path(path)
        #tmp_pred = self.model(input_image)
        grayscale_cams = self.gc_model2(input=input_image, class_idx=tclass)
        attribution = 255*grayscale_cams[0].detach().cpu().numpy().squeeze()
        attribution /= attribution.max()
        if self.overlay:
            overlay_numpy(img, attribution, path)
            #print()
        return attribution

    def target_from_path(self, path):
        disease = path.split('/')[-2]
        indx = DISEASES.index(disease) if disease!='No Finding' else 0
        return torch.tensor(indx, device=self.device)

    def save_img(self, image, input_path):
        gc_filename = input_path[:-4]+'_gc'+input_path[-4:]
        image_PIL = Image.fromarray(image).convert('L')
        image_PIL.save(gc_filename)

    def transform_pil_to_tensor(self, pil_image):
        # ImageNet mean and std
        mean = [0.485, 0.456, 0.406]
        std = [0.229, 0.224, 0.225]

        # transformation to be applied
        transform = transforms.Compose([
            transforms.Resize(224),
            transforms.ToTensor(),
            transforms.Normalize(mean, std)
        ])
        tensor = transform(pil_image).to(self.device)
        return tensor.unsqueeze(0)

    def pil_loader(self, path, n_channels):
        # open path as file to avoid ResourceWarning (https://github.com/python-pillow/Pillow/issues/835)
        with open(path, 'rb') as f:
            img = Image.open(f)
            if n_channels == 1:
                return img.convert('L')
            elif n_channels == 3:
                return img.convert('RGB')
            else:
                raise ValueError('Invalid value for parameter n_channels!')

def create_GC_from_folder(path, classifier='checkpoint', layer_name='features.norm5', overlay=True, override_gc=True):
    GC = GradCamGenerator(classifier, layer_name, overlay=overlay)
    folds = ['data/' + i for i in os.listdir(path) if 'No Finding' not in i][10:]
    for cf in folds:
        files = [cf+'/'+f for f in os.listdir(cf) if ('_gc' not in f and 'overlay' not in f and
                                                      (not os.path.exists(cf+'/'+f[:-4]+'_gc.png') or override_gc) and
                                                      (not os.path.exists(cf+'/'+f[:-4]+'_overlay.png') or not overlay))]
        for cfil in tqdm(files):
            GC.generate_grad_cam(cfil)
            #GCmap = GC.generate_grad_cam(cfil)
            #GC.save_img(GCmap, cfil)

if __name__=='__main__':
    import matplotlib.pyplot as plt
    path = 'data/'
    create_GC_from_folder(path)