{
  "id": 159476,
  "title": "Microscope augmentation",
  "url": "/competitions/siim-isic-melanoma-classification/discussion/159476",
  "author_name": "Roman",
  "post_date": "2020-06-17T14:59:08.524000",
  "votes": 77,
  "comment_count": 29,
  "views": 0,
  "content": "<p>Going through the dataset and looking at the images I've noticed that some of the have a black areas around the center circle of the image, like those images were taken through the microscope.</p>\n\n<p>Here are just a few examples from the dataset:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2F4603040c7965099739ddb2e31a3df3b8%2Fdatasets_701123_1225697_train_train_ISIC_0000036_downsampled.jpg?generation=1592405347628414&amp;alt=media\" alt=\"\">\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fc05e026a746b3eb8c126c31510902423%2Fdatasets_701123_1225697_train_train_ISIC_0000140_downsampled.jpg?generation=1592405347205545&amp;alt=media\" alt=\"\">\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fdb4078e4e073acffa35cab14a0231725%2Fdatasets_701123_1225697_train_train_ISIC_0000004.jpg?generation=1592405346486890&amp;alt=media\" alt=\"\"></p>\n\n<p>That gave me an idea to create the same type of augmentation for the images. It is basically still the cutout, but reworked for this particular task.</p>\n\n<p>Here is a code:\n```\nclass Microscope:\n    def <strong>init</strong>(self, p: float = 0.5):\n        self.p = p</p>\n\n<pre><code>def __call__(self, img):\n    if random.random() &amp;lt; self.p:\n        circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                    (img.shape[0]//2, img.shape[1]//2),\n                    random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                    (0, 0, 0),\n                    -1)\n\n        mask = circle - 255\n        img = np.multiply(img, mask)\n\n    return img\n\ndef __repr__(self):\n    return f'{self.__class__.__name__}(p={self.p})'\n</code></pre>\n\n<p>```</p>\n\n<p>And the result of this augmentation apply:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fcab2acbe978d20b162ae454136c262ac%2Fmicro_aug.png?generation=1592405923559019&amp;alt=media\" alt=\"\"></p>",
  "messages": [
    {
      "id": 890545,
      "postDate": "2020-06-17T14:59:08.523Z",
      "content": "<p>Going through the dataset and looking at the images I've noticed that some of the have a black areas around the center circle of the image, like those images were taken through the microscope.</p>\n\n<p>Here are just a few examples from the dataset:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2F4603040c7965099739ddb2e31a3df3b8%2Fdatasets_701123_1225697_train_train_ISIC_0000036_downsampled.jpg?generation=1592405347628414&amp;alt=media\" alt=\"\">\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fc05e026a746b3eb8c126c31510902423%2Fdatasets_701123_1225697_train_train_ISIC_0000140_downsampled.jpg?generation=1592405347205545&amp;alt=media\" alt=\"\">\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fdb4078e4e073acffa35cab14a0231725%2Fdatasets_701123_1225697_train_train_ISIC_0000004.jpg?generation=1592405346486890&amp;alt=media\" alt=\"\"></p>\n\n<p>That gave me an idea to create the same type of augmentation for the images. It is basically still the cutout, but reworked for this particular task.</p>\n\n<p>Here is a code:\n```\nclass Microscope:\n    def <strong>init</strong>(self, p: float = 0.5):\n        self.p = p</p>\n\n<pre><code>def __call__(self, img):\n    if random.random() &amp;lt; self.p:\n        circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                    (img.shape[0]//2, img.shape[1]//2),\n                    random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                    (0, 0, 0),\n                    -1)\n\n        mask = circle - 255\n        img = np.multiply(img, mask)\n\n    return img\n\ndef __repr__(self):\n    return f'{self.__class__.__name__}(p={self.p})'\n</code></pre>\n\n<p>```</p>\n\n<p>And the result of this augmentation apply:\n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fcab2acbe978d20b162ae454136c262ac%2Fmicro_aug.png?generation=1592405923559019&amp;alt=media\" alt=\"\"></p>",
      "rawMarkdown": "Going through the dataset and looking at the images I've noticed that some of the have a black areas around the center circle of the image, like those images were taken through the microscope.\n\nHere are just a few examples from the dataset:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2F4603040c7965099739ddb2e31a3df3b8%2Fdatasets_701123_1225697_train_train_ISIC_0000036_downsampled.jpg?generation=1592405347628414&amp;alt=media)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fc05e026a746b3eb8c126c31510902423%2Fdatasets_701123_1225697_train_train_ISIC_0000140_downsampled.jpg?generation=1592405347205545&amp;alt=media)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fdb4078e4e073acffa35cab14a0231725%2Fdatasets_701123_1225697_train_train_ISIC_0000004.jpg?generation=1592405346486890&amp;alt=media)\n\nThat gave me an idea to create the same type of augmentation for the images. It is basically still the cutout, but reworked for this particular task.\n\nHere is a code:\n```\nclass Microscope:\n    def __init__(self, p: float = 0.5):\n        self.p = p\n\n    def __call__(self, img):\n        if random.random() &lt; self.p:\n            circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                        (img.shape[0]//2, img.shape[1]//2),\n                        random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                        (0, 0, 0),\n                        -1)\n\n            mask = circle - 255\n            img = np.multiply(img, mask)\n        \n        return img\n\n    def __repr__(self):\n        return f'{self.__class__.__name__}(p={self.p})'\n```\n\nAnd the result of this augmentation apply:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fcab2acbe978d20b162ae454136c262ac%2Fmicro_aug.png?generation=1592405923559019&amp;alt=media)\n",
      "votes": 77
    },
    {
      "id": 900271,
      "postDate": "2020-06-24T17:59:22.880Z",
      "content": "<p>Hi. Thanks for sharing. For those, who use albumentations, here is the code for this library.\n```\nimport albumentations as A\nclass Microscope(A.ImageOnlyTransform):\n    def <strong>init</strong>(self, p: float = 0.5, always_apply=False):\n        super().<strong>init</strong>(always_apply, p)</p>\n\n<pre><code>def apply(self, img, **params):\n    if random.random() &lt; self.p:\n        circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                    (img.shape[0]//2, img.shape[1]//2),\n                    random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                    (0, 0, 0),\n                    -1)\n\n        mask = circle - 255\n        img = np.multiply(img, mask)\n\n    return img\n</code></pre>\n\n<p>```</p>",
      "rawMarkdown": "Hi. Thanks for sharing. For those, who use albumentations, here is the code for this library.\n```\nimport albumentations as A\nclass Microscope(A.ImageOnlyTransform):\n    def __init__(self, p: float = 0.5, always_apply=False):\n        super().__init__(always_apply, p)\n\n    def apply(self, img, **params):\n        if random.random() &lt; self.p:\n            circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                        (img.shape[0]//2, img.shape[1]//2),\n                        random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                        (0, 0, 0),\n                        -1)\n\n            mask = circle - 255\n            img = np.multiply(img, mask)\n\n        return img\n```",
      "votes": 19
    },
    {
      "id": 955342,
      "postDate": "2020-08-02T15:08:16.197Z",
      "content": "<p>nice to see this kind of great work</p>",
      "rawMarkdown": "nice to see this kind of great work",
      "votes": 1
    },
    {
      "id": 913853,
      "postDate": "2020-07-03T13:29:43.937Z",
      "content": "<p>Just a parallel thought. Are the microsope-like images currently adding bias in training data or testing ? For instance when the doctor's think there's a high chance of being cancer, they would check it under microscope. And our models will get biased towards this. So whenever it sees a microscope like image it will predict cancer</p>",
      "rawMarkdown": "Just a parallel thought. Are the microsope-like images currently adding bias in training data or testing ? For instance when the doctor's think there's a high chance of being cancer, they would check it under microscope. And our models will get biased towards this. So whenever it sees a microscope like image it will predict cancer",
      "votes": 1
    },
    {
      "id": 924570,
      "postDate": "2020-07-11T14:04:10.670Z",
      "content": "<p>fyi: The black circle is caused by the limited field-of-view of the dermatoscope (which in essence is an in-vivo \"microscope\" with ~10x magnification). <strong>All</strong> images were taken with a dermatoscope - but you won't see that black circle in most as the camera is zoomed in.\nSo, indirectly, seeing this black circle could provide information about the <strong>real size</strong> of the skin lesion. Unfortunately, not seeing it, and not having a ruler within the image, makes is hard to impossible to infer the real size in others.</p>",
      "rawMarkdown": "fyi: The black circle is caused by the limited field-of-view of the dermatoscope (which in essence is an in-vivo \"microscope\" with ~10x magnification). **All** images were taken with a dermatoscope - but you won't see that black circle in most as the camera is zoomed in.\nSo, indirectly, seeing this black circle could provide information about the **real size** of the skin lesion. Unfortunately, not seeing it, and not having a ruler within the image, makes is hard to impossible to infer the real size in others.",
      "votes": 2
    },
    {
      "id": 896873,
      "postDate": "2020-06-22T13:50:27.460Z",
      "content": "<p>Has anyone improved its LB score using this augmentation? I augmented my 5-fold CV from 0.9025 to 0.9122 (the standard deviation of CV scores decreased) but LB has dropped from 0.915 to 0.902. The variance of my CV folds is quite high, so I assume it might be because of bad luck... </p>",
      "rawMarkdown": "Has anyone improved its LB score using this augmentation? I augmented my 5-fold CV from 0.9025 to 0.9122 (the standard deviation of CV scores decreased) but LB has dropped from 0.915 to 0.902. The variance of my CV folds is quite high, so I assume it might be because of bad luck... ",
      "votes": 2,
      "replies": [
        {
          "id": 896923,
          "postDate": "2020-06-22T14:22:25.417Z",
          "content": "<p>Depends on how you use it. First I replaced Cutout with Microscope augmentation and my score slightly dropped. But then I combined those two and my score raised up.</p>",
          "rawMarkdown": "Depends on how you use it. First I replaced Cutout with Microscope augmentation and my score slightly dropped. But then I combined those two and my score raised up.",
          "votes": 3
        },
        {
          "id": 959812,
          "postDate": "2020-08-05T23:09:15.180Z",
          "content": "<p><a href=\"/nroman\">@nroman</a> are you still using Microscope?  Did you see my comments on your pytorch notebook about the way you are doing averaging?  I have been using Microscope in a \"cocktail\" with cutout and coarse dropout:</p>\n\n<p><code>\n    A.OneOf([A.CoarseDropout(max_holes=8, min_holes=3, max_height=param['image_size'][0] // 10, max_width=param['image_size'][0] // 10, p=0.5),\n         A.Cutout(num_holes=1, max_h_size=param['image_size'][0] // 3, max_w_size=param['image_size'][0] // 3, p=0.25),\n         Microscope(p=0.25),], p=1.0),\n</code></p>",
          "rawMarkdown": "@nroman are you still using Microscope?  Did you see my comments on your pytorch notebook about the way you are doing averaging?  I have been using Microscope in a \"cocktail\" with cutout and coarse dropout:\n\n```\n    A.OneOf([A.CoarseDropout(max_holes=8, min_holes=3, max_height=param['image_size'][0] // 10, max_width=param['image_size'][0] // 10, p=0.5),\n         A.Cutout(num_holes=1, max_h_size=param['image_size'][0] // 3, max_w_size=param['image_size'][0] // 3, p=0.25),\n         Microscope(p=0.25),], p=1.0),\n```"
        },
        {
          "id": 960115,
          "postDate": "2020-08-06T06:37:44.003Z",
          "content": "<p><a href=\"/brianfeeny\">@brianfeeny</a> \nYes I did and in fact I answered you there in a comments.</p>",
          "rawMarkdown": "@brianfeeny \nYes I did and in fact I answered you there in a comments.\n"
        }
      ]
    },
    {
      "id": 969476,
      "postDate": "2020-08-13T17:54:07.623Z",
      "content": "<p>Great Work Mate!</p>",
      "rawMarkdown": "Great Work Mate!"
    },
    {
      "id": 969195,
      "postDate": "2020-08-13T14:39:13.200Z",
      "content": "<p>Microscope augmentation transform for <code>fastai</code> using only <code>pytorch</code>  <br>\n<strong>now with black and white background</strong></p>\n<pre><code># 2.5 hours\ndef mic_transform(img, p=0.3):\n    '''\n    in:\n        img: NxNx3 torch.tensor containing image\n    out:\n        maskd_img: NxNx3 torch.tensor containing mic augmented image\n    '''\n    # Get image size\n    if torch.rand(1).item() &lt; p:\n        im_size = img.shape[-1]\n\n        # Generating circle mask\n        xy = torch.arange(im_size) \n        grid_x, grid_y = torch.meshgrid(xy, xy)\n        circle = (grid_x - im_size//2) ** 2 + (grid_y - im_size//2) ** 2\n        mean_coordinate = circle[0, im_size//2]\n        mask = torch.logical_xor(circle &lt; 0, circle &gt; mean_coordinate).type(torch.int)\n\n        # Mask invertion\n        mask = torch.abs(mask - 1) \n\n        # unsqueeze mask into (3,im_size,im_size) shape\n        new_mask = torch.rand((3,im_size,im_size))\n        for i in range(3):\n            new_mask[i] = mask\n\n        img = img * new_mask\n\n        # make corners wight\n        if torch.rand(1).item() &lt; 0.5:\n            img = img + (torch.zeros((3,im_size,im_size))+255) * torch.abs(mask - 1)\n    return img\n\nmic_trans = TfmPixel(mic_transform)\n</code></pre>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2919655%2F64d9daa0688e94739d74834cb2a46b62%2Fjust%20image.png?generation=1597391248791719&amp;alt=media\" alt=\"\"></p>",
      "rawMarkdown": "Microscope augmentation transform for `fastai` using only `pytorch`  \n**now with black and white background**\n\n```python\n# 2.5 hours\ndef mic_transform(img, p=0.3):\n    '''\n    in:\n        img: NxNx3 torch.tensor containing image\n    out:\n        maskd_img: NxNx3 torch.tensor containing mic augmented image\n    '''\n    # Get image size\n    if torch.rand(1).item() < p:\n        im_size = img.shape[-1]\n\n        # Generating circle mask\n        xy = torch.arange(im_size) \n        grid_x, grid_y = torch.meshgrid(xy, xy)\n        circle = (grid_x - im_size//2) ** 2 + (grid_y - im_size//2) ** 2\n        mean_coordinate = circle[0, im_size//2]\n        mask = torch.logical_xor(circle < 0, circle > mean_coordinate).type(torch.int)\n\n        # Mask invertion\n        mask = torch.abs(mask - 1) \n\n        # unsqueeze mask into (3,im_size,im_size) shape\n        new_mask = torch.rand((3,im_size,im_size))\n        for i in range(3):\n            new_mask[i] = mask\n\n        img = img * new_mask\n        \n        # make corners wight\n        if torch.rand(1).item() < 0.5:\n            img = img + (torch.zeros((3,im_size,im_size))+255) * torch.abs(mask - 1)\n    return img\n    \nmic_trans = TfmPixel(mic_transform)\n```\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2919655%2F64d9daa0688e94739d74834cb2a46b62%2Fjust%20image.png?generation=1597391248791719&alt=media)"
    },
    {
      "id": 949028,
      "postDate": "2020-07-28T11:47:45.217Z",
      "content": "<p>good work!</p>",
      "rawMarkdown": "good work!"
    },
    {
      "id": 947728,
      "postDate": "2020-07-27T12:58:04.707Z",
      "content": "<p>wooow so interesting field,good job👍 </p>",
      "rawMarkdown": "wooow so interesting field,good job👍 "
    },
    {
      "id": 943786,
      "postDate": "2020-07-24T15:24:26.057Z",
      "content": "<p>Facinating</p>",
      "rawMarkdown": "Facinating"
    },
    {
      "id": 892463,
      "postDate": "2020-06-18T22:43:16.843Z",
      "content": "<p>Great augmentations, however I always struggle gettin these augmentations into a tensorflow pipeline, do you have an idea on how to do it ?</p>",
      "rawMarkdown": "Great augmentations, however I always struggle gettin these augmentations into a tensorflow pipeline, do you have an idea on how to do it ?",
      "replies": [
        {
          "id": 893186,
          "postDate": "2020-06-19T12:48:48.853Z",
          "content": "<p>Create a custom ImageGenerator by subclassing a DataGenerator. Then in your pipeline, pass the np.array containing your image into an albumentation class. \nHave a look at this link: <a href=\"https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly\">https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly</a></p>",
          "rawMarkdown": "Create a custom ImageGenerator by subclassing a DataGenerator. Then in your pipeline, pass the np.array containing your image into an albumentation class. \nHave a look at this link: https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly\n",
          "votes": 1
        },
        {
          "id": 893198,
          "postDate": "2020-06-19T12:58:26.667Z",
          "content": "<p><a href=\"/rftexas\">@rftexas</a> nice one.\n<a href=\"/aziz69\">@aziz69</a> if you're following to make a custom generator using <code>tf 2.x</code> for advance augmentation, you can check my notebook here, <a href=\"https://www.kaggle.com/ipythonx/tf-keras-melanoma-classification-starter-tabnet\">https://www.kaggle.com/ipythonx/tf-keras-melanoma-classification-starter-tabnet</a>.</p>\n\n<p>For modeling with the image data, I made custom single <code>tf.keras</code> generator for both the training phase and prediction phase. The library of <code>albumentation</code> and <code>img_aug</code> both are shown how to apply on these pipelines. Hope this will help.</p>",
          "rawMarkdown": "@rftexas nice one.\n@aziz69 if you're following to make a custom generator using `tf 2.x` for advance augmentation, you can check my notebook here, https://www.kaggle.com/ipythonx/tf-keras-melanoma-classification-starter-tabnet.\n\nFor modeling with the image data, I made custom single `tf.keras` generator for both the training phase and prediction phase. The library of `albumentation` and `img_aug` both are shown how to apply on these pipelines. Hope this will help.",
          "votes": 1
        },
        {
          "id": 893399,
          "postDate": "2020-06-19T15:19:27.577Z",
          "content": "<p>thanks I'll try it !</p>",
          "rawMarkdown": "thanks I'll try it !",
          "votes": 1
        },
        {
          "id": 893400,
          "postDate": "2020-06-19T15:19:40.480Z",
          "rawMarkdown": "",
          "isDeleted": true
        }
      ]
    },
    {
      "id": 892192,
      "postDate": "2020-06-18T17:51:43.137Z",
      "content": "<p>wah, now the model will feel, it is embedded in a microscope 😄 </p>",
      "rawMarkdown": "wah, now the model will feel, it is embedded in a microscope 😄 "
    },
    {
      "id": 891839,
      "postDate": "2020-06-18T13:35:00.587Z",
      "content": "<p>Interesting!😄 </p>",
      "rawMarkdown": "Interesting!😄 "
    },
    {
      "id": 891198,
      "postDate": "2020-06-18T02:26:32.143Z",
      "content": "<p>Interesting! \nBy the way, you really enjoy sharing!</p>",
      "rawMarkdown": "Interesting! \nBy the way, you really enjoy sharing!"
    },
    {
      "id": 891122,
      "postDate": "2020-06-17T23:20:10.637Z",
      "content": "<p>Great idea! Your ideas for the augmentations are soo good!</p>",
      "rawMarkdown": "Great idea! Your ideas for the augmentations are soo good!"
    },
    {
      "id": 891007,
      "postDate": "2020-06-17T20:23:35.217Z",
      "content": "<p>haha, fun idea</p>",
      "rawMarkdown": "haha, fun idea"
    },
    {
      "id": 890738,
      "postDate": "2020-06-17T17:03:54.297Z",
      "content": "<p><a href=\"/nroman\">@nroman</a> can I use this in my notebook? This solves the \"mystery\" I was puzzled about :-D.\nHow about circle crop as an alternative - it works the same way as microscope augmentations.</p>",
      "rawMarkdown": "@nroman can I use this in my notebook? This solves the \"mystery\" I was puzzled about :-D.\nHow about circle crop as an alternative - it works the same way as microscope augmentations.",
      "replies": [
        {
          "id": 892204,
          "postDate": "2020-06-18T17:56:19.283Z",
          "content": "<p>Of course you can. Everything that is publicly shared is an open source, so you can do whatever you want with this code.</p>",
          "rawMarkdown": "Of course you can. Everything that is publicly shared is an open source, so you can do whatever you want with this code.",
          "votes": 1
        }
      ]
    },
    {
      "id": 890607,
      "postDate": "2020-06-17T15:47:56.100Z",
      "content": "<p>Great work!</p>",
      "rawMarkdown": "Great work!",
      "replies": [
        {
          "id": 890621,
          "postDate": "2020-06-17T15:54:13.477Z",
          "content": "<p>Hey, L.\nCurrently commiting a new version of <a href=\"https://www.kaggle.com/nroman/melanoma-pytorch-starter-efficientnet\">my kernel </a> which uses this augmentation. Will see the result in a few hours.</p>",
          "rawMarkdown": "Hey, L.\nCurrently commiting a new version of [my kernel ](https://www.kaggle.com/nroman/melanoma-pytorch-starter-efficientnet) which uses this augmentation. Will see the result in a few hours.",
          "votes": 3
        },
        {
          "id": 890711,
          "postDate": "2020-06-17T16:47:36.027Z",
          "content": "<p>L..lol ! xD\nThanks for the fast reply,\nLooking forward for the kernel!</p>",
          "rawMarkdown": "L..lol ! xD\nThanks for the fast reply,\nLooking forward for the kernel!"
        },
        {
          "id": 891983,
          "postDate": "2020-06-18T15:19:39.470Z",
          "content": "<p>Hi, have you finished running your kernel?😁 Any improvement to the score?</p>",
          "rawMarkdown": "Hi, have you finished running your kernel?😁 Any improvement to the score?"
        }
      ]
    }
  ],
  "comments": [
    {
      "id": 900271,
      "author_name": "Aykhan.py [dsmlkz]",
      "author_url": "",
      "post_date": "2020-06-24T17:59:22.880000",
      "content": "<p>Hi. Thanks for sharing. For those, who use albumentations, here is the code for this library.\n```\nimport albumentations as A\nclass Microscope(A.ImageOnlyTransform):\n    def <strong>init</strong>(self, p: float = 0.5, always_apply=False):\n        super().<strong>init</strong>(always_apply, p)</p>\n\n<pre><code>def apply(self, img, **params):\n    if random.random() &lt; self.p:\n        circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                    (img.shape[0]//2, img.shape[1]//2),\n                    random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                    (0, 0, 0),\n                    -1)\n\n        mask = circle - 255\n        img = np.multiply(img, mask)\n\n    return img\n</code></pre>\n\n<p>```</p>",
      "votes": 19,
      "replies": []
    },
    {
      "id": 955342,
      "author_name": "Nilay Desmukh",
      "author_url": "",
      "post_date": "2020-08-02T15:08:16.197000",
      "content": "<p>nice to see this kind of great work</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 913853,
      "author_name": "adityapatil",
      "author_url": "",
      "post_date": "2020-07-03T13:29:43.937000",
      "content": "<p>Just a parallel thought. Are the microsope-like images currently adding bias in training data or testing ? For instance when the doctor's think there's a high chance of being cancer, they would check it under microscope. And our models will get biased towards this. So whenever it sees a microscope like image it will predict cancer</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 924570,
      "author_name": "chdlr",
      "author_url": "",
      "post_date": "2020-07-11T14:04:10.670000",
      "content": "<p>fyi: The black circle is caused by the limited field-of-view of the dermatoscope (which in essence is an in-vivo \"microscope\" with ~10x magnification). <strong>All</strong> images were taken with a dermatoscope - but you won't see that black circle in most as the camera is zoomed in.\nSo, indirectly, seeing this black circle could provide information about the <strong>real size</strong> of the skin lesion. Unfortunately, not seeing it, and not having a ruler within the image, makes is hard to impossible to infer the real size in others.</p>",
      "votes": 2,
      "replies": []
    },
    {
      "id": 896873,
      "author_name": "PAB97",
      "author_url": "",
      "post_date": "2020-06-22T13:50:27.460000",
      "content": "<p>Has anyone improved its LB score using this augmentation? I augmented my 5-fold CV from 0.9025 to 0.9122 (the standard deviation of CV scores decreased) but LB has dropped from 0.915 to 0.902. The variance of my CV folds is quite high, so I assume it might be because of bad luck... </p>",
      "votes": 2,
      "replies": [
        {
          "id": 896923,
          "author_name": "Roman",
          "author_url": "",
          "post_date": "2020-06-22T14:22:25.417000",
          "content": "<p>Depends on how you use it. First I replaced Cutout with Microscope augmentation and my score slightly dropped. But then I combined those two and my score raised up.</p>",
          "votes": 3,
          "replies": []
        },
        {
          "id": 959812,
          "author_name": "Signal",
          "author_url": "",
          "post_date": "2020-08-05T23:09:15.180000",
          "content": "<p><a href=\"/nroman\">@nroman</a> are you still using Microscope?  Did you see my comments on your pytorch notebook about the way you are doing averaging?  I have been using Microscope in a \"cocktail\" with cutout and coarse dropout:</p>\n\n<p><code>\n    A.OneOf([A.CoarseDropout(max_holes=8, min_holes=3, max_height=param['image_size'][0] // 10, max_width=param['image_size'][0] // 10, p=0.5),\n         A.Cutout(num_holes=1, max_h_size=param['image_size'][0] // 3, max_w_size=param['image_size'][0] // 3, p=0.25),\n         Microscope(p=0.25),], p=1.0),\n</code></p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 960115,
          "author_name": "Roman",
          "author_url": "",
          "post_date": "2020-08-06T06:37:44.003000",
          "content": "<p><a href=\"/brianfeeny\">@brianfeeny</a> \nYes I did and in fact I answered you there in a comments.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 969476,
      "author_name": "Raoof Naushad",
      "author_url": "",
      "post_date": "2020-08-13T17:54:07.623000",
      "content": "<p>Great Work Mate!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 969195,
      "author_name": "Ivan Void",
      "author_url": "",
      "post_date": "2020-08-13T14:39:13.200000",
      "content": "<p>Microscope augmentation transform for <code>fastai</code> using only <code>pytorch</code>  <br>\n<strong>now with black and white background</strong></p>\n<pre><code># 2.5 hours\ndef mic_transform(img, p=0.3):\n    '''\n    in:\n        img: NxNx3 torch.tensor containing image\n    out:\n        maskd_img: NxNx3 torch.tensor containing mic augmented image\n    '''\n    # Get image size\n    if torch.rand(1).item() &lt; p:\n        im_size = img.shape[-1]\n\n        # Generating circle mask\n        xy = torch.arange(im_size) \n        grid_x, grid_y = torch.meshgrid(xy, xy)\n        circle = (grid_x - im_size//2) ** 2 + (grid_y - im_size//2) ** 2\n        mean_coordinate = circle[0, im_size//2]\n        mask = torch.logical_xor(circle &lt; 0, circle &gt; mean_coordinate).type(torch.int)\n\n        # Mask invertion\n        mask = torch.abs(mask - 1) \n\n        # unsqueeze mask into (3,im_size,im_size) shape\n        new_mask = torch.rand((3,im_size,im_size))\n        for i in range(3):\n            new_mask[i] = mask\n\n        img = img * new_mask\n\n        # make corners wight\n        if torch.rand(1).item() &lt; 0.5:\n            img = img + (torch.zeros((3,im_size,im_size))+255) * torch.abs(mask - 1)\n    return img\n\nmic_trans = TfmPixel(mic_transform)\n</code></pre>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2919655%2F64d9daa0688e94739d74834cb2a46b62%2Fjust%20image.png?generation=1597391248791719&amp;alt=media\" alt=\"\"></p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 949028,
      "author_name": "Stacy",
      "author_url": "",
      "post_date": "2020-07-28T11:47:45.217000",
      "content": "<p>good work!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 947728,
      "author_name": "leilaSoleymani",
      "author_url": "",
      "post_date": "2020-07-27T12:58:04.707000",
      "content": "<p>wooow so interesting field,good job👍 </p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 943786,
      "author_name": "Nicolas",
      "author_url": "",
      "post_date": "2020-07-24T15:24:26.057000",
      "content": "<p>Facinating</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 892463,
      "author_name": "Aziz_Belaweid",
      "author_url": "",
      "post_date": "2020-06-18T22:43:16.843000",
      "content": "<p>Great augmentations, however I always struggle gettin these augmentations into a tensorflow pipeline, do you have an idea on how to do it ?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 893186,
          "author_name": "PAB97",
          "author_url": "",
          "post_date": "2020-06-19T12:48:48.853000",
          "content": "<p>Create a custom ImageGenerator by subclassing a DataGenerator. Then in your pipeline, pass the np.array containing your image into an albumentation class. \nHave a look at this link: <a href=\"https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly\">https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly</a></p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 893198,
          "author_name": "Innat",
          "author_url": "",
          "post_date": "2020-06-19T12:58:26.667000",
          "content": "<p><a href=\"/rftexas\">@rftexas</a> nice one.\n<a href=\"/aziz69\">@aziz69</a> if you're following to make a custom generator using <code>tf 2.x</code> for advance augmentation, you can check my notebook here, <a href=\"https://www.kaggle.com/ipythonx/tf-keras-melanoma-classification-starter-tabnet\">https://www.kaggle.com/ipythonx/tf-keras-melanoma-classification-starter-tabnet</a>.</p>\n\n<p>For modeling with the image data, I made custom single <code>tf.keras</code> generator for both the training phase and prediction phase. The library of <code>albumentation</code> and <code>img_aug</code> both are shown how to apply on these pipelines. Hope this will help.</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 893399,
          "author_name": "Aziz_Belaweid",
          "author_url": "",
          "post_date": "2020-06-19T15:19:27.577000",
          "content": "<p>thanks I'll try it !</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 893400,
          "author_name": "",
          "author_url": "",
          "post_date": "2020-06-19T15:19:40.480000",
          "content": "",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 892192,
      "author_name": "Innat",
      "author_url": "",
      "post_date": "2020-06-18T17:51:43.137000",
      "content": "<p>wah, now the model will feel, it is embedded in a microscope 😄 </p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 891839,
      "author_name": "xiaopeng",
      "author_url": "",
      "post_date": "2020-06-18T13:35:00.587000",
      "content": "<p>Interesting!😄 </p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 891198,
      "author_name": "gakki",
      "author_url": "",
      "post_date": "2020-06-18T02:26:32.143000",
      "content": "<p>Interesting! \nBy the way, you really enjoy sharing!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 891122,
      "author_name": "Jan Malin",
      "author_url": "",
      "post_date": "2020-06-17T23:20:10.637000",
      "content": "<p>Great idea! Your ideas for the augmentations are soo good!</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 891007,
      "author_name": "Chris Deotte",
      "author_url": "",
      "post_date": "2020-06-17T20:23:35.217000",
      "content": "<p>haha, fun idea</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 890738,
      "author_name": "Trigram",
      "author_url": "",
      "post_date": "2020-06-17T17:03:54.297000",
      "content": "<p><a href=\"/nroman\">@nroman</a> can I use this in my notebook? This solves the \"mystery\" I was puzzled about :-D.\nHow about circle crop as an alternative - it works the same way as microscope augmentations.</p>",
      "votes": 0,
      "replies": [
        {
          "id": 892204,
          "author_name": "Roman",
          "author_url": "",
          "post_date": "2020-06-18T17:56:19.283000",
          "content": "<p>Of course you can. Everything that is publicly shared is an open source, so you can do whatever you want with this code.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 890607,
      "author_name": "",
      "author_url": "",
      "post_date": "2020-06-17T15:47:56.100000",
      "content": "<p>Great work!</p>",
      "votes": 0,
      "replies": [
        {
          "id": 890621,
          "author_name": "Roman",
          "author_url": "",
          "post_date": "2020-06-17T15:54:13.477000",
          "content": "<p>Hey, L.\nCurrently commiting a new version of <a href=\"https://www.kaggle.com/nroman/melanoma-pytorch-starter-efficientnet\">my kernel </a> which uses this augmentation. Will see the result in a few hours.</p>",
          "votes": 3,
          "replies": []
        },
        {
          "id": 890711,
          "author_name": "",
          "author_url": "",
          "post_date": "2020-06-17T16:47:36.027000",
          "content": "<p>L..lol ! xD\nThanks for the fast reply,\nLooking forward for the kernel!</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 891983,
          "author_name": "Jan Malin",
          "author_url": "",
          "post_date": "2020-06-18T15:19:39.470000",
          "content": "<p>Hi, have you finished running your kernel?😁 Any improvement to the score?</p>",
          "votes": 0,
          "replies": []
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "890545": "Going through the dataset and looking at the images I've noticed that some of the have a black areas around the center circle of the image, like those images were taken through the microscope.\n\nHere are just a few examples from the dataset:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2F4603040c7965099739ddb2e31a3df3b8%2Fdatasets_701123_1225697_train_train_ISIC_0000036_downsampled.jpg?generation=1592405347628414&amp;alt=media)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fc05e026a746b3eb8c126c31510902423%2Fdatasets_701123_1225697_train_train_ISIC_0000140_downsampled.jpg?generation=1592405347205545&amp;alt=media)\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fdb4078e4e073acffa35cab14a0231725%2Fdatasets_701123_1225697_train_train_ISIC_0000004.jpg?generation=1592405346486890&amp;alt=media)\n\nThat gave me an idea to create the same type of augmentation for the images. It is basically still the cutout, but reworked for this particular task.\n\nHere is a code:\n```\nclass Microscope:\n    def __init__(self, p: float = 0.5):\n        self.p = p\n\n    def __call__(self, img):\n        if random.random() &lt; self.p:\n            circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                        (img.shape[0]//2, img.shape[1]//2),\n                        random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                        (0, 0, 0),\n                        -1)\n\n            mask = circle - 255\n            img = np.multiply(img, mask)\n        \n        return img\n\n    def __repr__(self):\n        return f'{self.__class__.__name__}(p={self.p})'\n```\n\nAnd the result of this augmentation apply:\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1696976%2Fcab2acbe978d20b162ae454136c262ac%2Fmicro_aug.png?generation=1592405923559019&amp;alt=media)\n",
    "900271": "Hi. Thanks for sharing. For those, who use albumentations, here is the code for this library.\n```\nimport albumentations as A\nclass Microscope(A.ImageOnlyTransform):\n    def __init__(self, p: float = 0.5, always_apply=False):\n        super().__init__(always_apply, p)\n\n    def apply(self, img, **params):\n        if random.random() &lt; self.p:\n            circle = cv2.circle((np.ones(img.shape) * 255).astype(np.uint8),\n                        (img.shape[0]//2, img.shape[1]//2),\n                        random.randint(img.shape[0]//2 - 3, img.shape[0]//2 + 15),\n                        (0, 0, 0),\n                        -1)\n\n            mask = circle - 255\n            img = np.multiply(img, mask)\n\n        return img\n```",
    "955342": "nice to see this kind of great work",
    "913853": "Just a parallel thought. Are the microsope-like images currently adding bias in training data or testing ? For instance when the doctor's think there's a high chance of being cancer, they would check it under microscope. And our models will get biased towards this. So whenever it sees a microscope like image it will predict cancer",
    "924570": "fyi: The black circle is caused by the limited field-of-view of the dermatoscope (which in essence is an in-vivo \"microscope\" with ~10x magnification). **All** images were taken with a dermatoscope - but you won't see that black circle in most as the camera is zoomed in.\nSo, indirectly, seeing this black circle could provide information about the **real size** of the skin lesion. Unfortunately, not seeing it, and not having a ruler within the image, makes is hard to impossible to infer the real size in others.",
    "896873": "Has anyone improved its LB score using this augmentation? I augmented my 5-fold CV from 0.9025 to 0.9122 (the standard deviation of CV scores decreased) but LB has dropped from 0.915 to 0.902. The variance of my CV folds is quite high, so I assume it might be because of bad luck... ",
    "969476": "Great Work Mate!",
    "969195": "Microscope augmentation transform for `fastai` using only `pytorch`  \n**now with black and white background**\n\n```python\n# 2.5 hours\ndef mic_transform(img, p=0.3):\n    '''\n    in:\n        img: NxNx3 torch.tensor containing image\n    out:\n        maskd_img: NxNx3 torch.tensor containing mic augmented image\n    '''\n    # Get image size\n    if torch.rand(1).item() < p:\n        im_size = img.shape[-1]\n\n        # Generating circle mask\n        xy = torch.arange(im_size) \n        grid_x, grid_y = torch.meshgrid(xy, xy)\n        circle = (grid_x - im_size//2) ** 2 + (grid_y - im_size//2) ** 2\n        mean_coordinate = circle[0, im_size//2]\n        mask = torch.logical_xor(circle < 0, circle > mean_coordinate).type(torch.int)\n\n        # Mask invertion\n        mask = torch.abs(mask - 1) \n\n        # unsqueeze mask into (3,im_size,im_size) shape\n        new_mask = torch.rand((3,im_size,im_size))\n        for i in range(3):\n            new_mask[i] = mask\n\n        img = img * new_mask\n        \n        # make corners wight\n        if torch.rand(1).item() < 0.5:\n            img = img + (torch.zeros((3,im_size,im_size))+255) * torch.abs(mask - 1)\n    return img\n    \nmic_trans = TfmPixel(mic_transform)\n```\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F2919655%2F64d9daa0688e94739d74834cb2a46b62%2Fjust%20image.png?generation=1597391248791719&alt=media)",
    "949028": "good work!",
    "947728": "wooow so interesting field,good job👍 ",
    "943786": "Facinating",
    "892463": "Great augmentations, however I always struggle gettin these augmentations into a tensorflow pipeline, do you have an idea on how to do it ?",
    "892192": "wah, now the model will feel, it is embedded in a microscope 😄 ",
    "891839": "Interesting!😄 ",
    "891198": "Interesting! \nBy the way, you really enjoy sharing!",
    "891122": "Great idea! Your ideas for the augmentations are soo good!",
    "891007": "haha, fun idea",
    "890738": "@nroman can I use this in my notebook? This solves the \"mystery\" I was puzzled about :-D.\nHow about circle crop as an alternative - it works the same way as microscope augmentations.",
    "890607": "Great work!"
  }
}