or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

core-image-operations.mdfeature-detection.mdimage-filtering.mdindex.mdmorphological-operations.mdregistration-transforms.mdsegmentation.md

morphological-operations.mddocs/

0

# Morphological Operations

1

2

Comprehensive morphological image processing operations for shape analysis, noise reduction, object separation, and structural enhancement using mathematical morphology principles.

3

4

## Capabilities

5

6

### Basic Morphological Operations

7

8

Fundamental morphological operations using structuring elements for image processing.

9

10

```python { .api }

11

def BinaryErode(image: Image, kernel: Image = None, kernelType: int = sitk.sitkBall,

12

kernelRadius: list = [1, 1, 1], foregroundValue: float = 1.0,

13

backgroundValue: float = 0.0, boundaryToForeground: bool = False) -> Image:

14

"""

15

Binary erosion operation

16

17

Args:

18

image: Binary input image

19

kernel: Custom structuring element (optional)

20

kernelType: Type of structuring element (Ball, Box, Cross)

21

kernelRadius: Radius of structuring element per dimension

22

foregroundValue: Value representing foreground

23

backgroundValue: Value representing background

24

boundaryToForeground: Treat boundary as foreground

25

26

Returns:

27

Eroded binary image

28

"""

29

30

def BinaryDilate(image: Image, kernel: Image = None, kernelType: int = sitk.sitkBall,

31

kernelRadius: list = [1, 1, 1], foregroundValue: float = 1.0,

32

backgroundValue: float = 0.0, boundaryToForeground: bool = False) -> Image:

33

"""

34

Binary dilation operation

35

36

Args:

37

image: Binary input image

38

kernel: Custom structuring element (optional)

39

kernelType: Type of structuring element

40

kernelRadius: Radius of structuring element per dimension

41

foregroundValue: Value representing foreground

42

backgroundValue: Value representing background

43

boundaryToForeground: Treat boundary as foreground

44

45

Returns:

46

Dilated binary image

47

"""

48

49

def BinaryOpening(image: Image, kernelRadius: list = [1, 1, 1],

50

kernelType: int = sitk.sitkBall, foregroundValue: float = 1.0,

51

backgroundValue: float = 0.0) -> Image:

52

"""

53

Binary opening (erosion followed by dilation)

54

55

Args:

56

image: Binary input image

57

kernelRadius: Radius of structuring element

58

kernelType: Type of structuring element

59

foregroundValue: Value representing foreground

60

backgroundValue: Value representing background

61

62

Returns:

63

Opened binary image (removes small objects, smooths boundaries)

64

"""

65

66

def BinaryClosing(image: Image, kernelRadius: list = [1, 1, 1],

67

kernelType: int = sitk.sitkBall, foregroundValue: float = 1.0,

68

backgroundValue: float = 0.0) -> Image:

69

"""

70

Binary closing (dilation followed by erosion)

71

72

Args:

73

image: Binary input image

74

kernelRadius: Radius of structuring element

75

kernelType: Type of structuring element

76

foregroundValue: Value representing foreground

77

backgroundValue: Value representing background

78

79

Returns:

80

Closed binary image (fills holes, connects nearby objects)

81

"""

82

```

83

84

**Usage Examples:**

85

86

```python

87

import SimpleITK as sitk

88

89

# Load binary image

90

binary_image = sitk.ReadImage('binary_shapes.png')

91

92

# Basic morphological operations

93

eroded = sitk.BinaryErode(binary_image, kernelRadius=[2, 2], kernelType=sitk.sitkBall)

94

dilated = sitk.BinaryDilate(binary_image, kernelRadius=[2, 2], kernelType=sitk.sitkBall)

95

96

# Opening and closing

97

opened = sitk.BinaryOpening(binary_image, kernelRadius=[3, 3])

98

closed = sitk.BinaryClosing(binary_image, kernelRadius=[3, 3])

99

100

# Different structuring elements

101

ball_kernel = sitk.BinaryOpening(binary_image, kernelRadius=[2, 2],

102

kernelType=sitk.sitkBall)

103

box_kernel = sitk.BinaryOpening(binary_image, kernelRadius=[2, 2],

104

kernelType=sitk.sitkBox)

105

cross_kernel = sitk.BinaryOpening(binary_image, kernelRadius=[2, 2],

106

kernelType=sitk.sitkCross)

107

108

# Display results

109

sitk.Show(binary_image, "Original")

110

sitk.Show(opened, "Opened (removes noise)")

111

sitk.Show(closed, "Closed (fills gaps)")

112

sitk.Show(eroded, "Eroded")

113

sitk.Show(dilated, "Dilated")

114

```

115

116

### Grayscale Morphological Operations

117

118

Morphological operations extended to grayscale images for intensity-based processing.

119

120

```python { .api }

121

def GrayscaleErode(image: Image, kernel: Image = None, kernelType: int = sitk.sitkBall,

122

kernelRadius: list = [1, 1, 1]) -> Image:

123

"""

124

Grayscale erosion (local minimum)

125

126

Args:

127

image: Grayscale input image

128

kernel: Custom structuring element

129

kernelType: Type of structuring element

130

kernelRadius: Radius of structuring element

131

132

Returns:

133

Eroded grayscale image (darkens features)

134

"""

135

136

def GrayscaleDilate(image: Image, kernel: Image = None, kernelType: int = sitk.sitkBall,

137

kernelRadius: list = [1, 1, 1]) -> Image:

138

"""

139

Grayscale dilation (local maximum)

140

141

Args:

142

image: Grayscale input image

143

kernel: Custom structuring element

144

kernelType: Type of structuring element

145

kernelRadius: Radius of structuring element

146

147

Returns:

148

Dilated grayscale image (brightens features)

149

"""

150

151

def GrayscaleOpening(image: Image, kernelRadius: list = [1, 1, 1],

152

kernelType: int = sitk.sitkBall) -> Image:

153

"""

154

Grayscale opening (erosion then dilation)

155

156

Args:

157

image: Grayscale input image

158

kernelRadius: Radius of structuring element

159

kernelType: Type of structuring element

160

161

Returns:

162

Opened grayscale image (removes bright features smaller than kernel)

163

"""

164

165

def GrayscaleClosing(image: Image, kernelRadius: list = [1, 1, 1],

166

kernelType: int = sitk.sitkBall) -> Image:

167

"""

168

Grayscale closing (dilation then erosion)

169

170

Args:

171

image: Grayscale input image

172

kernelRadius: Radius of structuring element

173

kernelType: Type of structuring element

174

175

Returns:

176

Closed grayscale image (removes dark features smaller than kernel)

177

"""

178

179

def MorphologicalGradient(image: Image, kernelRadius: list = [1, 1, 1],

180

kernelType: int = sitk.sitkBall) -> Image:

181

"""

182

Morphological gradient (dilation - erosion)

183

184

Args:

185

image: Input image

186

kernelRadius: Radius of structuring element

187

kernelType: Type of structuring element

188

189

Returns:

190

Gradient image highlighting edges

191

"""

192

193

def WhiteTopHat(image: Image, kernelRadius: list = [1, 1, 1],

194

kernelType: int = sitk.sitkBall) -> Image:

195

"""

196

White top-hat transform (original - opening)

197

198

Args:

199

image: Input image

200

kernelRadius: Radius of structuring element

201

kernelType: Type of structuring element

202

203

Returns:

204

White top-hat image (bright features smaller than kernel)

205

"""

206

207

def BlackTopHat(image: Image, kernelRadius: list = [1, 1, 1],

208

kernelType: int = sitk.sitkBall) -> Image:

209

"""

210

Black top-hat transform (closing - original)

211

212

Args:

213

image: Input image

214

kernelRadius: Radius of structuring element

215

kernelType: Type of structuring element

216

217

Returns:

218

Black top-hat image (dark features smaller than kernel)

219

"""

220

```

221

222

**Usage Examples:**

223

224

```python

225

import SimpleITK as sitk

226

227

# Load grayscale image

228

image = sitk.ReadImage('texture.png', sitk.sitkFloat32)

229

230

# Basic grayscale morphology

231

eroded_gray = sitk.GrayscaleErode(image, kernelRadius=[3, 3])

232

dilated_gray = sitk.GrayscaleDilate(image, kernelRadius=[3, 3])

233

234

# Opening and closing for noise removal

235

opened_gray = sitk.GrayscaleOpening(image, kernelRadius=[2, 2])

236

closed_gray = sitk.GrayscaleClosing(image, kernelRadius=[2, 2])

237

238

# Morphological gradient for edge detection

239

gradient = sitk.MorphologicalGradient(image, kernelRadius=[1, 1])

240

241

# Top-hat transforms for feature extraction

242

white_tophat = sitk.WhiteTopHat(image, kernelRadius=[10, 10]) # Small bright features

243

black_tophat = sitk.BlackTopHat(image, kernelRadius=[10, 10]) # Small dark features

244

245

# Combine operations for enhanced processing

246

# Remove noise then enhance features

247

denoised = sitk.GrayscaleOpening(image, kernelRadius=[2, 2])

248

enhanced = sitk.Add(denoised, sitk.Multiply(white_tophat, 0.5))

249

250

# Display results

251

sitk.Show(image, "Original")

252

sitk.Show(gradient, "Morphological Gradient")

253

sitk.Show(white_tophat, "White Top-hat")

254

sitk.Show(enhanced, "Enhanced")

255

```

256

257

### Distance Maps and Skeletonization

258

259

Distance transforms and skeleton extraction for shape analysis.

260

261

```python { .api }

262

def SignedMaurerDistanceMap(image: Image, insideIsPositive: bool = False,

263

squaredDistance: bool = False, useImageSpacing: bool = False,

264

backgroundValue: float = 0.0) -> Image:

265

"""

266

Signed Maurer distance map

267

268

Args:

269

image: Binary input image

270

insideIsPositive: Whether inside distance is positive

271

squaredDistance: Return squared distances

272

useImageSpacing: Use actual image spacing

273

backgroundValue: Value representing background

274

275

Returns:

276

Signed distance map (negative inside, positive outside or vice versa)

277

"""

278

279

def DanielssonDistanceMap(image: Image, inputIsBinary: bool = False,

280

squaredDistance: bool = False, useImageSpacing: bool = False) -> Image:

281

"""

282

Danielsson distance map algorithm

283

284

Args:

285

image: Input image (binary or label)

286

inputIsBinary: Whether input is binary image

287

squaredDistance: Return squared distances

288

useImageSpacing: Use actual image spacing

289

290

Returns:

291

Distance map from object boundaries

292

"""

293

294

def BinaryThinning(image: Image) -> Image:

295

"""

296

Binary thinning (skeletonization)

297

298

Args:

299

image: Binary input image

300

301

Returns:

302

Thinned binary image (skeleton)

303

"""

304

305

def BinaryPruning(image: Image, iteration: int = 1) -> Image:

306

"""

307

Remove endpoints from binary skeleton

308

309

Args:

310

image: Binary skeleton image

311

iteration: Number of pruning iterations

312

313

Returns:

314

Pruned skeleton with endpoints removed

315

"""

316

```

317

318

**Usage Examples:**

319

320

```python

321

import SimpleITK as sitk

322

import numpy as np

323

324

# Load binary shape

325

shape = sitk.ReadImage('shape.png')

326

327

# Distance maps

328

distance_map = sitk.SignedMaurerDistanceMap(shape,

329

insideIsPositive=True,

330

useImageSpacing=True)

331

332

# Separate interior and exterior distances

333

interior_distance = sitk.Mask(distance_map, shape)

334

exterior_distance = sitk.Mask(distance_map, sitk.Not(shape))

335

336

# Skeletonization

337

skeleton = sitk.BinaryThinning(shape)

338

339

# Prune skeleton to remove small branches

340

pruned_skeleton = sitk.BinaryPruning(skeleton, iteration=3)

341

342

# Use distance map for morphological operations

343

# Create structuring element from distance

344

distance_array = sitk.GetArrayFromImage(distance_map)

345

max_distance = np.max(distance_array)

346

347

# Progressive morphological opening based on distance

348

opened_distance = sitk.BinaryOpening(shape,

349

kernelRadius=[int(max_distance//4)] * 2)

350

351

# Visualization

352

sitk.Show(shape, "Original Shape")

353

sitk.Show(distance_map, "Distance Map")

354

sitk.Show(skeleton, "Skeleton")

355

sitk.Show(pruned_skeleton, "Pruned Skeleton")

356

```

357

358

### Hit-or-Miss and Advanced Operations

359

360

Advanced morphological operations for pattern matching and complex shape analysis.

361

362

```python { .api }

363

def HitOrMiss(image: Image, kernel: Image, kernelType: int = sitk.sitkBall) -> Image:

364

"""

365

Hit-or-miss transform for pattern matching

366

367

Args:

368

image: Binary input image

369

kernel: Structuring element defining pattern

370

kernelType: Type of kernel if not providing custom kernel

371

372

Returns:

373

Binary image with pattern matches

374

"""

375

376

def BinaryMorphologicalOpening(image: Image, kernelRadius: list = [1, 1, 1],

377

kernelType: int = sitk.sitkBall,

378

foregroundValue: float = 1.0,

379

backgroundValue: float = 0.0) -> Image:

380

"""

381

Binary morphological opening with detailed control

382

383

Args:

384

image: Binary input image

385

kernelRadius: Radius of structuring element

386

kernelType: Type of structuring element

387

foregroundValue: Foreground pixel value

388

backgroundValue: Background pixel value

389

390

Returns:

391

Morphologically opened image

392

"""

393

394

def BinaryMorphologicalClosing(image: Image, kernelRadius: list = [1, 1, 1],

395

kernelType: int = sitk.sitkBall,

396

foregroundValue: float = 1.0,

397

backgroundValue: float = 0.0) -> Image:

398

"""

399

Binary morphological closing with detailed control

400

401

Args:

402

image: Binary input image

403

kernelRadius: Radius of structuring element

404

kernelType: Type of structuring element

405

foregroundValue: Foreground pixel value

406

backgroundValue: Background pixel value

407

408

Returns:

409

Morphologically closed image

410

"""

411

412

def VotingBinary(image: Image, radius: list = [1, 1, 1],

413

birthThreshold: int = 1, survivalThreshold: int = 1,

414

foregroundValue: float = 1.0, backgroundValue: float = 0.0) -> Image:

415

"""

416

Voting-based binary morphological operation

417

418

Args:

419

image: Binary input image

420

radius: Neighborhood radius for voting

421

birthThreshold: Votes needed to create foreground pixel

422

survivalThreshold: Votes needed to keep foreground pixel

423

foregroundValue: Foreground pixel value

424

backgroundValue: Background pixel value

425

426

Returns:

427

Filtered binary image based on voting

428

"""

429

```

430

431

**Usage Examples:**

432

433

```python

434

import SimpleITK as sitk

435

436

# Create custom structuring elements for hit-or-miss

437

def create_line_detector(length, angle_degrees):

438

"""Create structuring element for line detection"""

439

size = [length + 4, length + 4]

440

kernel = sitk.Image(size, sitk.sitkUInt8)

441

442

# Create line pattern (simplified example)

443

center = [size[0]//2, size[1]//2]

444

kernel[center[0], center[1]] = 1

445

# Add more points along line based on angle...

446

447

return kernel

448

449

# Load binary image with lines

450

binary_lines = sitk.ReadImage('lines.png')

451

452

# Detect horizontal lines

453

h_line_kernel = create_line_detector(length=7, angle_degrees=0)

454

horizontal_lines = sitk.HitOrMiss(binary_lines, h_line_kernel)

455

456

# Detect vertical lines

457

v_line_kernel = create_line_detector(length=7, angle_degrees=90)

458

vertical_lines = sitk.HitOrMiss(binary_lines, v_line_kernel)

459

460

# Voting-based noise removal

461

cleaned = sitk.VotingBinary(binary_lines,

462

radius=[2, 2],

463

birthThreshold=3,

464

survivalThreshold=2)

465

466

# Advanced morphological reconstruction

467

def morphological_reconstruction(marker, mask):

468

"""Morphological reconstruction by dilation"""

469

previous = marker

470

current = marker

471

472

while True:

473

# Dilate marker

474

current = sitk.BinaryDilate(previous, kernelRadius=[1, 1])

475

# Intersect with mask

476

current = sitk.And(current, mask)

477

478

# Check for convergence

479

if sitk.GetArrayFromImage(current).sum() == sitk.GetArrayFromImage(previous).sum():

480

break

481

previous = current

482

483

return current

484

485

# Example: Remove objects touching border

486

border_objects = sitk.ReadImage('border_objects.png')

487

h, w = border_objects.GetSize()

488

489

# Create marker from border pixels

490

marker = sitk.Image([w, h], sitk.sitkUInt8)

491

# Set border pixels as markers (simplified)

492

for i in range(w):

493

marker[i, 0] = border_objects[i, 0]

494

marker[i, h-1] = border_objects[i, h-1]

495

for j in range(h):

496

marker[0, j] = border_objects[0, j]

497

marker[w-1, j] = border_objects[w-1, j]

498

499

# Reconstruct border-connected objects

500

border_connected = morphological_reconstruction(marker, border_objects)

501

502

# Remove them from original

503

internal_objects = sitk.Subtract(border_objects, border_connected)

504

```

505

506

### Multi-Scale Morphological Operations

507

508

Morphological operations across multiple scales for comprehensive analysis.

509

510

```python { .api }

511

def MultiScaleMorphologicalOpening(image: Image, kernelType: int = sitk.sitkBall,

512

scales: list = [1, 2, 4]) -> list:

513

"""

514

Multi-scale morphological opening

515

516

Args:

517

image: Input binary/grayscale image

518

kernelType: Type of structuring element

519

scales: List of kernel radii for different scales

520

521

Returns:

522

List of opened images at different scales

523

"""

524

525

def MultiScaleMorphologicalClosing(image: Image, kernelType: int = sitk.sitkBall,

526

scales: list = [1, 2, 4]) -> list:

527

"""

528

Multi-scale morphological closing

529

530

Args:

531

image: Input binary/grayscale image

532

kernelType: Type of structuring element

533

scales: List of kernel radii for different scales

534

535

Returns:

536

List of closed images at different scales

537

"""

538

```

539

540

**Usage Examples:**

541

542

```python

543

import SimpleITK as sitk

544

545

def multiscale_morphological_analysis(image, scales=[1, 2, 4, 8]):

546

"""Comprehensive multi-scale morphological analysis"""

547

548

results = {

549

'openings': [],

550

'closings': [],

551

'white_tophats': [],

552

'black_tophats': [],

553

'gradients': []

554

}

555

556

for scale in scales:

557

radius = [scale, scale] if image.GetDimension() == 2 else [scale, scale, scale]

558

559

# Basic operations

560

opening = sitk.GrayscaleOpening(image, kernelRadius=radius)

561

closing = sitk.GrayscaleClosing(image, kernelRadius=radius)

562

563

# Feature extraction

564

white_tophat = sitk.WhiteTopHat(image, kernelRadius=radius)

565

black_tophat = sitk.BlackTopHat(image, kernelRadius=radius)

566

gradient = sitk.MorphologicalGradient(image, kernelRadius=radius)

567

568

results['openings'].append(opening)

569

results['closings'].append(closing)

570

results['white_tophats'].append(white_tophat)

571

results['black_tophats'].append(black_tophat)

572

results['gradients'].append(gradient)

573

574

return results

575

576

def granulometry_analysis(binary_image, max_scale=20):

577

"""Granulometry analysis for size distribution"""

578

579

granulometry = []

580

original_volume = sitk.GetArrayFromImage(binary_image).sum()

581

582

for scale in range(1, max_scale + 1):

583

opened = sitk.BinaryOpening(binary_image, kernelRadius=[scale, scale])

584

remaining_volume = sitk.GetArrayFromImage(opened).sum()

585

586

if original_volume > 0:

587

proportion = remaining_volume / original_volume

588

else:

589

proportion = 0

590

591

granulometry.append(proportion)

592

593

# Stop if all objects removed

594

if remaining_volume == 0:

595

break

596

597

return granulometry

598

599

# Apply multi-scale analysis

600

image = sitk.ReadImage('texture_image.png')

601

multiscale_results = multiscale_morphological_analysis(image)

602

603

# Combine results for enhanced processing

604

combined_features = multiscale_results['white_tophats'][0]

605

for i in range(1, len(multiscale_results['white_tophats'])):

606

combined_features = sitk.Add(combined_features,

607

multiscale_results['white_tophats'][i])

608

609

# Size analysis

610

binary_objects = sitk.ReadImage('particles.png')

611

size_distribution = granulometry_analysis(binary_objects, max_scale=15)

612

613

print("Size distribution:", size_distribution)

614

```

615

616

### Watershed and Region-Based Operations

617

618

Advanced morphological operations for object separation and region analysis.

619

620

```python { .api }

621

def HMinimaImageFilter(image: Image, height: float = 2.0,

622

fullyConnected: bool = False) -> Image:

623

"""

624

H-minima transform (suppress minima)

625

626

Args:

627

image: Grayscale input image

628

height: Minimum depth of minima to preserve

629

fullyConnected: Use full connectivity

630

631

Returns:

632

Image with shallow minima removed

633

"""

634

635

def HMaximaImageFilter(image: Image, height: float = 2.0,

636

fullyConnected: bool = False) -> Image:

637

"""

638

H-maxima transform (suppress maxima)

639

640

Args:

641

image: Grayscale input image

642

height: Minimum height of maxima to preserve

643

fullyConnected: Use full connectivity

644

645

Returns:

646

Image with low maxima removed

647

"""

648

649

def RegionalMinimaImageFilter(image: Image, fullyConnected: bool = False,

650

flatIsMinima: bool = True) -> Image:

651

"""

652

Find regional minima in image

653

654

Args:

655

image: Grayscale input image

656

fullyConnected: Use full connectivity

657

flatIsMinima: Consider flat regions as minima

658

659

Returns:

660

Binary image marking regional minima

661

"""

662

663

def RegionalMaximaImageFilter(image: Image, fullyConnected: bool = False,

664

flatIsMaxima: bool = True) -> Image:

665

"""

666

Find regional maxima in image

667

668

Args:

669

image: Grayscale input image

670

fullyConnected: Use full connectivity

671

flatIsMaxima: Consider flat regions as maxima

672

673

Returns:

674

Binary image marking regional maxima

675

"""

676

```

677

678

**Usage Examples:**

679

680

```python

681

import SimpleITK as sitk

682

683

def watershed_segmentation_workflow(image):

684

"""Complete watershed segmentation workflow"""

685

686

# Preprocessing

687

smoothed = sitk.Median(image, radius=[2, 2])

688

689

# Create gradient magnitude for watershed

690

gradient = sitk.GradientMagnitude(smoothed)

691

692

# Find markers using H-minima

693

h_minima_filter = sitk.HMinimaImageFilter()

694

h_minima_filter.SetHeight(10)

695

minima_markers = h_minima_filter.Execute(sitk.InvertIntensity(smoothed))

696

697

# Label the markers

698

markers = sitk.ConnectedComponent(minima_markers)

699

700

# Apply marker-controlled watershed

701

watershed_result = sitk.MorphologicalWatershedFromMarkers(

702

gradient, markers, markWatershedLine=True

703

)

704

705

# Post-processing: remove small regions

706

relabeled = sitk.RelabelComponent(watershed_result, minimumObjectSize=50)

707

708

return relabeled

709

710

def morphological_feature_extraction(image):

711

"""Extract morphological features for analysis"""

712

713

# Regional extrema

714

maxima = sitk.RegionalMaximaImageFilter()

715

maxima.SetFlatIsMaxima(False)

716

local_maxima = maxima.Execute(image)

717

718

minima = sitk.RegionalMinimaImageFilter()

719

minima.SetFlatIsMinima(False)

720

local_minima = minima.Execute(image)

721

722

# Multi-scale top-hat features

723

features = []

724

scales = [3, 7, 15, 31]

725

726

for scale in scales:

727

radius = [scale, scale]

728

729

# Bright features

730

white_th = sitk.WhiteTopHat(image, kernelRadius=radius)

731

features.append(('white_tophat_' + str(scale), white_th))

732

733

# Dark features

734

black_th = sitk.BlackTopHat(image, kernelRadius=radius)

735

features.append(('black_tophat_' + str(scale), black_th))

736

737

# Texture features

738

opening = sitk.GrayscaleOpening(image, kernelRadius=radius)

739

closing = sitk.GrayscaleClosing(image, kernelRadius=radius)

740

texture = sitk.Subtract(closing, opening)

741

features.append(('texture_' + str(scale), texture))

742

743

return {

744

'local_maxima': local_maxima,

745

'local_minima': local_minima,

746

'features': features

747

}

748

749

# Apply watershed segmentation

750

input_image = sitk.ReadImage('cells.tif')

751

segmented = watershed_segmentation_workflow(input_image)

752

753

# Extract morphological features

754

feature_results = morphological_feature_extraction(input_image)

755

756

# Display key results

757

sitk.Show(input_image, "Original")

758

sitk.Show(segmented, "Watershed Segmentation")

759

sitk.Show(feature_results['local_maxima'], "Local Maxima")

760

761

# Save feature images

762

for name, feature_image in feature_results['features']:

763

sitk.WriteImage(feature_image, f'feature_{name}.nii')

764

```

765

766

## Advanced Morphological Patterns

767

768

### Morphological Reconstruction

769

770

```python

771

import SimpleITK as sitk

772

773

def morphological_reconstruction_by_dilation(marker, mask, connectivity=4):

774

"""Morphological reconstruction by dilation"""

775

776

# Ensure marker is subset of mask

777

marker = sitk.And(marker, mask)

778

779

previous = marker

780

iteration = 0

781

max_iterations = 1000

782

783

while iteration < max_iterations:

784

# Geodesic dilation: dilate then intersect with mask

785

dilated = sitk.BinaryDilate(previous, kernelRadius=[1, 1])

786

current = sitk.And(dilated, mask)

787

788

# Check convergence

789

diff = sitk.Xor(current, previous)

790

if sitk.GetArrayFromImage(diff).sum() == 0:

791

break

792

793

previous = current

794

iteration += 1

795

796

return current

797

798

def morphological_reconstruction_by_erosion(marker, mask, connectivity=4):

799

"""Morphological reconstruction by erosion"""

800

801

# Ensure marker is superset of mask

802

marker = sitk.Or(marker, mask)

803

804

previous = marker

805

iteration = 0

806

max_iterations = 1000

807

808

while iteration < max_iterations:

809

# Geodesic erosion: erode then union with mask

810

eroded = sitk.BinaryErode(previous, kernelRadius=[1, 1])

811

current = sitk.Or(eroded, mask)

812

813

# Check convergence

814

diff = sitk.Xor(current, previous)

815

if sitk.GetArrayFromImage(diff).sum() == 0:

816

break

817

818

previous = current

819

iteration += 1

820

821

return current

822

823

def remove_border_objects(binary_image):

824

"""Remove objects touching image border"""

825

826

size = binary_image.GetSize()

827

828

# Create border marker

829

border_marker = sitk.Image(size, sitk.sitkUInt8)

830

831

# Set border pixels from original image

832

array = sitk.GetArrayFromImage(binary_image)

833

border_array = sitk.GetArrayFromImage(border_marker)

834

835

# Copy border pixels

836

border_array[0, :] = array[0, :] # Top

837

border_array[-1, :] = array[-1, :] # Bottom

838

border_array[:, 0] = array[:, 0] # Left

839

border_array[:, -1] = array[:, -1] # Right

840

841

border_marker = sitk.GetImageFromArray(border_array)

842

border_marker.CopyInformation(binary_image)

843

844

# Reconstruct border-connected components

845

border_objects = morphological_reconstruction_by_dilation(border_marker, binary_image)

846

847

# Remove from original

848

internal_objects = sitk.Subtract(binary_image, border_objects)

849

850

return internal_objects, border_objects

851

852

# Example usage

853

binary_img = sitk.ReadImage('binary_objects.png')

854

internal, border = remove_border_objects(binary_img)

855

```

856

857

### Morphological Filtering Chains

858

859

```python

860

import SimpleITK as sitk

861

862

class MorphologicalFilterChain:

863

"""Chain morphological operations with parameter optimization"""

864

865

def __init__(self, image):

866

self.image = image

867

self.operations = []

868

869

def add_operation(self, operation, **params):

870

"""Add morphological operation to chain"""

871

self.operations.append((operation, params))

872

return self

873

874

def execute(self):

875

"""Execute the complete filter chain"""

876

result = self.image

877

878

for operation, params in self.operations:

879

if operation == 'opening':

880

result = sitk.BinaryOpening(result, **params)

881

elif operation == 'closing':

882

result = sitk.BinaryClosing(result, **params)

883

elif operation == 'erosion':

884

result = sitk.BinaryErode(result, **params)

885

elif operation == 'dilation':

886

result = sitk.BinaryDilate(result, **params)

887

elif operation == 'thinning':

888

result = sitk.BinaryThinning(result)

889

elif operation == 'pruning':

890

result = sitk.BinaryPruning(result, **params)

891

# Add more operations as needed

892

893

return result

894

895

def optimize_parameters(self, ground_truth=None, metric='dice'):

896

"""Optimize filter parameters (simplified example)"""

897

if ground_truth is None:

898

return self

899

900

best_params = None

901

best_score = 0

902

903

# Simple grid search over kernel sizes

904

for kernel_size in [1, 2, 3, 5]:

905

# Update parameters

906

test_chain = MorphologicalFilterChain(self.image)

907

for op, params in self.operations:

908

new_params = params.copy()

909

if 'kernelRadius' in params:

910

new_params['kernelRadius'] = [kernel_size] * len(params['kernelRadius'])

911

test_chain.add_operation(op, **new_params)

912

913

# Execute and evaluate

914

result = test_chain.execute()

915

score = self._compute_dice(result, ground_truth)

916

917

if score > best_score:

918

best_score = score

919

best_params = kernel_size

920

921

# Update with best parameters

922

if best_params is not None:

923

for i, (op, params) in enumerate(self.operations):

924

if 'kernelRadius' in params:

925

self.operations[i] = (op, {

926

**params,

927

'kernelRadius': [best_params] * len(params['kernelRadius'])

928

})

929

930

return self

931

932

def _compute_dice(self, pred, truth):

933

"""Compute Dice coefficient"""

934

pred_array = sitk.GetArrayFromImage(pred)

935

truth_array = sitk.GetArrayFromImage(truth)

936

937

intersection = (pred_array * truth_array).sum()

938

union = pred_array.sum() + truth_array.sum()

939

940

if union == 0:

941

return 1.0

942

943

return 2.0 * intersection / union

944

945

# Example usage

946

def denoise_and_segment_workflow(noisy_binary):

947

"""Complete morphological processing workflow"""

948

949

# Create processing chain

950

chain = (MorphologicalFilterChain(noisy_binary)

951

.add_operation('opening', kernelRadius=[2, 2], kernelType=sitk.sitkBall)

952

.add_operation('closing', kernelRadius=[3, 3], kernelType=sitk.sitkBall)

953

.add_operation('opening', kernelRadius=[1, 1], kernelType=sitk.sitkCross))

954

955

# Execute chain

956

cleaned = chain.execute()

957

958

# Additional processing

959

skeleton = sitk.BinaryThinning(cleaned)

960

pruned = sitk.BinaryPruning(skeleton, iteration=2)

961

962

return {

963

'cleaned': cleaned,

964

'skeleton': skeleton,

965

'pruned_skeleton': pruned

966

}

967

968

# Apply workflow

969

noisy_image = sitk.ReadImage('noisy_binary.png')

970

results = denoise_and_segment_workflow(noisy_image)

971

```

972

973

## Type Definitions

974

975

```python { .api }

976

# Core Types

977

Image = sitk.Image

978

"""SimpleITK Image object"""

979

980

KernelType = int

981

"""Structuring element type (sitkBall, sitkBox, sitkCross, etc.)"""

982

983

# Kernel Types Constants

984

BALL_KERNEL = sitk.sitkBall

985

"""Spherical/circular structuring element"""

986

987

BOX_KERNEL = sitk.sitkBox

988

"""Rectangular/cubic structuring element"""

989

990

CROSS_KERNEL = sitk.sitkCross

991

"""Cross-shaped structuring element"""

992

993

# Parameter Types

994

KernelRadius = list[int]

995

"""Radius of structuring element per dimension"""

996

997

ForegroundValue = float

998

"""Value representing foreground pixels"""

999

1000

BackgroundValue = float

1001

"""Value representing background pixels"""

1002

1003

IterationCount = int

1004

"""Number of iterations for iterative operations"""

1005

1006

# Distance Transform Types

1007

DistanceMap = Image

1008

"""Distance transform result image"""

1009

1010

SignedDistanceMap = Image

1011

"""Signed distance transform (negative inside, positive outside)"""

1012

1013

# Morphological Operation Results

1014

MorphologicalResult = Image

1015

"""Result of morphological operation"""

1016

1017

SkeletonImage = Image

1018

"""Binary skeleton/medial axis image"""

1019

1020

# Advanced Operation Types

1021

ReconstructionMarker = Image

1022

"""Marker image for morphological reconstruction"""

1023

1024

ReconstructionMask = Image

1025

"""Mask image for morphological reconstruction"""

1026

1027

# Multi-scale Types

1028

ScaleList = list[int]

1029

"""List of scales/kernel sizes for multi-scale processing"""

1030

1031

FeatureImageSet = list[tuple[str, Image]]

1032

"""Named collection of morphological feature images"""

1033

1034

GranulometryProfile = list[float]

1035

"""Size distribution profile from granulometry analysis"""

1036

1037

# Voting Parameters

1038

VotingRadius = list[int]

1039

"""Neighborhood radius for voting operations"""

1040

1041

ThresholdValue = int

1042

"""Vote threshold for binary decisions"""

1043

```