{
  "id": 680280,
  "title": "6-th place solution",
  "url": "/competitions/vesuvius-challenge-surface-detection/discussion/680280",
  "author_name": "da da",
  "post_date": "2026-03-06T23:17:56.218000",
  "votes": 6,
  "comment_count": 0,
  "views": 0,
  "content": "<h3>Model</h3>\n<p>We used nnUnetTrainerMedialSurfaceRecall with additional augmentation for rotation between axes 1 and 2, as the papyrus sheets in the training data were only oriented along these axes. </p>\n<p>Patch size: 128 \nBatch size: 8 \nEpochs: 1200 </p>\n<p>For the final submit, we ensembled 5 folds + the \"all\" fold. </p>\n<h3>Inference</h3>\n<p>To fit these models into the inference time limits, we modified the TTA logic. For each flip combination, we run an equal number of different models (resulting in 3/8 flip combinations per each of the 6 models). Additionally, we parallelized the inference across 2 T4 GPUs and simply averaged the predictions. </p>\n<p>We managed to submit the ensemble only on the last day. It turned out that the inference took 6 out of 9 hours, meaning we could have significantly increased the number of models or TTA passes. </p>\n<h3>Watershed</h3>\n<p>We calculate geodesic distance using fastgeodesis, then search for local peaks and apply the watershed algorithm. </p>\n<h3>Diagonal Holes</h3>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b34731c6f56e04029ba398d3ee28e0d%2Fimage.png?generation=1772837114106636&amp;alt=media\" alt=\"\">\n(image from Vesuvius Team solution) </p>\n<h3>Betti Numbers Calculation</h3>\n<p>Using a library for 3D Betti matching, we can do more than just estimate the number of topological loops; we can also find their contours and birth/death coordinates. By parallelizing the calculations across multiple CPU cores, we managed to complete the processing within a reasonable timeframe. More optimizations were possible, but we haven't hit the time limit yet. However, the contours often shifted, sometimes even moving onto other sheets. </p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F01e7cfc04f13348eb9be3a41e96285f1%2Fimage%20copy.png?generation=1772837137230519&amp;alt=media\" alt=\"\"></p>\n<h3>Topological Loops</h3>\n<p>We clustered the points and extracted the primary cycle, calculated its volume, and filtered out excessively large ones. We then constructed a plane based on these points. Due to contour inaccuracies, this method only correctly filled small loops. </p>\n<h3>Loop Unrolling (Opening Loops)</h3>\n<p>We isolate only the specific sheet containing the loop, create a 2D projection, and select a point inside the loop. Using Dijkstra's algorithm on the projection, we find the shortest path to the edge, applying a higher penalty for paths that cross the sheet. We then remove this path in 3D on that specific sheet. </p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b2fcda3ed0c93a474d6e33ed881e993%2Fimage%20copy%202.png?generation=1772837146850649&amp;alt=media\" alt=\"\"></p>\n<h3>RBF</h3>\n<p>In addition, we extracted loop contours by identifying connected components in 2D projections of the sheets and filled them using RBF</p>\n<h3>Filling Gaps</h3>\n<p>We also generated other projections, such as the number of transitions from \"sheet\" to \"non-sheet.\" If the area of pixels representing multiple transitions between sheets is small in this projection, we can connect them. This is quite important when we are limited in the choice of projection axes; this method helps close cycles in axes that other algorithms cannot reach. </p>\n<h3>Removing Cavities</h3>\n<p>We can invert the surface and the background, then calculate the connected components and remove the small ones. By inverting the result back, we effectively eliminate internal cavities. </p>\n<h3>Creating Cavities</h3>\n<p>We noticed a pattern: the higher the average proportion of \"1s\" in the prediction, the more likely the sample is to have poor topology, making it beneficial to add cavities. However, some of these cavities might fall into unlabeled regions and not be counted. We calculated several coordinates to maximize the conditional probability of a cavity landing in a labeled zone, based on the training labels. Ultimately, we decided to add 3 cavities to samples where the proportion of ones in the prediction exceeded 0.175. </p>\n<p>Furthermore, we observed that certain combinations of post-processing methods are effective only for specific scrolls. </p>\n<h3>Impact of Unlabeled Regions</h3>\n<p>In many samples, no matter how much we improved the post-processing, the unlabeled regions continued to generate loops. Examples of how unlabeled regions degrade the topology: 0 --&gt; 17 loops \n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F8945ff90be2c1d86a8ea4ee3b1e26f78%2Fimage%20copy%203.png?generation=1772837156577642&amp;alt=media\" alt=\"\"></p>\n<p>0 --&gt; 12 loops (these are diagonal and have problematic contours) \n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F20ef60eb83c0fcb20a907e6c9591e468%2Fimage%20copy%204.png?generation=1772837167320334&amp;alt=media\" alt=\"\"></p>\n<p>This occurred primarily on scrolls with high sheet density (35360, 53997). The impact on scrolls with low sheet density and favorable unlabeled regions (34117, 26010) was much lower. Therefore, we used more aggressive post-processing on 34117 and 26010, while using a lighter version for the others. </p>\n<p>We have encountered significant issues when working with merged sheets. If there are two bridges between the sheets, a loop is formed. Furthermore, such sheets are more susceptible to the influence of unlabeled areas, which frequently create additional loops in these specific locations. We tested several algorithms but were ultimately unable to resolve this.</p>",
  "messages": [
    {
      "id": 3418042,
      "postDate": "2026-03-06T23:17:56.220Z",
      "content": "<h3>Model</h3>\n<p>We used nnUnetTrainerMedialSurfaceRecall with additional augmentation for rotation between axes 1 and 2, as the papyrus sheets in the training data were only oriented along these axes. </p>\n<p>Patch size: 128 \nBatch size: 8 \nEpochs: 1200 </p>\n<p>For the final submit, we ensembled 5 folds + the \"all\" fold. </p>\n<h3>Inference</h3>\n<p>To fit these models into the inference time limits, we modified the TTA logic. For each flip combination, we run an equal number of different models (resulting in 3/8 flip combinations per each of the 6 models). Additionally, we parallelized the inference across 2 T4 GPUs and simply averaged the predictions. </p>\n<p>We managed to submit the ensemble only on the last day. It turned out that the inference took 6 out of 9 hours, meaning we could have significantly increased the number of models or TTA passes. </p>\n<h3>Watershed</h3>\n<p>We calculate geodesic distance using fastgeodesis, then search for local peaks and apply the watershed algorithm. </p>\n<h3>Diagonal Holes</h3>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b34731c6f56e04029ba398d3ee28e0d%2Fimage.png?generation=1772837114106636&amp;alt=media\" alt=\"\">\n(image from Vesuvius Team solution) </p>\n<h3>Betti Numbers Calculation</h3>\n<p>Using a library for 3D Betti matching, we can do more than just estimate the number of topological loops; we can also find their contours and birth/death coordinates. By parallelizing the calculations across multiple CPU cores, we managed to complete the processing within a reasonable timeframe. More optimizations were possible, but we haven't hit the time limit yet. However, the contours often shifted, sometimes even moving onto other sheets. </p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F01e7cfc04f13348eb9be3a41e96285f1%2Fimage%20copy.png?generation=1772837137230519&amp;alt=media\" alt=\"\"></p>\n<h3>Topological Loops</h3>\n<p>We clustered the points and extracted the primary cycle, calculated its volume, and filtered out excessively large ones. We then constructed a plane based on these points. Due to contour inaccuracies, this method only correctly filled small loops. </p>\n<h3>Loop Unrolling (Opening Loops)</h3>\n<p>We isolate only the specific sheet containing the loop, create a 2D projection, and select a point inside the loop. Using Dijkstra's algorithm on the projection, we find the shortest path to the edge, applying a higher penalty for paths that cross the sheet. We then remove this path in 3D on that specific sheet. </p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b2fcda3ed0c93a474d6e33ed881e993%2Fimage%20copy%202.png?generation=1772837146850649&amp;alt=media\" alt=\"\"></p>\n<h3>RBF</h3>\n<p>In addition, we extracted loop contours by identifying connected components in 2D projections of the sheets and filled them using RBF</p>\n<h3>Filling Gaps</h3>\n<p>We also generated other projections, such as the number of transitions from \"sheet\" to \"non-sheet.\" If the area of pixels representing multiple transitions between sheets is small in this projection, we can connect them. This is quite important when we are limited in the choice of projection axes; this method helps close cycles in axes that other algorithms cannot reach. </p>\n<h3>Removing Cavities</h3>\n<p>We can invert the surface and the background, then calculate the connected components and remove the small ones. By inverting the result back, we effectively eliminate internal cavities. </p>\n<h3>Creating Cavities</h3>\n<p>We noticed a pattern: the higher the average proportion of \"1s\" in the prediction, the more likely the sample is to have poor topology, making it beneficial to add cavities. However, some of these cavities might fall into unlabeled regions and not be counted. We calculated several coordinates to maximize the conditional probability of a cavity landing in a labeled zone, based on the training labels. Ultimately, we decided to add 3 cavities to samples where the proportion of ones in the prediction exceeded 0.175. </p>\n<p>Furthermore, we observed that certain combinations of post-processing methods are effective only for specific scrolls. </p>\n<h3>Impact of Unlabeled Regions</h3>\n<p>In many samples, no matter how much we improved the post-processing, the unlabeled regions continued to generate loops. Examples of how unlabeled regions degrade the topology: 0 --&gt; 17 loops \n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F8945ff90be2c1d86a8ea4ee3b1e26f78%2Fimage%20copy%203.png?generation=1772837156577642&amp;alt=media\" alt=\"\"></p>\n<p>0 --&gt; 12 loops (these are diagonal and have problematic contours) \n<img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F20ef60eb83c0fcb20a907e6c9591e468%2Fimage%20copy%204.png?generation=1772837167320334&amp;alt=media\" alt=\"\"></p>\n<p>This occurred primarily on scrolls with high sheet density (35360, 53997). The impact on scrolls with low sheet density and favorable unlabeled regions (34117, 26010) was much lower. Therefore, we used more aggressive post-processing on 34117 and 26010, while using a lighter version for the others. </p>\n<p>We have encountered significant issues when working with merged sheets. If there are two bridges between the sheets, a loop is formed. Furthermore, such sheets are more susceptible to the influence of unlabeled areas, which frequently create additional loops in these specific locations. We tested several algorithms but were ultimately unable to resolve this.</p>",
      "rawMarkdown": "### Model  \n\nWe used nnUnetTrainerMedialSurfaceRecall with additional augmentation for rotation between axes 1 and 2, as the papyrus sheets in the training data were only oriented along these axes. \n\nPatch size: 128 \nBatch size: 8 \nEpochs: 1200 \n\nFor the final submit, we ensembled 5 folds + the \"all\" fold. \n\n### Inference \nTo fit these models into the inference time limits, we modified the TTA logic. For each flip combination, we run an equal number of different models (resulting in 3/8 flip combinations per each of the 6 models). Additionally, we parallelized the inference across 2 T4 GPUs and simply averaged the predictions. \n\nWe managed to submit the ensemble only on the last day. It turned out that the inference took 6 out of 9 hours, meaning we could have significantly increased the number of models or TTA passes. \n\n### Watershed  \n\nWe calculate geodesic distance using fastgeodesis, then search for local peaks and apply the watershed algorithm. \n\n### Diagonal Holes \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b34731c6f56e04029ba398d3ee28e0d%2Fimage.png?generation=1772837114106636&alt=media)\n(image from Vesuvius Team solution) \n### Betti Numbers Calculation  \n\nUsing a library for 3D Betti matching, we can do more than just estimate the number of topological loops; we can also find their contours and birth/death coordinates. By parallelizing the calculations across multiple CPU cores, we managed to complete the processing within a reasonable timeframe. More optimizations were possible, but we haven't hit the time limit yet. However, the contours often shifted, sometimes even moving onto other sheets. \n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F01e7cfc04f13348eb9be3a41e96285f1%2Fimage%20copy.png?generation=1772837137230519&alt=media)\n### Topological Loops  \n\nWe clustered the points and extracted the primary cycle, calculated its volume, and filtered out excessively large ones. We then constructed a plane based on these points. Due to contour inaccuracies, this method only correctly filled small loops. \n\n### Loop Unrolling (Opening Loops)  \n\nWe isolate only the specific sheet containing the loop, create a 2D projection, and select a point inside the loop. Using Dijkstra's algorithm on the projection, we find the shortest path to the edge, applying a higher penalty for paths that cross the sheet. We then remove this path in 3D on that specific sheet. \n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b2fcda3ed0c93a474d6e33ed881e993%2Fimage%20copy%202.png?generation=1772837146850649&alt=media)\n### RBF  \n\nIn addition, we extracted loop contours by identifying connected components in 2D projections of the sheets and filled them using RBF\n### Filling Gaps  \n\nWe also generated other projections, such as the number of transitions from \"sheet\" to \"non-sheet.\" If the area of pixels representing multiple transitions between sheets is small in this projection, we can connect them. This is quite important when we are limited in the choice of projection axes; this method helps close cycles in axes that other algorithms cannot reach. \n\n### Removing Cavities\n\nWe can invert the surface and the background, then calculate the connected components and remove the small ones. By inverting the result back, we effectively eliminate internal cavities. \n\n### Creating Cavities  \n\nWe noticed a pattern: the higher the average proportion of \"1s\" in the prediction, the more likely the sample is to have poor topology, making it beneficial to add cavities. However, some of these cavities might fall into unlabeled regions and not be counted. We calculated several coordinates to maximize the conditional probability of a cavity landing in a labeled zone, based on the training labels. Ultimately, we decided to add 3 cavities to samples where the proportion of ones in the prediction exceeded 0.175. \n\nFurthermore, we observed that certain combinations of post-processing methods are effective only for specific scrolls. \n\n### Impact of Unlabeled Regions  \n\nIn many samples, no matter how much we improved the post-processing, the unlabeled regions continued to generate loops. Examples of how unlabeled regions degrade the topology: 0 --> 17 loops \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F8945ff90be2c1d86a8ea4ee3b1e26f78%2Fimage%20copy%203.png?generation=1772837156577642&alt=media)\n\n0 --> 12 loops (these are diagonal and have problematic contours) \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F20ef60eb83c0fcb20a907e6c9591e468%2Fimage%20copy%204.png?generation=1772837167320334&alt=media)\n\n\nThis occurred primarily on scrolls with high sheet density (35360, 53997). The impact on scrolls with low sheet density and favorable unlabeled regions (34117, 26010) was much lower. Therefore, we used more aggressive post-processing on 34117 and 26010, while using a lighter version for the others. \n\nWe have encountered significant issues when working with merged sheets. If there are two bridges between the sheets, a loop is formed. Furthermore, such sheets are more susceptible to the influence of unlabeled areas, which frequently create additional loops in these specific locations. We tested several algorithms but were ultimately unable to resolve this.",
      "votes": 6
    }
  ],
  "comments": [],
  "raw_markdown_by_id": {
    "3418042": "### Model  \n\nWe used nnUnetTrainerMedialSurfaceRecall with additional augmentation for rotation between axes 1 and 2, as the papyrus sheets in the training data were only oriented along these axes. \n\nPatch size: 128 \nBatch size: 8 \nEpochs: 1200 \n\nFor the final submit, we ensembled 5 folds + the \"all\" fold. \n\n### Inference \nTo fit these models into the inference time limits, we modified the TTA logic. For each flip combination, we run an equal number of different models (resulting in 3/8 flip combinations per each of the 6 models). Additionally, we parallelized the inference across 2 T4 GPUs and simply averaged the predictions. \n\nWe managed to submit the ensemble only on the last day. It turned out that the inference took 6 out of 9 hours, meaning we could have significantly increased the number of models or TTA passes. \n\n### Watershed  \n\nWe calculate geodesic distance using fastgeodesis, then search for local peaks and apply the watershed algorithm. \n\n### Diagonal Holes \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b34731c6f56e04029ba398d3ee28e0d%2Fimage.png?generation=1772837114106636&alt=media)\n(image from Vesuvius Team solution) \n### Betti Numbers Calculation  \n\nUsing a library for 3D Betti matching, we can do more than just estimate the number of topological loops; we can also find their contours and birth/death coordinates. By parallelizing the calculations across multiple CPU cores, we managed to complete the processing within a reasonable timeframe. More optimizations were possible, but we haven't hit the time limit yet. However, the contours often shifted, sometimes even moving onto other sheets. \n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F01e7cfc04f13348eb9be3a41e96285f1%2Fimage%20copy.png?generation=1772837137230519&alt=media)\n### Topological Loops  \n\nWe clustered the points and extracted the primary cycle, calculated its volume, and filtered out excessively large ones. We then constructed a plane based on these points. Due to contour inaccuracies, this method only correctly filled small loops. \n\n### Loop Unrolling (Opening Loops)  \n\nWe isolate only the specific sheet containing the loop, create a 2D projection, and select a point inside the loop. Using Dijkstra's algorithm on the projection, we find the shortest path to the edge, applying a higher penalty for paths that cross the sheet. We then remove this path in 3D on that specific sheet. \n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F2b2fcda3ed0c93a474d6e33ed881e993%2Fimage%20copy%202.png?generation=1772837146850649&alt=media)\n### RBF  \n\nIn addition, we extracted loop contours by identifying connected components in 2D projections of the sheets and filled them using RBF\n### Filling Gaps  \n\nWe also generated other projections, such as the number of transitions from \"sheet\" to \"non-sheet.\" If the area of pixels representing multiple transitions between sheets is small in this projection, we can connect them. This is quite important when we are limited in the choice of projection axes; this method helps close cycles in axes that other algorithms cannot reach. \n\n### Removing Cavities\n\nWe can invert the surface and the background, then calculate the connected components and remove the small ones. By inverting the result back, we effectively eliminate internal cavities. \n\n### Creating Cavities  \n\nWe noticed a pattern: the higher the average proportion of \"1s\" in the prediction, the more likely the sample is to have poor topology, making it beneficial to add cavities. However, some of these cavities might fall into unlabeled regions and not be counted. We calculated several coordinates to maximize the conditional probability of a cavity landing in a labeled zone, based on the training labels. Ultimately, we decided to add 3 cavities to samples where the proportion of ones in the prediction exceeded 0.175. \n\nFurthermore, we observed that certain combinations of post-processing methods are effective only for specific scrolls. \n\n### Impact of Unlabeled Regions  \n\nIn many samples, no matter how much we improved the post-processing, the unlabeled regions continued to generate loops. Examples of how unlabeled regions degrade the topology: 0 --> 17 loops \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F8945ff90be2c1d86a8ea4ee3b1e26f78%2Fimage%20copy%203.png?generation=1772837156577642&alt=media)\n\n0 --> 12 loops (these are diagonal and have problematic contours) \n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F32208420%2F20ef60eb83c0fcb20a907e6c9591e468%2Fimage%20copy%204.png?generation=1772837167320334&alt=media)\n\n\nThis occurred primarily on scrolls with high sheet density (35360, 53997). The impact on scrolls with low sheet density and favorable unlabeled regions (34117, 26010) was much lower. Therefore, we used more aggressive post-processing on 34117 and 26010, while using a lighter version for the others. \n\nWe have encountered significant issues when working with merged sheets. If there are two bridges between the sheets, a loop is formed. Furthermore, such sheets are more susceptible to the influence of unlabeled areas, which frequently create additional loops in these specific locations. We tested several algorithms but were ultimately unable to resolve this."
  }
}