FDM 3D Printing Defect Detection Model Prototyping

By Eldar · Published July 11, 2026

Phase 1 data pipeline for FDM defect detection: sources ~280 frames from HuggingFace and Kaggle, applies zero-shot Grounding DINO pre-labeling across four defect classes (stringing, spaghetti, layer shift, warping).

  • computer-vision
  • object-detection
  • 3d-printing
  • defect-detection
  • grounding-dino
  • data-preparation
73 cells1 experiment11 views0 forks

Inside this notebook

# FDM 3D-Printing Defect Detection — Phase 1: Data Sourcing & Pre-labeling This notebook builds the data foundation for an FDM (Fused Deposition Modeling) 3D-printing defect detector across four classes with a **fixed class order**: `stringing=0, spaghetti=1, layer_shift=2, warping=3`. **Phase 1 pipeline:** 1. **GPU + pinned stack** — verify GPU, install pinned dependency versions. 2. **Data sourcing** — stage ~280 real frames from two sources: HuggingFace `DasKunststoffZentrumSKZ/Errors_Additive_Manufacturing_Plattform_Cam` (stringing/spaghetti/warping) and Kaggle `wengmhu/fdm-3d-printing-defect-dataset` (layer_shift), producing `frames/` + `frames/manifest.json`. 3. **Zero-shot pre-labeling** — Grounding DINO tiny detects the four defect classes on every frame, recall-first thresholds, raw detections saved to `frames/predictions.json`. 4. **Auto-accept / uncertain split** — confident detections (≥0.45) become YOLO-format labels in `auto_labels/`; low-confidence frames go to a capped (≤30) human-review queue at `labeling/review_queue.json` + `labeling/review_frames/`. All artifacts are persisted to the outputs bucket under `labeling/`.

import subprocess
print(subprocess.run(["nvidia-smi"], capture_output=True, text=True).stdout)

import torch
print("torch version:", torch.__version__)
print("CUDA available:", torch.cuda.is_available())
if torch.cuda.is_available():
    print("Device:", torch.cuda.get_device_name(0))
else:
    print("⚠️ WARNING: No GPU detected — proceeding on CPU. This will be much slower for Grounding DINO inference.")
Sat Jul 11 16:57:41 2026       
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 580.95.05              Driver Version: 580.95.05      CUDA Version: 13.0     |
+-----------------------------------------+------------------------+----------------------+
| GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
| Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
|…
%pip install -q "transformers>=4.44" "ultralytics==8.4.90" opencv-python-headless pillow kagglehub datasets huggingface_hub

import importlib
pkgs = ["torch", "transformers", "ultralytics", "cv2", "PIL", "kagglehub", "datasets", "huggingface_hub"]
name_map = {"cv2": "opencv-python-headless", "PIL": "pillow"}
for p in pkgs:
    try:
        mod = importlib.import_module(p)
        ver = getattr(mod, "__version__", "unknown")
        if p == "PIL":
            import PIL
            ver = PIL.__version__
        print(f"{name_map.get(p, p)}: {ver}")
    except Exception as e:
        print(f"{p}: FAILED to import -> {e}")
Note: you may need to restart the kernel to use updated packages.
torch: 2.3.1
transformers: 4.44.2
Creating new Ultralytics Settings v0.0.6 file ✅ 
View Ultralytics Settings with 'yolo settings' or at '/root/.config/Ultralytics/settings.json'
Update Settings with 'yolo settings key=value', i.e. 'yolo settings runs_dir=path/to/dir'. For help see https://docs.ultralytics.com/quickstart/#ultralytics-settings.
ultralytics: 8.4.90
opencv-python-headless: 5.0.0
pillow: 10.3.0
kagglehub: 1.0.2
dataset…
from huggingface_hub import hf_hub_download

yaml_path = hf_hub_download(
    repo_id="DasKunststoffZentrumSKZ/Errors_Additive_Manufacturing_Plattform_Cam",
    filename="dataset.yaml",
    repo_type="dataset",
)
with open(yaml_path) as f:
    yaml_content = f.read()
print(yaml_content)
path: coco_images
train: images/train
val: images/val
test: images/test
nc: 9
names: ['Nozzle', 'Object', 'Purge Line', 'Spaghetti', 'Stringing', 'Unterextrusion', 'Warping', 'schlechte erste Schicht', 'Double Print']
from huggingface_hub import snapshot_download
import os

# Confirmed class mapping from dataset.yaml (authoritative)
hf_class_names = ['Nozzle', 'Object', 'Purge Line', 'Spaghetti', 'Stringing', 'Unterextrusion', 'Warping', 'schlechte erste Schicht', 'Double Print']
hf_id_to_name = {i: n for i, n in enumerate(hf_class_names)}
print("HF class id -> name mapping:", hf_id_to_name)

# Classes of interest for this dataset (test split, smallest = 1428 rows)
HF_TARGET = {3: "spaghetti", 4: "stringing", 6: "warping"}
print("Target HF class ids of interest:", HF_TARGET)

snap_dir = snapshot_download(
    repo_id="DasKunststoffZentrumSKZ/Errors_Additive_Manufacturing_Plattform_Cam",
    repo_type="dataset",
    allow_patterns=["images/test/*", "labels/test/*", "dataset.yaml"],
    local_dir="/home/user/hf_snapshot",
)
…
HF class id -> name mapping: {0: 'Nozzle', 1: 'Object', 2: 'Purge Line', 3: 'Spaghetti', 4: 'Stringing', 5: 'Unterextrusion', 6: 'Warping', 7: 'schlechte erste Schicht', 8: 'Double Print'}
Target HF class ids of interest: {3: 'spaghetti', 4: 'stringing', 6: 'warping'}
Snapshot dir: /home/user/hf_snapshot
images/test count: 334
labels/test count: 334
import glob
from collections import defaultdict

labels_dir = os.path.join(snap_dir, "labels", "test")
images_dir = os.path.join(snap_dir, "images", "test")

label_files = sorted(glob.glob(os.path.join(labels_dir, "*.txt")))
print("Total label files found:", len(label_files))

# Parse each label file -> set of HF class ids present
frame_classes = {}
for lf in label_files:
    stem = os.path.splitext(os.path.basename(lf))[0]
    classes_present = set()
    with open(lf) as f:
        for line in f:
            parts = line.strip().split()
            if not parts:
…
Total label files found: 334
Frames available per target class (HF dataset, test split):
  spaghetti: 154
  stringing: 3
from huggingface_hub import HfApi
api = HfApi()
all_files = api.list_repo_files("DasKunststoffZentrumSKZ/Errors_Additive_Manufacturing_Plattform_Cam", repo_type="dataset")
test_images = [f for f in all_files if f.startswith("images/test/")]
test_labels = [f for f in all_files if f.startswith("labels/test/")]
train_images = [f for f in all_files if f.startswith("images/train/")]
val_images = [f for f in all_files if f.startswith("images/val/")]
print("Total files in repo:", len(all_files))
print("images/test:", len(test_images))
print("labels/test:", len(test_labels))
print("images/train:", len(train_images))
print("images/val:", len(val_images))

This is a preview. Open the live notebook to see all 73 cells with their charts and full outputs, or fork it into your own Clusy workspace.