{
  "id": 556962,
  "title": "3D Visualization of 6 Particle Types in TS_5_4",
  "url": "/competitions/czii-cryo-et-object-identification/discussion/556962",
  "author_name": "Viralvector",
  "post_date": "2025-01-16T01:37:27.434000",
  "votes": 9,
  "comment_count": 3,
  "views": 0,
  "content": "<p>Hi everyone,<br>\nI’ve uploaded a video showcasing the spatial distribution of six particle types in the TS_5_4 dataset. You can watch it here:<br>\n<a href=\"https://www.kaggle.com/datasets/wulinteo/3d-visualization-with-class-overlays\" target=\"_blank\">https://www.kaggle.com/datasets/wulinteo/3d-visualization-with-class-overlays</a><br>\nThe video provides a 3D view through the Z-axis of the tomogram, with each particle type labeled in semi-transparent colors.<br>\nAfter going through the discussions, I noticed there wasn’t a clear way to visualize the relationship between the tomogram and particle distributions. Using napari and clipchamp, I created this video to help bridge that gap.<br>\nWhat’s Included:</p>\n<ul>\n<li>A video showing six particle types: Virus-like particles, Thyroglobulin, Ribosome, Beta-galactosidase, Beta-amylase, Apo-ferritin.</li>\n<li>Clear visualization of size, distribution, and spatial alignment with the tomogram.</li>\n<li>Pause the video to inspect and compare particle sizes, positions, and distributions.<br>\nHoping this contribution adds value to the Kaggle community—feedback is welcome!<br>\nThanks!</li>\n</ul>",
  "messages": [
    {
      "id": 3098024,
      "postDate": "2025-01-16T01:37:27.433Z",
      "content": "<p>Hi everyone,<br>\nI’ve uploaded a video showcasing the spatial distribution of six particle types in the TS_5_4 dataset. You can watch it here:<br>\n<a href=\"https://www.kaggle.com/datasets/wulinteo/3d-visualization-with-class-overlays\" target=\"_blank\">https://www.kaggle.com/datasets/wulinteo/3d-visualization-with-class-overlays</a><br>\nThe video provides a 3D view through the Z-axis of the tomogram, with each particle type labeled in semi-transparent colors.<br>\nAfter going through the discussions, I noticed there wasn’t a clear way to visualize the relationship between the tomogram and particle distributions. Using napari and clipchamp, I created this video to help bridge that gap.<br>\nWhat’s Included:</p>\n<ul>\n<li>A video showing six particle types: Virus-like particles, Thyroglobulin, Ribosome, Beta-galactosidase, Beta-amylase, Apo-ferritin.</li>\n<li>Clear visualization of size, distribution, and spatial alignment with the tomogram.</li>\n<li>Pause the video to inspect and compare particle sizes, positions, and distributions.<br>\nHoping this contribution adds value to the Kaggle community—feedback is welcome!<br>\nThanks!</li>\n</ul>",
      "rawMarkdown": "\nHi everyone,\n\nI’ve uploaded a video showcasing the spatial distribution of six particle types in the TS_5_4 dataset. You can watch it here:\nhttps://www.kaggle.com/datasets/wulinteo/3d-visualization-with-class-overlays\n\nThe video provides a 3D view through the Z-axis of the tomogram, with each particle type labeled in semi-transparent colors.\n\nAfter going through the discussions, I noticed there wasn’t a clear way to visualize the relationship between the tomogram and particle distributions. Using napari and clipchamp, I created this video to help bridge that gap.\n\nWhat’s Included:\n- A video showing six particle types: Virus-like particles, Thyroglobulin, Ribosome, Beta-galactosidase, Beta-amylase, Apo-ferritin.\n- Clear visualization of size, distribution, and spatial alignment with the tomogram.\n- Pause the video to inspect and compare particle sizes, positions, and distributions.\n\nHoping this contribution adds value to the Kaggle community—feedback is welcome!\n\nThanks!\n",
      "votes": 9
    },
    {
      "id": 3100681,
      "postDate": "2025-01-19T17:46:53.410Z",
      "content": "<p>I'm sharing the Python script to visualize <strong>3D Zarr image data</strong> with overlays for <strong>6 particle types</strong> using <strong>Napari</strong>.</p>\n<blockquote>\n  <p><strong>Note</strong>: Napari requires a GUI and cannot run in this Kaggle environment. To run this notebook, please download it and use a local Python environment.</p>\n</blockquote>\n<pre><code>\n\n\n\n\n napari\n zarr\n json\n numpy  np\n os\n itertools  cycle\n qtpy.QtWidgets  QLabel, QVBoxLayout, QWidget\n\n\nzarr_path = \noverlay_folder = \n\n\nCOLOR_CYCLE = cycle([, , , , , , ])\n\n\n:\n    zarr_image = zarr.(zarr_path, mode=)\n    high_res_data = zarr_image[]  \n    ()\n    \n    ()\n    ()\n    ()\n\n    \n    high_res_data_np = np.array(high_res_data)  \n\n    \n    scaled_data = (high_res_data_np - high_res_data_np.()) / (high_res_data_np.() - high_res_data_np.())\n    scaled_data = (scaled_data * ).astype(np.uint8)  \n Exception  e:\n    ()\n    \n\n\nviewer = napari.Viewer()\n\n\nviewer.add_image(scaled_data, name=, scale=(, , ))\n\n\n\n\n\nimage_layer = viewer.add_image(high_res_data, name=, scale=(, , ))\n\n\n ():\n     ():\n        ().__init__()\n        .label = QLabel()\n        layout = QVBoxLayout()\n        layout.addWidget(.label)\n        .setLayout(layout)\n\n     ():\n        .label.setText()\n\nintensity_display = IntensityDisplay()\nviewer.window.add_dock_widget(intensity_display, area=)\n\n\n\n ():\n    \n    position = event.position\n     position     ( &lt;= position[] &lt; high_res_data.shape[] \n                                 &lt;= position[] &lt; high_res_data.shape[] \n                                 &lt;= position[] &lt; high_res_data.shape[]):\n        \n\n    \n    z, y, x = (, position)\n\n    \n    intensity = high_res_data[z, y, x]\n\n    \n    intensity_display.update_label(x, y, z, intensity)\n\n\nparticle_colors = {}\n\n\n\n\n json_file  os.listdir(overlay_folder):\n     json_file.endswith():  \n        json_path = os.path.join(overlay_folder, json_file)\n        :\n             (json_path, )  f:\n                json_data = json.load(f)\n\n            \n            particle_type = json_data.get(, )\n\n\n            \n             particle_type   particle_colors:\n                particle_colors[particle_type] = (COLOR_CYCLE)\n\n            \n            points_array = np.array([\n                (point[][] / ,  \n                 point[][] / ,\n                 point[][] / )\n                 point  json_data[]\n            ])\n\n            \n            viewer.add_points(\n                points_array,\n                size=,\n                face_color=particle_colors[particle_type],  \n                name=,\n                scale=(, , ),  \n                opacity=  \n            )\n            ()\n         Exception  e:\n            ()\n\n\nnapari.run()\n</code></pre>",
      "rawMarkdown": "I'm sharing the Python script to visualize **3D Zarr image data** with overlays for **6 particle types** using **Napari**.\n\n> **Note**: Napari requires a GUI and cannot run in this Kaggle environment. To run this notebook, please download it and use a local Python environment.\n\n\n```python\n\n# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Wed Jan 15 13:18:37 2025\n\nAuthor: Wulin Teo\n\nDescription:\nThis script visualizes 3D Zarr image data with particle overlays using Napari. \nThe script is designed for local execution due to GUI requirements.\n\nVersion: 1.0\n\"\"\"\n\n\n\nimport napari\nimport zarr\nimport json\nimport numpy as np\nimport os\nfrom itertools import cycle\nfrom qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget\n\n# Paths to the Zarr image and overlay folder\nzarr_path = r\"D:\\3D 6 features EM project\\czii-cryo-et-object-identification\\train\\static\\ExperimentRuns\\TS_6_4\\VoxelSpacing10.000\\denoised.zarr\"\noverlay_folder = r\"D:\\3D 6 features EM project\\czii-cryo-et-object-identification\\train\\overlay\\ExperimentRuns\\TS_6_4\\Picks\"\n\n# Define unique colors for particles (you can customize this)\nCOLOR_CYCLE = cycle(['red', 'blue', 'green', 'orange', 'purple', 'cyan', 'yellow'])\n\n# Load the Zarr file\ntry:\n    zarr_image = zarr.open(zarr_path, mode='r')\n    high_res_data = zarr_image['0']  # Use the highest resolution dataset\n    print(f\"Zarr dataset shape (highest resolution): {high_res_data.shape}\")\n    # Inspect raw data statistics\n    print(f\"Data type: {high_res_data.dtype}\")\n    print(f\"Raw data min: {high_res_data[:].min()}, max: {high_res_data[:].max()}\")\n    print(f\"Raw data shape: {high_res_data.shape}\")\n\n    # Convert the Zarr array to a NumPy array\n    high_res_data_np = np.array(high_res_data)  # Ensure the data fits into memory\n\n    # Apply linear scaling for visualization\n    scaled_data = (high_res_data_np - high_res_data_np.min()) / (high_res_data_np.max() - high_res_data_np.min())\n    scaled_data = (scaled_data * 255).astype(np.uint8)  # Convert to 8-bit for visualization\nexcept Exception as e:\n    print(f\"Error loading Zarr file: {e}\")\n    raise\n\n# Initialize Napari viewer\nviewer = napari.Viewer()\n\n# Add the scaled image layer to the viewer\nviewer.add_image(scaled_data, name='Denoised Zarr Image (Scaled)', scale=(1, 1, 1))\n\n\n\n\n# Add the Zarr image layer\nimage_layer = viewer.add_image(high_res_data, name='Denoised Zarr Image (High Res)', scale=(1, 1, 1))\n\n# Create a widget to display intensity and coordinates\nclass IntensityDisplay(QWidget):\n    def __init__(self):\n        super().__init__()\n        self.label = QLabel(\"Hover over the image to see intensity and coordinates.\")\n        layout = QVBoxLayout()\n        layout.addWidget(self.label)\n        self.setLayout(layout)\n\n    def update_label(self, x, y, z, intensity):\n        self.label.setText(f\"X: {x}, Y: {y}, Z: {z}, Intensity: {intensity}\")\n\nintensity_display = IntensityDisplay()\nviewer.window.add_dock_widget(intensity_display, area='right')\n\n# Function to update intensity and coordinates\n@viewer.mouse_move_callbacks.append\ndef update_intensity(viewer, event):\n    # Get the mouse position in world coordinates\n    position = event.position\n    if position is None or not (0 <= position[0] < high_res_data.shape[0] and\n                                0 <= position[1] < high_res_data.shape[1] and\n                                0 <= position[2] < high_res_data.shape[2]):\n        return\n\n    # Convert world coordinates to indices\n    z, y, x = map(int, position)\n\n    # Get the intensity value\n    intensity = high_res_data[z, y, x]\n\n    # Update the display widget\n    intensity_display.update_label(x, y, z, intensity)\n\n# Map to keep track of particle type to color mapping\nparticle_colors = {}\n\n\n\n# Loop through all JSON files in the overlay folder\nfor json_file in os.listdir(overlay_folder):\n    if json_file.endswith('.json'):  # Ensure we're processing only JSON files\n        json_path = os.path.join(overlay_folder, json_file)\n        try:\n            with open(json_path, 'r') as f:\n                json_data = json.load(f)\n\n            # Extract the particle type from the JSON metadata\n            particle_type = json_data.get('pickable_object_name', 'Unknown Particle')\n            \n\n            # Assign a unique color if not already assigned\n            if particle_type not in particle_colors:\n                particle_colors[particle_type] = next(COLOR_CYCLE)\n\n            # Extract and scale points\n            points_array = np.array([\n                (point['location']['z'] / 10,  # Note the axis order: (z, y, x)\n                 point['location']['y'] / 10,\n                 point['location']['x'] / 10)\n                for point in json_data['points']\n            ])\n\n            # Add points layer to Napari\n            viewer.add_points(\n                points_array,\n                size=20,\n                face_color=particle_colors[particle_type],  # Use the unique color\n                name=f'{particle_type} Points',\n                scale=(1, 1, 1),  # Adjust scale as needed\n                opacity=0.5  # Set the translucency\n            )\n            print(f\"Added layer for {particle_type} with color {particle_colors[particle_type]}: {points_array.shape[0]} points.\")\n        except Exception as e:\n            print(f\"Error processing {json_file}: {e}\")\n\n# Run Napari\nnapari.run()\n\n```",
      "votes": 2
    },
    {
      "id": 3100676,
      "postDate": "2025-01-19T17:41:18.320Z",
      "rawMarkdown": "",
      "isDeleted": true
    },
    {
      "id": 3100673,
      "postDate": "2025-01-19T17:39:38.347Z",
      "rawMarkdown": "",
      "isDeleted": true
    }
  ],
  "comments": [
    {
      "id": 3100681,
      "author_name": "Viralvector",
      "author_url": "",
      "post_date": "2025-01-19T17:46:53.410000",
      "content": "<p>I'm sharing the Python script to visualize <strong>3D Zarr image data</strong> with overlays for <strong>6 particle types</strong> using <strong>Napari</strong>.</p>\n<blockquote>\n  <p><strong>Note</strong>: Napari requires a GUI and cannot run in this Kaggle environment. To run this notebook, please download it and use a local Python environment.</p>\n</blockquote>\n<pre><code>\n\n\n\n\n napari\n zarr\n json\n numpy  np\n os\n itertools  cycle\n qtpy.QtWidgets  QLabel, QVBoxLayout, QWidget\n\n\nzarr_path = \noverlay_folder = \n\n\nCOLOR_CYCLE = cycle([, , , , , , ])\n\n\n:\n    zarr_image = zarr.(zarr_path, mode=)\n    high_res_data = zarr_image[]  \n    ()\n    \n    ()\n    ()\n    ()\n\n    \n    high_res_data_np = np.array(high_res_data)  \n\n    \n    scaled_data = (high_res_data_np - high_res_data_np.()) / (high_res_data_np.() - high_res_data_np.())\n    scaled_data = (scaled_data * ).astype(np.uint8)  \n Exception  e:\n    ()\n    \n\n\nviewer = napari.Viewer()\n\n\nviewer.add_image(scaled_data, name=, scale=(, , ))\n\n\n\n\n\nimage_layer = viewer.add_image(high_res_data, name=, scale=(, , ))\n\n\n ():\n     ():\n        ().__init__()\n        .label = QLabel()\n        layout = QVBoxLayout()\n        layout.addWidget(.label)\n        .setLayout(layout)\n\n     ():\n        .label.setText()\n\nintensity_display = IntensityDisplay()\nviewer.window.add_dock_widget(intensity_display, area=)\n\n\n\n ():\n    \n    position = event.position\n     position     ( &lt;= position[] &lt; high_res_data.shape[] \n                                 &lt;= position[] &lt; high_res_data.shape[] \n                                 &lt;= position[] &lt; high_res_data.shape[]):\n        \n\n    \n    z, y, x = (, position)\n\n    \n    intensity = high_res_data[z, y, x]\n\n    \n    intensity_display.update_label(x, y, z, intensity)\n\n\nparticle_colors = {}\n\n\n\n\n json_file  os.listdir(overlay_folder):\n     json_file.endswith():  \n        json_path = os.path.join(overlay_folder, json_file)\n        :\n             (json_path, )  f:\n                json_data = json.load(f)\n\n            \n            particle_type = json_data.get(, )\n\n\n            \n             particle_type   particle_colors:\n                particle_colors[particle_type] = (COLOR_CYCLE)\n\n            \n            points_array = np.array([\n                (point[][] / ,  \n                 point[][] / ,\n                 point[][] / )\n                 point  json_data[]\n            ])\n\n            \n            viewer.add_points(\n                points_array,\n                size=,\n                face_color=particle_colors[particle_type],  \n                name=,\n                scale=(, , ),  \n                opacity=  \n            )\n            ()\n         Exception  e:\n            ()\n\n\nnapari.run()\n</code></pre>",
      "votes": 2,
      "replies": []
    },
    {
      "id": 3100676,
      "author_name": "",
      "author_url": "",
      "post_date": "2025-01-19T17:41:18.320000",
      "content": "",
      "votes": 0,
      "replies": []
    },
    {
      "id": 3100673,
      "author_name": "",
      "author_url": "",
      "post_date": "2025-01-19T17:39:38.347000",
      "content": "",
      "votes": 0,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "3098024": "\nHi everyone,\n\nI’ve uploaded a video showcasing the spatial distribution of six particle types in the TS_5_4 dataset. You can watch it here:\nhttps://www.kaggle.com/datasets/wulinteo/3d-visualization-with-class-overlays\n\nThe video provides a 3D view through the Z-axis of the tomogram, with each particle type labeled in semi-transparent colors.\n\nAfter going through the discussions, I noticed there wasn’t a clear way to visualize the relationship between the tomogram and particle distributions. Using napari and clipchamp, I created this video to help bridge that gap.\n\nWhat’s Included:\n- A video showing six particle types: Virus-like particles, Thyroglobulin, Ribosome, Beta-galactosidase, Beta-amylase, Apo-ferritin.\n- Clear visualization of size, distribution, and spatial alignment with the tomogram.\n- Pause the video to inspect and compare particle sizes, positions, and distributions.\n\nHoping this contribution adds value to the Kaggle community—feedback is welcome!\n\nThanks!\n",
    "3100681": "I'm sharing the Python script to visualize **3D Zarr image data** with overlays for **6 particle types** using **Napari**.\n\n> **Note**: Napari requires a GUI and cannot run in this Kaggle environment. To run this notebook, please download it and use a local Python environment.\n\n\n```python\n\n# -*- coding: utf-8 -*-\n\"\"\"\nCreated on Wed Jan 15 13:18:37 2025\n\nAuthor: Wulin Teo\n\nDescription:\nThis script visualizes 3D Zarr image data with particle overlays using Napari. \nThe script is designed for local execution due to GUI requirements.\n\nVersion: 1.0\n\"\"\"\n\n\n\nimport napari\nimport zarr\nimport json\nimport numpy as np\nimport os\nfrom itertools import cycle\nfrom qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget\n\n# Paths to the Zarr image and overlay folder\nzarr_path = r\"D:\\3D 6 features EM project\\czii-cryo-et-object-identification\\train\\static\\ExperimentRuns\\TS_6_4\\VoxelSpacing10.000\\denoised.zarr\"\noverlay_folder = r\"D:\\3D 6 features EM project\\czii-cryo-et-object-identification\\train\\overlay\\ExperimentRuns\\TS_6_4\\Picks\"\n\n# Define unique colors for particles (you can customize this)\nCOLOR_CYCLE = cycle(['red', 'blue', 'green', 'orange', 'purple', 'cyan', 'yellow'])\n\n# Load the Zarr file\ntry:\n    zarr_image = zarr.open(zarr_path, mode='r')\n    high_res_data = zarr_image['0']  # Use the highest resolution dataset\n    print(f\"Zarr dataset shape (highest resolution): {high_res_data.shape}\")\n    # Inspect raw data statistics\n    print(f\"Data type: {high_res_data.dtype}\")\n    print(f\"Raw data min: {high_res_data[:].min()}, max: {high_res_data[:].max()}\")\n    print(f\"Raw data shape: {high_res_data.shape}\")\n\n    # Convert the Zarr array to a NumPy array\n    high_res_data_np = np.array(high_res_data)  # Ensure the data fits into memory\n\n    # Apply linear scaling for visualization\n    scaled_data = (high_res_data_np - high_res_data_np.min()) / (high_res_data_np.max() - high_res_data_np.min())\n    scaled_data = (scaled_data * 255).astype(np.uint8)  # Convert to 8-bit for visualization\nexcept Exception as e:\n    print(f\"Error loading Zarr file: {e}\")\n    raise\n\n# Initialize Napari viewer\nviewer = napari.Viewer()\n\n# Add the scaled image layer to the viewer\nviewer.add_image(scaled_data, name='Denoised Zarr Image (Scaled)', scale=(1, 1, 1))\n\n\n\n\n# Add the Zarr image layer\nimage_layer = viewer.add_image(high_res_data, name='Denoised Zarr Image (High Res)', scale=(1, 1, 1))\n\n# Create a widget to display intensity and coordinates\nclass IntensityDisplay(QWidget):\n    def __init__(self):\n        super().__init__()\n        self.label = QLabel(\"Hover over the image to see intensity and coordinates.\")\n        layout = QVBoxLayout()\n        layout.addWidget(self.label)\n        self.setLayout(layout)\n\n    def update_label(self, x, y, z, intensity):\n        self.label.setText(f\"X: {x}, Y: {y}, Z: {z}, Intensity: {intensity}\")\n\nintensity_display = IntensityDisplay()\nviewer.window.add_dock_widget(intensity_display, area='right')\n\n# Function to update intensity and coordinates\n@viewer.mouse_move_callbacks.append\ndef update_intensity(viewer, event):\n    # Get the mouse position in world coordinates\n    position = event.position\n    if position is None or not (0 <= position[0] < high_res_data.shape[0] and\n                                0 <= position[1] < high_res_data.shape[1] and\n                                0 <= position[2] < high_res_data.shape[2]):\n        return\n\n    # Convert world coordinates to indices\n    z, y, x = map(int, position)\n\n    # Get the intensity value\n    intensity = high_res_data[z, y, x]\n\n    # Update the display widget\n    intensity_display.update_label(x, y, z, intensity)\n\n# Map to keep track of particle type to color mapping\nparticle_colors = {}\n\n\n\n# Loop through all JSON files in the overlay folder\nfor json_file in os.listdir(overlay_folder):\n    if json_file.endswith('.json'):  # Ensure we're processing only JSON files\n        json_path = os.path.join(overlay_folder, json_file)\n        try:\n            with open(json_path, 'r') as f:\n                json_data = json.load(f)\n\n            # Extract the particle type from the JSON metadata\n            particle_type = json_data.get('pickable_object_name', 'Unknown Particle')\n            \n\n            # Assign a unique color if not already assigned\n            if particle_type not in particle_colors:\n                particle_colors[particle_type] = next(COLOR_CYCLE)\n\n            # Extract and scale points\n            points_array = np.array([\n                (point['location']['z'] / 10,  # Note the axis order: (z, y, x)\n                 point['location']['y'] / 10,\n                 point['location']['x'] / 10)\n                for point in json_data['points']\n            ])\n\n            # Add points layer to Napari\n            viewer.add_points(\n                points_array,\n                size=20,\n                face_color=particle_colors[particle_type],  # Use the unique color\n                name=f'{particle_type} Points',\n                scale=(1, 1, 1),  # Adjust scale as needed\n                opacity=0.5  # Set the translucency\n            )\n            print(f\"Added layer for {particle_type} with color {particle_colors[particle_type]}: {points_array.shape[0]} points.\")\n        except Exception as e:\n            print(f\"Error processing {json_file}: {e}\")\n\n# Run Napari\nnapari.run()\n\n```",
    "3100676": "",
    "3100673": ""
  }
}