{
  "id": 285623,
  "title": "Getting per pixel scores from the detectron",
  "url": "/competitions/sartorius-cell-instance-segmentation/discussion/285623",
  "author_name": "Slawek Biel",
  "post_date": "2021-11-05T14:14:07.209000",
  "votes": 53,
  "comment_count": 26,
  "views": 0,
  "content": "<p>In the notebooks from <a href=\"https://www.kaggle.com/julian3833\" target=\"_blank\">@julian3833</a> and <a href=\"https://www.kaggle.com/rluethy\" target=\"_blank\">@rluethy</a> I've noticed that they use scores for every pixel to decide whether to include it in the mask or not. Whereas my detectron model only outputs a binary mask and a single score per object. So I went digging in the code and found that this information gets discarded during postprocessing. I didn't see any elegant way to expose it, but luckily python makes it easy to monkeypatch stuff, so bellow you can see a snippet of code that substitutes the original detectron function into mine, slightly edited.</p>\n<p>I recognize it's an ugly hack, but it boosted my LB score from .302 to .307 so it might be worth adding to your bag of tricks.</p>\n<pre><code>def paste_masks_in_image(masks, boxes, image_shape, threshold=0.5):\n    \"\"\"\n    Copy pasted from detectron2.layers.mask_ops.paste_masks_in_image and deleted thresholding of the mask\n    \"\"\"\n    assert masks.shape[-1] == masks.shape[-2], \"Only square mask predictions are supported\"\n    N = len(masks)\n    if N == 0:\n        return masks.new_empty((0,) + image_shape, dtype=torch.uint8)\n    if not isinstance(boxes, torch.Tensor):\n        boxes = boxes.tensor\n    device = boxes.device\n    assert len(boxes) == N, boxes.shape\n\n    img_h, img_w = image_shape\n\n    # The actual implementation split the input into chunks,\n    # and paste them chunk by chunk.\n    if device.type == \"cpu\":\n        # CPU is most efficient when they are pasted one by one with skip_empty=True\n        # so that it performs minimal number of operations.\n        num_chunks = N\n    else:\n        # GPU benefits from parallelism for larger chunks, but may have memory issue\n        num_chunks = int(np.ceil(N * img_h * img_w * BYTES_PER_FLOAT / GPU_MEM_LIMIT))\n        assert (\n            num_chunks &lt;= N\n        ), \"Default GPU_MEM_LIMIT in mask_ops.py is too small; try increasing it\"\n    chunks = torch.chunk(torch.arange(N, device=device), num_chunks)\n\n    img_masks = torch.zeros(\n        N, img_h, img_w, device=device, dtype=torch.float32\n    )\n    for inds in chunks:\n        masks_chunk, spatial_inds = _do_paste_mask(\n            masks[inds, None, :, :], boxes[inds], img_h, img_w, skip_empty=device.type == \"cpu\"\n        )\n        img_masks[(inds,) + spatial_inds] = masks_chunk\n    return img_masks\n\ndetectron2.layers.mask_ops.paste_masks_in_image.__code__ = paste_masks_in_image.__code__\n</code></pre>",
  "messages": [
    {
      "id": 1572213,
      "postDate": "2021-11-05T14:14:07.210Z",
      "content": "<p>In the notebooks from <a href=\"https://www.kaggle.com/julian3833\" target=\"_blank\">@julian3833</a> and <a href=\"https://www.kaggle.com/rluethy\" target=\"_blank\">@rluethy</a> I've noticed that they use scores for every pixel to decide whether to include it in the mask or not. Whereas my detectron model only outputs a binary mask and a single score per object. So I went digging in the code and found that this information gets discarded during postprocessing. I didn't see any elegant way to expose it, but luckily python makes it easy to monkeypatch stuff, so bellow you can see a snippet of code that substitutes the original detectron function into mine, slightly edited.</p>\n<p>I recognize it's an ugly hack, but it boosted my LB score from .302 to .307 so it might be worth adding to your bag of tricks.</p>\n<pre><code>def paste_masks_in_image(masks, boxes, image_shape, threshold=0.5):\n    \"\"\"\n    Copy pasted from detectron2.layers.mask_ops.paste_masks_in_image and deleted thresholding of the mask\n    \"\"\"\n    assert masks.shape[-1] == masks.shape[-2], \"Only square mask predictions are supported\"\n    N = len(masks)\n    if N == 0:\n        return masks.new_empty((0,) + image_shape, dtype=torch.uint8)\n    if not isinstance(boxes, torch.Tensor):\n        boxes = boxes.tensor\n    device = boxes.device\n    assert len(boxes) == N, boxes.shape\n\n    img_h, img_w = image_shape\n\n    # The actual implementation split the input into chunks,\n    # and paste them chunk by chunk.\n    if device.type == \"cpu\":\n        # CPU is most efficient when they are pasted one by one with skip_empty=True\n        # so that it performs minimal number of operations.\n        num_chunks = N\n    else:\n        # GPU benefits from parallelism for larger chunks, but may have memory issue\n        num_chunks = int(np.ceil(N * img_h * img_w * BYTES_PER_FLOAT / GPU_MEM_LIMIT))\n        assert (\n            num_chunks &lt;= N\n        ), \"Default GPU_MEM_LIMIT in mask_ops.py is too small; try increasing it\"\n    chunks = torch.chunk(torch.arange(N, device=device), num_chunks)\n\n    img_masks = torch.zeros(\n        N, img_h, img_w, device=device, dtype=torch.float32\n    )\n    for inds in chunks:\n        masks_chunk, spatial_inds = _do_paste_mask(\n            masks[inds, None, :, :], boxes[inds], img_h, img_w, skip_empty=device.type == \"cpu\"\n        )\n        img_masks[(inds,) + spatial_inds] = masks_chunk\n    return img_masks\n\ndetectron2.layers.mask_ops.paste_masks_in_image.__code__ = paste_masks_in_image.__code__\n</code></pre>",
      "rawMarkdown": "In the notebooks from @julian3833 and @rluethy I've noticed that they use scores for every pixel to decide whether to include it in the mask or not. Whereas my detectron model only outputs a binary mask and a single score per object. So I went digging in the code and found that this information gets discarded during postprocessing. I didn't see any elegant way to expose it, but luckily python makes it easy to monkeypatch stuff, so bellow you can see a snippet of code that substitutes the original detectron function into mine, slightly edited.\n\nI recognize it's an ugly hack, but it boosted my LB score from .302 to .307 so it might be worth adding to your bag of tricks.\n\n```\ndef paste_masks_in_image(masks, boxes, image_shape, threshold=0.5):\n    \"\"\"\n    Copy pasted from detectron2.layers.mask_ops.paste_masks_in_image and deleted thresholding of the mask\n    \"\"\"\n    assert masks.shape[-1] == masks.shape[-2], \"Only square mask predictions are supported\"\n    N = len(masks)\n    if N == 0:\n        return masks.new_empty((0,) + image_shape, dtype=torch.uint8)\n    if not isinstance(boxes, torch.Tensor):\n        boxes = boxes.tensor\n    device = boxes.device\n    assert len(boxes) == N, boxes.shape\n\n    img_h, img_w = image_shape\n\n    # The actual implementation split the input into chunks,\n    # and paste them chunk by chunk.\n    if device.type == \"cpu\":\n        # CPU is most efficient when they are pasted one by one with skip_empty=True\n        # so that it performs minimal number of operations.\n        num_chunks = N\n    else:\n        # GPU benefits from parallelism for larger chunks, but may have memory issue\n        num_chunks = int(np.ceil(N * img_h * img_w * BYTES_PER_FLOAT / GPU_MEM_LIMIT))\n        assert (\n            num_chunks <= N\n        ), \"Default GPU_MEM_LIMIT in mask_ops.py is too small; try increasing it\"\n    chunks = torch.chunk(torch.arange(N, device=device), num_chunks)\n\n    img_masks = torch.zeros(\n        N, img_h, img_w, device=device, dtype=torch.float32\n    )\n    for inds in chunks:\n        masks_chunk, spatial_inds = _do_paste_mask(\n            masks[inds, None, :, :], boxes[inds], img_h, img_w, skip_empty=device.type == \"cpu\"\n        )\n        img_masks[(inds,) + spatial_inds] = masks_chunk\n    return img_masks\n\ndetectron2.layers.mask_ops.paste_masks_in_image.__code__ = paste_masks_in_image.__code__\n```",
      "votes": 53
    },
    {
      "id": 1574676,
      "postDate": "2021-11-07T19:23:12.480Z",
      "content": "<p><a href=\"https://www.kaggle.com/sanchitvj\" target=\"_blank\">@sanchitvj</a>  <a href=\"https://www.kaggle.com/gmhost\" target=\"_blank\">@gmhost</a> I was using an older version of detectron when testing this. I just checked in 0.5 and there was another cast happening. Try adding this on top of the change to <code>paste_masks_in_image</code>:</p>\n<pre><code>from detectron2.layers.mask_ops import paste_masks_in_image\nfrom types import SimpleNamespace\nfrom detectron2.structures.masks import ROIMasks\ndef to_bitmasks(self, boxes: torch.Tensor, height, width, threshold=0.5):\n        bitmasks = paste_masks_in_image(\n            self.tensor,\n            boxes,\n            (height, width),\n            threshold=threshold,\n        )\n        return SimpleNamespace(tensor=bitmasks)\n\nROIMasks.to_bitmasks=to_bitmasks\n</code></pre>",
      "rawMarkdown": "@sanchitvj  @gmhost I was using an older version of detectron when testing this. I just checked in 0.5 and there was another cast happening. Try adding this on top of the change to `paste_masks_in_image`:\n```\nfrom detectron2.layers.mask_ops import paste_masks_in_image\nfrom types import SimpleNamespace\nfrom detectron2.structures.masks import ROIMasks\ndef to_bitmasks(self, boxes: torch.Tensor, height, width, threshold=0.5):\n        bitmasks = paste_masks_in_image(\n            self.tensor,\n            boxes,\n            (height, width),\n            threshold=threshold,\n        )\n        return SimpleNamespace(tensor=bitmasks)\n\nROIMasks.to_bitmasks=to_bitmasks\n```",
      "votes": 8,
      "replies": [
        {
          "id": 1574679,
          "postDate": "2021-11-07T19:28:06.233Z",
          "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> Thanks. But I have doubt: At what point of prediction are we using this.</p>",
          "rawMarkdown": "@slawekbiel Thanks. But I have doubt: At what point of prediction are we using this.",
          "votes": 2
        },
        {
          "id": 1574683,
          "postDate": "2021-11-07T19:31:52.790Z",
          "content": "<p>Well, it's up to you what to do with it. <br>\nThe point of my post was just to show that this information is accessible if one really wants it.</p>",
          "rawMarkdown": "Well, it's up to you what to do with it. \nThe point of my post was just to show that this information is accessible if one really wants it.",
          "votes": 2
        },
        {
          "id": 1574882,
          "postDate": "2021-11-08T01:30:28.877Z",
          "content": "<p>Indeed, I saw the same issue, but I modified in another way</p>\n<p>from typing import Any, Iterator, List, Union<br>\nimport numpy as np</p>\n<p>def BitMasks__init__(self, tensor: Union[torch.Tensor, np.ndarray]):<br>\n    \"\"\"<br>\n    Args:<br>\n        tensor: bool Tensor of N,H,W, representing N instances in the image.<br>\n    \"\"\"<br>\n    device = tensor.device if isinstance(tensor, torch.Tensor) else torch.device(\"cpu\")<br>\n    tensor = torch.as_tensor(tensor, dtype=torch.float32, device=device) # Original code: tensor = torch.as_tensor(tensor, dtype=torch.bool, device=device)<br>\n    assert tensor.dim() == 3, tensor.size()<br>\n    self.image_size = tensor.shape[1:]<br>\n    self.tensor = tensor</p>\n<p>detectron2.structures.masks.BitMasks.<strong>init</strong>.<strong>code</strong> = BitMasks__init__.<strong>code</strong></p>\n<p>I wonder how this is different from your idea. It would be nice if you can take a look. And btw, though I did obtain the pixel-level score, I failed to have significant gain in my CV and LB score. <br>\n<a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> </p>",
          "rawMarkdown": "Indeed, I saw the same issue, but I modified in another way\n\nfrom typing import Any, Iterator, List, Union\nimport numpy as np\n\ndef BitMasks__init__(self, tensor: Union[torch.Tensor, np.ndarray]):\n    \"\"\"\n    Args:\n        tensor: bool Tensor of N,H,W, representing N instances in the image.\n    \"\"\"\n    device = tensor.device if isinstance(tensor, torch.Tensor) else torch.device(\"cpu\")\n    tensor = torch.as_tensor(tensor, dtype=torch.float32, device=device) # Original code: tensor = torch.as_tensor(tensor, dtype=torch.bool, device=device)\n    assert tensor.dim() == 3, tensor.size()\n    self.image_size = tensor.shape[1:]\n    self.tensor = tensor\n\ndetectron2.structures.masks.BitMasks.__init__.__code__ = BitMasks__init__.__code__\n\nI wonder how this is different from your idea. It would be nice if you can take a look. And btw, though I did obtain the pixel-level score, I failed to have significant gain in my CV and LB score. \n@slawekbiel ",
          "votes": 2
        },
        {
          "id": 1574896,
          "postDate": "2021-11-08T01:53:26.930Z",
          "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> Hi, thanks for your information and codes. I already managed to generate the pixes score intead of binary mask.</p>",
          "rawMarkdown": "@slawekbiel Hi, thanks for your information and codes. I already managed to generate the pixes score intead of binary mask.",
          "votes": 1
        },
        {
          "id": 1575176,
          "postDate": "2021-11-08T08:02:56.160Z",
          "content": "<p><a href=\"https://www.kaggle.com/namgalielei\" target=\"_blank\">@namgalielei</a> yeah that solves the same problem. My change is just a call earlier, rather than modify the <code>Bitmasks</code> I substitute <code>return BitMasks(bitmasks)</code> to <code>return SimpleNamespace(tensor=bitmasks)</code></p>\n<p>Your scores are much higher than mine, so maybe you already have higher quality masks which benefited less from additional tuning?</p>",
          "rawMarkdown": "@namgalielei yeah that solves the same problem. My change is just a call earlier, rather than modify the `Bitmasks` I substitute `return BitMasks(bitmasks)` to `return SimpleNamespace(tensor=bitmasks)`\n\nYour scores are much higher than mine, so maybe you already have higher quality masks which benefited less from additional tuning?",
          "votes": 1
        },
        {
          "id": 1591094,
          "postDate": "2021-11-22T00:38:55.177Z",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/namgalielei\" target=\"_blank\">@namgalielei</a> this works better for me</p>",
          "rawMarkdown": "Thanks @namgalielei this works better for me"
        },
        {
          "id": 1624384,
          "postDate": "2021-12-20T20:35:09.197Z",
          "content": "<p>Thanks for the info! Just one remark - namgalielei's post has a few typos, here's a fixed code:</p>\n<pre><code>from typing import Any, Iterator, List, Union\nimport numpy as np\n\ndef BitMasks__init__(self, tensor: Union[torch.Tensor, np.ndarray]):\n    \"\"\"\n    Args:\n    tensor: bool Tensor of N,H,W, representing N instances in the image.\n    \"\"\"\n    device = tensor.device if isinstance(tensor, torch.Tensor) else torch.device(\"cpu\")\n    tensor = torch.as_tensor(tensor, dtype=torch.float32, device=device) # Original code: tensor = torch.as_tensor(tensor, dtype=torch.bool, device=device)\n    assert tensor.dim() == 3, tensor.size()\n    self.image_size = tensor.shape[1:]\n    self.tensor = tensor\n\ndetectron2.structures.masks.BitMasks.__init__.__code__ = BitMasks__init__.__code__\n</code></pre>",
          "rawMarkdown": "Thanks for the info! Just one remark - namgalielei's post has a few typos, here's a fixed code:\n```\nfrom typing import Any, Iterator, List, Union\nimport numpy as np\n\ndef BitMasks__init__(self, tensor: Union[torch.Tensor, np.ndarray]):\n    \"\"\"\n    Args:\n    tensor: bool Tensor of N,H,W, representing N instances in the image.\n    \"\"\"\n    device = tensor.device if isinstance(tensor, torch.Tensor) else torch.device(\"cpu\")\n    tensor = torch.as_tensor(tensor, dtype=torch.float32, device=device) # Original code: tensor = torch.as_tensor(tensor, dtype=torch.bool, device=device)\n    assert tensor.dim() == 3, tensor.size()\n    self.image_size = tensor.shape[1:]\n    self.tensor = tensor\n\ndetectron2.structures.masks.BitMasks.__init__.__code__ = BitMasks__init__.__code__\n```"
        }
      ]
    },
    {
      "id": 1581642,
      "postDate": "2021-11-14T04:34:17.383Z",
      "content": "<p>This trick is very helpful. Indeed, I just noticed your post yesterday, It tooks me a while to understand and hours for fixing bugs :(( and it helps me choose the best map iou score when training but haven't checked with LB yet</p>",
      "rawMarkdown": "This trick is very helpful. Indeed, I just noticed your post yesterday, It tooks me a while to understand and hours for fixing bugs :(( and it helps me choose the best map iou score when training but haven't checked with LB yet",
      "votes": 3
    },
    {
      "id": 1611982,
      "postDate": "2021-12-08T12:49:53.520Z",
      "content": "<p>If you can't make this work on local, try deleting __pycache__.</p>",
      "rawMarkdown": "If you can't make this work on local, try deleting \\__pycache__.",
      "votes": 1
    },
    {
      "id": 1590644,
      "postDate": "2021-11-21T14:21:16.647Z",
      "content": "<p>Hi <a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> when I add the above snippet to your detectron inference code my LB score goes from 0.294 to 0.12 Any idea why this may be the case? Yet to debug the extra snippet you suggested on 7-11-2021 for 0.5 version</p>",
      "rawMarkdown": "Hi @slawekbiel when I add the above snippet to your detectron inference code my LB score goes from 0.294 to 0.12 Any idea why this may be the case? Yet to debug the extra snippet you suggested on 7-11-2021 for 0.5 version",
      "votes": 1,
      "replies": [
        {
          "id": 1590657,
          "postDate": "2021-11-21T14:38:01.600Z",
          "content": "<p>All my snippets do is forcing the model to output raw pixel scores as a float tensor, it's then up to you to build bitmasks based on that. </p>",
          "rawMarkdown": "All my snippets do is forcing the model to output raw pixel scores as a float tensor, it's then up to you to build bitmasks based on that. "
        },
        {
          "id": 1594136,
          "postDate": "2021-11-24T15:04:24.783Z",
          "content": "<p>Where to build the bitmask?</p>",
          "rawMarkdown": "Where to build the bitmask?"
        }
      ]
    },
    {
      "id": 1573108,
      "postDate": "2021-11-06T09:28:44.657Z",
      "content": "<p>great!  optimize further.</p>",
      "rawMarkdown": "great!  optimize further.",
      "votes": 2
    },
    {
      "id": 1594289,
      "postDate": "2021-11-24T17:10:09.513Z",
      "content": "<p>Could you tell me the paste_masks_in_image</p>\n<pre><code>If threshold &amp; gt; = 0:\nMasks_chunk = (masks_chunk &amp; gt; = threshold).to(dtype=torch.bool)\nThe else:\n# for visualization and debugging\nMasks_chunk = (masks_chunk * 255).to(dtype=torch. Uint8)\n</code></pre>\n<p>Isn't a threshold already used to generate a binary mask for each pixel? Why generate your own score and then filter it?</p>",
      "rawMarkdown": "Could you tell me the paste_masks_in_image\n```\nIf threshold & gt; = 0:\nMasks_chunk = (masks_chunk & gt; = threshold).to(dtype=torch.bool)\nThe else:\n# for visualization and debugging\nMasks_chunk = (masks_chunk * 255).to(dtype=torch. Uint8)\n```\nIsn't a threshold already used to generate a binary mask for each pixel? Why generate your own score and then filter it?",
      "replies": [
        {
          "id": 1594317,
          "postDate": "2021-11-24T17:44:04.137Z",
          "content": "<p>I wanted to get raw scores so I have more control in doing my own postprocessing. But if the default behavior works for you then of course yo don’t need to touch it.</p>",
          "rawMarkdown": "I wanted to get raw scores so I have more control in doing my own postprocessing. But if the default behavior works for you then of course yo don’t need to touch it."
        }
      ]
    },
    {
      "id": 1593777,
      "postDate": "2021-11-24T09:05:12.033Z",
      "content": "<p>Why this error occurs:<br>\nValueError: paste_masks_in_image() requires a code object with 2 free vars, not 0</p>",
      "rawMarkdown": "Why this error occurs:\nValueError: paste_masks_in_image() requires a code object with 2 free vars, not 0",
      "replies": [
        {
          "id": 1593791,
          "postDate": "2021-11-24T09:28:22.473Z",
          "content": "<p>In detectron2 0.6 they added decorator <code>@torch.jit.script_if_tracing</code> to this method.</p>",
          "rawMarkdown": "In detectron2 0.6 they added decorator `@torch.jit.script_if_tracing` to this method.",
          "votes": 2
        },
        {
          "id": 1593797,
          "postDate": "2021-11-24T09:31:19.680Z",
          "rawMarkdown": "",
          "isDeleted": true
        },
        {
          "id": 1593802,
          "postDate": "2021-11-24T09:36:00.473Z",
          "rawMarkdown": "",
          "isDeleted": true
        }
      ]
    },
    {
      "id": 1574249,
      "postDate": "2021-11-07T11:22:10.180Z",
      "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> I am not sure how to use the code snippet above. During inference it  returns box of masks. Can you help here?</p>",
      "rawMarkdown": "@slawekbiel I am not sure how to use the code snippet above. During inference it  returns box of masks. Can you help here?"
    },
    {
      "id": 1572886,
      "postDate": "2021-11-06T04:03:53.677Z",
      "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> Hi,when I used the detectron2 predictor to generate output, I found the predictor's pred_mask only bool values,how do you change the code to generate pixes scores?Thanks</p>",
      "rawMarkdown": "@slawekbiel Hi,when I used the detectron2 predictor to generate output, I found the predictor's pred_mask only bool values,how do you change the code to generate pixes scores?Thanks",
      "replies": [
        {
          "id": 1573044,
          "postDate": "2021-11-06T08:17:29.083Z",
          "content": "<p>It’s exactly the code I pasted above. Can you add a print statement to verify the substituted version gets called?</p>",
          "rawMarkdown": "It’s exactly the code I pasted above. Can you add a print statement to verify the substituted version gets called?"
        },
        {
          "id": 1573333,
          "postDate": "2021-11-06T14:32:09.167Z",
          "content": "<p>when I added the code you pasted above,  also printed statement,but predictor's pred_mask was bool.</p>",
          "rawMarkdown": "when I added the code you pasted above,  also printed statement,but predictor's pred_mask was bool."
        },
        {
          "id": 1573543,
          "postDate": "2021-11-06T18:00:45.483Z",
          "content": "<p>I don't know then, you'd have to debug it in your own setup. For me the float tensor returned from <code>paste_masks_in_image</code> is propagated to <code>pred_masks</code> field in the predictor's output.</p>",
          "rawMarkdown": "I don't know then, you'd have to debug it in your own setup. For me the float tensor returned from `paste_masks_in_image` is propagated to `pred_masks` field in the predictor's output.",
          "votes": 2
        },
        {
          "id": 1589475,
          "postDate": "2021-11-20T10:12:29.067Z",
          "content": "<p><a href=\"https://www.kaggle.com/gmhost\" target=\"_blank\">@gmhost</a> were you able to fix bool / float issue ? I also have the same problem - I tried changing types and of  course it didn't work. I tried with detectron2 0.5 and 0.6. </p>",
          "rawMarkdown": "@gmhost were you able to fix bool / float issue ? I also have the same problem - I tried changing types and of  course it didn't work. I tried with detectron2 0.5 and 0.6. "
        }
      ]
    }
  ],
  "comments": [
    {
      "id": 1574676,
      "author_name": "Slawek Biel",
      "author_url": "",
      "post_date": "2021-11-07T19:23:12.480000",
      "content": "<p><a href=\"https://www.kaggle.com/sanchitvj\" target=\"_blank\">@sanchitvj</a>  <a href=\"https://www.kaggle.com/gmhost\" target=\"_blank\">@gmhost</a> I was using an older version of detectron when testing this. I just checked in 0.5 and there was another cast happening. Try adding this on top of the change to <code>paste_masks_in_image</code>:</p>\n<pre><code>from detectron2.layers.mask_ops import paste_masks_in_image\nfrom types import SimpleNamespace\nfrom detectron2.structures.masks import ROIMasks\ndef to_bitmasks(self, boxes: torch.Tensor, height, width, threshold=0.5):\n        bitmasks = paste_masks_in_image(\n            self.tensor,\n            boxes,\n            (height, width),\n            threshold=threshold,\n        )\n        return SimpleNamespace(tensor=bitmasks)\n\nROIMasks.to_bitmasks=to_bitmasks\n</code></pre>",
      "votes": 8,
      "replies": [
        {
          "id": 1574679,
          "author_name": "Sanchit Vijay",
          "author_url": "",
          "post_date": "2021-11-07T19:28:06.233000",
          "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> Thanks. But I have doubt: At what point of prediction are we using this.</p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1574683,
          "author_name": "Slawek Biel",
          "author_url": "",
          "post_date": "2021-11-07T19:31:52.790000",
          "content": "<p>Well, it's up to you what to do with it. <br>\nThe point of my post was just to show that this information is accessible if one really wants it.</p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1574882,
          "author_name": "Liam Nguyen",
          "author_url": "",
          "post_date": "2021-11-08T01:30:28.877000",
          "content": "<p>Indeed, I saw the same issue, but I modified in another way</p>\n<p>from typing import Any, Iterator, List, Union<br>\nimport numpy as np</p>\n<p>def BitMasks__init__(self, tensor: Union[torch.Tensor, np.ndarray]):<br>\n    \"\"\"<br>\n    Args:<br>\n        tensor: bool Tensor of N,H,W, representing N instances in the image.<br>\n    \"\"\"<br>\n    device = tensor.device if isinstance(tensor, torch.Tensor) else torch.device(\"cpu\")<br>\n    tensor = torch.as_tensor(tensor, dtype=torch.float32, device=device) # Original code: tensor = torch.as_tensor(tensor, dtype=torch.bool, device=device)<br>\n    assert tensor.dim() == 3, tensor.size()<br>\n    self.image_size = tensor.shape[1:]<br>\n    self.tensor = tensor</p>\n<p>detectron2.structures.masks.BitMasks.<strong>init</strong>.<strong>code</strong> = BitMasks__init__.<strong>code</strong></p>\n<p>I wonder how this is different from your idea. It would be nice if you can take a look. And btw, though I did obtain the pixel-level score, I failed to have significant gain in my CV and LB score. <br>\n<a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> </p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1574896,
          "author_name": "kwang",
          "author_url": "",
          "post_date": "2021-11-08T01:53:26.930000",
          "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> Hi, thanks for your information and codes. I already managed to generate the pixes score intead of binary mask.</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1575176,
          "author_name": "Slawek Biel",
          "author_url": "",
          "post_date": "2021-11-08T08:02:56.160000",
          "content": "<p><a href=\"https://www.kaggle.com/namgalielei\" target=\"_blank\">@namgalielei</a> yeah that solves the same problem. My change is just a call earlier, rather than modify the <code>Bitmasks</code> I substitute <code>return BitMasks(bitmasks)</code> to <code>return SimpleNamespace(tensor=bitmasks)</code></p>\n<p>Your scores are much higher than mine, so maybe you already have higher quality masks which benefited less from additional tuning?</p>",
          "votes": 1,
          "replies": []
        },
        {
          "id": 1591094,
          "author_name": "Khubchandani",
          "author_url": "",
          "post_date": "2021-11-22T00:38:55.177000",
          "content": "<p>Thanks <a href=\"https://www.kaggle.com/namgalielei\" target=\"_blank\">@namgalielei</a> this works better for me</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1624384,
          "author_name": "Araik Tamazian",
          "author_url": "",
          "post_date": "2021-12-20T20:35:09.197000",
          "content": "<p>Thanks for the info! Just one remark - namgalielei's post has a few typos, here's a fixed code:</p>\n<pre><code>from typing import Any, Iterator, List, Union\nimport numpy as np\n\ndef BitMasks__init__(self, tensor: Union[torch.Tensor, np.ndarray]):\n    \"\"\"\n    Args:\n    tensor: bool Tensor of N,H,W, representing N instances in the image.\n    \"\"\"\n    device = tensor.device if isinstance(tensor, torch.Tensor) else torch.device(\"cpu\")\n    tensor = torch.as_tensor(tensor, dtype=torch.float32, device=device) # Original code: tensor = torch.as_tensor(tensor, dtype=torch.bool, device=device)\n    assert tensor.dim() == 3, tensor.size()\n    self.image_size = tensor.shape[1:]\n    self.tensor = tensor\n\ndetectron2.structures.masks.BitMasks.__init__.__code__ = BitMasks__init__.__code__\n</code></pre>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1581642,
      "author_name": "Vu",
      "author_url": "",
      "post_date": "2021-11-14T04:34:17.383000",
      "content": "<p>This trick is very helpful. Indeed, I just noticed your post yesterday, It tooks me a while to understand and hours for fixing bugs :(( and it helps me choose the best map iou score when training but haven't checked with LB yet</p>",
      "votes": 3,
      "replies": []
    },
    {
      "id": 1611982,
      "author_name": "Gunes Evitan",
      "author_url": "",
      "post_date": "2021-12-08T12:49:53.520000",
      "content": "<p>If you can't make this work on local, try deleting __pycache__.</p>",
      "votes": 1,
      "replies": []
    },
    {
      "id": 1590644,
      "author_name": "Khubchandani",
      "author_url": "",
      "post_date": "2021-11-21T14:21:16.647000",
      "content": "<p>Hi <a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> when I add the above snippet to your detectron inference code my LB score goes from 0.294 to 0.12 Any idea why this may be the case? Yet to debug the extra snippet you suggested on 7-11-2021 for 0.5 version</p>",
      "votes": 1,
      "replies": [
        {
          "id": 1590657,
          "author_name": "Slawek Biel",
          "author_url": "",
          "post_date": "2021-11-21T14:38:01.600000",
          "content": "<p>All my snippets do is forcing the model to output raw pixel scores as a float tensor, it's then up to you to build bitmasks based on that. </p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1594136,
          "author_name": "yerongg",
          "author_url": "",
          "post_date": "2021-11-24T15:04:24.783000",
          "content": "<p>Where to build the bitmask?</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1573108,
      "author_name": "dragon zhang",
      "author_url": "",
      "post_date": "2021-11-06T09:28:44.657000",
      "content": "<p>great!  optimize further.</p>",
      "votes": 2,
      "replies": []
    },
    {
      "id": 1594289,
      "author_name": "yerongg",
      "author_url": "",
      "post_date": "2021-11-24T17:10:09.513000",
      "content": "<p>Could you tell me the paste_masks_in_image</p>\n<pre><code>If threshold &amp; gt; = 0:\nMasks_chunk = (masks_chunk &amp; gt; = threshold).to(dtype=torch.bool)\nThe else:\n# for visualization and debugging\nMasks_chunk = (masks_chunk * 255).to(dtype=torch. Uint8)\n</code></pre>\n<p>Isn't a threshold already used to generate a binary mask for each pixel? Why generate your own score and then filter it?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1594317,
          "author_name": "Slawek Biel",
          "author_url": "",
          "post_date": "2021-11-24T17:44:04.137000",
          "content": "<p>I wanted to get raw scores so I have more control in doing my own postprocessing. But if the default behavior works for you then of course yo don’t need to touch it.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1593777,
      "author_name": "yerongg",
      "author_url": "",
      "post_date": "2021-11-24T09:05:12.033000",
      "content": "<p>Why this error occurs:<br>\nValueError: paste_masks_in_image() requires a code object with 2 free vars, not 0</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1593791,
          "author_name": "Valentin Nikotin",
          "author_url": "",
          "post_date": "2021-11-24T09:28:22.473000",
          "content": "<p>In detectron2 0.6 they added decorator <code>@torch.jit.script_if_tracing</code> to this method.</p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1593797,
          "author_name": "",
          "author_url": "",
          "post_date": "2021-11-24T09:31:19.680000",
          "content": "",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1593802,
          "author_name": "",
          "author_url": "",
          "post_date": "2021-11-24T09:36:00.473000",
          "content": "",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 1574249,
      "author_name": "Sanchit Vijay",
      "author_url": "",
      "post_date": "2021-11-07T11:22:10.180000",
      "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> I am not sure how to use the code snippet above. During inference it  returns box of masks. Can you help here?</p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 1572886,
      "author_name": "kwang",
      "author_url": "",
      "post_date": "2021-11-06T04:03:53.677000",
      "content": "<p><a href=\"https://www.kaggle.com/slawekbiel\" target=\"_blank\">@slawekbiel</a> Hi,when I used the detectron2 predictor to generate output, I found the predictor's pred_mask only bool values,how do you change the code to generate pixes scores?Thanks</p>",
      "votes": 0,
      "replies": [
        {
          "id": 1573044,
          "author_name": "Slawek Biel",
          "author_url": "",
          "post_date": "2021-11-06T08:17:29.083000",
          "content": "<p>It’s exactly the code I pasted above. Can you add a print statement to verify the substituted version gets called?</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1573333,
          "author_name": "kwang",
          "author_url": "",
          "post_date": "2021-11-06T14:32:09.167000",
          "content": "<p>when I added the code you pasted above,  also printed statement,but predictor's pred_mask was bool.</p>",
          "votes": 0,
          "replies": []
        },
        {
          "id": 1573543,
          "author_name": "Slawek Biel",
          "author_url": "",
          "post_date": "2021-11-06T18:00:45.483000",
          "content": "<p>I don't know then, you'd have to debug it in your own setup. For me the float tensor returned from <code>paste_masks_in_image</code> is propagated to <code>pred_masks</code> field in the predictor's output.</p>",
          "votes": 2,
          "replies": []
        },
        {
          "id": 1589475,
          "author_name": "RB",
          "author_url": "",
          "post_date": "2021-11-20T10:12:29.067000",
          "content": "<p><a href=\"https://www.kaggle.com/gmhost\" target=\"_blank\">@gmhost</a> were you able to fix bool / float issue ? I also have the same problem - I tried changing types and of  course it didn't work. I tried with detectron2 0.5 and 0.6. </p>",
          "votes": 0,
          "replies": []
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "1572213": "In the notebooks from @julian3833 and @rluethy I've noticed that they use scores for every pixel to decide whether to include it in the mask or not. Whereas my detectron model only outputs a binary mask and a single score per object. So I went digging in the code and found that this information gets discarded during postprocessing. I didn't see any elegant way to expose it, but luckily python makes it easy to monkeypatch stuff, so bellow you can see a snippet of code that substitutes the original detectron function into mine, slightly edited.\n\nI recognize it's an ugly hack, but it boosted my LB score from .302 to .307 so it might be worth adding to your bag of tricks.\n\n```\ndef paste_masks_in_image(masks, boxes, image_shape, threshold=0.5):\n    \"\"\"\n    Copy pasted from detectron2.layers.mask_ops.paste_masks_in_image and deleted thresholding of the mask\n    \"\"\"\n    assert masks.shape[-1] == masks.shape[-2], \"Only square mask predictions are supported\"\n    N = len(masks)\n    if N == 0:\n        return masks.new_empty((0,) + image_shape, dtype=torch.uint8)\n    if not isinstance(boxes, torch.Tensor):\n        boxes = boxes.tensor\n    device = boxes.device\n    assert len(boxes) == N, boxes.shape\n\n    img_h, img_w = image_shape\n\n    # The actual implementation split the input into chunks,\n    # and paste them chunk by chunk.\n    if device.type == \"cpu\":\n        # CPU is most efficient when they are pasted one by one with skip_empty=True\n        # so that it performs minimal number of operations.\n        num_chunks = N\n    else:\n        # GPU benefits from parallelism for larger chunks, but may have memory issue\n        num_chunks = int(np.ceil(N * img_h * img_w * BYTES_PER_FLOAT / GPU_MEM_LIMIT))\n        assert (\n            num_chunks <= N\n        ), \"Default GPU_MEM_LIMIT in mask_ops.py is too small; try increasing it\"\n    chunks = torch.chunk(torch.arange(N, device=device), num_chunks)\n\n    img_masks = torch.zeros(\n        N, img_h, img_w, device=device, dtype=torch.float32\n    )\n    for inds in chunks:\n        masks_chunk, spatial_inds = _do_paste_mask(\n            masks[inds, None, :, :], boxes[inds], img_h, img_w, skip_empty=device.type == \"cpu\"\n        )\n        img_masks[(inds,) + spatial_inds] = masks_chunk\n    return img_masks\n\ndetectron2.layers.mask_ops.paste_masks_in_image.__code__ = paste_masks_in_image.__code__\n```",
    "1574676": "@sanchitvj  @gmhost I was using an older version of detectron when testing this. I just checked in 0.5 and there was another cast happening. Try adding this on top of the change to `paste_masks_in_image`:\n```\nfrom detectron2.layers.mask_ops import paste_masks_in_image\nfrom types import SimpleNamespace\nfrom detectron2.structures.masks import ROIMasks\ndef to_bitmasks(self, boxes: torch.Tensor, height, width, threshold=0.5):\n        bitmasks = paste_masks_in_image(\n            self.tensor,\n            boxes,\n            (height, width),\n            threshold=threshold,\n        )\n        return SimpleNamespace(tensor=bitmasks)\n\nROIMasks.to_bitmasks=to_bitmasks\n```",
    "1581642": "This trick is very helpful. Indeed, I just noticed your post yesterday, It tooks me a while to understand and hours for fixing bugs :(( and it helps me choose the best map iou score when training but haven't checked with LB yet",
    "1611982": "If you can't make this work on local, try deleting \\__pycache__.",
    "1590644": "Hi @slawekbiel when I add the above snippet to your detectron inference code my LB score goes from 0.294 to 0.12 Any idea why this may be the case? Yet to debug the extra snippet you suggested on 7-11-2021 for 0.5 version",
    "1573108": "great!  optimize further.",
    "1594289": "Could you tell me the paste_masks_in_image\n```\nIf threshold & gt; = 0:\nMasks_chunk = (masks_chunk & gt; = threshold).to(dtype=torch.bool)\nThe else:\n# for visualization and debugging\nMasks_chunk = (masks_chunk * 255).to(dtype=torch. Uint8)\n```\nIsn't a threshold already used to generate a binary mask for each pixel? Why generate your own score and then filter it?",
    "1593777": "Why this error occurs:\nValueError: paste_masks_in_image() requires a code object with 2 free vars, not 0",
    "1574249": "@slawekbiel I am not sure how to use the code snippet above. During inference it  returns box of masks. Can you help here?",
    "1572886": "@slawekbiel Hi,when I used the detectron2 predictor to generate output, I found the predictor's pred_mask only bool values,how do you change the code to generate pixes scores?Thanks"
  }
}