{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# CLAM\n\nNOTE: Some of the descriptions or images are cited from: https://github.com/mahmoodlab/CLAM\n\n![img](https://github.com/mahmoodlab/CLAM/raw/master/docs/CLAM2.jpg)\n\n## TL;DR:\n\n+ CLAM is a high-throughput and interpretable method for data efficient whole slide image (WSI) classification using slide-level labels without any ROI extraction or patch-level annotations, and is capable of handling multi-class subtyping problems. Tested on three different WSI datasets, trained models adapt to independent test cohorts of WSI resections and biopsies as well as smartphone microscopy images (photomicrographs).\n+ paper: https://arxiv.org/abs/2004.09666\n\n## How to apply CLAM on the STRIP AI dataset ?\n\n+ I prepared four notebooks for pre-process, train and inference:\n\n### pre-process\n\n+ <b>&gt; THIS NOTEBOOK &lt;</b> (1) image generation: https://www.kaggle.com/code/fx6300/clam-strip-ai-image-generation\n+ (2) feature extraction: https://www.kaggle.com/code/fx6300/clam-strip-ai-feature-extraction\n\n### train\n\n+ (3) train: https://www.kaggle.com/code/fx6300/clam-strip-ai-train\n\n### inference\n\n+ (4) inference: https://www.kaggle.com/code/fx6300/clam-strip-ai-inference\n\n## How to visualize the attention generated by CLAM ?\n\n+ I prepared an example:\n  + https://www.kaggle.com/fx6300/clam-strip-ai-attention-heatmap\n\n## NOTE\n\n+ The source code from CLAM (https://github.com/mahmoodlab/CLAM) is licensed under GPLv3 and available for non-commercial academic purposes.","metadata":{}},{"cell_type":"code","source":"!conda install ../input/how-to-use-pyvips-offline/*.tar.bz2\n!pip install tiling\n!pip install rembg","metadata":{"execution":{"iopub.status.busy":"2022-09-26T16:59:56.102201Z","iopub.execute_input":"2022-09-26T16:59:56.102625Z","iopub.status.idle":"2022-09-26T17:00:56.446437Z","shell.execute_reply.started":"2022-09-26T16:59:56.102592Z","shell.execute_reply":"2022-09-26T17:00:56.444922Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport gc\nimport cv2\nimport time\nimport random\nimport joblib\nimport numpy as np \nimport pandas as pd \nfrom tqdm.notebook import tqdm\nimport warnings\nfrom PIL import Image\nimport pyvips\nimport skimage.exposure\nfrom tiling import ConstStrideTiles\nfrom rembg import remove\nwarnings.filterwarnings(\"ignore\")","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":3.221842,"end_time":"2022-07-08T22:23:18.429026","exception":false,"start_time":"2022-07-08T22:23:15.207184","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-09-26T17:01:07.543552Z","iopub.execute_input":"2022-09-26T17:01:07.545487Z","iopub.status.idle":"2022-09-26T17:01:38.00863Z","shell.execute_reply.started":"2022-09-26T17:01:07.545394Z","shell.execute_reply":"2022-09-26T17:01:38.007473Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"debug = False\ngenerate_new = True\ntrain_df = pd.read_csv(\"../input/mayo-clinic-strip-ai/train.csv\").head(10 if debug else 1000)\ntest_df = pd.read_csv(\"../input/mayo-clinic-strip-ai/test.csv\")\ndirs = [\"../input/mayo-clinic-strip-ai/train/\", \"../input/mayo-clinic-strip-ai/test/\"]","metadata":{"papermill":{"duration":0.026694,"end_time":"2022-07-08T22:23:18.458885","exception":false,"start_time":"2022-07-08T22:23:18.432191","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-09-26T17:01:38.011352Z","iopub.execute_input":"2022-09-26T17:01:38.011887Z","iopub.status.idle":"2022-09-26T17:01:38.052602Z","shell.execute_reply.started":"2022-09-26T17:01:38.011826Z","shell.execute_reply":"2022-09-26T17:01:38.051108Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def tiling(img):\n    h, w = img.height, img.width\n    tiles = ConstStrideTiles(image_size=(h, w), tile_size=(512, 512), stride=(512, 512), \n                             origin=(0, 0),\n                             scale=1.0,\n                             include_nodata=True)\n    print(\"Number of tiles: %i\" % len(tiles))\n    imgs = []\n    for extent, out_size in tiles:\n        x, y, width, height = extent\n        data = img.crop(x, y, width, height)\n        imgs.append(data)\n    return imgs","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:01:38.054537Z","iopub.execute_input":"2022-09-26T17:01:38.055495Z","iopub.status.idle":"2022-09-26T17:01:38.066068Z","shell.execute_reply.started":"2022-09-26T17:01:38.055431Z","shell.execute_reply":"2022-09-26T17:01:38.064118Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def resize_image(img: Image, width) -> Image:\n    return pyvips2pil(img.thumbnail_image(width))","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:01:38.067887Z","iopub.execute_input":"2022-09-26T17:01:38.068348Z","iopub.status.idle":"2022-09-26T17:01:38.080244Z","shell.execute_reply.started":"2022-09-26T17:01:38.068307Z","shell.execute_reply":"2022-09-26T17:01:38.078551Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def resize_image_with_canvas(img: Image, width) -> Image:\n    canvas = Image.new(\"RGB\", (width, width), \"white\")\n    img2 = pyvips2pil(img.thumbnail_image(width))\n    canvas.paste(img2, (0, 0))\n    return canvas","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:01:38.083064Z","iopub.execute_input":"2022-09-26T17:01:38.083641Z","iopub.status.idle":"2022-09-26T17:01:38.09415Z","shell.execute_reply.started":"2022-09-26T17:01:38.083604Z","shell.execute_reply":"2022-09-26T17:01:38.093079Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def bg_trim(_img: Image) -> Image:\n    img = np.array(_img)\n    yidx = []\n    xidx = []\n    for y in range(img.shape[0]):\n        if sum(sum(255 - img[y,:])) < 10:\n            yidx += [y]\n    for x in range(img.shape[1]):\n        if sum(sum(255 - img[:,x])) < 10:\n            xidx += [x]\n    img = np.delete(img, xidx, axis=1)\n    img = np.delete(img, yidx, axis=0)\n    return Image.fromarray(img)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:01:38.095601Z","iopub.execute_input":"2022-09-26T17:01:38.096169Z","iopub.status.idle":"2022-09-26T17:01:38.108408Z","shell.execute_reply.started":"2022-09-26T17:01:38.096132Z","shell.execute_reply":"2022-09-26T17:01:38.107066Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pyvips2pil(img: pyvips.Image) -> Image:\n    # RGB -> BGR\n    imgBGR = cv2.cvtColor(img.numpy(), cv2.COLOR_RGB2BGR)\n    return Image.fromarray(imgBGR)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:01:38.110642Z","iopub.execute_input":"2022-09-26T17:01:38.11152Z","iopub.status.idle":"2022-09-26T17:01:38.1236Z","shell.execute_reply.started":"2022-09-26T17:01:38.111468Z","shell.execute_reply":"2022-09-26T17:01:38.122252Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def pil2pyvips(img: Image) -> pyvips.Image:\n    # BGR -> RGB\n    imgRGB = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)\n    return pyvips.Image.new_from_array(imgRGB)","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:01:38.125705Z","iopub.execute_input":"2022-09-26T17:01:38.12617Z","iopub.status.idle":"2022-09-26T17:01:38.136014Z","shell.execute_reply.started":"2022-09-26T17:01:38.126131Z","shell.execute_reply":"2022-09-26T17:01:38.135048Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def my_remove(img: Image) -> Image:\n    img = remove(img) # PIL -> PIL\n    B, G, R, A = img.split()\n    alpha = np.array(A) / 255\n    R = (255 * (1 - alpha) + np.array(R) * alpha).astype(np.uint8)\n    G = (255 * (1 - alpha) + np.array(G) * alpha).astype(np.uint8)\n    B = (255 * (1 - alpha) + np.array(B) * alpha).astype(np.uint8)\n    return cv2.merge((B, G, R))","metadata":{"execution":{"iopub.status.busy":"2022-09-26T17:01:38.137718Z","iopub.execute_input":"2022-09-26T17:01:38.138495Z","iopub.status.idle":"2022-09-26T17:01:38.155189Z","shell.execute_reply.started":"2022-09-26T17:01:38.138457Z","shell.execute_reply":"2022-09-26T17:01:38.153305Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if(generate_new):\n    if not os.path.exists(\"./train/\"):\n        os.mkdir(\"./train/\")\n    if not os.path.exists(\"./test/\"):\n        os.mkdir(\"./test/\")\n    if not os.path.exists(\"./train/4096-tiles-v4\"):\n        os.mkdir(\"./train/4096-tiles-v4\")\n    if not os.path.exists(\"./test/4096-tiles-v4\"):\n        os.mkdir(\"./test/4096-tiles-v4\")\n    for i in tqdm(range(test_df.shape[0])):\n        img_id = test_df.iloc[i].image_id\n        img = pyvips.Image.new_from_file(dirs[1] + img_id + \".tif\", access='sequential')\n        img = resize_image(img, 4096) # pyvips -> PIL\n        img = my_remove(img) # PIL -> PIL\n        img = bg_trim(img) # PIL -> PIL\n        img = resize_image_with_canvas(pil2pyvips(img), 4096) # PIL -> pyvips -> PIL\n        cv2.imwrite(f\"./test/4096-tiles-v4/{img_id}.jpg\", np.array(img))\n        imgs = tiling(pyvips.Image.new_from_array(img)) # retain BGR order\n        for i, _img in enumerate(imgs):\n            cv2.imwrite(f\"./test/4096-tiles-v4/{img_id}_{i}.jpg\", _img.numpy()) # BGR\n        del imgs\n        gc.collect()\n    for i in tqdm(range(train_df.shape[0])):\n        img_id = train_df.iloc[i].image_id\n        img = pyvips.Image.new_from_file(dirs[0] + img_id + \".tif\", access='sequential')\n        img = resize_image(img, 4096) # pyvips -> PIL\n        img = my_remove(img) # PIL -> PIL\n        img = bg_trim(img) # PIL -> PIL\n        img = resize_image_with_canvas(pil2pyvips(img), 4096) # PIL -> pyvips -> PIL\n        cv2.imwrite(f\"./train/4096-tiles-v4/{img_id}.jpg\", np.array(img))\n        imgs = tiling(pyvips.Image.new_from_array(img)) # retain BGR order\n        for i, _img in enumerate(imgs):\n            cv2.imwrite(f\"./train/4096-tiles-v4/{img_id}_{i}.jpg\", _img.numpy()) # BGR\n        del imgs\n        gc.collect()","metadata":{"papermill":{"duration":0.01374,"end_time":"2022-07-08T22:23:18.475237","exception":false,"start_time":"2022-07-08T22:23:18.461497","status":"completed"},"tags":[],"execution":{"iopub.status.busy":"2022-09-26T17:01:38.157066Z","iopub.execute_input":"2022-09-26T17:01:38.157757Z","iopub.status.idle":"2022-09-26T17:03:31.271468Z","shell.execute_reply.started":"2022-09-26T17:01:38.157714Z","shell.execute_reply":"2022-09-26T17:03:31.268746Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}