{
  "id": 679222,
  "title": "4-th Place Solution",
  "url": "/competitions/vesuvius-challenge-surface-detection/discussion/679222",
  "author_name": "Starry",
  "post_date": "2026-02-28T00:53:20.801000",
  "votes": 31,
  "comment_count": 13,
  "views": 0,
  "content": "<p><strong>Thanks for host for this meaningful and interesting competetion as well as detail answer for metric questions. And congradulations for winners!</strong></p>\n<p><strong>This competetion just show the powerful generalization of nnUNet again. It's the best segmentation framework in the world (I think) !</strong></p>\n<h1>TLDR:</h1>\n<p><strong>Huge nnUNet and heavy postprocessing</strong></p>\n<h1>1  . <strong>Segmentation Model:</strong></h1>\n<p>Model training : \nI did nothing just train a huge nnUNet (Resenc UNet with 7 stages covolution) with sufficient epoch (2000). </p>\n<p>Inference: Uses nnUNetPredictor with a tile_step_size of 0.5 and test-time augmentation (mirroring) to ensure smooth and robust predictions.</p>\n<p>Preliminary postprocessing: threshould=0.2 and remove components with size &lt; 1000.</p>\n<h2>2  . <strong>Postprocessing: Two Stage hole Filling and Metric Hacking</strong></h2>\n<ul>\n<li><strong>1st Stage: PCA Hole Filling (Projection-based)</strong></li>\n</ul>\n<p>This stage targets large, obvious topological loops (Betti-1 errors) by analyzing 2D projections.</p>\n<p>Detection: The projection_betti1_finding function projects 3D connected components onto XY, YZ, and ZX planes. It identifies \"isolated regions\" (background holes) that do not touch the image boundaries.</p>\n<p>Refinement:</p>\n<p>It extracts the 3D coordinates of these problematic surfaces and applies Principal Component Analysis (PCA) to find the optimal 2D fitting plane.\nThe points are mapped to 2D, where a binary_fill_holes operation is performed.\nThe filled results are re-mapped back to 3D space using the inverse_transform of the PCA.\nThis step intend to fill some large holes.</p>\n<p>In fact, there are another more elegant solution for this part: \ncomponent detection -&gt; PCA transformation -&gt; RBF function fitting (torchrbf acceleration) -&gt; interpolation -&gt; PCA inverse transformation -&gt; 3D dialation</p>\n<p>However, there are a critical problem in this part:</p>\n<p><strong>How to separate adhering sheets ?</strong></p>\n<p>For those adhering sheets, this method would fail. And till now, I still can not find a good method to solve this problem. Looking forward other teams solution.</p>\n<ul>\n<li><strong>2nd Stage: PCA Hole Filling (Betti-Matching-based)</strong></li>\n</ul>\n<p>A more granular refinement stage using specialized topological tools.</p>\n<p>Detection: Utilizing the betti_matching (C++) library, the bmbarcode_betti1_finding function performs a local persistent homology analysis.</p>\n<p>Local Processing: The volume is divided into 20, 20, 20 chunks. If a local block exhibits a Betti-1 error (determined via barcode calculation), the PCA filling logic is applied locally to seal small breaks or gaps in the structure.</p>\n<p>If one check the competetion metric, it can be found that the topo score is very sensitive, even if a small hole would contribute to a large drop for topo score.</p>\n<ul>\n<li><strong>Hacking the Metric</strong></li>\n</ul>\n<p>This specific logic is designed to optimize the final topological score by manipulating the betti-2 count. Although host claim that <a href=\"https://www.kaggle.com/competitions/vesuvius-challenge-surface-detection/discussion/678904#3414194\" target=\"_blank\">practical impact at the top end should be limited</a>, it still exist some bugs (I think) and can be utilized in the follow way:</p>\n<p>Automatic Betti-2 Hole Filling: The fill_betti2_holes function identifies background components completely enclosed by the foreground and fills them to eliminate unwanted internal cavities. Set betti-2 = 0</p>\n<p>Manual create betti2 number: In cases where specific error thresholds are met (e.g., high betti-0 or betti-1 error, for example: betti-0 &gt; 50 or betti-1 &gt; 5 , can be calculated using the bm.compute_barcode function), then create a small betti-2 error mannually. This step is dangerous to some degree, as it can just calculate  betti-0 or betti-1 numbers of the whole predictions, but in evaluation, there are igore masks which could contribute to totally different betti-0 / 1 numbers.</p>\n<p>It locates a voxel that is \"fully enclosed\" (all 26 neighbors are foreground) within the largest connected component.\nIt sets that single voxel to 0, effectively creating a manual Betti-2 hole.</p>\n<p>Strategy: This \"hack\" serves to balance the topological metrics if the evaluation criteria favor a specific distribution of Betti errors across different dimensions.</p>\n<h1>Some other thoughs but have not try:</h1>\n<ol>\n<li>end-2-end training a rbf function to get 0 holes sheets</li>\n<li>traing a point cloud seperation model to seperate the adhering sheets</li>\n</ol>\n<p><strong>It must be acknowledged that my solution is not so elegant, but it do works. Looking forword to learn other teams solutions.</strong></p>\n<p><strong>This writeup maybe lose some details. I will refine it in following days. Feel free to ask any questions.</strong></p>\n<p>Some ablations (Based on my best models)</p>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>Public LB</th>\n<th>Private LB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Segmentation Model + Remove dust</td>\n<td>0.590</td>\n<td>0.609</td>\n</tr>\n<tr>\n<td>+ Stage1 + Stage2 + Hacking</td>\n<td>0.602</td>\n<td>0.622</td>\n</tr>\n</tbody>\n</table>\n<p>submission notebook: <a href=\"https://www.kaggle.com/code/shtljw/vcsd-4th-place-solution\" target=\"_blank\">https://www.kaggle.com/code/shtljw/vcsd-4th-place-solution</a></p>",
  "messages": [
    {
      "id": 3414937,
      "postDate": "2026-02-28T00:53:20.803Z",
      "content": "<p><strong>Thanks for host for this meaningful and interesting competetion as well as detail answer for metric questions. And congradulations for winners!</strong></p>\n<p><strong>This competetion just show the powerful generalization of nnUNet again. It's the best segmentation framework in the world (I think) !</strong></p>\n<h1>TLDR:</h1>\n<p><strong>Huge nnUNet and heavy postprocessing</strong></p>\n<h1>1  . <strong>Segmentation Model:</strong></h1>\n<p>Model training : \nI did nothing just train a huge nnUNet (Resenc UNet with 7 stages covolution) with sufficient epoch (2000). </p>\n<p>Inference: Uses nnUNetPredictor with a tile_step_size of 0.5 and test-time augmentation (mirroring) to ensure smooth and robust predictions.</p>\n<p>Preliminary postprocessing: threshould=0.2 and remove components with size &lt; 1000.</p>\n<h2>2  . <strong>Postprocessing: Two Stage hole Filling and Metric Hacking</strong></h2>\n<ul>\n<li><strong>1st Stage: PCA Hole Filling (Projection-based)</strong></li>\n</ul>\n<p>This stage targets large, obvious topological loops (Betti-1 errors) by analyzing 2D projections.</p>\n<p>Detection: The projection_betti1_finding function projects 3D connected components onto XY, YZ, and ZX planes. It identifies \"isolated regions\" (background holes) that do not touch the image boundaries.</p>\n<p>Refinement:</p>\n<p>It extracts the 3D coordinates of these problematic surfaces and applies Principal Component Analysis (PCA) to find the optimal 2D fitting plane.\nThe points are mapped to 2D, where a binary_fill_holes operation is performed.\nThe filled results are re-mapped back to 3D space using the inverse_transform of the PCA.\nThis step intend to fill some large holes.</p>\n<p>In fact, there are another more elegant solution for this part: \ncomponent detection -&gt; PCA transformation -&gt; RBF function fitting (torchrbf acceleration) -&gt; interpolation -&gt; PCA inverse transformation -&gt; 3D dialation</p>\n<p>However, there are a critical problem in this part:</p>\n<p><strong>How to separate adhering sheets ?</strong></p>\n<p>For those adhering sheets, this method would fail. And till now, I still can not find a good method to solve this problem. Looking forward other teams solution.</p>\n<ul>\n<li><strong>2nd Stage: PCA Hole Filling (Betti-Matching-based)</strong></li>\n</ul>\n<p>A more granular refinement stage using specialized topological tools.</p>\n<p>Detection: Utilizing the betti_matching (C++) library, the bmbarcode_betti1_finding function performs a local persistent homology analysis.</p>\n<p>Local Processing: The volume is divided into 20, 20, 20 chunks. If a local block exhibits a Betti-1 error (determined via barcode calculation), the PCA filling logic is applied locally to seal small breaks or gaps in the structure.</p>\n<p>If one check the competetion metric, it can be found that the topo score is very sensitive, even if a small hole would contribute to a large drop for topo score.</p>\n<ul>\n<li><strong>Hacking the Metric</strong></li>\n</ul>\n<p>This specific logic is designed to optimize the final topological score by manipulating the betti-2 count. Although host claim that <a href=\"https://www.kaggle.com/competitions/vesuvius-challenge-surface-detection/discussion/678904#3414194\" target=\"_blank\">practical impact at the top end should be limited</a>, it still exist some bugs (I think) and can be utilized in the follow way:</p>\n<p>Automatic Betti-2 Hole Filling: The fill_betti2_holes function identifies background components completely enclosed by the foreground and fills them to eliminate unwanted internal cavities. Set betti-2 = 0</p>\n<p>Manual create betti2 number: In cases where specific error thresholds are met (e.g., high betti-0 or betti-1 error, for example: betti-0 &gt; 50 or betti-1 &gt; 5 , can be calculated using the bm.compute_barcode function), then create a small betti-2 error mannually. This step is dangerous to some degree, as it can just calculate  betti-0 or betti-1 numbers of the whole predictions, but in evaluation, there are igore masks which could contribute to totally different betti-0 / 1 numbers.</p>\n<p>It locates a voxel that is \"fully enclosed\" (all 26 neighbors are foreground) within the largest connected component.\nIt sets that single voxel to 0, effectively creating a manual Betti-2 hole.</p>\n<p>Strategy: This \"hack\" serves to balance the topological metrics if the evaluation criteria favor a specific distribution of Betti errors across different dimensions.</p>\n<h1>Some other thoughs but have not try:</h1>\n<ol>\n<li>end-2-end training a rbf function to get 0 holes sheets</li>\n<li>traing a point cloud seperation model to seperate the adhering sheets</li>\n</ol>\n<p><strong>It must be acknowledged that my solution is not so elegant, but it do works. Looking forword to learn other teams solutions.</strong></p>\n<p><strong>This writeup maybe lose some details. I will refine it in following days. Feel free to ask any questions.</strong></p>\n<p>Some ablations (Based on my best models)</p>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>Public LB</th>\n<th>Private LB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Segmentation Model + Remove dust</td>\n<td>0.590</td>\n<td>0.609</td>\n</tr>\n<tr>\n<td>+ Stage1 + Stage2 + Hacking</td>\n<td>0.602</td>\n<td>0.622</td>\n</tr>\n</tbody>\n</table>\n<p>submission notebook: <a href=\"https://www.kaggle.com/code/shtljw/vcsd-4th-place-solution\" target=\"_blank\">https://www.kaggle.com/code/shtljw/vcsd-4th-place-solution</a></p>",
      "rawMarkdown": "**Thanks for host for this meaningful and interesting competetion as well as detail answer for metric questions. And congradulations for winners!**\n\n**This competetion just show the powerful generalization of nnUNet again. It's the best segmentation framework in the world (I think) !**\n\n# TLDR:\n\n**Huge nnUNet and heavy postprocessing**\n\n# 1  . **Segmentation Model:**\n\nModel training : \nI did nothing just train a huge nnUNet (Resenc UNet with 7 stages covolution) with sufficient epoch (2000). \n\nInference: Uses nnUNetPredictor with a tile_step_size of 0.5 and test-time augmentation (mirroring) to ensure smooth and robust predictions.\n\nPreliminary postprocessing: threshould=0.2 and remove components with size < 1000.\n\n## 2  . **Postprocessing: Two Stage hole Filling and Metric Hacking**\n\n- **1st Stage: PCA Hole Filling (Projection-based)**\n\nThis stage targets large, obvious topological loops (Betti-1 errors) by analyzing 2D projections.\n\nDetection: The projection_betti1_finding function projects 3D connected components onto XY, YZ, and ZX planes. It identifies \"isolated regions\" (background holes) that do not touch the image boundaries.\n\nRefinement:\n\nIt extracts the 3D coordinates of these problematic surfaces and applies Principal Component Analysis (PCA) to find the optimal 2D fitting plane.\nThe points are mapped to 2D, where a binary_fill_holes operation is performed.\nThe filled results are re-mapped back to 3D space using the inverse_transform of the PCA.\nThis step intend to fill some large holes.\n\nIn fact, there are another more elegant solution for this part: \ncomponent detection -> PCA transformation -> RBF function fitting (torchrbf acceleration) -> interpolation -> PCA inverse transformation -> 3D dialation\n\nHowever, there are a critical problem in this part:\n\n**How to separate adhering sheets ?**\n\nFor those adhering sheets, this method would fail. And till now, I still can not find a good method to solve this problem. Looking forward other teams solution.\n\n- **2nd Stage: PCA Hole Filling (Betti-Matching-based)**\n\nA more granular refinement stage using specialized topological tools.\n\nDetection: Utilizing the betti_matching (C++) library, the bmbarcode_betti1_finding function performs a local persistent homology analysis.\n\nLocal Processing: The volume is divided into 20, 20, 20 chunks. If a local block exhibits a Betti-1 error (determined via barcode calculation), the PCA filling logic is applied locally to seal small breaks or gaps in the structure.\n\nIf one check the competetion metric, it can be found that the topo score is very sensitive, even if a small hole would contribute to a large drop for topo score.\n\n- **Hacking the Metric**\n\nThis specific logic is designed to optimize the final topological score by manipulating the betti-2 count. Although host claim that [practical impact at the top end should be limited](https://www.kaggle.com/competitions/vesuvius-challenge-surface-detection/discussion/678904#3414194), it still exist some bugs (I think) and can be utilized in the follow way:\n\nAutomatic Betti-2 Hole Filling: The fill_betti2_holes function identifies background components completely enclosed by the foreground and fills them to eliminate unwanted internal cavities. Set betti-2 = 0\n\nManual create betti2 number: In cases where specific error thresholds are met (e.g., high betti-0 or betti-1 error, for example: betti-0 > 50 or betti-1 > 5 , can be calculated using the bm.compute_barcode function), then create a small betti-2 error mannually. This step is dangerous to some degree, as it can just calculate  betti-0 or betti-1 numbers of the whole predictions, but in evaluation, there are igore masks which could contribute to totally different betti-0 / 1 numbers.\n\nIt locates a voxel that is \"fully enclosed\" (all 26 neighbors are foreground) within the largest connected component.\nIt sets that single voxel to 0, effectively creating a manual Betti-2 hole.\n\nStrategy: This \"hack\" serves to balance the topological metrics if the evaluation criteria favor a specific distribution of Betti errors across different dimensions.\n\n# Some other thoughs but have not try:\n\n1. end-2-end training a rbf function to get 0 holes sheets\n2. traing a point cloud seperation model to seperate the adhering sheets\n\n**It must be acknowledged that my solution is not so elegant, but it do works. Looking forword to learn other teams solutions.**\n\n**This writeup maybe lose some details. I will refine it in following days. Feel free to ask any questions.**\n\nSome ablations (Based on my best models)\n|  | Public LB | Private LB |\n| --- | --- | --- |\n| Segmentation Model + Remove dust | 0.590 | 0.609 |\n| + Stage1 + Stage2 + Hacking | 0.602 | 0.622 |\n\nsubmission notebook: https://www.kaggle.com/code/shtljw/vcsd-4th-place-solution\n",
      "votes": 31
    },
    {
      "id": 3415332,
      "postDate": "2026-02-28T17:36:25.260Z",
      "content": "<p>Haha, I think nnUnet is the worst. Unflexible and hard to integrate with anything external. They even advertise with forcing you to adopt their standards: \"Simply convert your dataset into the nnU-Net format and enjoy the power of AI - no expertise required!\"</p>\n<p>But everybody can have his own opinion. Congrats to your result!</p>",
      "rawMarkdown": "Haha, I think nnUnet is the worst. Unflexible and hard to integrate with anything external. They even advertise with forcing you to adopt their standards: \"Simply convert your dataset into the nnU-Net format and enjoy the power of AI - no expertise required!\"\n\nBut everybody can have his own opinion. Congrats to your result!",
      "replies": [
        {
          "id": 3415350,
          "postDate": "2026-02-28T18:22:05.677Z",
          "content": "<p>haha, the code of nnunet is definitely huge and highly intergrated so that hard to adopt for various ideas. But for most of us, it is powerful enough to get a relative good result. At least for me, develop a strong baseline is not so easy.</p>\n<p>Indeed, I read your byu and cryo \ncompetition solution writeups and try to train a diverse model based on monai framework in last few days for final ensemble , but I give up as the result is not so good. So I (as well as most of participates I think, as most of us are using nnunet) looking forward to learn your solution since I guess you use monai framework. I wonder what I lose to get a comparable results relative to you even if we use the same monai framework :) .</p>",
          "rawMarkdown": "haha, the code of nnunet is definitely huge and highly intergrated so that hard to adopt for various ideas. But for most of us, it is powerful enough to get a relative good result. At least for me, develop a strong baseline is not so easy.\n\nIndeed, I read your byu and cryo \ncompetition solution writeups and try to train a diverse model based on monai framework in last few days for final ensemble , but I give up as the result is not so good. So I (as well as most of participates I think, as most of us are using nnunet) looking forward to learn your solution since I guess you use monai framework. I wonder what I lose to get a comparable results relative to you even if we use the same monai framework :) .",
          "replies": [
            {
              "id": 3415359,
              "postDate": "2026-02-28T18:46:20.007Z",
              "content": "<p>I did not use monai either. Just coded everything from scratch. Or rather let it code by an Agent. E.g. Having an agent recode medicai based SEResNext + AttentionUnet in pure pytorch gives you more control and a 60% speedup. Our solution has in common to use bettimatching library (or rather a speed-up version) for tunnel detection and then filling the found tunnels. I think thats smart and directly addresses the tunnel problem.</p>\n<p>I think main differentiator is my loss. Really mad at hosts for prolonging 2 weeks for no critical reason. Additionally to that they shared nnUnet based top10 solution few weeks before end and pushing everyone to use that. To me it shows they are already set on the outcome and not interested in anything creative. </p>\n<p>Will do a write-up later, not for hosts but rather for the kaggle community.</p>",
              "rawMarkdown": "I did not use monai either. Just coded everything from scratch. Or rather let it code by an Agent. E.g. Having an agent recode medicai based SEResNext + AttentionUnet in pure pytorch gives you more control and a 60% speedup. Our solution has in common to use bettimatching library (or rather a speed-up version) for tunnel detection and then filling the found tunnels. I think thats smart and directly addresses the tunnel problem.\n\nI think main differentiator is my loss. Really mad at hosts for prolonging 2 weeks for no critical reason. Additionally to that they shared nnUnet based top10 solution few weeks before end and pushing everyone to use that. To me it shows they are already set on the outcome and not interested in anything creative. \n\nWill do a write-up later, not for hosts but rather for the kaggle community.",
              "votes": 1
            },
            {
              "id": 3415381,
              "postDate": "2026-02-28T20:08:36.447Z",
              "content": "<p>Dear Christof,</p>\n<p>First of all, I want to say that besides adopting nnUNet, many Kagglers, like <a href=\"https://www.kaggle.com/shtljw\" target=\"_blank\">@shtljw</a> , have been creative, especially with post-processing. This is very useful. We are learning a lot from it, and some of these ideas will matter even beyond this competition.</p>\n<p>Of course we care about the final outcome, but we also believe progress comes from curiosity and trying different ideas. That is why we did not publicize our baseline at the beginning. The baseline is a “simple” nnUNet, and we knew it was very strong. We wanted to avoid everyone jumping to it immediately and stopping the exploration of the solution space.</p>\n<p>When we later decided to publicize it, we also chose not to say publicly that, at that moment, the baseline was second on the private LB and almost tied with first. We felt that would have been even more discouraging for the community. After two months, with that result still holding, we felt it was the right time to publicize what we had in a pinned post. In any case, it was already public and well known to many contestants, especially those familiar with the Vesuvius Challenge community, and to anyone who had looked at our monorepo.</p>\n<p>Even then, we did not want to push everyone to use nnUNet. We expected teams with a strong approach to keep going with their own ideas, and maybe take inspiration only from the loss functions or the post-processing. In the end, most people moved to nnUNet, and they trained in an exceptional way. You were probably the only one with a solid alternative, and that is truly remarkable.</p>\n<p>About the two-week extension: after talking with the Kaggle support team, we felt it was needed. The metrics take a long time to compute, and each team had a limited number of submissions per day. Also, after the urgent fixes to the test data, people needed time to recalibrate to the new public LB and understand what had changed. We knew this was controversial, because many people and also us hosts had already planned around the deadline, for example Chinese New Year for many Chinese Kagglers, or Carnival for Europeans and Brazilians. Still, given the situation, we felt we had to do it.</p>\n<p>If your solution is comparable to nnUNet but faster, we would really love to see it. We have to run these models on full scroll volumes, blending overlapping chunks. It is a lot of compute, and any speedup helps a lot.</p>\n<p>We are very happy that you participated in this competition.</p>",
              "rawMarkdown": "Dear Christof,\n\nFirst of all, I want to say that besides adopting nnUNet, many Kagglers, like @shtljw , have been creative, especially with post-processing. This is very useful. We are learning a lot from it, and some of these ideas will matter even beyond this competition.\n\nOf course we care about the final outcome, but we also believe progress comes from curiosity and trying different ideas. That is why we did not publicize our baseline at the beginning. The baseline is a “simple” nnUNet, and we knew it was very strong. We wanted to avoid everyone jumping to it immediately and stopping the exploration of the solution space.\n\nWhen we later decided to publicize it, we also chose not to say publicly that, at that moment, the baseline was second on the private LB and almost tied with first. We felt that would have been even more discouraging for the community. After two months, with that result still holding, we felt it was the right time to publicize what we had in a pinned post. In any case, it was already public and well known to many contestants, especially those familiar with the Vesuvius Challenge community, and to anyone who had looked at our monorepo.\n\nEven then, we did not want to push everyone to use nnUNet. We expected teams with a strong approach to keep going with their own ideas, and maybe take inspiration only from the loss functions or the post-processing. In the end, most people moved to nnUNet, and they trained in an exceptional way. You were probably the only one with a solid alternative, and that is truly remarkable.\n\nAbout the two-week extension: after talking with the Kaggle support team, we felt it was needed. The metrics take a long time to compute, and each team had a limited number of submissions per day. Also, after the urgent fixes to the test data, people needed time to recalibrate to the new public LB and understand what had changed. We knew this was controversial, because many people and also us hosts had already planned around the deadline, for example Chinese New Year for many Chinese Kagglers, or Carnival for Europeans and Brazilians. Still, given the situation, we felt we had to do it.\n\nIf your solution is comparable to nnUNet but faster, we would really love to see it. We have to run these models on full scroll volumes, blending overlapping chunks. It is a lot of compute, and any speedup helps a lot.\n\nWe are very happy that you participated in this competition.",
              "votes": 1
            },
            {
              "id": 3420947,
              "postDate": "2026-03-14T09:18:09.317Z",
              "rawMarkdown": "",
              "isDeleted": true
            }
          ]
        },
        {
          "id": 3420930,
          "postDate": "2026-03-14T08:01:12.597Z",
          "content": "<p>I observed the same at medical competitions… most people directly default to nnUnet…which is great baseline by default. But then all solution's look alike and the worst part.. some very fine details are very deep in the convoluted nnUnet code…and were not documented until recently, such as data preprocessing.</p>",
          "rawMarkdown": "I observed the same at medical competitions... most people directly default to nnUnet...which is great baseline by default. But then all solution's look alike and the worst part.. some very fine details are very deep in the convoluted nnUnet code...and were not documented until recently, such as data preprocessing.",
          "votes": 1
        }
      ]
    },
    {
      "id": 3415383,
      "postDate": "2026-02-28T20:14:37.507Z",
      "content": "<p>I really like the Betti-matching based post-processing. It's a very nice idea!</p>",
      "rawMarkdown": "I really like the Betti-matching based post-processing. It's a very nice idea!",
      "replies": [
        {
          "id": 3415636,
          "postDate": "2026-03-01T04:16:59.503Z",
          "content": "<p>To be honest, In my opinion, the best direction I think to get perfect surface is to fit a end-to-end model, input is image and output can be different fiited RBF functions, as I use the following method and get perfect segmentation (0 holes).</p>\n<p>component detection -&gt; Voxel to Point Cloud -&gt; PCA transformation -&gt; RBF function fitting (torchrbf acceleration) -&gt; interpolation -&gt; PCA inverse transformation -&gt; Point cloud to Voxel -&gt;3D dialation </p>\n<p>However, the above method have a critical issue, which is this method can not handle the adhereing of multi sheets (RBF fitting would fail). But it is not easy to achieve. At least, I can not figure it out in few weeks.</p>",
          "rawMarkdown": "To be honest, In my opinion, the best direction I think to get perfect surface is to fit a end-to-end model, input is image and output can be different fiited RBF functions, as I use the following method and get perfect segmentation (0 holes).\n\ncomponent detection -> Voxel to Point Cloud -> PCA transformation -> RBF function fitting (torchrbf acceleration) -> interpolation -> PCA inverse transformation -> Point cloud to Voxel ->3D dialation \n\nHowever, the above method have a critical issue, which is this method can not handle the adhereing of multi sheets (RBF fitting would fail). But it is not easy to achieve. At least, I can not figure it out in few weeks."
        }
      ]
    },
    {
      "id": 3415156,
      "postDate": "2026-02-28T09:53:21.390Z",
      "content": "<p><strong>Impressive</strong></p>",
      "rawMarkdown": "**Impressive**"
    },
    {
      "id": 3415090,
      "postDate": "2026-02-28T06:44:23.683Z",
      "content": "<p>Great job. nnU-Net truly is an impressive framework; it’s no wonder it’s consistently referenced in medical research literature. </p>",
      "rawMarkdown": "Great job. nnU-Net truly is an impressive framework; it’s no wonder it’s consistently referenced in medical research literature. ",
      "replies": [
        {
          "id": 3415106,
          "postDate": "2026-02-28T07:12:14.507Z",
          "content": "<p>Indeed, I use nnUNet get a gold medal in a medical segmentation competetion 4 years ago. nnUNet is not so popular in Kaggle at that moment. And 4 years past, nnUNet is still powerful to get gold medal.</p>",
          "rawMarkdown": "Indeed, I use nnUNet get a gold medal in a medical segmentation competetion 4 years ago. nnUNet is not so popular in Kaggle at that moment. And 4 years past, nnUNet is still powerful to get gold medal."
        }
      ]
    },
    {
      "id": 3414941,
      "postDate": "2026-02-28T01:11:26.227Z",
      "content": "<p>Great solution. I started looking at PP during last two days and I implemented a closest plane filling a bit similar to yours. But I didn't went as far as using binary hole filling in the plane, I rather went for filling a circle in that plane, resulting in too much filling sometimes. Moving to the plane coordinates is a great idea.</p>\n<p>It is surprising that adding some cavities helps improve the metric. How much improvement do you get from filling cavities then adding back some?</p>",
      "rawMarkdown": "Great solution. I started looking at PP during last two days and I implemented a closest plane filling a bit similar to yours. But I didn't went as far as using binary hole filling in the plane, I rather went for filling a circle in that plane, resulting in too much filling sometimes. Moving to the plane coordinates is a great idea.\n\nIt is surprising that adding some cavities helps improve the metric. How much improvement do you get from filling cavities then adding back some?",
      "replies": [
        {
          "id": 3414942,
          "postDate": "2026-02-28T01:20:20.297Z",
          "content": "<p>It can get ~0.007 (0.602 -&gt; 0.609) improvment in Public LB, but in Private LB, it can just get ~0.001 (0.618 -&gt; 0.619) improvement.\nFor different stage ablition, I would update afterward :).</p>",
          "rawMarkdown": "It can get ~0.007 (0.602 -> 0.609) improvment in Public LB, but in Private LB, it can just get ~0.001 (0.618 -> 0.619) improvement.\nFor different stage ablition, I would update afterward :).",
          "votes": 1
        }
      ]
    }
  ],
  "comments": [
    {
      "id": 3415332,
      "author_name": "Dieter",
      "author_url": "",
      "post_date": "2026-02-28T17:36:25.260000",
      "content": "<p>Haha, I think nnUnet is the worst. Unflexible and hard to integrate with anything external. They even advertise with forcing you to adopt their standards: \"Simply convert your dataset into the nnU-Net format and enjoy the power of AI - no expertise required!\"</p>\n<p>But everybody can have his own opinion. Congrats to your result!</p>",
      "votes": 0,
      "replies": [
        {
          "id": 3415350,
          "author_name": "Starry",
          "author_url": "",
          "post_date": "2026-02-28T18:22:05.677000",
          "content": "<p>haha, the code of nnunet is definitely huge and highly intergrated so that hard to adopt for various ideas. But for most of us, it is powerful enough to get a relative good result. At least for me, develop a strong baseline is not so easy.</p>\n<p>Indeed, I read your byu and cryo \ncompetition solution writeups and try to train a diverse model based on monai framework in last few days for final ensemble , but I give up as the result is not so good. So I (as well as most of participates I think, as most of us are using nnunet) looking forward to learn your solution since I guess you use monai framework. I wonder what I lose to get a comparable results relative to you even if we use the same monai framework :) .</p>",
          "votes": 0,
          "replies": [
            {
              "id": 3415359,
              "author_name": "Dieter",
              "author_url": "",
              "post_date": "2026-02-28T18:46:20.007000",
              "content": "<p>I did not use monai either. Just coded everything from scratch. Or rather let it code by an Agent. E.g. Having an agent recode medicai based SEResNext + AttentionUnet in pure pytorch gives you more control and a 60% speedup. Our solution has in common to use bettimatching library (or rather a speed-up version) for tunnel detection and then filling the found tunnels. I think thats smart and directly addresses the tunnel problem.</p>\n<p>I think main differentiator is my loss. Really mad at hosts for prolonging 2 weeks for no critical reason. Additionally to that they shared nnUnet based top10 solution few weeks before end and pushing everyone to use that. To me it shows they are already set on the outcome and not interested in anything creative. </p>\n<p>Will do a write-up later, not for hosts but rather for the kaggle community.</p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3415381,
              "author_name": "Giorgio Angelotti",
              "author_url": "",
              "post_date": "2026-02-28T20:08:36.447000",
              "content": "<p>Dear Christof,</p>\n<p>First of all, I want to say that besides adopting nnUNet, many Kagglers, like <a href=\"https://www.kaggle.com/shtljw\" target=\"_blank\">@shtljw</a> , have been creative, especially with post-processing. This is very useful. We are learning a lot from it, and some of these ideas will matter even beyond this competition.</p>\n<p>Of course we care about the final outcome, but we also believe progress comes from curiosity and trying different ideas. That is why we did not publicize our baseline at the beginning. The baseline is a “simple” nnUNet, and we knew it was very strong. We wanted to avoid everyone jumping to it immediately and stopping the exploration of the solution space.</p>\n<p>When we later decided to publicize it, we also chose not to say publicly that, at that moment, the baseline was second on the private LB and almost tied with first. We felt that would have been even more discouraging for the community. After two months, with that result still holding, we felt it was the right time to publicize what we had in a pinned post. In any case, it was already public and well known to many contestants, especially those familiar with the Vesuvius Challenge community, and to anyone who had looked at our monorepo.</p>\n<p>Even then, we did not want to push everyone to use nnUNet. We expected teams with a strong approach to keep going with their own ideas, and maybe take inspiration only from the loss functions or the post-processing. In the end, most people moved to nnUNet, and they trained in an exceptional way. You were probably the only one with a solid alternative, and that is truly remarkable.</p>\n<p>About the two-week extension: after talking with the Kaggle support team, we felt it was needed. The metrics take a long time to compute, and each team had a limited number of submissions per day. Also, after the urgent fixes to the test data, people needed time to recalibrate to the new public LB and understand what had changed. We knew this was controversial, because many people and also us hosts had already planned around the deadline, for example Chinese New Year for many Chinese Kagglers, or Carnival for Europeans and Brazilians. Still, given the situation, we felt we had to do it.</p>\n<p>If your solution is comparable to nnUNet but faster, we would really love to see it. We have to run these models on full scroll volumes, blending overlapping chunks. It is a lot of compute, and any speedup helps a lot.</p>\n<p>We are very happy that you participated in this competition.</p>",
              "votes": 1,
              "replies": []
            },
            {
              "id": 3420947,
              "author_name": "",
              "author_url": "",
              "post_date": "2026-03-14T09:18:09.317000",
              "content": "",
              "votes": 0,
              "replies": []
            }
          ]
        },
        {
          "id": 3420930,
          "author_name": "Maxim Shatskiy",
          "author_url": "",
          "post_date": "2026-03-14T08:01:12.597000",
          "content": "<p>I observed the same at medical competitions… most people directly default to nnUnet…which is great baseline by default. But then all solution's look alike and the worst part.. some very fine details are very deep in the convoluted nnUnet code…and were not documented until recently, such as data preprocessing.</p>",
          "votes": 1,
          "replies": []
        }
      ]
    },
    {
      "id": 3415383,
      "author_name": "Giorgio Angelotti",
      "author_url": "",
      "post_date": "2026-02-28T20:14:37.507000",
      "content": "<p>I really like the Betti-matching based post-processing. It's a very nice idea!</p>",
      "votes": 0,
      "replies": [
        {
          "id": 3415636,
          "author_name": "Starry",
          "author_url": "",
          "post_date": "2026-03-01T04:16:59.503000",
          "content": "<p>To be honest, In my opinion, the best direction I think to get perfect surface is to fit a end-to-end model, input is image and output can be different fiited RBF functions, as I use the following method and get perfect segmentation (0 holes).</p>\n<p>component detection -&gt; Voxel to Point Cloud -&gt; PCA transformation -&gt; RBF function fitting (torchrbf acceleration) -&gt; interpolation -&gt; PCA inverse transformation -&gt; Point cloud to Voxel -&gt;3D dialation </p>\n<p>However, the above method have a critical issue, which is this method can not handle the adhereing of multi sheets (RBF fitting would fail). But it is not easy to achieve. At least, I can not figure it out in few weeks.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 3415156,
      "author_name": "SINAN SHEREEF",
      "author_url": "",
      "post_date": "2026-02-28T09:53:21.390000",
      "content": "<p><strong>Impressive</strong></p>",
      "votes": 0,
      "replies": []
    },
    {
      "id": 3415090,
      "author_name": "Innat",
      "author_url": "",
      "post_date": "2026-02-28T06:44:23.683000",
      "content": "<p>Great job. nnU-Net truly is an impressive framework; it’s no wonder it’s consistently referenced in medical research literature. </p>",
      "votes": 0,
      "replies": [
        {
          "id": 3415106,
          "author_name": "Starry",
          "author_url": "",
          "post_date": "2026-02-28T07:12:14.507000",
          "content": "<p>Indeed, I use nnUNet get a gold medal in a medical segmentation competetion 4 years ago. nnUNet is not so popular in Kaggle at that moment. And 4 years past, nnUNet is still powerful to get gold medal.</p>",
          "votes": 0,
          "replies": []
        }
      ]
    },
    {
      "id": 3414941,
      "author_name": "CPMP",
      "author_url": "",
      "post_date": "2026-02-28T01:11:26.227000",
      "content": "<p>Great solution. I started looking at PP during last two days and I implemented a closest plane filling a bit similar to yours. But I didn't went as far as using binary hole filling in the plane, I rather went for filling a circle in that plane, resulting in too much filling sometimes. Moving to the plane coordinates is a great idea.</p>\n<p>It is surprising that adding some cavities helps improve the metric. How much improvement do you get from filling cavities then adding back some?</p>",
      "votes": 0,
      "replies": [
        {
          "id": 3414942,
          "author_name": "Starry",
          "author_url": "",
          "post_date": "2026-02-28T01:20:20.297000",
          "content": "<p>It can get ~0.007 (0.602 -&gt; 0.609) improvment in Public LB, but in Private LB, it can just get ~0.001 (0.618 -&gt; 0.619) improvement.\nFor different stage ablition, I would update afterward :).</p>",
          "votes": 1,
          "replies": []
        }
      ]
    }
  ],
  "raw_markdown_by_id": {
    "3414937": "**Thanks for host for this meaningful and interesting competetion as well as detail answer for metric questions. And congradulations for winners!**\n\n**This competetion just show the powerful generalization of nnUNet again. It's the best segmentation framework in the world (I think) !**\n\n# TLDR:\n\n**Huge nnUNet and heavy postprocessing**\n\n# 1  . **Segmentation Model:**\n\nModel training : \nI did nothing just train a huge nnUNet (Resenc UNet with 7 stages covolution) with sufficient epoch (2000). \n\nInference: Uses nnUNetPredictor with a tile_step_size of 0.5 and test-time augmentation (mirroring) to ensure smooth and robust predictions.\n\nPreliminary postprocessing: threshould=0.2 and remove components with size < 1000.\n\n## 2  . **Postprocessing: Two Stage hole Filling and Metric Hacking**\n\n- **1st Stage: PCA Hole Filling (Projection-based)**\n\nThis stage targets large, obvious topological loops (Betti-1 errors) by analyzing 2D projections.\n\nDetection: The projection_betti1_finding function projects 3D connected components onto XY, YZ, and ZX planes. It identifies \"isolated regions\" (background holes) that do not touch the image boundaries.\n\nRefinement:\n\nIt extracts the 3D coordinates of these problematic surfaces and applies Principal Component Analysis (PCA) to find the optimal 2D fitting plane.\nThe points are mapped to 2D, where a binary_fill_holes operation is performed.\nThe filled results are re-mapped back to 3D space using the inverse_transform of the PCA.\nThis step intend to fill some large holes.\n\nIn fact, there are another more elegant solution for this part: \ncomponent detection -> PCA transformation -> RBF function fitting (torchrbf acceleration) -> interpolation -> PCA inverse transformation -> 3D dialation\n\nHowever, there are a critical problem in this part:\n\n**How to separate adhering sheets ?**\n\nFor those adhering sheets, this method would fail. And till now, I still can not find a good method to solve this problem. Looking forward other teams solution.\n\n- **2nd Stage: PCA Hole Filling (Betti-Matching-based)**\n\nA more granular refinement stage using specialized topological tools.\n\nDetection: Utilizing the betti_matching (C++) library, the bmbarcode_betti1_finding function performs a local persistent homology analysis.\n\nLocal Processing: The volume is divided into 20, 20, 20 chunks. If a local block exhibits a Betti-1 error (determined via barcode calculation), the PCA filling logic is applied locally to seal small breaks or gaps in the structure.\n\nIf one check the competetion metric, it can be found that the topo score is very sensitive, even if a small hole would contribute to a large drop for topo score.\n\n- **Hacking the Metric**\n\nThis specific logic is designed to optimize the final topological score by manipulating the betti-2 count. Although host claim that [practical impact at the top end should be limited](https://www.kaggle.com/competitions/vesuvius-challenge-surface-detection/discussion/678904#3414194), it still exist some bugs (I think) and can be utilized in the follow way:\n\nAutomatic Betti-2 Hole Filling: The fill_betti2_holes function identifies background components completely enclosed by the foreground and fills them to eliminate unwanted internal cavities. Set betti-2 = 0\n\nManual create betti2 number: In cases where specific error thresholds are met (e.g., high betti-0 or betti-1 error, for example: betti-0 > 50 or betti-1 > 5 , can be calculated using the bm.compute_barcode function), then create a small betti-2 error mannually. This step is dangerous to some degree, as it can just calculate  betti-0 or betti-1 numbers of the whole predictions, but in evaluation, there are igore masks which could contribute to totally different betti-0 / 1 numbers.\n\nIt locates a voxel that is \"fully enclosed\" (all 26 neighbors are foreground) within the largest connected component.\nIt sets that single voxel to 0, effectively creating a manual Betti-2 hole.\n\nStrategy: This \"hack\" serves to balance the topological metrics if the evaluation criteria favor a specific distribution of Betti errors across different dimensions.\n\n# Some other thoughs but have not try:\n\n1. end-2-end training a rbf function to get 0 holes sheets\n2. traing a point cloud seperation model to seperate the adhering sheets\n\n**It must be acknowledged that my solution is not so elegant, but it do works. Looking forword to learn other teams solutions.**\n\n**This writeup maybe lose some details. I will refine it in following days. Feel free to ask any questions.**\n\nSome ablations (Based on my best models)\n|  | Public LB | Private LB |\n| --- | --- | --- |\n| Segmentation Model + Remove dust | 0.590 | 0.609 |\n| + Stage1 + Stage2 + Hacking | 0.602 | 0.622 |\n\nsubmission notebook: https://www.kaggle.com/code/shtljw/vcsd-4th-place-solution\n",
    "3415332": "Haha, I think nnUnet is the worst. Unflexible and hard to integrate with anything external. They even advertise with forcing you to adopt their standards: \"Simply convert your dataset into the nnU-Net format and enjoy the power of AI - no expertise required!\"\n\nBut everybody can have his own opinion. Congrats to your result!",
    "3415383": "I really like the Betti-matching based post-processing. It's a very nice idea!",
    "3415156": "**Impressive**",
    "3415090": "Great job. nnU-Net truly is an impressive framework; it’s no wonder it’s consistently referenced in medical research literature. ",
    "3414941": "Great solution. I started looking at PP during last two days and I implemented a closest plane filling a bit similar to yours. But I didn't went as far as using binary hole filling in the plane, I rather went for filling a circle in that plane, resulting in too much filling sometimes. Moving to the plane coordinates is a great idea.\n\nIt is surprising that adding some cavities helps improve the metric. How much improvement do you get from filling cavities then adding back some?"
  }
}