or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

coefficient-utils.mdcontinuous-dwt.mdindex.mdmulti-level-dwt.mdmultiresolution-analysis.mdsingle-level-dwt.mdstationary-dwt.mdthresholding.mdwavelet-packets.mdwavelets.md

thresholding.mddocs/

0

# Thresholding and Denoising

1

2

Signal thresholding functions for noise reduction, feature extraction, and signal enhancement with various thresholding methods for wavelet coefficient processing.

3

4

## Capabilities

5

6

### Basic Thresholding

7

8

Fundamental thresholding operations for coefficient processing.

9

10

```python { .api }

11

def threshold(data, value, mode: str = 'soft', substitute: float = 0):

12

"""

13

Apply thresholding to data.

14

15

Parameters:

16

- data: Input array to threshold

17

- value: Threshold value (scalar or array-like for per-element thresholds)

18

- mode: Thresholding mode ('soft', 'hard', 'garrote', 'greater', 'less')

19

- substitute: Substitute value for thresholded elements (default: 0)

20

21

Returns:

22

Thresholded array of same shape as input

23

"""

24

```

25

26

#### Usage Examples

27

28

```python

29

import pywt

30

import numpy as np

31

import matplotlib.pyplot as plt

32

33

# Create test signal with noise

34

np.random.seed(42)

35

t = np.linspace(0, 1, 1000)

36

clean_signal = np.sin(2 * np.pi * 5 * t) + 0.5 * np.sin(2 * np.pi * 20 * t)

37

noise = 0.5 * np.random.randn(len(t))

38

noisy_signal = clean_signal + noise

39

40

print(f"Signal length: {len(noisy_signal)}")

41

print(f"Noise std: {np.std(noise):.3f}")

42

43

# Wavelet decomposition

44

coeffs = pywt.wavedec(noisy_signal, 'db8', level=6)

45

print(f"Decomposition levels: {len(coeffs) - 1}")

46

47

# Different thresholding modes

48

threshold_value = 0.1

49

modes = ['soft', 'hard', 'garrote', 'greater', 'less']

50

51

# Apply different thresholding modes to detail coefficients

52

thresholded_results = {}

53

54

for mode in modes:

55

coeffs_thresh = coeffs.copy()

56

57

# Apply thresholding to all detail coefficients (skip approximation)

58

for i in range(1, len(coeffs_thresh)):

59

coeffs_thresh[i] = pywt.threshold(coeffs_thresh[i], threshold_value, mode=mode)

60

61

# Reconstruct signal

62

reconstructed = pywt.waverec(coeffs_thresh, 'db8')

63

thresholded_results[mode] = reconstructed

64

65

# Visualization

66

fig, axes = plt.subplots(3, 2, figsize=(15, 12))

67

axes = axes.flatten()

68

69

# Original signals

70

axes[0].plot(t, clean_signal, 'b-', label='Clean signal', linewidth=2)

71

axes[0].plot(t, noisy_signal, 'r-', alpha=0.7, label='Noisy signal')

72

axes[0].set_title('Original Signals')

73

axes[0].legend()

74

axes[0].grid(True)

75

76

# Thresholded results

77

for i, (mode, result) in enumerate(thresholded_results.items()):

78

axes[i+1].plot(t, clean_signal, 'b-', alpha=0.7, label='Clean')

79

axes[i+1].plot(t, result, 'g-', linewidth=2, label=f'{mode.capitalize()} threshold')

80

axes[i+1].set_title(f'{mode.capitalize()} Thresholding')

81

axes[i+1].legend()

82

axes[i+1].grid(True)

83

84

plt.tight_layout()

85

plt.show()

86

87

# Compare thresholding effectiveness

88

def calculate_mse(signal1, signal2):

89

"""Calculate Mean Squared Error."""

90

return np.mean((signal1 - signal2)**2)

91

92

def calculate_snr(clean, noisy):

93

"""Calculate Signal-to-Noise Ratio in dB."""

94

signal_power = np.mean(clean**2)

95

noise_power = np.mean((noisy - clean)**2)

96

return 10 * np.log10(signal_power / noise_power)

97

98

print("\nThresholding Performance:")

99

print(f"Original SNR: {calculate_snr(clean_signal, noisy_signal):.2f} dB")

100

print()

101

102

for mode, result in thresholded_results.items():

103

mse = calculate_mse(clean_signal, result)

104

snr = calculate_snr(clean_signal, result)

105

print(f"{mode.capitalize():>8}: MSE = {mse:.6f}, SNR = {snr:.2f} dB")

106

107

# Demonstrate coefficient-specific thresholding

108

coeffs_detailed = pywt.wavedec(noisy_signal, 'db8', level=6)

109

110

# Analyze coefficient statistics

111

print("\nCoefficient Statistics:")

112

for i, coeff in enumerate(coeffs_detailed):

113

coeff_type = "Approximation" if i == 0 else f"Detail {len(coeffs_detailed)-i}"

114

print(f"{coeff_type:>15}: std = {np.std(coeff):.4f}, max = {np.max(np.abs(coeff)):.4f}")

115

116

# Adaptive thresholding based on coefficient statistics

117

coeffs_adaptive = coeffs_detailed.copy()

118

for i in range(1, len(coeffs_adaptive)): # Skip approximation

119

# Use different threshold for each level

120

sigma = np.std(coeffs_adaptive[i])

121

adaptive_threshold = 2 * sigma # 2-sigma rule

122

coeffs_adaptive[i] = pywt.threshold(coeffs_adaptive[i], adaptive_threshold, mode='soft')

123

124

adaptive_result = pywt.waverec(coeffs_adaptive, 'db8')

125

adaptive_snr = calculate_snr(clean_signal, adaptive_result)

126

print(f"\nAdaptive thresholding SNR: {adaptive_snr:.2f} dB")

127

```

128

129

### Advanced Thresholding

130

131

More sophisticated thresholding methods.

132

133

```python { .api }

134

def threshold_firm(data, value_low, value_high):

135

"""

136

Firm thresholding (intermediate between soft and hard).

137

138

Parameters:

139

- data: Input array to threshold

140

- value_low: Lower threshold value

141

- value_high: Upper threshold value (must be >= value_low)

142

143

Returns:

144

Firm thresholded array

145

"""

146

```

147

148

#### Usage Examples

149

150

```python

151

import pywt

152

import numpy as np

153

import matplotlib.pyplot as plt

154

155

# Create test signal with mixed noise characteristics

156

t = np.linspace(0, 2, 2000)

157

signal = (np.sin(2 * np.pi * 3 * t) + # Low frequency component

158

0.7 * np.sin(2 * np.pi * 15 * t) + # Medium frequency component

159

0.4 * np.sin(2 * np.pi * 50 * t)) # High frequency component

160

161

# Add different types of noise

162

gaussian_noise = 0.3 * np.random.randn(len(t))

163

impulse_noise = np.zeros_like(t)

164

impulse_indices = np.random.choice(len(t), size=50, replace=False)

165

impulse_noise[impulse_indices] = 2 * np.random.randn(50)

166

167

mixed_noisy = signal + gaussian_noise + impulse_noise

168

169

# Wavelet decomposition

170

coeffs = pywt.wavedec(mixed_noisy, 'db6', level=7)

171

172

# Compare soft, hard, and firm thresholding

173

threshold_low = 0.05

174

threshold_high = 0.15

175

176

# Soft thresholding

177

coeffs_soft = coeffs.copy()

178

for i in range(1, len(coeffs_soft)):

179

coeffs_soft[i] = pywt.threshold(coeffs_soft[i], threshold_high, mode='soft')

180

reconstructed_soft = pywt.waverec(coeffs_soft, 'db6')

181

182

# Hard thresholding

183

coeffs_hard = coeffs.copy()

184

for i in range(1, len(coeffs_hard)):

185

coeffs_hard[i] = pywt.threshold(coeffs_hard[i], threshold_high, mode='hard')

186

reconstructed_hard = pywt.waverec(coeffs_hard, 'db6')

187

188

# Firm thresholding

189

coeffs_firm = coeffs.copy()

190

for i in range(1, len(coeffs_firm)):

191

coeffs_firm[i] = pywt.threshold_firm(coeffs_firm[i], threshold_low, threshold_high)

192

reconstructed_firm = pywt.waverec(coeffs_firm, 'db6')

193

194

# Visualization

195

fig, axes = plt.subplots(2, 2, figsize=(15, 10))

196

197

axes[0, 0].plot(t, signal, 'b-', label='Clean signal', linewidth=2)

198

axes[0, 0].plot(t, mixed_noisy, 'r-', alpha=0.7, label='Noisy signal')

199

axes[0, 0].set_title('Original vs Noisy Signal')

200

axes[0, 0].legend()

201

axes[0, 0].grid(True)

202

203

axes[0, 1].plot(t, signal, 'b-', alpha=0.7, label='Clean')

204

axes[0, 1].plot(t, reconstructed_soft, 'g-', linewidth=2, label='Soft threshold')

205

axes[0, 1].set_title('Soft Thresholding')

206

axes[0, 1].legend()

207

axes[0, 1].grid(True)

208

209

axes[1, 0].plot(t, signal, 'b-', alpha=0.7, label='Clean')

210

axes[1, 0].plot(t, reconstructed_hard, 'r-', linewidth=2, label='Hard threshold')

211

axes[1, 0].set_title('Hard Thresholding')

212

axes[1, 0].legend()

213

axes[1, 0].grid(True)

214

215

axes[1, 1].plot(t, signal, 'b-', alpha=0.7, label='Clean')

216

axes[1, 1].plot(t, reconstructed_firm, 'm-', linewidth=2, label='Firm threshold')

217

axes[1, 1].set_title('Firm Thresholding')

218

axes[1, 1].legend()

219

axes[1, 1].grid(True)

220

221

plt.tight_layout()

222

plt.show()

223

224

# Performance comparison

225

def calculate_metrics(clean, denoised):

226

"""Calculate denoising metrics."""

227

mse = np.mean((clean - denoised)**2)

228

snr = 10 * np.log10(np.mean(clean**2) / np.mean((clean - denoised)**2))

229

230

# Peak Signal-to-Noise Ratio

231

max_val = np.max(clean)

232

psnr = 20 * np.log10(max_val / np.sqrt(mse))

233

234

return mse, snr, psnr

235

236

print("Thresholding Method Comparison:")

237

print(f"{'Method':<15} {'MSE':<10} {'SNR (dB)':<10} {'PSNR (dB)':<10}")

238

print("-" * 50)

239

240

original_mse, original_snr, original_psnr = calculate_metrics(signal, mixed_noisy)

241

print(f"{'Original':<15} {original_mse:<10.6f} {original_snr:<10.2f} {original_psnr:<10.2f}")

242

243

soft_mse, soft_snr, soft_psnr = calculate_metrics(signal, reconstructed_soft)

244

print(f"{'Soft':<15} {soft_mse:<10.6f} {soft_snr:<10.2f} {soft_psnr:<10.2f}")

245

246

hard_mse, hard_snr, hard_psnr = calculate_metrics(signal, reconstructed_hard)

247

print(f"{'Hard':<15} {hard_mse:<10.6f} {hard_snr:<10.2f} {hard_psnr:<10.2f}")

248

249

firm_mse, firm_snr, firm_psnr = calculate_metrics(signal, reconstructed_firm)

250

print(f"{'Firm':<15} {firm_mse:<10.6f} {firm_snr:<10.2f} {firm_psnr:<10.2f}")

251

252

# Demonstrate threshold selection strategies

253

def sure_threshold_estimate(coeffs, sigma=None):

254

"""Estimate threshold using Stein's Unbiased Risk Estimate (SURE)."""

255

if sigma is None:

256

# Estimate noise standard deviation from finest detail coefficients

257

sigma = np.median(np.abs(coeffs[-1])) / 0.6745

258

259

n = len(coeffs[-1])

260

threshold = sigma * np.sqrt(2 * np.log(n))

261

return threshold

262

263

def bayes_threshold_estimate(coeffs):

264

"""Simple Bayesian threshold estimate."""

265

# Estimate based on coefficient statistics

266

all_details = np.concatenate([coeff for coeff in coeffs[1:]])

267

return np.std(all_details)

268

269

# Apply different threshold selection methods

270

sure_thresh = sure_threshold_estimate(coeffs)

271

bayes_thresh = bayes_threshold_estimate(coeffs)

272

273

print(f"\nThreshold Selection:")

274

print(f"SURE threshold: {sure_thresh:.4f}")

275

print(f"Bayesian threshold: {bayes_thresh:.4f}")

276

print(f"Manual threshold: {threshold_high:.4f}")

277

278

# Test SURE threshold

279

coeffs_sure = coeffs.copy()

280

for i in range(1, len(coeffs_sure)):

281

coeffs_sure[i] = pywt.threshold(coeffs_sure[i], sure_thresh, mode='soft')

282

reconstructed_sure = pywt.waverec(coeffs_sure, 'db6')

283

284

sure_mse, sure_snr, sure_psnr = calculate_metrics(signal, reconstructed_sure)

285

print(f"{'SURE':<15} {sure_mse:<10.6f} {sure_snr:<10.2f} {sure_psnr:<10.2f}")

286

```

287

288

### 2D Image Thresholding

289

290

Thresholding applications for image denoising and processing.

291

292

#### Usage Examples

293

294

```python

295

import pywt

296

import numpy as np

297

import matplotlib.pyplot as plt

298

299

# Create test image

300

size = 256

301

x, y = np.mgrid[0:size, 0:size]

302

image = np.zeros((size, size))

303

304

# Add geometric shapes

305

image[64:192, 64:192] = 1.0 # Square

306

center_x, center_y = 128, 128

307

radius = 40

308

circle_mask = (x - center_x)**2 + (y - center_y)**2 <= radius**2

309

image[circle_mask] = 0.5

310

311

# Add texture and noise

312

texture = 0.1 * np.sin(2 * np.pi * x / 16) * np.cos(2 * np.pi * y / 16)

313

noise = 0.3 * np.random.randn(size, size)

314

noisy_image = image + texture + noise

315

316

print(f"Image shape: {noisy_image.shape}")

317

print(f"Pixel value range: [{np.min(noisy_image):.3f}, {np.max(noisy_image):.3f}]")

318

319

# 2D wavelet decomposition

320

coeffs_2d = pywt.wavedec2(noisy_image, 'db4', level=4)

321

print(f"2D decomposition levels: {len(coeffs_2d) - 1}")

322

323

# Apply thresholding to 2D coefficients

324

def threshold_2d_coeffs(coeffs, threshold_value, mode='soft'):

325

"""Apply thresholding to 2D wavelet coefficients."""

326

coeffs_thresh = [coeffs[0]] # Keep approximation unchanged

327

328

for level_coeffs in coeffs[1:]:

329

cH, cV, cD = level_coeffs

330

cH_thresh = pywt.threshold(cH, threshold_value, mode=mode)

331

cV_thresh = pywt.threshold(cV, threshold_value, mode=mode)

332

cD_thresh = pywt.threshold(cD, threshold_value, mode=mode)

333

coeffs_thresh.append((cH_thresh, cV_thresh, cD_thresh))

334

335

return coeffs_thresh

336

337

# Different threshold values

338

thresholds = [0.05, 0.1, 0.2, 0.3]

339

denoised_images = {}

340

341

for thresh in thresholds:

342

coeffs_thresh = threshold_2d_coeffs(coeffs_2d, thresh, mode='soft')

343

denoised = pywt.waverec2(coeffs_thresh, 'db4')

344

denoised_images[thresh] = denoised

345

346

# Visualization

347

fig, axes = plt.subplots(2, 3, figsize=(18, 12))

348

349

axes[0, 0].imshow(image, cmap='gray')

350

axes[0, 0].set_title('Original Clean Image')

351

axes[0, 0].axis('off')

352

353

axes[0, 1].imshow(noisy_image, cmap='gray')

354

axes[0, 1].set_title('Noisy Image')

355

axes[0, 1].axis('off')

356

357

# Show wavelet decomposition

358

cA = coeffs_2d[0]

359

axes[0, 2].imshow(cA, cmap='gray')

360

axes[0, 2].set_title('Approximation (Level 4)')

361

axes[0, 2].axis('off')

362

363

# Show denoised results

364

for i, (thresh, denoised) in enumerate(denoised_images.items()):

365

if i < 3:

366

axes[1, i].imshow(denoised, cmap='gray')

367

axes[1, i].set_title(f'Denoised (threshold = {thresh})')

368

axes[1, i].axis('off')

369

370

plt.tight_layout()

371

plt.show()

372

373

# Quantitative evaluation

374

def image_quality_metrics(original, denoised):

375

"""Calculate image quality metrics."""

376

mse = np.mean((original - denoised)**2)

377

378

# Peak Signal-to-Noise Ratio

379

max_val = np.max(original)

380

psnr = 20 * np.log10(max_val / np.sqrt(mse)) if mse > 0 else float('inf')

381

382

# Structural Similarity Index (simplified)

383

mu1, mu2 = np.mean(original), np.mean(denoised)

384

sigma1, sigma2 = np.std(original), np.std(denoised)

385

sigma12 = np.mean((original - mu1) * (denoised - mu2))

386

387

c1, c2 = 0.01**2, 0.03**2 # Constants for stability

388

ssim = ((2 * mu1 * mu2 + c1) * (2 * sigma12 + c2)) / ((mu1**2 + mu2**2 + c1) * (sigma1**2 + sigma2**2 + c2))

389

390

return mse, psnr, ssim

391

392

print("\n2D Image Denoising Results:")

393

print(f"{'Threshold':<10} {'MSE':<10} {'PSNR (dB)':<12} {'SSIM':<10}")

394

print("-" * 50)

395

396

for thresh, denoised in denoised_images.items():

397

mse, psnr, ssim = image_quality_metrics(image, denoised)

398

print(f"{thresh:<10} {mse:<10.6f} {psnr:<12.2f} {ssim:<10.4f}")

399

400

# Adaptive 2D thresholding

401

def adaptive_2d_threshold(coeffs):

402

"""Apply adaptive thresholding based on coefficient statistics at each level."""

403

coeffs_adaptive = [coeffs[0]] # Keep approximation

404

405

for level, (cH, cV, cD) in enumerate(coeffs[1:]):

406

# Calculate adaptive thresholds for each orientation

407

thresh_H = 3 * np.std(cH)

408

thresh_V = 3 * np.std(cV)

409

thresh_D = 3 * np.std(cD)

410

411

cH_thresh = pywt.threshold(cH, thresh_H, mode='soft')

412

cV_thresh = pywt.threshold(cV, thresh_V, mode='soft')

413

cD_thresh = pywt.threshold(cD, thresh_D, mode='soft')

414

415

coeffs_adaptive.append((cH_thresh, cV_thresh, cD_thresh))

416

417

print(f"Level {len(coeffs)-1-level}: thresholds H={thresh_H:.4f}, V={thresh_V:.4f}, D={thresh_D:.4f}")

418

419

return coeffs_adaptive

420

421

coeffs_adaptive = adaptive_2d_threshold(coeffs_2d)

422

adaptive_denoised = pywt.waverec2(coeffs_adaptive, 'db4')

423

424

adaptive_mse, adaptive_psnr, adaptive_ssim = image_quality_metrics(image, adaptive_denoised)

425

print(f"{'Adaptive':<10} {adaptive_mse:<10.6f} {adaptive_psnr:<12.2f} {adaptive_ssim:<10.4f}")

426

427

# Show adaptive result

428

plt.figure(figsize=(15, 5))

429

plt.subplot(1, 3, 1)

430

plt.imshow(image, cmap='gray')

431

plt.title('Original')

432

plt.axis('off')

433

434

plt.subplot(1, 3, 2)

435

plt.imshow(noisy_image, cmap='gray')

436

plt.title('Noisy')

437

plt.axis('off')

438

439

plt.subplot(1, 3, 3)

440

plt.imshow(adaptive_denoised, cmap='gray')

441

plt.title('Adaptive Denoised')

442

plt.axis('off')

443

444

plt.tight_layout()

445

plt.show()

446

```

447

448

## Types

449

450

```python { .api }

451

# Thresholding modes

452

ThresholdMode = Literal['soft', 'hard', 'garrote', 'greater', 'less']

453

454

# Threshold value types

455

ThresholdValue = Union[float, np.ndarray] # Scalar or array for per-element thresholds

456

457

# Common threshold selection methods

458

ThresholdMethod = Literal['sure', 'bayes', 'minimax', 'manual']

459

```