{
  "id": 334644,
  "title": "Host Question - Can We Access The Functions Used for RLE/JSON Mask Generation?",
  "url": "/competitions/hubmap-organ-segmentation/discussion/334644",
  "author_name": "",
  "post_date": "2022-07-02T13:10:03.847500900Z",
  "votes": 8,
  "comment_count": 3,
  "views": 0,
  "content": "<p>Tagging for visibility: <a href=\"https://www.kaggle.com/yashvrdnjain\" target=\"_blank\">@yashvrdnjain</a> <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a> </p>\n<p>Hi there everyone, I'm seeing discrepancies between the polygons in the JSON file and the decoded RLE and need some help.</p>\n<p>I did the following experiment (note I have used <strong><code>rle_decode</code></strong> and <strong><code>rle_decode_tf</code></strong> in previous competitions without issue):</p>\n<pre><code>demo = np.zeros((100,100))\ndemo[40:60, 40:60] = 1\ndemo_rle = rle_encode(demo)\n\nrle_demo_1 = rle_decode(demo_rle, (100,100))\nrle_demo_2 = rle_decode_v2(demo_rle, (100,100))\nrle_demo_3 = rle_decode_tf(demo_rle, (100,100))\n\nprint(\"rle_decode    : \", (rle_demo_1!=demo).sum())\nprint(\"rle_decode_v2 : \", (rle_demo_2!=demo).sum())\nprint(\"rle_decode_tf : \", (rle_demo_3!=demo).numpy().sum())\n</code></pre>\n<p>which outputs:</p>\n<pre><code>rle_decode    :  0\nrle_decode_v2 :  40\nrle_decode_tf :  0\n</code></pre>\n<p><br></p>\n<p><strong>This would be fine, however, the .JSON file polygon mask is the same as the decoded RLE mask created by <code>rle_decode_v2</code> which as we can see does not recreate the original shape correctly</strong></p>\n<p>There's a good chance I'm messing something up here. I know RLE is a heavily discussed topic and I thought I had a handle on it… it would appear not. If someone can clarify this for me, or alternatively if the hosts can provide the RLE-and-JSON-related functions that would be very helpful.</p>\n<p>Thanks in advance!</p>\n<p><br><br></p>\n<hr>\n<p><br></p>\n<p><strong><em>Appendix of used functions below:</em></strong></p>\n<p><br><br></p>\n<p><strong>Decode Functions</strong></p>\n<p><br></p>\n<pre><code># ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\n# modified from: https://www.kaggle.com/inversion/run-length-decoding-quick-start\ndef rle_decode(mask_rle, shape, color=1):\n    \"\"\" TBD\n\n    Args:\n        mask_rle (str): run-length as string formated (start length)\n        shape (tuple of ints): (height,width) of array to return \n\n    Returns: \n        Mask (np.array)\n            - 1 indicating mask\n            - 0 indicating background\n\n    \"\"\"\n    # Split the string by space, then convert it into a integer array\n    s = np.array(mask_rle.split(), dtype=int)\n\n    # Every even value is the start, every odd value is the \"run\" length\n    starts = s[0::2] - 1\n    lengths = s[1::2]\n    ends = starts + lengths\n\n    # The image image is actually flattened since RLE is a 1D \"run\"\n    if len(shape)==3:\n        h, w, d = shape\n        img = np.zeros((h * w, d), dtype=np.float32)\n    else:\n        h, w = shape\n        img = np.zeros((h * w,), dtype=np.float32)\n\n    # The color here is actually just any integer you want!\n    for lo, hi in zip(starts, ends):\n        img[lo : hi] = color\n\n    # Don't forget to change the image back to the original shape\n    return img.reshape(shape).T\n\n# https://www.kaggle.com/code/jirkaborovec/ftus-segm-eda-export-rle-mask\ndef rle_decode_v2(mask_rle: str, img_shape: tuple = None) -&gt; np.ndarray:\n    seq = mask_rle.split()\n    starts = np.array(list(map(int, seq[0::2])))\n    lengths = np.array(list(map(int, seq[1::2])))\n    assert len(starts) == len(lengths)\n    ends = starts + lengths\n    img = np.zeros((np.product(img_shape),), dtype=np.uint8)\n    for begin, end in zip(starts, ends):\n        img[begin:end] = 1\n    return img.reshape(img_shape).T\n\ndef rle_decode_tf(mask_rle, shape):\n    \"\"\" TBD \"\"\"\n\n    shape = tf.convert_to_tensor(shape, tf.int64)\n    size = tf.math.reduce_prod(shape)\n\n    # Split string\n    s = tf.strings.split(mask_rle)\n    s = tf.strings.to_number(s, tf.int64)\n\n    # Get starts and lengths\n    starts = s[::2] - 1\n    lens = s[1::2]\n\n    # Make ones to be scattered\n    total_ones = tf.reduce_sum(lens)\n    ones = tf.ones([total_ones], tf.uint8)\n\n    # Make scattering indices\n    r = tf.range(total_ones)\n    lens_cum = tf.math.cumsum(lens)\n    s = tf.searchsorted(lens_cum, r, 'right')\n    idx = r + tf.gather(starts - tf.pad(lens_cum[:-1], [(1, 0)]), s)\n\n    # Scatter ones into flattened mask\n    mask_flat = tf.scatter_nd(tf.expand_dims(idx, 1), ones, [size])\n\n    # Reshape into mask\n    return tf.transpose(tf.reshape(mask_flat, shape))\n</code></pre>\n<p><br></p>\n<p><strong>and the encode function</strong></p>\n<p><br></p>\n<pre><code># ref.: https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_encode(img):\n    \"\"\" TBD\n\n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n\n    Returns: \n        run length as string formated\n    \"\"\"\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n</code></pre>\n<hr>",
  "messages": [
    {
      "id": "1840655",
      "postDate": "07/02/2022 13:10:03",
      "content": "<p>Tagging for visibility: <a href=\"https://www.kaggle.com/yashvrdnjain\" target=\"_blank\">@yashvrdnjain</a> <a href=\"https://www.kaggle.com/jirkaborovec\" target=\"_blank\">@jirkaborovec</a> </p>\n<p>Hi there everyone, I'm seeing discrepancies between the polygons in the JSON file and the decoded RLE and need some help.</p>\n<p>I did the following experiment (note I have used <strong><code>rle_decode</code></strong> and <strong><code>rle_decode_tf</code></strong> in previous competitions without issue):</p>\n<pre><code>demo = np.zeros((100,100))\ndemo[40:60, 40:60] = 1\ndemo_rle = rle_encode(demo)\n\nrle_demo_1 = rle_decode(demo_rle, (100,100))\nrle_demo_2 = rle_decode_v2(demo_rle, (100,100))\nrle_demo_3 = rle_decode_tf(demo_rle, (100,100))\n\nprint(\"rle_decode    : \", (rle_demo_1!=demo).sum())\nprint(\"rle_decode_v2 : \", (rle_demo_2!=demo).sum())\nprint(\"rle_decode_tf : \", (rle_demo_3!=demo).numpy().sum())\n</code></pre>\n<p>which outputs:</p>\n<pre><code>rle_decode    :  0\nrle_decode_v2 :  40\nrle_decode_tf :  0\n</code></pre>\n<p><br></p>\n<p><strong>This would be fine, however, the .JSON file polygon mask is the same as the decoded RLE mask created by <code>rle_decode_v2</code> which as we can see does not recreate the original shape correctly</strong></p>\n<p>There's a good chance I'm messing something up here. I know RLE is a heavily discussed topic and I thought I had a handle on it… it would appear not. If someone can clarify this for me, or alternatively if the hosts can provide the RLE-and-JSON-related functions that would be very helpful.</p>\n<p>Thanks in advance!</p>\n<p><br><br></p>\n<hr>\n<p><br></p>\n<p><strong><em>Appendix of used functions below:</em></strong></p>\n<p><br><br></p>\n<p><strong>Decode Functions</strong></p>\n<p><br></p>\n<pre><code># ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\n# modified from: https://www.kaggle.com/inversion/run-length-decoding-quick-start\ndef rle_decode(mask_rle, shape, color=1):\n    \"\"\" TBD\n\n    Args:\n        mask_rle (str): run-length as string formated (start length)\n        shape (tuple of ints): (height,width) of array to return \n\n    Returns: \n        Mask (np.array)\n            - 1 indicating mask\n            - 0 indicating background\n\n    \"\"\"\n    # Split the string by space, then convert it into a integer array\n    s = np.array(mask_rle.split(), dtype=int)\n\n    # Every even value is the start, every odd value is the \"run\" length\n    starts = s[0::2] - 1\n    lengths = s[1::2]\n    ends = starts + lengths\n\n    # The image image is actually flattened since RLE is a 1D \"run\"\n    if len(shape)==3:\n        h, w, d = shape\n        img = np.zeros((h * w, d), dtype=np.float32)\n    else:\n        h, w = shape\n        img = np.zeros((h * w,), dtype=np.float32)\n\n    # The color here is actually just any integer you want!\n    for lo, hi in zip(starts, ends):\n        img[lo : hi] = color\n\n    # Don't forget to change the image back to the original shape\n    return img.reshape(shape).T\n\n# https://www.kaggle.com/code/jirkaborovec/ftus-segm-eda-export-rle-mask\ndef rle_decode_v2(mask_rle: str, img_shape: tuple = None) -&gt; np.ndarray:\n    seq = mask_rle.split()\n    starts = np.array(list(map(int, seq[0::2])))\n    lengths = np.array(list(map(int, seq[1::2])))\n    assert len(starts) == len(lengths)\n    ends = starts + lengths\n    img = np.zeros((np.product(img_shape),), dtype=np.uint8)\n    for begin, end in zip(starts, ends):\n        img[begin:end] = 1\n    return img.reshape(img_shape).T\n\ndef rle_decode_tf(mask_rle, shape):\n    \"\"\" TBD \"\"\"\n\n    shape = tf.convert_to_tensor(shape, tf.int64)\n    size = tf.math.reduce_prod(shape)\n\n    # Split string\n    s = tf.strings.split(mask_rle)\n    s = tf.strings.to_number(s, tf.int64)\n\n    # Get starts and lengths\n    starts = s[::2] - 1\n    lens = s[1::2]\n\n    # Make ones to be scattered\n    total_ones = tf.reduce_sum(lens)\n    ones = tf.ones([total_ones], tf.uint8)\n\n    # Make scattering indices\n    r = tf.range(total_ones)\n    lens_cum = tf.math.cumsum(lens)\n    s = tf.searchsorted(lens_cum, r, 'right')\n    idx = r + tf.gather(starts - tf.pad(lens_cum[:-1], [(1, 0)]), s)\n\n    # Scatter ones into flattened mask\n    mask_flat = tf.scatter_nd(tf.expand_dims(idx, 1), ones, [size])\n\n    # Reshape into mask\n    return tf.transpose(tf.reshape(mask_flat, shape))\n</code></pre>\n<p><br></p>\n<p><strong>and the encode function</strong></p>\n<p><br></p>\n<pre><code># ref.: https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_encode(img):\n    \"\"\" TBD\n\n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n\n    Returns: \n        run length as string formated\n    \"\"\"\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n</code></pre>\n<hr>",
      "rawMarkdown": "Tagging for visibility: @yashvrdnjain @jirkaborovec \n\nHi there everyone, I'm seeing discrepancies between the polygons in the JSON file and the decoded RLE and need some help.\n\nI did the following experiment (note I have used **`rle_decode`** and **`rle_decode_tf`** in previous competitions without issue):\n\n```python\ndemo = np.zeros((100,100))\ndemo[40:60, 40:60] = 1\ndemo_rle = rle_encode(demo)\n\nrle_demo_1 = rle_decode(demo_rle, (100,100))\nrle_demo_2 = rle_decode_v2(demo_rle, (100,100))\nrle_demo_3 = rle_decode_tf(demo_rle, (100,100))\n\nprint(\"rle_decode    : \", (rle_demo_1!=demo).sum())\nprint(\"rle_decode_v2 : \", (rle_demo_2!=demo).sum())\nprint(\"rle_decode_tf : \", (rle_demo_3!=demo).numpy().sum())\n```\n\nwhich outputs:\n\n```\nrle_decode    :  0\nrle_decode_v2 :  40\nrle_decode_tf :  0\n```\n\n<br>\n\n**This would be fine, however, the .JSON file polygon mask is the same as the decoded RLE mask created by `rle_decode_v2` which as we can see does not recreate the original shape correctly**\n\nThere's a good chance I'm messing something up here. I know RLE is a heavily discussed topic and I thought I had a handle on it... it would appear not. If someone can clarify this for me, or alternatively if the hosts can provide the RLE-and-JSON-related functions that would be very helpful.\n\nThanks in advance!\n\n<br><br>\n\n---\n\n<br>\n\n***Appendix of used functions below:***\n\n<br><br>\n\n**Decode Functions**\n\n<br>\n\n```\n# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\n# modified from: https://www.kaggle.com/inversion/run-length-decoding-quick-start\ndef rle_decode(mask_rle, shape, color=1):\n    \"\"\" TBD\n    \n    Args:\n        mask_rle (str): run-length as string formated (start length)\n        shape (tuple of ints): (height,width) of array to return \n    \n    Returns: \n        Mask (np.array)\n            - 1 indicating mask\n            - 0 indicating background\n\n    \"\"\"\n    # Split the string by space, then convert it into a integer array\n    s = np.array(mask_rle.split(), dtype=int)\n\n    # Every even value is the start, every odd value is the \"run\" length\n    starts = s[0::2] - 1\n    lengths = s[1::2]\n    ends = starts + lengths\n\n    # The image image is actually flattened since RLE is a 1D \"run\"\n    if len(shape)==3:\n        h, w, d = shape\n        img = np.zeros((h * w, d), dtype=np.float32)\n    else:\n        h, w = shape\n        img = np.zeros((h * w,), dtype=np.float32)\n\n    # The color here is actually just any integer you want!\n    for lo, hi in zip(starts, ends):\n        img[lo : hi] = color\n        \n    # Don't forget to change the image back to the original shape\n    return img.reshape(shape).T\n\n# https://www.kaggle.com/code/jirkaborovec/ftus-segm-eda-export-rle-mask\ndef rle_decode_v2(mask_rle: str, img_shape: tuple = None) -> np.ndarray:\n    seq = mask_rle.split()\n    starts = np.array(list(map(int, seq[0::2])))\n    lengths = np.array(list(map(int, seq[1::2])))\n    assert len(starts) == len(lengths)\n    ends = starts + lengths\n    img = np.zeros((np.product(img_shape),), dtype=np.uint8)\n    for begin, end in zip(starts, ends):\n        img[begin:end] = 1\n    return img.reshape(img_shape).T\n\ndef rle_decode_tf(mask_rle, shape):\n    \"\"\" TBD \"\"\"\n    \n    shape = tf.convert_to_tensor(shape, tf.int64)\n    size = tf.math.reduce_prod(shape)\n    \n    # Split string\n    s = tf.strings.split(mask_rle)\n    s = tf.strings.to_number(s, tf.int64)\n    \n    # Get starts and lengths\n    starts = s[::2] - 1\n    lens = s[1::2]\n    \n    # Make ones to be scattered\n    total_ones = tf.reduce_sum(lens)\n    ones = tf.ones([total_ones], tf.uint8)\n    \n    # Make scattering indices\n    r = tf.range(total_ones)\n    lens_cum = tf.math.cumsum(lens)\n    s = tf.searchsorted(lens_cum, r, 'right')\n    idx = r + tf.gather(starts - tf.pad(lens_cum[:-1], [(1, 0)]), s)\n    \n    # Scatter ones into flattened mask\n    mask_flat = tf.scatter_nd(tf.expand_dims(idx, 1), ones, [size])\n    \n    # Reshape into mask\n    return tf.transpose(tf.reshape(mask_flat, shape))\n```\n\n<br>\n\n**and the encode function**\n\n<br>\n\n```\n# ref.: https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_encode(img):\n    \"\"\" TBD\n    \n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n    \n    Returns: \n        run length as string formated\n    \"\"\"\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n```\n\n---",
      "votes": null
    },
    {
      "id": "1844428",
      "postDate": "07/05/2022 14:50:46",
      "content": "<p>Perhaps this comment can clarify: <a href=\"https://www.kaggle.com/competitions/hubmap-organ-segmentation/discussion/332958#1833181\" target=\"_blank\">https://www.kaggle.com/competitions/hubmap-organ-segmentation/discussion/332958#1833181</a> </p>",
      "rawMarkdown": "Perhaps this comment can clarify: https://www.kaggle.com/competitions/hubmap-organ-segmentation/discussion/332958#1833181",
      "votes": null
    },
    {
      "id": "1844485",
      "postDate": "07/05/2022 15:18:41",
      "content": "<p>I'm not sure how that clarifies things, but maybe I'm missing something. </p>\n<p>My takeaways from that comment are:</p>\n<ul>\n<li>The annotations appear inaccurate in some cases</li>\n<li>The RLE annotations appear off by 1 pixel compared to the Polygon mask</li>\n</ul>\n<p>If there's something else I'm missing that would help understand the problem and how to correct for it, please let me know.</p>",
      "rawMarkdown": "I'm not sure how that clarifies things, but maybe I'm missing something. \n\nMy takeaways from that comment are:\n- The annotations appear inaccurate in some cases\n- The RLE annotations appear off by 1 pixel compared to the Polygon mask\n\nIf there's something else I'm missing that would help understand the problem and how to correct for it, please let me know.",
      "votes": null
    },
    {
      "id": "1844618",
      "postDate": "07/05/2022 16:55:08",
      "content": "<p>Actually, I found this comment further up from <a href=\"https://www.kaggle.com/sohier\" target=\"_blank\"><strong>Sohier Dane</strong></a> on that post you linked: </p>\n<blockquote>\n  <p>The json versions are more raw. The annotations might have issues that don't exist in the RLE copies, like overlaps, but can also allow you to distinguish between multiple adjacent FTUs which would all end up in the same mask with RLE.</p>\n</blockquote>\n<p><strong>My take away from this is that we should take the RLE's to be more accurate than the JSON polygons. And, further to that, my RLE decode function 1 and 3 above is correct (even though it produces a different mask than the JSON file)…</strong></p>\n<p><a href=\"https://www.kaggle.com/yashvrdnjain\" target=\"_blank\">@yashvrdnjain</a> </p>",
      "rawMarkdown": "Actually, I found this comment further up from [**Sohier Dane**](https://www.kaggle.com/sohier) on that post you linked: \n\n> The json versions are more raw. The annotations might have issues that don't exist in the RLE copies, like overlaps, but can also allow you to distinguish between multiple adjacent FTUs which would all end up in the same mask with RLE.\n\n**My take away from this is that we should take the RLE's to be more accurate than the JSON polygons. And, further to that, my RLE decode function 1 and 3 above is correct (even though it produces a different mask than the JSON file)...**\n\n@yashvrdnjain",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 1844428,
      "author_name": "yashvrdnjain",
      "author_url": "",
      "post_date": "07/05/2022 14:50:46",
      "content": "<p>Perhaps this comment can clarify: <a href=\"https://www.kaggle.com/competitions/hubmap-organ-segmentation/discussion/332958#1833181\" target=\"_blank\">https://www.kaggle.com/competitions/hubmap-organ-segmentation/discussion/332958#1833181</a> </p>",
      "votes": null,
      "replies": [
        {
          "id": 1844485,
          "author_name": "dschettler8845",
          "author_url": "",
          "post_date": "07/05/2022 15:18:41",
          "content": "<p>I'm not sure how that clarifies things, but maybe I'm missing something. </p>\n<p>My takeaways from that comment are:</p>\n<ul>\n<li>The annotations appear inaccurate in some cases</li>\n<li>The RLE annotations appear off by 1 pixel compared to the Polygon mask</li>\n</ul>\n<p>If there's something else I'm missing that would help understand the problem and how to correct for it, please let me know.</p>",
          "votes": null,
          "replies": []
        },
        {
          "id": 1844618,
          "author_name": "dschettler8845",
          "author_url": "",
          "post_date": "07/05/2022 16:55:08",
          "content": "<p>Actually, I found this comment further up from <a href=\"https://www.kaggle.com/sohier\" target=\"_blank\"><strong>Sohier Dane</strong></a> on that post you linked: </p>\n<blockquote>\n  <p>The json versions are more raw. The annotations might have issues that don't exist in the RLE copies, like overlaps, but can also allow you to distinguish between multiple adjacent FTUs which would all end up in the same mask with RLE.</p>\n</blockquote>\n<p><strong>My take away from this is that we should take the RLE's to be more accurate than the JSON polygons. And, further to that, my RLE decode function 1 and 3 above is correct (even though it produces a different mask than the JSON file)…</strong></p>\n<p><a href=\"https://www.kaggle.com/yashvrdnjain\" target=\"_blank\">@yashvrdnjain</a> </p>",
          "votes": null,
          "replies": []
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "1840655": "Tagging for visibility: @yashvrdnjain @jirkaborovec \n\nHi there everyone, I'm seeing discrepancies between the polygons in the JSON file and the decoded RLE and need some help.\n\nI did the following experiment (note I have used **`rle_decode`** and **`rle_decode_tf`** in previous competitions without issue):\n\n```python\ndemo = np.zeros((100,100))\ndemo[40:60, 40:60] = 1\ndemo_rle = rle_encode(demo)\n\nrle_demo_1 = rle_decode(demo_rle, (100,100))\nrle_demo_2 = rle_decode_v2(demo_rle, (100,100))\nrle_demo_3 = rle_decode_tf(demo_rle, (100,100))\n\nprint(\"rle_decode    : \", (rle_demo_1!=demo).sum())\nprint(\"rle_decode_v2 : \", (rle_demo_2!=demo).sum())\nprint(\"rle_decode_tf : \", (rle_demo_3!=demo).numpy().sum())\n```\n\nwhich outputs:\n\n```\nrle_decode    :  0\nrle_decode_v2 :  40\nrle_decode_tf :  0\n```\n\n<br>\n\n**This would be fine, however, the .JSON file polygon mask is the same as the decoded RLE mask created by `rle_decode_v2` which as we can see does not recreate the original shape correctly**\n\nThere's a good chance I'm messing something up here. I know RLE is a heavily discussed topic and I thought I had a handle on it... it would appear not. If someone can clarify this for me, or alternatively if the hosts can provide the RLE-and-JSON-related functions that would be very helpful.\n\nThanks in advance!\n\n<br><br>\n\n---\n\n<br>\n\n***Appendix of used functions below:***\n\n<br><br>\n\n**Decode Functions**\n\n<br>\n\n```\n# ref: https://www.kaggle.com/paulorzp/run-length-encode-and-decode\n# modified from: https://www.kaggle.com/inversion/run-length-decoding-quick-start\ndef rle_decode(mask_rle, shape, color=1):\n    \"\"\" TBD\n    \n    Args:\n        mask_rle (str): run-length as string formated (start length)\n        shape (tuple of ints): (height,width) of array to return \n    \n    Returns: \n        Mask (np.array)\n            - 1 indicating mask\n            - 0 indicating background\n\n    \"\"\"\n    # Split the string by space, then convert it into a integer array\n    s = np.array(mask_rle.split(), dtype=int)\n\n    # Every even value is the start, every odd value is the \"run\" length\n    starts = s[0::2] - 1\n    lengths = s[1::2]\n    ends = starts + lengths\n\n    # The image image is actually flattened since RLE is a 1D \"run\"\n    if len(shape)==3:\n        h, w, d = shape\n        img = np.zeros((h * w, d), dtype=np.float32)\n    else:\n        h, w = shape\n        img = np.zeros((h * w,), dtype=np.float32)\n\n    # The color here is actually just any integer you want!\n    for lo, hi in zip(starts, ends):\n        img[lo : hi] = color\n        \n    # Don't forget to change the image back to the original shape\n    return img.reshape(shape).T\n\n# https://www.kaggle.com/code/jirkaborovec/ftus-segm-eda-export-rle-mask\ndef rle_decode_v2(mask_rle: str, img_shape: tuple = None) -> np.ndarray:\n    seq = mask_rle.split()\n    starts = np.array(list(map(int, seq[0::2])))\n    lengths = np.array(list(map(int, seq[1::2])))\n    assert len(starts) == len(lengths)\n    ends = starts + lengths\n    img = np.zeros((np.product(img_shape),), dtype=np.uint8)\n    for begin, end in zip(starts, ends):\n        img[begin:end] = 1\n    return img.reshape(img_shape).T\n\ndef rle_decode_tf(mask_rle, shape):\n    \"\"\" TBD \"\"\"\n    \n    shape = tf.convert_to_tensor(shape, tf.int64)\n    size = tf.math.reduce_prod(shape)\n    \n    # Split string\n    s = tf.strings.split(mask_rle)\n    s = tf.strings.to_number(s, tf.int64)\n    \n    # Get starts and lengths\n    starts = s[::2] - 1\n    lens = s[1::2]\n    \n    # Make ones to be scattered\n    total_ones = tf.reduce_sum(lens)\n    ones = tf.ones([total_ones], tf.uint8)\n    \n    # Make scattering indices\n    r = tf.range(total_ones)\n    lens_cum = tf.math.cumsum(lens)\n    s = tf.searchsorted(lens_cum, r, 'right')\n    idx = r + tf.gather(starts - tf.pad(lens_cum[:-1], [(1, 0)]), s)\n    \n    # Scatter ones into flattened mask\n    mask_flat = tf.scatter_nd(tf.expand_dims(idx, 1), ones, [size])\n    \n    # Reshape into mask\n    return tf.transpose(tf.reshape(mask_flat, shape))\n```\n\n<br>\n\n**and the encode function**\n\n<br>\n\n```\n# ref.: https://www.kaggle.com/stainsby/fast-tested-rle\ndef rle_encode(img):\n    \"\"\" TBD\n    \n    Args:\n        img (np.array): \n            - 1 indicating mask\n            - 0 indicating background\n    \n    Returns: \n        run length as string formated\n    \"\"\"\n    pixels = img.T.flatten()\n    pixels = np.concatenate([[0], pixels, [0]])\n    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1\n    runs[1::2] -= runs[::2]\n    return ' '.join(str(x) for x in runs)\n```\n\n---",
    "1844428": "Perhaps this comment can clarify: https://www.kaggle.com/competitions/hubmap-organ-segmentation/discussion/332958#1833181",
    "1844485": "I'm not sure how that clarifies things, but maybe I'm missing something. \n\nMy takeaways from that comment are:\n- The annotations appear inaccurate in some cases\n- The RLE annotations appear off by 1 pixel compared to the Polygon mask\n\nIf there's something else I'm missing that would help understand the problem and how to correct for it, please let me know.",
    "1844618": "Actually, I found this comment further up from [**Sohier Dane**](https://www.kaggle.com/sohier) on that post you linked: \n\n> The json versions are more raw. The annotations might have issues that don't exist in the RLE copies, like overlaps, but can also allow you to distinguish between multiple adjacent FTUs which would all end up in the same mask with RLE.\n\n**My take away from this is that we should take the RLE's to be more accurate than the JSON polygons. And, further to that, my RLE decode function 1 and 3 above is correct (even though it produces a different mask than the JSON file)...**\n\n@yashvrdnjain"
  },
  "source": "meta"
}