or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

aruco.mdcamera-calibration.mdcomputational-photography.mdcontours-shapes.mdcore-operations.mddnn.mdfeature-detection.mdgui-drawing.mdimage-processing.mdimage-video-io.mdindex.mdmachine-learning.mdobject-detection.mdtask-log.mdvideo-analysis.md

feature-detection.mddocs/

0

# Feature Detection and Description

1

2

OpenCV's 2D Features Framework provides comprehensive tools for detecting, describing, and matching distinctive features in images. These features are essential for tasks like object recognition, image stitching, 3D reconstruction, and visual tracking.

3

4

## Capabilities

5

6

### Feature Detectors

7

8

#### SIFT (Scale-Invariant Feature Transform)

9

10

Patent-free since 2020, SIFT is one of the most robust feature detection algorithms, invariant to scale, rotation, and illumination changes.

11

12

```python { .api }

13

cv2.SIFT_create(

14

nfeatures=0, # Number of best features to retain (0 = all)

15

nOctaveLayers=3, # Number of layers in each octave

16

contrastThreshold=0.04, # Contrast threshold for filtering weak features

17

edgeThreshold=10, # Edge threshold for filtering edge responses

18

sigma=1.6 # Gaussian sigma for the first octave

19

) -> cv2.SIFT

20

```

21

22

**Returns**: SIFT detector/descriptor object

23

24

**Common usage**:

25

```python

26

sift = cv2.SIFT_create()

27

keypoints, descriptors = sift.detectAndCompute(image, None)

28

```

29

30

#### ORB (Oriented FAST and Rotated BRIEF)

31

32

A fast, efficient alternative to SIFT and SURF, ORB is rotation invariant and resistant to noise.

33

34

```python { .api }

35

cv2.ORB_create(

36

nfeatures=500, # Maximum number of features to retain

37

scaleFactor=1.2, # Pyramid decimation ratio

38

nlevels=8, # Number of pyramid levels

39

edgeThreshold=31, # Size of border where features are not detected

40

firstLevel=0, # Level of pyramid to put source image

41

WTA_K=2, # Number of points for BRIEF descriptor (2, 3, 4)

42

scoreType=cv2.ORB_HARRIS_SCORE, # HARRIS_SCORE or FAST_SCORE

43

patchSize=31, # Size of patch used by oriented BRIEF

44

fastThreshold=20 # FAST threshold

45

) -> cv2.ORB

46

```

47

48

**Returns**: ORB detector/descriptor object

49

50

**Score types**:

51

- `cv2.ORB_HARRIS_SCORE` - Harris corner measure for ranking features

52

- `cv2.ORB_FAST_SCORE` - FAST score for ranking features

53

54

#### AKAZE (Accelerated-KAZE)

55

56

A fast variant of KAZE that uses binary descriptors, providing good performance with lower computational cost.

57

58

```python { .api }

59

cv2.AKAZE_create(

60

descriptor_type=cv2.AKAZE_DESCRIPTOR_MLDB, # Descriptor type

61

descriptor_size=0, # Size of descriptor (0 = full size)

62

descriptor_channels=3, # Number of channels in descriptor

63

threshold=0.001, # Detector response threshold

64

nOctaves=4, # Number of octaves

65

nOctaveLayers=4, # Number of sublevels per octave

66

diffusivity=cv2.KAZE_DIFF_PM_G2 # Diffusivity type

67

) -> cv2.AKAZE

68

```

69

70

**Returns**: AKAZE detector/descriptor object

71

72

**Descriptor types**:

73

- `cv2.AKAZE_DESCRIPTOR_KAZE` - Upright KAZE descriptor

74

- `cv2.AKAZE_DESCRIPTOR_KAZE_UPRIGHT` - Upright KAZE descriptor

75

- `cv2.AKAZE_DESCRIPTOR_MLDB` - Modified Local Difference Binary descriptor

76

- `cv2.AKAZE_DESCRIPTOR_MLDB_UPRIGHT` - Upright MLDB descriptor

77

78

**Diffusivity types**:

79

- `cv2.KAZE_DIFF_PM_G1` - Perona-Malik diffusivity

80

- `cv2.KAZE_DIFF_PM_G2` - Perona-Malik diffusivity (default)

81

- `cv2.KAZE_DIFF_WEICKERT` - Weickert diffusivity

82

- `cv2.KAZE_DIFF_CHARBONNIER` - Charbonnier diffusivity

83

84

#### BRISK (Binary Robust Invariant Scalable Keypoints)

85

86

A binary descriptor algorithm that is fast and provides good performance for real-time applications.

87

88

```python { .api }

89

cv2.BRISK_create(

90

thresh=30, # AGAST detection threshold score

91

octaves=3, # Detection octaves (0 = single scale)

92

patternScale=1.0 # Scale of sampling pattern

93

) -> cv2.BRISK

94

```

95

96

**Returns**: BRISK detector/descriptor object

97

98

#### KAZE

99

100

A multiscale feature detection and description algorithm using nonlinear diffusion filtering.

101

102

```python { .api }

103

cv2.KAZE_create(

104

extended=False, # Use extended 128-element descriptor

105

upright=False, # Use upright (non-rotation invariant) descriptor

106

threshold=0.001, # Detector response threshold

107

nOctaves=4, # Number of octaves

108

nOctaveLayers=4, # Number of sublevels per octave

109

diffusivity=cv2.KAZE_DIFF_PM_G2 # Diffusivity type

110

) -> cv2.KAZE

111

```

112

113

**Returns**: KAZE detector/descriptor object

114

115

#### SimpleBlobDetector

116

117

Detects blobs (regions of connected pixels) in images based on various properties like size, color, shape, and convexity.

118

119

```python { .api }

120

cv2.SimpleBlobDetector_create(

121

parameters=None # SimpleBlobDetector_Params object

122

) -> cv2.SimpleBlobDetector

123

```

124

125

**Returns**: SimpleBlobDetector object

126

127

**Parameters class**:

128

```python

129

params = cv2.SimpleBlobDetector_Params()

130

131

# Threshold parameters

132

params.minThreshold = 50

133

params.maxThreshold = 220

134

params.thresholdStep = 10

135

136

# Filter by area

137

params.filterByArea = True

138

params.minArea = 100

139

params.maxArea = 5000

140

141

# Filter by circularity

142

params.filterByCircularity = True

143

params.minCircularity = 0.1

144

145

# Filter by convexity

146

params.filterByConvexity = True

147

params.minConvexity = 0.87

148

149

# Filter by inertia

150

params.filterByInertia = True

151

params.minInertiaRatio = 0.01

152

153

# Filter by color

154

params.filterByColor = True

155

params.blobColor = 255 # 0 for dark blobs, 255 for light blobs

156

157

detector = cv2.SimpleBlobDetector_create(params)

158

```

159

160

#### MSER (Maximally Stable Extremal Regions)

161

162

Detects stable regions in images across different threshold levels, useful for text detection and wide baseline matching.

163

164

```python { .api }

165

cv2.MSER_create(

166

delta=5, # Delta for MSER test

167

min_area=60, # Minimum area of region

168

max_area=14400, # Maximum area of region

169

max_variation=0.25, # Maximum variation in region stability

170

min_diversity=0.2, # Minimum diversity

171

max_evolution=200, # Maximum evolution steps

172

area_threshold=1.01, # Area threshold for filtering

173

min_margin=0.003, # Minimum margin

174

edge_blur_size=5 # Edge blur size

175

) -> cv2.MSER

176

```

177

178

**Returns**: MSER detector object

179

180

**Note**: MSER.detect() returns regions as contours, not KeyPoint objects.

181

182

#### FAST (Features from Accelerated Segment Test)

183

184

A high-speed corner detection algorithm, often used as the first stage in feature detection pipelines.

185

186

```python { .api }

187

cv2.FastFeatureDetector_create(

188

threshold=10, # Threshold on difference between intensity

189

nonmaxSuppression=True, # Apply non-maximum suppression

190

type=cv2.FAST_FEATURE_DETECTOR_TYPE_9_16 # FAST detector type

191

) -> cv2.FastFeatureDetector

192

```

193

194

**Returns**: FAST detector object

195

196

**Detector types**:

197

- `cv2.FAST_FEATURE_DETECTOR_TYPE_5_8` - 8 pixels on circle of radius 1

198

- `cv2.FAST_FEATURE_DETECTOR_TYPE_7_12` - 12 pixels on circle of radius 2

199

- `cv2.FAST_FEATURE_DETECTOR_TYPE_9_16` - 16 pixels on circle of radius 3

200

201

#### AGAST (Adaptive and Generic Accelerated Segment Test)

202

203

An improved version of FAST with better performance and adaptivity.

204

205

```python { .api }

206

cv2.AgastFeatureDetector_create(

207

threshold=10, # Threshold on difference between intensity

208

nonmaxSuppression=True, # Apply non-maximum suppression

209

type=cv2.AGAST_FEATURE_DETECTOR_OAST_9_16 # AGAST detector type

210

) -> cv2.AgastFeatureDetector

211

```

212

213

**Returns**: AGAST detector object

214

215

**Detector types**:

216

- `cv2.AGAST_FEATURE_DETECTOR_AGAST_5_8` - AGAST-5_8 decision tree

217

- `cv2.AGAST_FEATURE_DETECTOR_AGAST_7_12d` - AGAST-7_12d decision tree

218

- `cv2.AGAST_FEATURE_DETECTOR_AGAST_7_12s` - AGAST-7_12s decision tree

219

- `cv2.AGAST_FEATURE_DETECTOR_OAST_9_16` - OAST-9_16 decision tree

220

221

#### GFTT (Good Features To Track)

222

223

Shi-Tomasi corner detector, which finds the most prominent corners in an image.

224

225

```python { .api }

226

cv2.GFTTDetector_create(

227

maxCorners=1000, # Maximum number of corners to return

228

qualityLevel=0.01, # Minimal accepted quality of corners

229

minDistance=1, # Minimum distance between corners

230

blockSize=3, # Size of averaging block

231

useHarrisDetector=False, # Use Harris detector (vs. Shi-Tomasi)

232

k=0.04 # Free parameter of Harris detector

233

) -> cv2.GFTTDetector

234

```

235

236

**Returns**: GFTT detector object

237

238

### Feature2D Base Class Interface

239

240

All feature detectors inherit from the `Feature2D` base class and share these common methods:

241

242

```python { .api }

243

detector.detect(

244

image, # Input image (grayscale)

245

mask=None # Optional mask specifying detection region

246

) -> keypoints

247

```

248

249

**Returns**: List of KeyPoint objects

250

251

```python { .api }

252

detector.compute(

253

image, # Input image (grayscale)

254

keypoints # List of KeyPoint objects to compute descriptors for

255

) -> keypoints, descriptors

256

```

257

258

**Returns**:

259

- `keypoints` - Input keypoints (may be modified)

260

- `descriptors` - Computed descriptors as numpy array (N × descriptor_size)

261

262

```python { .api }

263

detector.detectAndCompute(

264

image, # Input image (grayscale)

265

mask=None # Optional mask specifying detection region

266

) -> keypoints, descriptors

267

```

268

269

**Returns**:

270

- `keypoints` - List of detected KeyPoint objects

271

- `descriptors` - Computed descriptors as numpy array (N × descriptor_size)

272

273

**Note**: `detectAndCompute()` is more efficient than calling `detect()` and `compute()` separately.

274

275

```python { .api }

276

detector.empty() -> bool

277

```

278

279

**Returns**: True if the detector object is empty

280

281

```python { .api }

282

detector.getDefaultName() -> str

283

```

284

285

**Returns**: Algorithm name as string

286

287

### Descriptor Matching

288

289

#### BFMatcher (Brute-Force Matcher)

290

291

Matches descriptors by comparing each descriptor in the first set against all descriptors in the second set using a specified distance metric.

292

293

```python { .api }

294

cv2.BFMatcher_create(

295

normType=cv2.NORM_L2, # Distance norm type

296

crossCheck=False # Enable cross-check for more robust matching

297

) -> cv2.BFMatcher

298

299

# Alternative constructor

300

cv2.BFMatcher(

301

normType=cv2.NORM_L2,

302

crossCheck=False

303

) -> cv2.BFMatcher

304

```

305

306

**Returns**: BFMatcher object

307

308

**Norm types**:

309

- `cv2.NORM_L1` - L1 norm (Manhattan distance)

310

- `cv2.NORM_L2` - L2 norm (Euclidean distance) - for SIFT, SURF

311

- `cv2.NORM_HAMMING` - Hamming distance - for ORB, BRISK, BRIEF

312

- `cv2.NORM_HAMMING2` - Hamming distance with 2 bits per dimension

313

- `cv2.NORM_INF` - Infinity norm

314

315

**Cross-check**: When enabled, only returns matches (i,j) where descriptor i in set A is the best match for descriptor j in set B, and vice versa.

316

317

#### FlannBasedMatcher

318

319

Fast Library for Approximate Nearest Neighbors (FLANN) based matcher, faster than brute-force for large datasets.

320

321

```python { .api }

322

cv2.FlannBasedMatcher_create() -> cv2.FlannBasedMatcher

323

324

# Constructor with index parameters

325

cv2.FlannBasedMatcher(

326

indexParams=None, # Index parameters dictionary

327

searchParams=None # Search parameters dictionary

328

) -> cv2.FlannBasedMatcher

329

```

330

331

**Returns**: FlannBasedMatcher object

332

333

**Index parameters examples**:

334

```python

335

# For SIFT/SURF descriptors (float)

336

index_params = dict(algorithm=1, trees=5) # FLANN_INDEX_KDTREE

337

338

# For ORB descriptors (binary)

339

index_params = dict(

340

algorithm=6, # FLANN_INDEX_LSH

341

table_number=6,

342

key_size=12,

343

multi_probe_level=1

344

)

345

```

346

347

**Search parameters**:

348

```python

349

search_params = dict(checks=50) # Number of times trees should be recursively traversed

350

```

351

352

**FLANN algorithm constants**:

353

- `FLANN_INDEX_LINEAR = 0` - Linear brute-force search

354

- `FLANN_INDEX_KDTREE = 1` - Randomized kd-tree (for float descriptors)

355

- `FLANN_INDEX_KMEANS = 2` - Hierarchical k-means tree

356

- `FLANN_INDEX_COMPOSITE = 3` - Combination of trees and kmeans

357

- `FLANN_INDEX_KDTREE_SINGLE = 4` - Single kd-tree

358

- `FLANN_INDEX_HIERARCHICAL = 5` - Hierarchical clustering

359

- `FLANN_INDEX_LSH = 6` - Locality-sensitive hashing (for binary descriptors)

360

361

#### DescriptorMatcher Common Methods

362

363

Both BFMatcher and FlannBasedMatcher inherit from DescriptorMatcher and share these methods:

364

365

```python { .api }

366

matcher.match(

367

queryDescriptors, # Query descriptors (N1 × descriptor_size)

368

trainDescriptors, # Train descriptors (N2 × descriptor_size)

369

mask=None # Optional mask for valid matches

370

) -> matches

371

```

372

373

**Returns**: List of DMatch objects (best match for each query descriptor)

374

375

```python { .api }

376

matcher.knnMatch(

377

queryDescriptors, # Query descriptors

378

trainDescriptors, # Train descriptors

379

k, # Number of best matches to return per descriptor

380

mask=None, # Optional mask

381

compactResult=False # Remove empty matches from result

382

) -> matches

383

```

384

385

**Returns**: List of lists of DMatch objects (k best matches per query descriptor)

386

387

**Common usage with ratio test**:

388

```python

389

matches = matcher.knnMatch(desc1, desc2, k=2)

390

# Apply Lowe's ratio test

391

good_matches = []

392

for m, n in matches:

393

if m.distance < 0.75 * n.distance:

394

good_matches.append(m)

395

```

396

397

```python { .api }

398

matcher.radiusMatch(

399

queryDescriptors, # Query descriptors

400

trainDescriptors, # Train descriptors

401

maxDistance, # Maximum allowed distance

402

mask=None, # Optional mask

403

compactResult=False # Remove empty matches from result

404

) -> matches

405

```

406

407

**Returns**: List of lists of DMatch objects (all matches within maxDistance)

408

409

```python { .api }

410

matcher.add(

411

descriptors # List of descriptor arrays to add to training set

412

) -> None

413

```

414

415

Adds descriptors to the training set for multi-image matching.

416

417

```python { .api }

418

matcher.train() -> None

419

```

420

421

Trains the matcher (required for some FLANN-based matchers).

422

423

```python { .api }

424

matcher.clear() -> None

425

```

426

427

Clears the training descriptor set.

428

429

```python { .api }

430

matcher.empty() -> bool

431

```

432

433

**Returns**: True if the matcher has no training data

434

435

### Drawing Functions

436

437

#### drawKeypoints

438

439

Draws keypoints on an image, optionally showing their size, orientation, and other properties.

440

441

```python { .api }

442

cv2.drawKeypoints(

443

image, # Source image

444

keypoints, # List of KeyPoint objects to draw

445

outImage, # Output image (can be the same as input)

446

color=(0, 255, 0), # Keypoint color (B, G, R)

447

flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT # Drawing flags

448

) -> outImage

449

```

450

451

**Returns**: Output image with keypoints drawn

452

453

**Drawing flags**:

454

- `cv2.DRAW_MATCHES_FLAGS_DEFAULT` - Simple circles at keypoint locations

455

- `cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS` - Draw circles with size and orientation

456

- `cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS` - Don't draw single points

457

- `cv2.DRAW_MATCHES_FLAGS_DRAW_OVER_OUTIMG` - Draw on existing output image

458

459

**Usage**:

460

```python

461

# Simple drawing

462

img_keypoints = cv2.drawKeypoints(img, keypoints, None, color=(0, 255, 0))

463

464

# Rich keypoints showing size and orientation

465

img_keypoints = cv2.drawKeypoints(

466

img, keypoints, None,

467

color=(0, 255, 0),

468

flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS

469

)

470

```

471

472

#### drawMatches

473

474

Draws matches between keypoints from two images side by side.

475

476

```python { .api }

477

cv2.drawMatches(

478

img1, # First source image

479

keypoints1, # Keypoints from first image

480

img2, # Second source image

481

keypoints2, # Keypoints from second image

482

matches1to2, # List of DMatch objects

483

outImg, # Output image (None = create new)

484

matchColor=(0, 255, 0), # Color for matches (B, G, R)

485

singlePointColor=(255, 0, 0), # Color for single points

486

matchesMask=None, # Mask determining which matches to draw

487

flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT # Drawing flags

488

) -> outImg

489

```

490

491

**Returns**: Output image showing both images with matches drawn between them

492

493

**Parameters**:

494

- `matchesMask` - List of bytes (0/1) same length as matches, 1 = draw match

495

- `matchColor` - Color for match lines, or tuple for random colors per match

496

- `singlePointColor` - Color for keypoints without matches

497

498

**Usage**:

499

```python

500

# Draw all matches

501

img_matches = cv2.drawMatches(

502

img1, kp1, img2, kp2, matches,

503

None, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS

504

)

505

506

# Draw only good matches with mask

507

mask = [[1] if m.distance < 50 else [0] for m in matches]

508

img_matches = cv2.drawMatches(

509

img1, kp1, img2, kp2, matches, None,

510

matchesMask=mask, flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS

511

)

512

```

513

514

#### drawMatchesKnn

515

516

Draws k best matches for each keypoint from two images.

517

518

```python { .api }

519

cv2.drawMatchesKnn(

520

img1, # First source image

521

keypoints1, # Keypoints from first image

522

img2, # Second source image

523

keypoints2, # Keypoints from second image

524

matches1to2, # List of lists of DMatch objects (from knnMatch)

525

outImg, # Output image (None = create new)

526

matchColor=(0, 255, 0), # Color for matches

527

singlePointColor=(255, 0, 0), # Color for single points

528

matchesMask=None, # Mask for matches (list of lists)

529

flags=cv2.DRAW_MATCHES_FLAGS_DEFAULT # Drawing flags

530

) -> outImg

531

```

532

533

**Returns**: Output image showing both images with k best matches per keypoint

534

535

**Usage with ratio test**:

536

```python

537

# Get k=2 best matches

538

matches = matcher.knnMatch(desc1, desc2, k=2)

539

540

# Apply ratio test and create mask

541

mask = []

542

for m, n in matches:

543

if m.distance < 0.75 * n.distance:

544

mask.append([1, 0]) # Draw first match only

545

else:

546

mask.append([0, 0]) # Don't draw

547

548

img_matches = cv2.drawMatchesKnn(

549

img1, kp1, img2, kp2, matches, None,

550

matchesMask=mask

551

)

552

```

553

554

### KeyPoint Class

555

556

Represents a detected feature point with its properties.

557

558

```python { .api }

559

cv2.KeyPoint(

560

x, # X coordinate

561

y, # Y coordinate

562

size, # Diameter of meaningful keypoint neighborhood

563

angle=-1, # Orientation in degrees [0, 360)

564

response=0, # Response strength

565

octave=0, # Pyramid octave where keypoint was detected

566

class_id=-1 # Object class (for categorized keypoints)

567

) -> cv2.KeyPoint

568

```

569

570

**Attributes**:

571

- `pt` - (x, y) tuple of keypoint coordinates

572

- `size` - Diameter of the meaningful keypoint neighborhood

573

- `angle` - Computed orientation of the keypoint (-1 if not applicable)

574

- `response` - Response strength (used for keypoint ranking)

575

- `octave` - Pyramid octave in which keypoint was detected

576

- `class_id` - Object ID for categorizing keypoints

577

578

**Methods**:

579

```python

580

# Convert keypoints to numpy array of (x, y) coordinates

581

points = cv2.KeyPoint_convert(keypoints)

582

583

# Convert numpy array to keypoints

584

keypoints = cv2.KeyPoint_convert(points, size=20)

585

586

# Get overlap between two keypoints

587

overlap = cv2.KeyPoint_overlap(kp1, kp2) # Returns 0-1

588

```

589

590

### DMatch Class

591

592

Represents a match between two feature descriptors.

593

594

```python { .api }

595

cv2.DMatch(

596

queryIdx, # Query descriptor index

597

trainIdx, # Train descriptor index

598

distance # Distance between descriptors (lower is better)

599

) -> cv2.DMatch

600

601

# Constructor with image index (for multi-image matching)

602

cv2.DMatch(

603

queryIdx,

604

trainIdx,

605

imgIdx, # Train image index

606

distance

607

) -> cv2.DMatch

608

```

609

610

**Attributes**:

611

- `queryIdx` - Index of descriptor in query set

612

- `trainIdx` - Index of descriptor in train set

613

- `imgIdx` - Index of train image (for multi-image matching)

614

- `distance` - Distance between descriptors (lower = better match)

615

616

**Usage**:

617

```python

618

# Sort matches by distance (best matches first)

619

matches = sorted(matches, key=lambda x: x.distance)

620

621

# Get top N matches

622

good_matches = matches[:50]

623

624

# Extract matched keypoint coordinates

625

src_pts = np.float32([kp1[m.queryIdx].pt for m in matches])

626

dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches])

627

```

628

629

## Common Workflows

630

631

### Basic Feature Detection and Matching

632

633

```python

634

import cv2

635

import numpy as np

636

637

# Read images

638

img1 = cv2.imread('image1.jpg', cv2.IMREAD_GRAYSCALE)

639

img2 = cv2.imread('image2.jpg', cv2.IMREAD_GRAYSCALE)

640

641

# Create detector

642

detector = cv2.SIFT_create()

643

644

# Detect and compute

645

kp1, desc1 = detector.detectAndCompute(img1, None)

646

kp2, desc2 = detector.detectAndCompute(img2, None)

647

648

# Create matcher

649

matcher = cv2.BFMatcher(cv2.NORM_L2, crossCheck=False)

650

651

# Match descriptors

652

matches = matcher.knnMatch(desc1, desc2, k=2)

653

654

# Apply ratio test

655

good_matches = []

656

for m, n in matches:

657

if m.distance < 0.75 * n.distance:

658

good_matches.append(m)

659

660

# Draw matches

661

img_matches = cv2.drawMatches(

662

img1, kp1, img2, kp2, good_matches, None,

663

flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS

664

)

665

666

cv2.imshow('Matches', img_matches)

667

cv2.waitKey(0)

668

```

669

670

### Finding Homography from Matches

671

672

```python

673

# Extract matched point coordinates

674

if len(good_matches) >= 4:

675

src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2)

676

dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2)

677

678

# Find homography

679

H, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)

680

681

# Use homography to transform image

682

h, w = img1.shape

683

img1_warped = cv2.warpPerspective(img1, H, (w, h))

684

```

685

686

### Feature Detection with Region of Interest

687

688

```python

689

# Create mask (detect features only in specific region)

690

mask = np.zeros(img.shape[:2], dtype=np.uint8)

691

mask[100:400, 200:600] = 255 # ROI

692

693

# Detect keypoints in masked region

694

keypoints = detector.detect(img, mask=mask)

695

keypoints, descriptors = detector.compute(img, keypoints)

696

```

697