{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"#coding: utf-8","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# constants\nTILE_WIDTH = 256\nTILE_HEIGHT = 256","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nimg_dir = '/kaggle/input/prostate-cancer-grade-assessment/test_images'\n\nis_test_phase = os.path.exists(img_dir)\n\nif is_test_phase:\n    img_name_list = sorted(os.listdir(img_dir))\nelse:\n    # テストデータが見えない (=submit前) 場合は訓練画像をいくつか取ってきて使う\n    img_dir = os.path.join(os.path.dirname(img_dir), 'train_images')\n    img_name_list = os.listdir(img_dir)[:5]\n    \nimg_path_list = [os.path.join(img_dir, s) for s in img_name_list]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"if not is_test_phase:\n    print('\\n'.join(img_path_list))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import time","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import numpy as np\nimport cv2\n\ndef fill_black_holes(img):\n    _img = img.copy()\n    contour, _ = cv2.findContours(_img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)\n    for cnt in contour:\n        cv2.drawContours(_img, [cnt], 0, 255, -1)\n    return _img\n    \ndef fill_white_holes(img):\n    return 255 - fill_black_holes(255 - img)\n\ndef crop_tiles(original_img, tile_width, tile_height, is_test_phase=True):\n    # 色々考えたのですが，マスクの作成はグレースケール変換して大津法で2値化して穴を埋める，という方法がいい気がしています．\n    \n    gray = cv2.cvtColor(original_img, cv2.COLOR_BGR2GRAY)\n    thresh_val, thresh_img = cv2.threshold(gray, 0, 255, cv2.THRESH_OTSU)\n    mask = fill_white_holes(thresh_img)\n    \n    target_region_points = np.where(mask == 0) # 注: ここでは背景が 255, 前景が 0 になっている (前景のほうが暗いので)\n    upperleft_y, upperleft_x = (target_region_points[i].min() for i in range(2))\n    lowerright_y, lowerright_x = (target_region_points[i].max() for i in range(2))\n    \n    img = original_img[upperleft_y : lowerright_y + 1, upperleft_x : lowerright_x + 1]\n    mask = mask[upperleft_y : lowerright_y + 1, upperleft_x : lowerright_x + 1]\n    \n    h, w = img.shape[:2]\n    pad_x = (tile_width - w % tile_width) % tile_width\n    pad_y = (tile_height - h % tile_height) % tile_height\n    \n    img = np.pad(img, [[0, pad_y], [0, pad_x], [0, 0]], 'constant', constant_values=255)\n    mask = np.pad(mask, [[0, pad_y], [0, pad_x]], 'constant', constant_values=0)\n    \n    num_tiles_y = img.shape[0] // tile_height\n    num_tiles_x = img.shape[1] // tile_width\n    tile_list = [None] * (num_tiles_y * num_tiles_x)\n    \n    # 予測の結果である2次元配列 (縦 num_tiles_y x 横 num_tiles_x) を，全行を横並びにして1次元にしたもの\n    if not is_test_phase:\n        prediction_list = [-1] * (num_tiles_y * num_tiles_x)\n    \n    tile_id = 0\n    for tile_id_y in range(num_tiles_y):\n        upperleft_y = tile_id_y * tile_height\n\n        for tile_id_x in range(num_tiles_x):\n            upperleft_x = tile_id_x * tile_width\n            \n            tile_img = img[upperleft_y : upperleft_y + tile_height, upperleft_x : upperleft_x + tile_width]\n            tile_mask = mask[upperleft_y : upperleft_y + tile_height, upperleft_x : upperleft_x + tile_width]\n            \n            if np.count_nonzero(tile_mask) > int(tile_mask.size * 0.5):\n                # 5割以上背景のタイルは無視\n                if not is_test_phase:\n                    prediction_list[tile_id_y * num_tiles_x + tile_id_x] = 0\n            else:\n                tile_list[tile_id] = tile_img\n                tile_id += 1 \n    \n    if not is_test_phase:\n        return np.array(tile_list[:tile_id]), prediction_list, (num_tiles_y, num_tiles_x)\n    return np.array(tile_list[:tile_id])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import openslide\n\ndef load_img(tiff_path, slide_level=1):\n    slide = openslide.OpenSlide(tiff_path)\n    img = np.array(slide.read_region((0, 0), slide_level, slide.level_dimensions[slide_level]))\n    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from torch import nn\n\nimport sys\nsys.path.append('/kaggle/input/efficientnetpytorch/')\nfrom efficientnet_pytorch.model import EfficientNet\n\nclass Enet(nn.Module):\n    def __init__(self, model_name, num_classes, use_pretrained_models):\n        super(Enet, self).__init__()\n        self.enet = EfficientNet.from_pretrained(model_name) if use_pretrained_models else EfficientNet.from_name(model_name)\n        # self.enet.load_state_dict(torch.load(pretrained_model[backbone]))\n        self.enet._fc = nn.Linear(self.enet._fc.in_features, num_classes)\n\n    def forward(self, x):\n        return self.enet(x)\n\n    def load_parameters(self, parameters_path):\n        self.load_state_dict(torch.load(parameters_path, map_location=device))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = Enet('efficientnet-b0', 5, False)\nmodel.load_parameters('../input/saved-parameters/net_epoch_0009.pth')\nmodel.to(device)\nmodel.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def gleason_map_to_isup(gmap):\n    '''\n        引数 gmap: 2次元の numpy 配列 (ndarray)．各要素は領域ごとの Gleason スコア (3-5) または 0 (背景)，1 (違う組織), 2 (良性組織)．\n        出力 isup_grade: ISUP グレード (以下のサンプルでは適当に 0 にしている)\n        \n        処理内容: \n        (1) gmap の要素のうち，もっとも多く含まれている要素 (a) と2番めに多く含まれている要素 (b) を調べる．\n        (2) a と b の値に応じて，ISUP グレードを計算して出力する．\n            - a + b == 6 ならば 1\n            - a == 3, b == 4 ならば 2\n            - a == 4, b == 3 ならば 3\n            - a + b == 8 ならば 4\n            - a + b == 9 または 10 ならば 5\n            - それ以外は 0\n        \n        ※(1) で，gmap の要素が一種類しかなければ，a と b は同じ値とする．\n        ※(1) で，もっとも多く含まれている要素が 2 種類以上ある場合は，値が大きいものから 2 つ選んで a, b とする．\n        ※(1) では 0 (背景)，1 (違う組織)，2 (良性組織) は無視する．gmap に 0, 1, 2 以外の要素が含まれなければ，(2) に関係なく 0 を返す．\n\n    '''\n    \n\n    X = np.count_nonzero(gmap == 3)\n    Y = np.count_nonzero(gmap == 4)\n    Z = np.count_nonzero(gmap == 5)\n\n    if X == 0 and Y == 0 and Z == 0:\n        isup_grade = 0\n\n    elif Y == 0 and Z == 0:\n        isup_grade = 1\n\n    elif X > Y > Z:\n        isup_grade = 2\n\n    elif Y >= X > Z:\n        isup_grade = 3\n\n    elif (Z >= Y and X > Y) or (X == 0 and Z == 0):\n        isup_grade = 4\n\n    elif (Z >= X and Y >= X) or (X == 0 and Y == 0):\n        isup_grade = 5\n    \n    else:\n        isup_grade = 0\n    \n\n    \n    return isup_grade","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"img_ID_list = [] #keep the img_IDs\nisup_G_list = [] #keep the isup_grades\nID_append = img_ID_list.append\nisup_append = isup_G_list.append\n# because List => pd.frame is faster\nwith torch.no_grad():\n    for img_no, img_path in enumerate(img_path_list, start=1):\n        img_ID = os.path.basename(img_path).split('.')[0] #The ID of the image added to submission.csv\n        if not is_test_phase:\n            print('Testing {} ... ({} / {})'.format(os.path.basename(img_path), img_no, len(img_path_list)))\n            loop_start_time = time.time()\n\n        img = load_img(img_path)\n        _cropped_tiles = crop_tiles(img, tile_width=TILE_WIDTH, tile_height=TILE_HEIGHT, is_test_phase=is_test_phase)\n        if is_test_phase:\n            tiles = _cropped_tiles\n        else:\n            tiles, pred_list, img_shape = _cropped_tiles\n        \n        if len(tiles) == 0:\n            isup_grade = 0\n        else:\n            tiles = tiles.astype(np.float) / 255.0\n            tiles = ((tiles - [0.485, 0.456, 0.406]) / [0.229, 0.224, 0.225]).transpose([0, 3, 1, 2])\n            tiles_tensor = torch.from_numpy(tiles)\n            tiles_tensor = tiles_tensor.float().to(device)\n\n            if tiles_tensor.shape[0] > 256:\n                # 大きすぎるとメモリに乗らなくなったりしそうなので分割\n                num_split = tiles_tensor.shape[0] // 256 + 1\n                tensor_list = [None] * num_split\n\n                for _i in range(num_split):\n                    tensor_list[_i] = model(tiles_tensor[_i * 256 : min((_i + 1) * 256, tiles_tensor.shape[0])])\n\n                output_tensor = torch.cat(tensor_list)\n            else:\n                output_tensor = model(tiles_tensor)\n\n            output_prob = nn.Softmax(dim=1)(model(tiles_tensor)).cpu()\n            _, classes = torch.max(output_prob, dim=1)\n            classes.add(1)\n\n            if is_test_phase:\n                # 実は2次元配列にする必要はなくて，背景のところも含める必要はない (無視される) ので，テストのときは時間短縮のためにモデルの出力をそのまま入れる\n                isup_grade = gleason_map_to_isup(classes.numpy())\n            else:\n                # デバッグ用に整形\n                _i = 0\n                for i, pred in enumerate(pred_list):\n                    if pred != 0:\n                        pred_list[i] = classes[_i].item()\n                        _i += 1\n\n                gleason_map = np.reshape(pred_list, img_shape)\n                isup_grade = gleason_map_to_isup(gleason_map)\n        \n        ID_append(img_ID) #add ID to the list\n        isup_append(isup_grade) # add isup grade to the list\n        print('image_id: {}'.format(img_ID))\n        print('ISUP grade: {}'.format(isup_grade))\n            \n\n        if not is_test_phase:\n            print('Done. Elapsed time: {:.2f}[s]\\n'.format(time.time() - loop_start_time))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"converting two colums `img_ID_list` and `isup_G_list` to the pandas frame, next making the submission data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import pandas as pd\nsubmission_data = pd.DataFrame(\n                    data={'image_id': img_ID_list, 'isup_grade': isup_G_list},\n                    columns=['image_id', 'isup_grade']\n                )\n\nsubmission_data.to_csv('submission.csv', index=False)\n# if is_test_phase:\n#     submission_data.to_csv('submission.csv', index=False)\n# else:\n#     print('submission_data\\n {}'.format(submission_data.head()))","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":4}