{
  "id": 671325,
  "title": "polynomial volume",
  "url": "/competitions/vesuvius-challenge-surface-detection/discussion/671325",
  "author_name": "",
  "post_date": "2026-02-01T09:51:13.258251100Z",
  "votes": 13,
  "comment_count": 5,
  "views": 0,
  "content": "<p>there is such a thing called \"polynomial volume\"<br>\nchatgpt and gemini can tell you more.  </p>\n<p>instead of fitting  line, surface, you can fit a volume!\nassume you have seeds point zyx for each connected compoent for K seeds, then each sample point is kzyx.</p>\n<p>example of fitting results</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fca2212f25a276572c92ac0b1ff9970c9%2FSelection_2327.png?generation=1769939462819142&amp;alt=media\" alt=\"\"> </p>\n<p>\"polynomial volume\"   may not be the best parameterisation, please experiment and look for better one.</p>\n<p>but the ground truth surface is indeed a polynomial surface (you can use degree 4 to 6).\nfor 0.5 scaled resolution of 160x160x160, fit median error for ground truth surface is 2 for degr 6, (95% quant error is about 5)</p>\n<p>this is results of fitting deg6 surface to high probability voxels (cc3d seed) from unet</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F909989b66b3fdee6d947d5f737e85a88%2FSelection_2309.png?generation=1769940593838186&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87a5316c302220fa54fd4cec2c7798e9%2FSelection_2326.png?generation=1769941310422367&amp;alt=media\" alt=\"\">\n(typo error: it should be truth annotation surface not truth polynomial surface)</p>",
  "messages": [
    {
      "id": "3400269",
      "postDate": "02/01/2026 09:51:13",
      "content": "<p>there is such a thing called \"polynomial volume\"<br>\nchatgpt and gemini can tell you more.  </p>\n<p>instead of fitting  line, surface, you can fit a volume!\nassume you have seeds point zyx for each connected compoent for K seeds, then each sample point is kzyx.</p>\n<p>example of fitting results</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fca2212f25a276572c92ac0b1ff9970c9%2FSelection_2327.png?generation=1769939462819142&amp;alt=media\" alt=\"\"> </p>\n<p>\"polynomial volume\"   may not be the best parameterisation, please experiment and look for better one.</p>\n<p>but the ground truth surface is indeed a polynomial surface (you can use degree 4 to 6).\nfor 0.5 scaled resolution of 160x160x160, fit median error for ground truth surface is 2 for degr 6, (95% quant error is about 5)</p>\n<p>this is results of fitting deg6 surface to high probability voxels (cc3d seed) from unet</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F909989b66b3fdee6d947d5f737e85a88%2FSelection_2309.png?generation=1769940593838186&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87a5316c302220fa54fd4cec2c7798e9%2FSelection_2326.png?generation=1769941310422367&amp;alt=media\" alt=\"\">\n(typo error: it should be truth annotation surface not truth polynomial surface)</p>",
      "rawMarkdown": "there is such a thing called \"polynomial volume\"  \nchatgpt and gemini can tell you more.  \n\ninstead of fitting  line, surface, you can fit a volume!\nassume you have seeds point zyx for each connected compoent for K seeds, then each sample point is kzyx.\n\nexample of fitting results\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fca2212f25a276572c92ac0b1ff9970c9%2FSelection_2327.png?generation=1769939462819142&alt=media) \n\n\n\"polynomial volume\"   may not be the best parameterisation, please experiment and look for better one.\n\nbut the ground truth surface is indeed a polynomial surface (you can use degree 4 to 6).\nfor 0.5 scaled resolution of 160x160x160, fit median error for ground truth surface is 2 for degr 6, (95% quant error is about 5)\n   \nthis is results of fitting deg6 surface to high probability voxels (cc3d seed) from unet\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F909989b66b3fdee6d947d5f737e85a88%2FSelection_2309.png?generation=1769940593838186&alt=media)\n\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87a5316c302220fa54fd4cec2c7798e9%2FSelection_2326.png?generation=1769941310422367&alt=media)\n(typo error: it should be truth annotation surface not truth polynomial surface)",
      "votes": null
    },
    {
      "id": "3400270",
      "postDate": "02/01/2026 09:55:09",
      "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fd9ac055c9156f1039eaf211efc0aba30%2FSelection_2328.png?generation=1769939696577354&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F16c47f5eab4c512948e4b0e4e1788d7a%2FSelection_2329.png?generation=1769939706834773&amp;alt=media\" alt=\"\"></p>",
      "rawMarkdown": "![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fd9ac055c9156f1039eaf211efc0aba30%2FSelection_2328.png?generation=1769939696577354&alt=media)\n\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F16c47f5eab4c512948e4b0e4e1788d7a%2FSelection_2329.png?generation=1769939706834773&alt=media)",
      "votes": null
    },
    {
      "id": "3400412",
      "postDate": "02/01/2026 14:07:14",
      "content": "<p>This sounds like designing a well-structured, math-oriented template.</p>",
      "rawMarkdown": "This sounds like designing a well-structured, math-oriented template.",
      "votes": null
    },
    {
      "id": "3400743",
      "postDate": "02/02/2026 07:06:35",
      "content": "<p>2d single surface fit code:</p>\n<pre><code>import torch\nimport torch.nn.functional as F\n\nimport numpy as np\nfrom my_utility import *\n\nimport torch\nimport matplotlib.pyplot as plt\n\nimport matplotlib\nmatplotlib.use('TkAgg')\nimport pyvista as pv\n\ndef fit_surface(volume, degree=8, grid_size = 100,\n    bbox_border=3,):\n    \"\"\"\n    Fits a PCA-aligned polynomial surface and trims it to the data extent.\n    - degree: Higher = more detail (try 8-12).\n    \"\"\"\n    volume = torch.from_numpy(volume)#.cuda()\n    device = volume.device\n\n    # 1. Extract and Center Data\n    point_zyx = torch.nonzero(volume).to(device).to(torch.float64)\n    if point_zyx.shape[0] &lt; 10: return\n\n    mean = point_zyx.mean(dim=0)\n    centered = point_zyx - mean\n\n    # 2. PCA Rotation\n    U, S, V = torch.pca_lowrank(centered, q=3)\n    pca_zyx = centered @ V\n\n    # Normalization for math stability\n    x_l, y_l = pca_zyx[:, 0], pca_zyx[:, 1]\n    x_scale, y_scale = x_l.abs().max(), y_l.abs().max()\n\n    # 3. Solve Polynomial\n    A_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_list.append(((x_l / x_scale) ** i) * ((y_l / y_scale) ** j))\n    A = torch.stack(A_list, dim=1)\n\n    lam = 1e-2 #1e-4\n    coeffs = torch.linalg.solve(\n        A.T @ A + lam * torch.eye(A.shape[1], device=device, dtype=torch.float64),\n        A.T @ pca_zyx[:, 2].unsqueeze(1)\n    )\n\n    # 4. Create Evaluation Grid (larger than data to ensure we hit the box edges)\n    gs = grid_size\n    gx = torch.linspace(x_l.min() * 1.5, x_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    gy = torch.linspace(y_l.min() * 1.5, y_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    grid_xl, grid_yl = torch.meshgrid(gx, gy, indexing='ij')\n\n    # Compute local Z\n    A_grid_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_grid_list.append(((grid_xl.flatten() / x_scale) ** i) * ((grid_yl.flatten() / y_scale) ** j))\n    grid_zl = (torch.stack(A_grid_list, dim=1) @ coeffs).reshape(gs, gs)\n\n    # 5. Transform back to original volume space\n    grid_point = torch.stack([grid_xl.flatten(), grid_yl.flatten(), grid_zl.flatten()], dim=-1)\n    grid_zyx = (grid_point @ V.T) + mean\n\n    # --- restict to orginal sapce\n    # Global coords: Index 0=Z, 1=Y, 2=X\n    gz = grid_zyx[:, 0].reshape(gs, gs)\n    gy = grid_zyx[:, 1].reshape(gs, gs)\n    gx = grid_zyx[:, 2].reshape(gs, gs)\n\n    D,H,W = volume.shape\n    B=bbox_border\n    out_of_bound = (gx &lt; B) | (gx &gt; W-B-1) | (gy &lt; B) | (gy &gt; H-B-1) | (gz &lt; B) | (gz &gt; D-B-1)\n    mask = (out_of_bound).cpu().numpy()\n    gx, gy, gz = gx.cpu().numpy(), gy.cpu().numpy(), gz.cpu().numpy()\n\n    # apply the trim mask\n    gz[mask] = np.nan\n    gy[mask] = np.nan\n    gx[mask] = np.nan\n\n    ########################################################3\n    valid = np.isfinite(gz)  # or support_mask before applying nan\n    lab, n = ndi.label(valid)  # 4-connectivity by default in 2D\n    if n &gt; 1:\n        counts = np.bincount(lab.ravel())  #this is not correct. we should drop part that doesn't contains any datapoint instead\n        counts[0] = 0\n        keep = counts.argmax()\n        drop = lab != keep\n        gz[drop] = np.nan\n        gy[drop] = np.nan\n        gx[drop] = np.nan\n\n    return gz, gy, gx\n\n##################################################################\ndef show_fit_result(prob_np, gz, gy, gx):\n    # pts = pts.data.float().cpu().numpy()\n    N = gx.shape[0]\n    # vertices: (N*N, 3)\n    verts = np.column_stack([\n        gx.reshape(-1),\n        gy.reshape(-1),\n        gz.reshape(-1),\n    ]).astype(np.float32)\n    # faces (two triangles per quad)\n    faces = []\n    for i in range(N - 1):\n        for j in range(N - 1):\n            v0 = i * N + j\n            v1 = v0 + 1\n            v2 = v0 + N\n            v3 = v2 + 1\n            faces.append([3, v0, v1, v3])\n            faces.append([3, v0, v3, v2])\n    faces = np.array(faces, dtype=np.int64).reshape(-1)\n    surface = pv.PolyData(verts, faces)\n    surface = surface.clean()\n\n    D, H, W = prob_np.shape\n    grid = pv.ImageData()\n    grid.dimensions = (W + 1, H + 1, D + 1)  # (X,Y,Z)+1\n    grid.spacing = (1, 1, 1)\n    grid.origin = (0, 0, 0)\n\n    # IMPORTANT: convert (Z,Y,X) -&gt; (X,Y,Z) before flatten(F)\n    prob_np = prob_np*0.8\n    prob_np[0,0,0]=1\n    labels_xyz = np.transpose(prob_np, (2, 1, 0))  # (W,H,D) == (X,Y,Z)\n    grid.cell_data[\"labels\"] = labels_xyz.flatten(order=\"F\")\n\n    #\n    # grid = pv.wrap(prob_np)\n    # labels = prob_np.astype(np.int32)\n    # grid['labels'] = labels.flatten(order=\"F\")\n\n    ########################################3\n    #axis\n    axes= np.zeros((D,H,W))\n    axes[:, 0, 0] = 1  # z axis\n    axes[0, :, 0] = 1\n    axes[0, 0, :] = 1\n    axes[0, :, -1] = 1\n    axes[0, -1, :] = 1\n    axes = pv.wrap(axes)\n\n    #####################################\n    p = pv.Plotter()\n\n    p.add_volume(\n        axes,\n        #scalars=\"labels\",\n        #opacity=0.5,\n        #opacity=\"sigmoid\",\n        cmap=\"binary\",\n        shade=False,\n    )\n\n    p.add_volume(\n        grid,\n        scalars=\"labels\",\n        #opacity=0.5,\n        opacity=\"sigmoid\",\n        cmap=\"reds\",\n        shade=False,\n    )\n\n    p.add_mesh(\n        surface,\n        color=\"gray\",\n        opacity=0.8,\n        show_edges=True,\n        edge_color=\"black\",\n    )\n    p.show()\n\n\nif __name__ == '__main__':\n    prob_np = np.load('sl.npy') #binary volume of size 80x80x80: fg/bg = 1/0\n    prob_np = prob_np.astype(np.float32)\n\n    gz, gy, gx = fit_surface(prob_np, degree=6, grid_size=100)\n\n    show_fit_result(prob_np, gz, gy, gx)\n</code></pre>",
      "rawMarkdown": "2d single surface fit code:\n\n```\nimport torch\nimport torch.nn.functional as F\n\nimport numpy as np\nfrom my_utility import *\n\nimport torch\nimport matplotlib.pyplot as plt\n\nimport matplotlib\nmatplotlib.use('TkAgg')\nimport pyvista as pv\n\ndef fit_surface(volume, degree=8, grid_size = 100,\n    bbox_border=3,):\n    \"\"\"\n    Fits a PCA-aligned polynomial surface and trims it to the data extent.\n    - degree: Higher = more detail (try 8-12).\n    \"\"\"\n    volume = torch.from_numpy(volume)#.cuda()\n    device = volume.device\n\n    # 1. Extract and Center Data\n    point_zyx = torch.nonzero(volume).to(device).to(torch.float64)\n    if point_zyx.shape[0] < 10: return\n\n    mean = point_zyx.mean(dim=0)\n    centered = point_zyx - mean\n\n    # 2. PCA Rotation\n    U, S, V = torch.pca_lowrank(centered, q=3)\n    pca_zyx = centered @ V\n\n    # Normalization for math stability\n    x_l, y_l = pca_zyx[:, 0], pca_zyx[:, 1]\n    x_scale, y_scale = x_l.abs().max(), y_l.abs().max()\n\n    # 3. Solve Polynomial\n    A_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_list.append(((x_l / x_scale) ** i) * ((y_l / y_scale) ** j))\n    A = torch.stack(A_list, dim=1)\n\n    lam = 1e-2 #1e-4\n    coeffs = torch.linalg.solve(\n        A.T @ A + lam * torch.eye(A.shape[1], device=device, dtype=torch.float64),\n        A.T @ pca_zyx[:, 2].unsqueeze(1)\n    )\n\n    # 4. Create Evaluation Grid (larger than data to ensure we hit the box edges)\n    gs = grid_size\n    gx = torch.linspace(x_l.min() * 1.5, x_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    gy = torch.linspace(y_l.min() * 1.5, y_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    grid_xl, grid_yl = torch.meshgrid(gx, gy, indexing='ij')\n\n    # Compute local Z\n    A_grid_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_grid_list.append(((grid_xl.flatten() / x_scale) ** i) * ((grid_yl.flatten() / y_scale) ** j))\n    grid_zl = (torch.stack(A_grid_list, dim=1) @ coeffs).reshape(gs, gs)\n\n    # 5. Transform back to original volume space\n    grid_point = torch.stack([grid_xl.flatten(), grid_yl.flatten(), grid_zl.flatten()], dim=-1)\n    grid_zyx = (grid_point @ V.T) + mean\n\n    # --- restict to orginal sapce\n    # Global coords: Index 0=Z, 1=Y, 2=X\n    gz = grid_zyx[:, 0].reshape(gs, gs)\n    gy = grid_zyx[:, 1].reshape(gs, gs)\n    gx = grid_zyx[:, 2].reshape(gs, gs)\n\n    D,H,W = volume.shape\n    B=bbox_border\n    out_of_bound = (gx < B) | (gx > W-B-1) | (gy < B) | (gy > H-B-1) | (gz < B) | (gz > D-B-1)\n    mask = (out_of_bound).cpu().numpy()\n    gx, gy, gz = gx.cpu().numpy(), gy.cpu().numpy(), gz.cpu().numpy()\n\n    # apply the trim mask\n    gz[mask] = np.nan\n    gy[mask] = np.nan\n    gx[mask] = np.nan\n\n    ########################################################3\n    valid = np.isfinite(gz)  # or support_mask before applying nan\n    lab, n = ndi.label(valid)  # 4-connectivity by default in 2D\n    if n > 1:\n        counts = np.bincount(lab.ravel())  #this is not correct. we should drop part that doesn't contains any datapoint instead\n        counts[0] = 0\n        keep = counts.argmax()\n        drop = lab != keep\n        gz[drop] = np.nan\n        gy[drop] = np.nan\n        gx[drop] = np.nan\n\n    return gz, gy, gx\n\n##################################################################\ndef show_fit_result(prob_np, gz, gy, gx):\n    # pts = pts.data.float().cpu().numpy()\n    N = gx.shape[0]\n    # vertices: (N*N, 3)\n    verts = np.column_stack([\n        gx.reshape(-1),\n        gy.reshape(-1),\n        gz.reshape(-1),\n    ]).astype(np.float32)\n    # faces (two triangles per quad)\n    faces = []\n    for i in range(N - 1):\n        for j in range(N - 1):\n            v0 = i * N + j\n            v1 = v0 + 1\n            v2 = v0 + N\n            v3 = v2 + 1\n            faces.append([3, v0, v1, v3])\n            faces.append([3, v0, v3, v2])\n    faces = np.array(faces, dtype=np.int64).reshape(-1)\n    surface = pv.PolyData(verts, faces)\n    surface = surface.clean()\n\n    D, H, W = prob_np.shape\n    grid = pv.ImageData()\n    grid.dimensions = (W + 1, H + 1, D + 1)  # (X,Y,Z)+1\n    grid.spacing = (1, 1, 1)\n    grid.origin = (0, 0, 0)\n\n    # IMPORTANT: convert (Z,Y,X) -> (X,Y,Z) before flatten(F)\n    prob_np = prob_np*0.8\n    prob_np[0,0,0]=1\n    labels_xyz = np.transpose(prob_np, (2, 1, 0))  # (W,H,D) == (X,Y,Z)\n    grid.cell_data[\"labels\"] = labels_xyz.flatten(order=\"F\")\n\n    #\n    # grid = pv.wrap(prob_np)\n    # labels = prob_np.astype(np.int32)\n    # grid['labels'] = labels.flatten(order=\"F\")\n\n    ########################################3\n    #axis\n    axes= np.zeros((D,H,W))\n    axes[:, 0, 0] = 1  # z axis\n    axes[0, :, 0] = 1\n    axes[0, 0, :] = 1\n    axes[0, :, -1] = 1\n    axes[0, -1, :] = 1\n    axes = pv.wrap(axes)\n\n    #####################################\n    p = pv.Plotter()\n\n    p.add_volume(\n        axes,\n        #scalars=\"labels\",\n        #opacity=0.5,\n        #opacity=\"sigmoid\",\n        cmap=\"binary\",\n        shade=False,\n    )\n\n    p.add_volume(\n        grid,\n        scalars=\"labels\",\n        #opacity=0.5,\n        opacity=\"sigmoid\",\n        cmap=\"reds\",\n        shade=False,\n    )\n\n    p.add_mesh(\n        surface,\n        color=\"gray\",\n        opacity=0.8,\n        show_edges=True,\n        edge_color=\"black\",\n    )\n    p.show()\n \n\nif __name__ == '__main__':\n    prob_np = np.load('sl.npy') #binary volume of size 80x80x80: fg/bg = 1/0\n    prob_np = prob_np.astype(np.float32)\n\n    gz, gy, gx = fit_surface(prob_np, degree=6, grid_size=100)\n\n    show_fit_result(prob_np, gz, gy, gx)\n\n```",
      "votes": null
    },
    {
      "id": "3401228",
      "postDate": "02/03/2026 07:55:36",
      "content": "<p><a href=\"https://github.com/complete3d/paco\" target=\"_blank\">https://github.com/complete3d/paco</a>\n<a href=\"https://unico-completion.github.io/\" target=\"_blank\">https://unico-completion.github.io/</a></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fdb319b4c7ee2e82ad47fabd2ae162a14%2FSelection_2357.png?generation=1770105264615727&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Ff59652639fef8a092a1029825dd57c7f%2FSelection_2356.png?generation=1770105283977607&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F35c0cd67ef2d6cffed6a774bcb878286%2FSelection_2358.png?generation=1770105305591162&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F89cc9f9726d7f260d7305eeb8cd02bf7%2FSelection_2355.png?generation=1770105333592985&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F51c31671518e4ed5674c5431ad149940%2FSelection_2359.png?generation=1770105804160715&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F0c83ea9266c1b796abc36761414ccb20%2FSelection_2360.png?generation=1770106837171237&amp;alt=media\" alt=\"\"></p>",
      "rawMarkdown": "https://github.com/complete3d/paco\nhttps://unico-completion.github.io/\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fdb319b4c7ee2e82ad47fabd2ae162a14%2FSelection_2357.png?generation=1770105264615727&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Ff59652639fef8a092a1029825dd57c7f%2FSelection_2356.png?generation=1770105283977607&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F35c0cd67ef2d6cffed6a774bcb878286%2FSelection_2358.png?generation=1770105305591162&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F89cc9f9726d7f260d7305eeb8cd02bf7%2FSelection_2355.png?generation=1770105333592985&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F51c31671518e4ed5674c5431ad149940%2FSelection_2359.png?generation=1770105804160715&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F0c83ea9266c1b796abc36761414ccb20%2FSelection_2360.png?generation=1770106837171237&alt=media)",
      "votes": null
    },
    {
      "id": "3401451",
      "postDate": "02/03/2026 18:11:22",
      "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F591c29e93689cfeb46d207ccf570690b%2FSelection_2361.png?generation=1770142280604922&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fc8f2984a738764527b4c62c502819a56%2FSelection_2362.png?generation=1770142588733008&amp;alt=media\" alt=\"\"></p>",
      "rawMarkdown": "![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F591c29e93689cfeb46d207ccf570690b%2FSelection_2361.png?generation=1770142280604922&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fc8f2984a738764527b4c62c502819a56%2FSelection_2362.png?generation=1770142588733008&alt=media)",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 3400270,
      "author_name": "hengck23",
      "author_url": "",
      "post_date": "02/01/2026 09:55:09",
      "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fd9ac055c9156f1039eaf211efc0aba30%2FSelection_2328.png?generation=1769939696577354&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F16c47f5eab4c512948e4b0e4e1788d7a%2FSelection_2329.png?generation=1769939706834773&amp;alt=media\" alt=\"\"></p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3400412,
      "author_name": "tom99763",
      "author_url": "",
      "post_date": "02/01/2026 14:07:14",
      "content": "<p>This sounds like designing a well-structured, math-oriented template.</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3400743,
      "author_name": "hengck23",
      "author_url": "",
      "post_date": "02/02/2026 07:06:35",
      "content": "<p>2d single surface fit code:</p>\n<pre><code>import torch\nimport torch.nn.functional as F\n\nimport numpy as np\nfrom my_utility import *\n\nimport torch\nimport matplotlib.pyplot as plt\n\nimport matplotlib\nmatplotlib.use('TkAgg')\nimport pyvista as pv\n\ndef fit_surface(volume, degree=8, grid_size = 100,\n    bbox_border=3,):\n    \"\"\"\n    Fits a PCA-aligned polynomial surface and trims it to the data extent.\n    - degree: Higher = more detail (try 8-12).\n    \"\"\"\n    volume = torch.from_numpy(volume)#.cuda()\n    device = volume.device\n\n    # 1. Extract and Center Data\n    point_zyx = torch.nonzero(volume).to(device).to(torch.float64)\n    if point_zyx.shape[0] &lt; 10: return\n\n    mean = point_zyx.mean(dim=0)\n    centered = point_zyx - mean\n\n    # 2. PCA Rotation\n    U, S, V = torch.pca_lowrank(centered, q=3)\n    pca_zyx = centered @ V\n\n    # Normalization for math stability\n    x_l, y_l = pca_zyx[:, 0], pca_zyx[:, 1]\n    x_scale, y_scale = x_l.abs().max(), y_l.abs().max()\n\n    # 3. Solve Polynomial\n    A_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_list.append(((x_l / x_scale) ** i) * ((y_l / y_scale) ** j))\n    A = torch.stack(A_list, dim=1)\n\n    lam = 1e-2 #1e-4\n    coeffs = torch.linalg.solve(\n        A.T @ A + lam * torch.eye(A.shape[1], device=device, dtype=torch.float64),\n        A.T @ pca_zyx[:, 2].unsqueeze(1)\n    )\n\n    # 4. Create Evaluation Grid (larger than data to ensure we hit the box edges)\n    gs = grid_size\n    gx = torch.linspace(x_l.min() * 1.5, x_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    gy = torch.linspace(y_l.min() * 1.5, y_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    grid_xl, grid_yl = torch.meshgrid(gx, gy, indexing='ij')\n\n    # Compute local Z\n    A_grid_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_grid_list.append(((grid_xl.flatten() / x_scale) ** i) * ((grid_yl.flatten() / y_scale) ** j))\n    grid_zl = (torch.stack(A_grid_list, dim=1) @ coeffs).reshape(gs, gs)\n\n    # 5. Transform back to original volume space\n    grid_point = torch.stack([grid_xl.flatten(), grid_yl.flatten(), grid_zl.flatten()], dim=-1)\n    grid_zyx = (grid_point @ V.T) + mean\n\n    # --- restict to orginal sapce\n    # Global coords: Index 0=Z, 1=Y, 2=X\n    gz = grid_zyx[:, 0].reshape(gs, gs)\n    gy = grid_zyx[:, 1].reshape(gs, gs)\n    gx = grid_zyx[:, 2].reshape(gs, gs)\n\n    D,H,W = volume.shape\n    B=bbox_border\n    out_of_bound = (gx &lt; B) | (gx &gt; W-B-1) | (gy &lt; B) | (gy &gt; H-B-1) | (gz &lt; B) | (gz &gt; D-B-1)\n    mask = (out_of_bound).cpu().numpy()\n    gx, gy, gz = gx.cpu().numpy(), gy.cpu().numpy(), gz.cpu().numpy()\n\n    # apply the trim mask\n    gz[mask] = np.nan\n    gy[mask] = np.nan\n    gx[mask] = np.nan\n\n    ########################################################3\n    valid = np.isfinite(gz)  # or support_mask before applying nan\n    lab, n = ndi.label(valid)  # 4-connectivity by default in 2D\n    if n &gt; 1:\n        counts = np.bincount(lab.ravel())  #this is not correct. we should drop part that doesn't contains any datapoint instead\n        counts[0] = 0\n        keep = counts.argmax()\n        drop = lab != keep\n        gz[drop] = np.nan\n        gy[drop] = np.nan\n        gx[drop] = np.nan\n\n    return gz, gy, gx\n\n##################################################################\ndef show_fit_result(prob_np, gz, gy, gx):\n    # pts = pts.data.float().cpu().numpy()\n    N = gx.shape[0]\n    # vertices: (N*N, 3)\n    verts = np.column_stack([\n        gx.reshape(-1),\n        gy.reshape(-1),\n        gz.reshape(-1),\n    ]).astype(np.float32)\n    # faces (two triangles per quad)\n    faces = []\n    for i in range(N - 1):\n        for j in range(N - 1):\n            v0 = i * N + j\n            v1 = v0 + 1\n            v2 = v0 + N\n            v3 = v2 + 1\n            faces.append([3, v0, v1, v3])\n            faces.append([3, v0, v3, v2])\n    faces = np.array(faces, dtype=np.int64).reshape(-1)\n    surface = pv.PolyData(verts, faces)\n    surface = surface.clean()\n\n    D, H, W = prob_np.shape\n    grid = pv.ImageData()\n    grid.dimensions = (W + 1, H + 1, D + 1)  # (X,Y,Z)+1\n    grid.spacing = (1, 1, 1)\n    grid.origin = (0, 0, 0)\n\n    # IMPORTANT: convert (Z,Y,X) -&gt; (X,Y,Z) before flatten(F)\n    prob_np = prob_np*0.8\n    prob_np[0,0,0]=1\n    labels_xyz = np.transpose(prob_np, (2, 1, 0))  # (W,H,D) == (X,Y,Z)\n    grid.cell_data[\"labels\"] = labels_xyz.flatten(order=\"F\")\n\n    #\n    # grid = pv.wrap(prob_np)\n    # labels = prob_np.astype(np.int32)\n    # grid['labels'] = labels.flatten(order=\"F\")\n\n    ########################################3\n    #axis\n    axes= np.zeros((D,H,W))\n    axes[:, 0, 0] = 1  # z axis\n    axes[0, :, 0] = 1\n    axes[0, 0, :] = 1\n    axes[0, :, -1] = 1\n    axes[0, -1, :] = 1\n    axes = pv.wrap(axes)\n\n    #####################################\n    p = pv.Plotter()\n\n    p.add_volume(\n        axes,\n        #scalars=\"labels\",\n        #opacity=0.5,\n        #opacity=\"sigmoid\",\n        cmap=\"binary\",\n        shade=False,\n    )\n\n    p.add_volume(\n        grid,\n        scalars=\"labels\",\n        #opacity=0.5,\n        opacity=\"sigmoid\",\n        cmap=\"reds\",\n        shade=False,\n    )\n\n    p.add_mesh(\n        surface,\n        color=\"gray\",\n        opacity=0.8,\n        show_edges=True,\n        edge_color=\"black\",\n    )\n    p.show()\n\n\nif __name__ == '__main__':\n    prob_np = np.load('sl.npy') #binary volume of size 80x80x80: fg/bg = 1/0\n    prob_np = prob_np.astype(np.float32)\n\n    gz, gy, gx = fit_surface(prob_np, degree=6, grid_size=100)\n\n    show_fit_result(prob_np, gz, gy, gx)\n</code></pre>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3401228,
      "author_name": "hengck23",
      "author_url": "",
      "post_date": "02/03/2026 07:55:36",
      "content": "<p><a href=\"https://github.com/complete3d/paco\" target=\"_blank\">https://github.com/complete3d/paco</a>\n<a href=\"https://unico-completion.github.io/\" target=\"_blank\">https://unico-completion.github.io/</a></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fdb319b4c7ee2e82ad47fabd2ae162a14%2FSelection_2357.png?generation=1770105264615727&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Ff59652639fef8a092a1029825dd57c7f%2FSelection_2356.png?generation=1770105283977607&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F35c0cd67ef2d6cffed6a774bcb878286%2FSelection_2358.png?generation=1770105305591162&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F89cc9f9726d7f260d7305eeb8cd02bf7%2FSelection_2355.png?generation=1770105333592985&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F51c31671518e4ed5674c5431ad149940%2FSelection_2359.png?generation=1770105804160715&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F0c83ea9266c1b796abc36761414ccb20%2FSelection_2360.png?generation=1770106837171237&amp;alt=media\" alt=\"\"></p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3401451,
      "author_name": "hengck23",
      "author_url": "",
      "post_date": "02/03/2026 18:11:22",
      "content": "<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F591c29e93689cfeb46d207ccf570690b%2FSelection_2361.png?generation=1770142280604922&amp;alt=media\" alt=\"\"></p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fc8f2984a738764527b4c62c502819a56%2FSelection_2362.png?generation=1770142588733008&amp;alt=media\" alt=\"\"></p>",
      "votes": null,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "3400269": "there is such a thing called \"polynomial volume\"  \nchatgpt and gemini can tell you more.  \n\ninstead of fitting  line, surface, you can fit a volume!\nassume you have seeds point zyx for each connected compoent for K seeds, then each sample point is kzyx.\n\nexample of fitting results\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fca2212f25a276572c92ac0b1ff9970c9%2FSelection_2327.png?generation=1769939462819142&alt=media) \n\n\n\"polynomial volume\"   may not be the best parameterisation, please experiment and look for better one.\n\nbut the ground truth surface is indeed a polynomial surface (you can use degree 4 to 6).\nfor 0.5 scaled resolution of 160x160x160, fit median error for ground truth surface is 2 for degr 6, (95% quant error is about 5)\n   \nthis is results of fitting deg6 surface to high probability voxels (cc3d seed) from unet\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F909989b66b3fdee6d947d5f737e85a88%2FSelection_2309.png?generation=1769940593838186&alt=media)\n\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F87a5316c302220fa54fd4cec2c7798e9%2FSelection_2326.png?generation=1769941310422367&alt=media)\n(typo error: it should be truth annotation surface not truth polynomial surface)",
    "3400270": "![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fd9ac055c9156f1039eaf211efc0aba30%2FSelection_2328.png?generation=1769939696577354&alt=media)\n\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F16c47f5eab4c512948e4b0e4e1788d7a%2FSelection_2329.png?generation=1769939706834773&alt=media)",
    "3400412": "This sounds like designing a well-structured, math-oriented template.",
    "3400743": "2d single surface fit code:\n\n```\nimport torch\nimport torch.nn.functional as F\n\nimport numpy as np\nfrom my_utility import *\n\nimport torch\nimport matplotlib.pyplot as plt\n\nimport matplotlib\nmatplotlib.use('TkAgg')\nimport pyvista as pv\n\ndef fit_surface(volume, degree=8, grid_size = 100,\n    bbox_border=3,):\n    \"\"\"\n    Fits a PCA-aligned polynomial surface and trims it to the data extent.\n    - degree: Higher = more detail (try 8-12).\n    \"\"\"\n    volume = torch.from_numpy(volume)#.cuda()\n    device = volume.device\n\n    # 1. Extract and Center Data\n    point_zyx = torch.nonzero(volume).to(device).to(torch.float64)\n    if point_zyx.shape[0] < 10: return\n\n    mean = point_zyx.mean(dim=0)\n    centered = point_zyx - mean\n\n    # 2. PCA Rotation\n    U, S, V = torch.pca_lowrank(centered, q=3)\n    pca_zyx = centered @ V\n\n    # Normalization for math stability\n    x_l, y_l = pca_zyx[:, 0], pca_zyx[:, 1]\n    x_scale, y_scale = x_l.abs().max(), y_l.abs().max()\n\n    # 3. Solve Polynomial\n    A_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_list.append(((x_l / x_scale) ** i) * ((y_l / y_scale) ** j))\n    A = torch.stack(A_list, dim=1)\n\n    lam = 1e-2 #1e-4\n    coeffs = torch.linalg.solve(\n        A.T @ A + lam * torch.eye(A.shape[1], device=device, dtype=torch.float64),\n        A.T @ pca_zyx[:, 2].unsqueeze(1)\n    )\n\n    # 4. Create Evaluation Grid (larger than data to ensure we hit the box edges)\n    gs = grid_size\n    gx = torch.linspace(x_l.min() * 1.5, x_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    gy = torch.linspace(y_l.min() * 1.5, y_l.max() * 1.5, gs, device=device, dtype=torch.float64)\n    grid_xl, grid_yl = torch.meshgrid(gx, gy, indexing='ij')\n\n    # Compute local Z\n    A_grid_list = []\n    for i in range(degree + 1):\n        for j in range(degree + 1 - i):\n            A_grid_list.append(((grid_xl.flatten() / x_scale) ** i) * ((grid_yl.flatten() / y_scale) ** j))\n    grid_zl = (torch.stack(A_grid_list, dim=1) @ coeffs).reshape(gs, gs)\n\n    # 5. Transform back to original volume space\n    grid_point = torch.stack([grid_xl.flatten(), grid_yl.flatten(), grid_zl.flatten()], dim=-1)\n    grid_zyx = (grid_point @ V.T) + mean\n\n    # --- restict to orginal sapce\n    # Global coords: Index 0=Z, 1=Y, 2=X\n    gz = grid_zyx[:, 0].reshape(gs, gs)\n    gy = grid_zyx[:, 1].reshape(gs, gs)\n    gx = grid_zyx[:, 2].reshape(gs, gs)\n\n    D,H,W = volume.shape\n    B=bbox_border\n    out_of_bound = (gx < B) | (gx > W-B-1) | (gy < B) | (gy > H-B-1) | (gz < B) | (gz > D-B-1)\n    mask = (out_of_bound).cpu().numpy()\n    gx, gy, gz = gx.cpu().numpy(), gy.cpu().numpy(), gz.cpu().numpy()\n\n    # apply the trim mask\n    gz[mask] = np.nan\n    gy[mask] = np.nan\n    gx[mask] = np.nan\n\n    ########################################################3\n    valid = np.isfinite(gz)  # or support_mask before applying nan\n    lab, n = ndi.label(valid)  # 4-connectivity by default in 2D\n    if n > 1:\n        counts = np.bincount(lab.ravel())  #this is not correct. we should drop part that doesn't contains any datapoint instead\n        counts[0] = 0\n        keep = counts.argmax()\n        drop = lab != keep\n        gz[drop] = np.nan\n        gy[drop] = np.nan\n        gx[drop] = np.nan\n\n    return gz, gy, gx\n\n##################################################################\ndef show_fit_result(prob_np, gz, gy, gx):\n    # pts = pts.data.float().cpu().numpy()\n    N = gx.shape[0]\n    # vertices: (N*N, 3)\n    verts = np.column_stack([\n        gx.reshape(-1),\n        gy.reshape(-1),\n        gz.reshape(-1),\n    ]).astype(np.float32)\n    # faces (two triangles per quad)\n    faces = []\n    for i in range(N - 1):\n        for j in range(N - 1):\n            v0 = i * N + j\n            v1 = v0 + 1\n            v2 = v0 + N\n            v3 = v2 + 1\n            faces.append([3, v0, v1, v3])\n            faces.append([3, v0, v3, v2])\n    faces = np.array(faces, dtype=np.int64).reshape(-1)\n    surface = pv.PolyData(verts, faces)\n    surface = surface.clean()\n\n    D, H, W = prob_np.shape\n    grid = pv.ImageData()\n    grid.dimensions = (W + 1, H + 1, D + 1)  # (X,Y,Z)+1\n    grid.spacing = (1, 1, 1)\n    grid.origin = (0, 0, 0)\n\n    # IMPORTANT: convert (Z,Y,X) -> (X,Y,Z) before flatten(F)\n    prob_np = prob_np*0.8\n    prob_np[0,0,0]=1\n    labels_xyz = np.transpose(prob_np, (2, 1, 0))  # (W,H,D) == (X,Y,Z)\n    grid.cell_data[\"labels\"] = labels_xyz.flatten(order=\"F\")\n\n    #\n    # grid = pv.wrap(prob_np)\n    # labels = prob_np.astype(np.int32)\n    # grid['labels'] = labels.flatten(order=\"F\")\n\n    ########################################3\n    #axis\n    axes= np.zeros((D,H,W))\n    axes[:, 0, 0] = 1  # z axis\n    axes[0, :, 0] = 1\n    axes[0, 0, :] = 1\n    axes[0, :, -1] = 1\n    axes[0, -1, :] = 1\n    axes = pv.wrap(axes)\n\n    #####################################\n    p = pv.Plotter()\n\n    p.add_volume(\n        axes,\n        #scalars=\"labels\",\n        #opacity=0.5,\n        #opacity=\"sigmoid\",\n        cmap=\"binary\",\n        shade=False,\n    )\n\n    p.add_volume(\n        grid,\n        scalars=\"labels\",\n        #opacity=0.5,\n        opacity=\"sigmoid\",\n        cmap=\"reds\",\n        shade=False,\n    )\n\n    p.add_mesh(\n        surface,\n        color=\"gray\",\n        opacity=0.8,\n        show_edges=True,\n        edge_color=\"black\",\n    )\n    p.show()\n \n\nif __name__ == '__main__':\n    prob_np = np.load('sl.npy') #binary volume of size 80x80x80: fg/bg = 1/0\n    prob_np = prob_np.astype(np.float32)\n\n    gz, gy, gx = fit_surface(prob_np, degree=6, grid_size=100)\n\n    show_fit_result(prob_np, gz, gy, gx)\n\n```",
    "3401228": "https://github.com/complete3d/paco\nhttps://unico-completion.github.io/\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fdb319b4c7ee2e82ad47fabd2ae162a14%2FSelection_2357.png?generation=1770105264615727&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Ff59652639fef8a092a1029825dd57c7f%2FSelection_2356.png?generation=1770105283977607&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F35c0cd67ef2d6cffed6a774bcb878286%2FSelection_2358.png?generation=1770105305591162&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F89cc9f9726d7f260d7305eeb8cd02bf7%2FSelection_2355.png?generation=1770105333592985&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F51c31671518e4ed5674c5431ad149940%2FSelection_2359.png?generation=1770105804160715&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F0c83ea9266c1b796abc36761414ccb20%2FSelection_2360.png?generation=1770106837171237&alt=media)",
    "3401451": "![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2F591c29e93689cfeb46d207ccf570690b%2FSelection_2361.png?generation=1770142280604922&alt=media)\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-forum-message-attachments/o/inbox%2F113660%2Fc8f2984a738764527b4c62c502819a56%2FSelection_2362.png?generation=1770142588733008&alt=media)"
  },
  "source": "meta"
}