仿射变换详解 warpAffine

今天遇到一个问题是关于仿射变换的,但是由于没有将仿射变换的具体原理型明白,看别人的代码看的很费解,最后终于在师兄的帮助下将原理弄明白了,我觉得最重要的是理解仿射变换可以看成是几种简单变换的复合实现,

具体实现形式即将几种简单变换的变换矩阵M相乘,这样就很容易理解啦



定义:仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,翻转,旋转和剪切。

这类变换可以用一个3*3的矩阵M来表示,其最后一行为(0,0,1)。该变换矩阵将原坐标为(x,y)变换为新坐标(x‘,y‘),

opencv中相应的函数是:

void warpAffine(InputArray src, OutputArray dst, InputArray M,
Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())?

Parameters:

  • src – input image.
  • dst – output image that has the size dsize and
    the same type as src .
  • M –  transformation matrix,最重要的东东了,本文中着重讲M的构造
  • dsize – size of the output image.ansformation (  ).
  • borderMode – pixel extrapolation method (see borderInterpolate());
    when borderMode=BORDER_TRANSPARENT , it means that the pixels in the destination image corresponding to the “outliers”
    in the source image are not modified by the function.
  • borderValue – value used in case of a constant border; by default, it is 0.

下面介绍一些典型的仿射变换:

(1)平移,将每一点移到到(x+t , y+t),变换矩阵为

(2)缩放变换  将每一点的横坐标放大或缩小sx倍,纵坐标放大(缩小)到sy倍,变换矩阵为

(3)旋转变换原点:目标图形围绕原点顺时针旋转Θ 弧度,变换矩阵为

(4) 旋转变换  :目标图形以(x , y )为轴心顺时针旋转θ弧度,变换矩阵为

相当于两次平移与一次原点旋转变换的复合,即先将轴心(x,y)移到到原点,然后做旋转变换,最后将图片的左上角置为图片的原点,即

有的人可能会说为什么这么复杂呢,那是因为在opencv的图像处理中,所有对图像的处理都是从原点进行的,而图像的原点默认为图像的左上角,而我们对图像作旋转处理时一般以图像的中点为轴心,因此就需要做如下处理

如果你觉得这样很麻烦,可以使用opencv中自带的Mat getRotationMatrix2D(Point2f
center, double angle, double scale)函数获得变换矩阵M,

  center:旋转中心

  angle:旋转弧度,一定要将角度转换成弧度

  scale:缩放尺度

它得到的矩阵是:

其中α = scale * cos( angle ) , β = scale  * sing( angle )  , ( center.x , center.y ) 表示旋转轴心

但是不得不说opencv的文档以及相关书籍中都把这个矩阵写错了,如下:

建议大家自己通过下式验证一下,即首先将轴心(x,y)移到原点,然后做旋转平绽放变换,最后再将图像的左上角转换为原点

没有去研究该函数的源码,不晓得源码中到底怎么写的,但是在别人的博客中看到这个函数貌似需要修正



opencv中还有一个函数:Mat getAffineTransform(InputArray src, InputArray dst)?

 它通过三组点对就可以获得它们之间的仿射变换,如果我们在一组图像变换中知道变换后的三组点,那么我们就可以利用该函数求得变换矩阵,然后对整张图片进行仿射变换

还有一种与仿射变换经常混淆的变换为透视变换,透视变换需要四组点对才能确定变换矩阵,由于仿射变换保持“平直性”与“平行性”,因此只需要三组点对,而透视变换没有这种约束,故需要四组点对

warpPerspective函数

主要作用:对图像进行透视变换,就是变形

函数的调用形式:

C++: void warpPerspective(InputArray src,
OutputArray dst, InputArray M,
Size dsize, int flags=INTER_LINEAR,
int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

参数详解:

InputArray src:输入的图像

OutputArray dst:输出的图像

InputArray M:透视变换的矩阵

Size dsize:输出图像的大小

int flags=INTER_LINEAR:输出图像的插值方法,

combination of interpolation methods (INTER_LINEAR or INTER_NEAREST)
and the optional flagWARP_INVERSE_MAP,
that sets M as the inverse transformation
 )

int borderMode=BORDER_CONSTANT:图像边界的处理方式

const Scalar& borderValue=Scalar():边界的颜色设置,一般默认是0

函数原理:

透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。通用的变换公式为:

u,v是原始图片左边,对应得到变换后的图片坐标x,y,其中

变换矩阵可以拆成4部分,表示线性变换,比如scaling,shearing和ratotion。用于平移,产生透视变换。所以可以理解成仿射等是透视变换的特殊形式。经过透视变换之后的图片通常不是平行四边形(除非映射视平面和原来平面平行的情况)。

重写之前的变换公式可以得到:

所以,已知变换对应的几个点就可以求取变换公式。反之,特定的变换公式也能新的变换后的图片。简单的看一个正方形到四边形的变换:

变换的4组对应点可以表示成:

根据变换公式得到:

定义几个辅助变量:

都为0时变换平面与原来是平行的,可以得到:

不为0时,得到:

求解出的变换矩阵就可以将一个正方形变换到四边形。反之,四边形变换到正方形也是一样的。于是,我们通过两次变换:四边形变换到正方形+正方形变换到四边形就可以将任意一个四边形变换到另一个四边形。

opencv代码:

[cpp] view
plain
copy

  1. #include<cv.h>
  2. #include<highgui.h>
  3. #pragma comment(lib, "cv.lib")
  4. #pragma comment(lib, "cxcore.lib")
  5. #pragma comment(lib, "highgui.lib")
  6. int main()
  7. {
  8. CvPoint2D32f srcTri[4], dstTri[4];
  9. CvMat*       warp_mat = cvCreateMat (3, 3, CV_32FC1);
  10. IplImage*    src = NULL;
  11. IplImage*    dst = NULL;
  12. src = cvLoadImage ("test.png", 1);
  13. dst = cvCloneImage (src);
  14. dst->origin = src->origin;
  15. cvZero (dst);
  16. srcTri[0].x = 0;
  17. srcTri[0].y = 0;
  18. srcTri[1].x = src->width - 1;
  19. srcTri[1].y = 0;
  20. srcTri[2].x = 0;
  21. srcTri[2].y = src->height - 1;
  22. srcTri[3].x = src->width - 1;
  23. srcTri[3].y = src->height - 1;
  24. dstTri[0].x = src->width * 0.05;
  25. dstTri[0].y = src->height * 0.33;
  26. dstTri[1].x = src->width * 0.9;
  27. dstTri[1].y = src->height * 0.25;
  28. dstTri[2].x = src->width * 0.2;
  29. dstTri[2].y = src->height * 0.7;
  30. dstTri[3].x = src->width * 0.8;
  31. dstTri[3].y = src->height * 0.9;
  32. cvGetPerspectiveTransform (srcTri, dstTri, warp_mat);
  33. cvWarpPerspective (src, dst, warp_mat);
  34. cvNamedWindow("src", 1);
  35. cvShowImage("src", src);
  36. cvNamedWindow ("Affine_Transform", 1);
  37. cvShowImage ("Affine_Transform", dst);
  38. cvWaitKey (0);
  39. cvReleaseImage (&src);
  40. cvReleaseImage (&dst);
  41. cvReleaseMat (&warp_mat);
  42. return 0;
  43. }

今天遇到一个问题是关于仿射变换的,但是由于没有将仿射变换的具体原理型明白,看别人的代码看的很费解,最后终于在师兄的帮助下将原理弄明白了,我觉得最重要的是理解仿射变换可以看成是几种简单变换的复合实现,

具体实现形式即将几种简单变换的变换矩阵M相乘,这样就很容易理解啦



定义:仿射变换的功能是从二维坐标到二维坐标之间的线性变换,且保持二维图形的“平直性”和“平行性”。仿射变换可以通过一系列的原子变换的复合来实现,包括平移,缩放,翻转,旋转和剪切。

这类变换可以用一个3*3的矩阵M来表示,其最后一行为(0,0,1)。该变换矩阵将原坐标为(x,y)变换为新坐标(x‘,y‘),

opencv中相应的函数是:

void warpAffine(InputArray src, OutputArray dst, InputArray M,
Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())?

Parameters:

  • src – input image.
  • dst – output image that has the size dsize and
    the same type as src .
  • M –  transformation matrix,最重要的东东了,本文中着重讲M的构造
  • dsize – size of the output image.
  • flags – combination of interpolation methods (see resize() )
    and the optional flag WARP_INVERSE_MAP that means that M is
    the inverse transformation (  ).
  • borderMode – pixel extrapolation method (see borderInterpolate());
    when borderMode=BORDER_TRANSPARENT , it means that the pixels in the destination image corresponding to the “outliers”
    in the source image are not modified by the function.
  • borderValue – value used in case of a constant border; by default, it is 0.

下面介绍一些典型的仿射变换:

(1)平移,将每一点移到到(x+t , y+t),变换矩阵为

(2)缩放变换  将每一点的横坐标放大或缩小sx倍,纵坐标放大(缩小)到sy倍,变换矩阵为

(3)旋转变换原点:目标图形围绕原点顺时针旋转Θ 弧度,变换矩阵为

(4) 旋转变换  :目标图形以(x , y )为轴心顺时针旋转θ弧度,变换矩阵为

相当于两次平移与一次原点旋转变换的复合,即先将轴心(x,y)移到到原点,然后做旋转变换,最后将图片的左上角置为图片的原点,即

有的人可能会说为什么这么复杂呢,那是因为在opencv的图像处理中,所有对图像的处理都是从原点进行的,而图像的原点默认为图像的左上角,而我们对图像作旋转处理时一般以图像的中点为轴心,因此就需要做如下处理

如果你觉得这样很麻烦,可以使用opencv中自带的Mat getRotationMatrix2D(Point2f
center, double angle, double scale)函数获得变换矩阵M,

  center:旋转中心

  angle:旋转弧度,一定要将角度转换成弧度

  scale:缩放尺度

它得到的矩阵是:



opencv中还有一个函数:Mat getAffineTransform(InputArray src, InputArray dst)?

 它通过三组点对就可以获得它们之间的仿射变换,如果我们在一组图像变换中知道变换后的三组点,那么我们就可以利用该函数求得变换矩阵,然后对整张图片进行仿射变换

还有一种与仿射变换经常混淆的变换为透视变换,透视变换需要四组点对才能确定变换矩阵,由于仿射变换保持“平直性”与“平行性”,因此只需要三组点对,而透视变换没有这种约束,故需要四组点对

本文将openCV中的RANSAC代码全部挑选出来,进行分析和讲解,以便大家更好的理解RANSAC算法。代码我都试过,可以直接运行。

在计算机视觉和图像处理等很多领域,都需要用到RANSAC算法。openCV中也有封装好的RANSAC算法,以便于人们使用。关于RANSAC算法的一些应用,可以看我的另一篇博客:

利用SIFT和RANSAC算法(openCV框架)实现物体的检测与定位,并求出变换矩阵(findFundamentalMat和findHomography的比较)

但是前几天师弟在使用openCV自带的RANSAC算法时,发现实验的运行时间并不会随着输入数据的增加而增加,感觉和理论上的不太相符。所以我就花了点时间,把openCV中关于RANSAC的源代码全部复制出来研究了一下。以便我们更加清晰的了解RANSAC算法的实际运行过程。

首先看两个类

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

//模型估计的基类,提供了估计矩阵的各种虚函数

//置信度设为0。99
循环次数设置为了2000

classCvModelEstimator2

{

public:

    CvModelEstimator2(int_modelPoints,
CvSize _modelSize,
int_maxBasicSolutions);

    virtual
~CvModelEstimator2();

    virtualintrunKernel(
constCvMat*
m1,
constCvMat*
m2, CvMat* model )=
0;

    //virtual
bool runLMeDS( const CvMat* m1, const CvMat* m2, CvMat* model,

                          //
CvMat* mask, double confidence=0.99, int maxIters=2000 );

    virtual
bool runRANSAC(
constCvMat*
m1,
constCvMat*
m2, CvMat* model,

                            CvMat*
mask,
doublethreshold,

                            doubleconfidence=0.99,intmaxIters=2000);

    virtual
bool refine(
constCvMat*,
constCvMat*,
CvMat*,
int)
{
returntrue;
}

    //virtual
void setSeed( int64 seed );

protected:

    virtualvoidcomputeReprojError(
constCvMat*
m1,
constCvMat*
m2,

                                     constCvMat*
model, CvMat* error ) =
0;

    virtualintfindInliers(
constCvMat*
m1,
constCvMat*
m2,

                             constCvMat*
model, CvMat* error,

                             CvMat*
mask,
doublethreshold
);

    virtual
bool getSubset(
constCvMat*
m1,
constCvMat*
m2,

                            CvMat*
ms1, CvMat* ms2,
intmaxAttempts=1000);

    virtual
bool checkSubset(
constCvMat*
ms1,
intcount
);

    CvRNG
rng;

    intmodelPoints;

    CvSize
modelSize;

    intmaxBasicSolutions;

    bool
checkPartialSubsets;

};

//单应矩阵估计的子类

classCvHomographyEstimator
:
publicCvModelEstimator2

{

public:

    CvHomographyEstimator(intmodelPoints
);

    virtualintrunKernel(
constCvMat*
m1,
constCvMat*
m2, CvMat* model );

    virtual
bool refine(
constCvMat*
m1,
constCvMat*
m2,

                         CvMat*
model,
intmaxIters
);

    

protected:

    virtualvoidcomputeReprojError(
constCvMat*
m1,
constCvMat*
m2,

                                     constCvMat*
model, CvMat* error );

};

上面的两个类中,CvModelEstimator2是一个基类,从名字就可以看出,这个类是用来估计模型的。可以看到里面提供了许多虚函数,这些函数有许多,比如runRANSAC是利用RANSAC方法计算单应矩阵,而runLMeDS是利用LMeDS方法计算单应矩阵,我们这里仅仅讲解RANSAC方法,所以其他不需要的内容我就直接注释掉了

CvHomographyEstimator继承自CvModelEstimator2,同样的,从名字也就可以看出,这个类使用来估计单应矩阵的。

接下来是两个类的构造函数和析构函数,这个没啥好说的了,基本都是默认的。

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

本范例的代码主要都是 学习OpenCV——通过KeyPoints进行目标定位这篇博客提供的,然后在它的基础上稍加修改,检测keypoints点的检测器是SURF,获取描述子也是用到SURF来描述,而用到的匹配器是FlannBased,匹配的方式是Knn方式,最后通过findHomography寻找单映射矩阵,perspectiveTransform获得最终的目标,在这个过程中还通过单映射矩阵来进一步去除伪匹配,这里只是贴出代码和代码解析,至于原理还没弄得特别明白,希望接下来可以继续学习,学懂了算法原理再来补充。

1、代码实现

[cpp] view
plain
 copy

  1. <span style="font-size:18px;">#include "stdafx.h"
  2. #include "opencv2/opencv.hpp"
  3. #include <vector>
  4. #include <iostream>
  5. using namespace cv;
  6. using namespace std;
  7. Mat src,frameImg;
  8. int width;
  9. int height;
  10. vector<Point> srcCorner(4);
  11. vector<Point> dstCorner(4);
  12. static bool createDetectorDescriptorMatcher( const string& detectorType, const string& descriptorType, const string& matcherType,
  13. Ptr<FeatureDetector>& featureDetector,
  14. Ptr<DescriptorExtractor>& descriptorExtractor,
  15. Ptr<DescriptorMatcher>& descriptorMatcher )
  16. {
  17. cout << "< Creating feature detector, descriptor extractor and descriptor matcher ..." << endl;
  18. if (detectorType=="SIFT"||detectorType=="SURF")
  19. initModule_nonfree();
  20. featureDetector = FeatureDetector::create( detectorType );
  21. descriptorExtractor = DescriptorExtractor::create( descriptorType );
  22. descriptorMatcher = DescriptorMatcher::create( matcherType );
  23. cout << ">" << endl;
  24. bool isCreated = !( featureDetector.empty() || descriptorExtractor.empty() || descriptorMatcher.empty() );
  25. if( !isCreated )
  26. cout << "Can not create feature detector or descriptor extractor or descriptor matcher of given types." << endl << ">" << endl;
  27. return isCreated;
  28. }
  29. bool refineMatchesWithHomography(const std::vector<cv::KeyPoint>& queryKeypoints,
  30. const std::vector<cv::KeyPoint>& trainKeypoints,
  31. float reprojectionThreshold,
  32. std::vector<cv::DMatch>& matches,
  33. cv::Mat& homography  )
  34. {
  35. const int minNumberMatchesAllowed = 4;
  36. if (matches.size() < minNumberMatchesAllowed)
  37. return false;
  38. // Prepare data for cv::findHomography
  39. std::vector<cv::Point2f> queryPoints(matches.size());
  40. std::vector<cv::Point2f> trainPoints(matches.size());
  41. for (size_t i = 0; i < matches.size(); i++)
  42. {
  43. queryPoints[i] = queryKeypoints[matches[i].queryIdx].pt;
  44. trainPoints[i] = trainKeypoints[matches[i].trainIdx].pt;
  45. }
  46. // Find homography matrix and get inliers mask
  47. std::vector<unsigned char> inliersMask(matches.size());
  48. homography = cv::findHomography(queryPoints,
  49. trainPoints,
  50. CV_FM_RANSAC,
  51. reprojectionThreshold,
  52. inliersMask);
  53. std::vector<cv::DMatch> inliers;
  54. for (size_t i=0; i<inliersMask.size(); i++)
  55. {
  56. if (inliersMask[i])
  57. inliers.push_back(matches[i]);
  58. }
  59. matches.swap(inliers);
  60. Mat homoShow;
  61. drawMatches(src,queryKeypoints,frameImg,trainKeypoints,matches,homoShow,Scalar::all(-1),CV_RGB(255,255,255),Mat(),2);
  62. imshow("homoShow",homoShow);
  63. return matches.size() > minNumberMatchesAllowed;
  64. }
  65. bool matchingDescriptor(const vector<KeyPoint>& queryKeyPoints,const vector<KeyPoint>& trainKeyPoints,
  66. const Mat& queryDescriptors,const Mat& trainDescriptors,
  67. Ptr<DescriptorMatcher>& descriptorMatcher,
  68. bool enableRatioTest = true)
  69. {
  70. vector<vector<DMatch>> m_knnMatches;
  71. vector<DMatch>m_Matches;
  72. if (enableRatioTest)
  73. {
  74. cout<<"KNN Matching"<<endl;
  75. const float minRatio = 1.f / 1.5f;
  76. descriptorMatcher->knnMatch(queryDescriptors,trainDescriptors,m_knnMatches,2);
  77. for (size_t i=0; i<m_knnMatches.size(); i++)
  78. {
  79. const cv::DMatch& bestMatch = m_knnMatches[i][0];
  80. const cv::DMatch& betterMatch = m_knnMatches[i][1];
  81. float distanceRatio = bestMatch.distance / betterMatch.distance;
  82. if (distanceRatio < minRatio)
  83. {
  84. m_Matches.push_back(bestMatch);
  85. }
  86. }
  87. }
  88. else
  89. {
  90. cout<<"Cross-Check"<<endl;
  91. Ptr<cv::DescriptorMatcher> BFMatcher(new cv::BFMatcher(cv::NORM_HAMMING, true));
  92. BFMatcher->match(queryDescriptors,trainDescriptors, m_Matches );
  93. }
  94. Mat homo;
  95. float homographyReprojectionThreshold = 1.0;
  96. bool homographyFound = refineMatchesWithHomography(
  97. queryKeyPoints,trainKeyPoints,homographyReprojectionThreshold,m_Matches,homo);
  98. if (!homographyFound)
  99. return false;
  100. else
  101. {
  102. if (m_Matches.size()>10)
  103. {
  104. std::vector<Point2f> obj_corners(4);
  105. obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( src.cols, 0 );
  106. obj_corners[2] = cvPoint( src.cols, src.rows ); obj_corners[3] = cvPoint( 0, src.rows );
  107. std::vector<Point2f> scene_corners(4);
  108. perspectiveTransform( obj_corners, scene_corners, homo);
  109. line(frameImg,scene_corners[0],scene_corners[1],CV_RGB(255,0,0),2);
  110. line(frameImg,scene_corners[1],scene_corners[2],CV_RGB(255,0,0),2);
  111. line(frameImg,scene_corners[2],scene_corners[3],CV_RGB(255,0,0),2);
  112. line(frameImg,scene_corners[3],scene_corners[0],CV_RGB(255,0,0),2);
  113. return true;
  114. }
  115. return true;
  116. }
  117. }
  118. int main()
  119. {
  120. string filename = "box.png";
  121. src = imread(filename,0);
  122. width = src.cols;
  123. height = src.rows;
  124. string detectorType = "SIFT";
  125. string descriptorType = "SIFT";
  126. string matcherType = "FlannBased";
  127. Ptr<FeatureDetector> featureDetector;
  128. Ptr<DescriptorExtractor> descriptorExtractor;
  129. Ptr<DescriptorMatcher> descriptorMatcher;
  130. if( !createDetectorDescriptorMatcher( detectorType, descriptorType, matcherType, featureDetector, descriptorExtractor, descriptorMatcher ) )
  131. {
  132. cout<<"Creat Detector Descriptor Matcher False!"<<endl;
  133. return -1;
  134. }
  135. //Intial: read the pattern img keyPoint
  136. vector<KeyPoint> queryKeypoints;
  137. Mat queryDescriptor;
  138. featureDetector->detect(src,queryKeypoints);
  139. descriptorExtractor->compute(src,queryKeypoints,queryDescriptor);
  140. VideoCapture cap(0); // open the default camera
  141. cap.set( CV_CAP_PROP_FRAME_WIDTH,320);
  142. cap.set( CV_CAP_PROP_FRAME_HEIGHT,240 );
  143. if(!cap.isOpened())  // check if we succeeded
  144. {
  145. cout<<"Can‘t Open Camera!"<<endl;
  146. return -1;
  147. }
  148. srcCorner[0] = Point(0,0);
  149. srcCorner[1] = Point(width,0);
  150. srcCorner[2] = Point(width,height);
  151. srcCorner[3] = Point(0,height);
  152. vector<KeyPoint> trainKeypoints;
  153. Mat trainDescriptor;
  154. Mat frame,grayFrame;
  155. char key=0;
  156. //  frame = imread("box_in_scene.png");
  157. while (key!=27)
  158. {
  159. cap>>frame;
  160. if (!frame.empty())
  161. {
  162. frame.copyTo(frameImg);
  163. printf("%d,%d\n",frame.depth(),frame.channels());
  164. grayFrame.zeros(frame.rows,frame.cols,CV_8UC1);
  165. cvtColor(frame,grayFrame,CV_BGR2GRAY);
  166. trainKeypoints.clear();
  167. trainDescriptor.setTo(0);
  168. featureDetector->detect(grayFrame,trainKeypoints);
  169. if(trainKeypoints.size()!=0)
  170. {
  171. descriptorExtractor->compute(grayFrame,trainKeypoints,trainDescriptor);
  172. bool isFound = matchingDescriptor(queryKeypoints,trainKeypoints,queryDescriptor,trainDescriptor,descriptorMatcher);
  173. imshow("foundImg",frameImg);
  174. }
  175. }
  176. key = waitKey(1);
  177. }
  178. cap.release();
  179. return 0;
  180. }  </span>

时间: 2024-08-06 16:06:06

仿射变换详解 warpAffine的相关文章

EasyPR--开发详解

我正在做一个开源的中文车牌识别系统,Git地址为:https://github.com/liuruoze/EasyPR. 我给它取的名字为EasyPR,也就是Easy to do Plate Recognition的意思.我开发这套系统的主要原因是因为我希望能够锻炼我在这方面的能力,包括C++技术.计算机图形学.机器学习等.我把这个项目开源的主要目的是:1.它基于开源的代码诞生,理应回归开源:2.我希望有人能够一起协助强化这套系统,包括代码.训练数据等,能够让这套系统的准确性更高,鲁棒性更强等等

SIFT算法详解(转)

http://blog.csdn.net/zddblog/article/details/7521424 目录(?)[-] 尺度不变特征变换匹配算法详解 Scale Invariant Feature TransformSIFT Just For Fun zdd  zddmailgmailcom or zddhubgmailcom SIFT综述 高斯模糊 1二维高斯函数 2 图像的二维高斯模糊 3分离高斯模糊 1 尺度空间理论 2 尺度空间的表示 3 高斯金字塔的构建 尺度空间在实现时使用高斯金

高斯消元法(Gauss Elimination)【超详解&amp;模板】

高斯消元法,是线性代数中的一个算法,可用来求解线性方程组,并可以求出矩阵的秩,以及求出可逆方阵的逆矩阵.高斯消元法的原理是:若用初等行变换将增广矩阵 化为 ,则AX = B与CX = D是同解方程组. 所以我们可以用初等行变换把增广矩阵转换为行阶梯阵,然后回代求出方程的解. 1.线性方程组 1)构造增广矩阵,即系数矩阵A增加上常数向量b(A|b) 2)通过以交换行.某行乘以非负常数和两行相加这三种初等变化将原系统转化为更简单的三角形式(triangular form) 注:这里的初等变化可以通过

【转】 SIFT算法详解

尺度不变特征变换匹配算法详解Scale Invariant Feature Transform(SIFT)Just For Fun zdd  [email protected] 对于初学者,从David G.Lowe的论文到实现,有许多鸿沟,本文帮你跨越. 1.SIFT综述 尺度不变特征转换(Scale-invariant feature transform或SIFT)是一种电脑视觉的算法用来侦测与描述影像中的局部性特征,它在空间尺度中寻找极值点,并提取出其位置.尺度.旋转不变量,此算法由 Da

安卓自定义View进阶-Matrix详解

这应该是目前最详细的一篇讲解Matrix的中文文章了,在上一篇文章Matrix原理中,我们对Matrix做了一个简单的了解,偏向理论,在本文中则会详细的讲解Matrix的具体用法,以及与Matrix相关的一些实用技巧. Matrix方法表 按照惯例,先放方法表做概览. 方法类别 相关API 摘要 基本方法 equals hashCode toString toShortString 比较. 获取哈希值. 转换为字符串 数值操作 set reset setValues getValues 设置.

【好程序员笔记分享】——UIView与CALayer详解

-iOS培训,iOS学习-------型技术博客.期待与您交流!------------ UIView与CALayer详解 研究Core Animation已经有段时间了,关于Core Animation,网上没什么好的介绍.苹果网站上有篇专门的总结性介绍,但是似乎原理性的东西不多,看得人云山雾罩,感觉,写那篇东西的人,其实是假 设读的人了解界面动画技术的原理的.今天有点别的事情要使用Linux,忘掉了ssh的密码,没办法重新设ssh,结果怎么也想不起来怎么设ssh远程登 陆了,没办法又到网上查

Android Matrix源码详解

尊重原创,转载请标明出处   http://blog.csdn.net/abcdef314159 Matrix是一个3*3的矩阵,通过矩阵执行对图像的平移,旋转,缩放,斜切等操作.先看一段代码 public static final int MSCALE_X = 0; //!< use with getValues/setValues public static final int MSKEW_X = 1; //!< use with getValues/setValues public st

Androidhttp请求加密机制详解

Android开发中,难免会遇到需要加解密一些数据内容存到本地文件.或者通过网络传输到其他服务器和设备的问题,但并不是使用了加密就绝对安全了,如果加密函数使用不正确,加密数据很容易受到逆向破解攻击.还有很多开发者没有意识到的加密算法的问题. 1.需要了解的基本概念 密码学的三大作用:加密( Encryption).认证(Authentication),鉴定(Identification) 加密:防止坏人获取你的数据. 鉴权:防止坏人假冒你的身份. 明文.密文.密钥.对称加密算法.非对称加密算法,

iOS开发——UIView与CALayer详解

UIView与CALayer详解 研究Core Animation已经有段时间了,关于Core Animation,网上没什么好的介绍.苹果网站上有篇专门的总结性介绍,但是似乎原理性的东西不多,看得人云山雾罩,感觉,写那篇东西的人,其实是假 设读的人了解界面动画技术的原理的.今天有点别的事情要使用Linux,忘掉了ssh的密码,没办法重新设ssh,结果怎么也想不起来怎么设ssh远程登 陆了,没办法又到网上查了一遍,太浪费时间了,痛感忘记记笔记是多么可怕的事情.鉴于Core Animation的内