Well, it’s possible with numpy, OpenCV, or GIMP, but when it comes to handling large amounts of data accurately, having a dedicated tool is probably better…
I often hear about Label Studio.
What you’re trying to create
For training binary semantic segmentation, you want a paired dataset:
Recommended storage: PNG (lossless). Avoid JPEG for masks.
Best free tools for “draw/paint the pixels by hand”
1) Label Studio (Community Edition) — best for brush/painting masks
Why it fits your description
- You can paint the region directly with a brush (“BrushLabels”). (Label Studio)
- Export supports PNG masks and NumPy 2D arrays, which is exactly what most segmentation training pipelines need. (Label Studio)
- Open-source; Apache-2.0 licensed. (GitHub)
How you would use it
- Create an image project using the “semantic segmentation with masks” template (BrushLabels).
- Define one label:
positive.
- Paint positives; erase mistakes; zoom in for edges.
- Export using Brush labels to NumPy and PNG. (Label Studio)
Important export detail (common surprise)
- “Each label outputs as one image.” (Label Studio)
For binary segmentation (one label), this is simple. For multi-class later, you may get multiple mask files per image and need a merge step (this is discussed in the project’s issue history). (Label Studio)
2) CVAT — best when you want a full annotation platform + many export formats
Why it fits
- Designed for annotation at scale and supports exporting masks in multiple formats, including Segmentation Mask (Pascal VOC-style raster masks) and COCO (RLE). (docs.cvat.ai)
- Open-source; MIT licensed. (GitHub)
How you would use it
- Create a task, add your images.
- Annotate using polygons/regions (and CVAT’s mask-oriented workflows).
- Export as Segmentation Mask for the most straightforward raster-mask output. (docs.cvat.ai)
Why “Segmentation Mask” export is usually the simplest
- It’s explicitly a “simple format” based on Pascal VOC segmentation, meant for training workflows that want mask images. (docs.cvat.ai)
Known friction point (worth knowing)
- Users often want “separate mask per class” exports to avoid overlap ambiguity; this is a recurring request. (GitHub)
For binary segmentation, you typically won’t care.
3) Labelme — best lightweight desktop polygon tool (fast for crisp boundaries)
Why it fits
- Local desktop annotation; draw polygons around objects/regions.
- Widely used for “polygon → raster label mask” workflows; community guides show converting its JSON annotations into segmentation masks. (GitHub)
- GPLv3 licensed (still free, but note GPL implications if embedding/redistributing). (GitHub)
Typical workflow
- Draw polygons for the positive region; save JSON.
- Convert JSON → mask PNG (label map). (GitHub)
4) VGG Image Annotator (VIA) — best “no install, runs in browser”
Why it fits
What you’ll need
- VIA exports polygons/regions (not a ready-to-train raster mask), so you’d rasterize polygons into a binary mask with a small script.
5) Fiji / ImageJ — strong option in scientific imaging workflows
If your background is microscopy/biomedical, ROI tooling can be excellent.
- There are Fiji plugins specifically for generating masks from ROIs. (ImageJ Wiki)
6) GIMP — simplest “just paint white over black” option (small datasets)
If you only have a small number of images and want maximum manual control:
- Use a black background layer and paint the positive region white, then export as PNG.
- GIMP’s mask/layer masking concepts are documented. (GIMP)
A practical methodology that works reliably
Step 1: Decide the mask convention once
Pick one canonical representation:
- Single-channel PNG
- Values: 0 = background, 1 = positive
Many tools export 0/255. That’s fine if you convert on load (or preprocess once).
Step 2: Choose the annotation style that matches your boundary type
- Messy / fuzzy boundaries (hair, shadows, soft edges): choose brush painting (Label Studio).
- Crisp geometric boundaries: choose polygons (Labelme / CVAT / VIA).
Step 3: Export masks and normalize them to your canonical format
A minimal one-time conversion for 0/255 → 0/1:
from pathlib import Path
import numpy as np
from PIL import Image
src_dir = Path("exported_masks") # your exported mask folder
dst_dir = Path("masks_01") # normalized masks
dst_dir.mkdir(parents=True, exist_ok=True)
for p in src_dir.glob("*.png"):
m = np.array(Image.open(p).convert("L"))
m01 = (m > 0).astype(np.uint8) # 0 or 1
Image.fromarray(m01, mode="L").save(dst_dir / p.name)
Step 4: Sanity-check before training (saves days)
For a sample of images:
- Verify image and mask sizes match
- Verify masks contain only
{0,1}
- Overlay mask on image and visually confirm alignment
Training-time pitfalls you should explicitly avoid
1) Do not “normalize” masks like images
Masks store labels, not intensities. Normalizing them destroys class IDs. This confusion comes up frequently in practice. (PyTorch Forums)
2) Use nearest-neighbor interpolation for masks during resizing/augmentation
Geometric transforms must be applied consistently to image+mask, but masks must use nearest-neighbor to avoid creating invalid intermediate label values. Albumentations documents this distinction explicitly. (Albumentations)
3) Keep color/intensity transforms away from masks
Color jitter, brightness/contrast, etc. apply to the image only; masks should not receive those transforms. (Albumentations)
4) Be aware of mask “types” in your framework
Torchvision’s newer transform APIs explicitly support segmentation masks as a first-class object type; treat them separately from images. (PyTorch Documentation)
What I would pick for you (based on your description)
If your priority is “free + draw/paint pixels by hand + export a binary mask easily”:
- Label Studio (Community Edition) for brush-based masks and direct PNG/NumPy export. (Label Studio)
- If you need a more “platform” workflow (teams, many formats), CVAT exporting Segmentation Mask. (docs.cvat.ai)
- If your shapes are crisp and you prefer outlining, Labelme (polygons) + convert JSON → mask. (GitHub)