目录

OpenCV计算机视觉编程攻略

Chapter_01 : 基础知识

Chapter_02 : 操作像素

Chapter_03 : 处理图像的颜色

Chapter_04 : 用直方图统计像素

Chapter_05 : 用形态学运算变换图像

Chapter_06 : 图像滤波

Chapter_07 : 提取直线、轮廓和区域

Chapter_08 : 检测兴趣点

Chapter_09 : 描述和匹配兴趣点

Chapter_10 : 估算图像之间的投影关系

Chapter_11 : 三维重建

Chapter_12 : 处理视频序列

Chapter_13 : 跟踪运动物体

Chapter_14 : 实用案列

Chapter_01 : 基础知识

1.图像的水平变换(flip),添加文字(putText),鼠标的触发事件 2.感兴趣区

 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
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

void onMouse(int event, int x, int y, int flags, void *param)
{
	//reinterpret_cast允许将任何指针转为任何其他指针类型,
	//也允许将任何整数类型转化为任何指针类型以及反向转换。
	Mat *im = reinterpret_cast<Mat *>(param);

	switch (event)
	{
	case EVENT_LBUTTONDOWN:
		cout << "at(" << x << "," << y << ") value is: " << static_cast<int>(im->at<uchar>(Point(x, y))) << endl;
		break;
	default:
		break;
	}
}

int main()
{
	//1.图像的水平变换(flip), 添加文字(putText), 鼠标的触发事件
	/*Mat image;
	cout << "This image is " << image.rows << "x" << image.cols << "y" << endl;

	image = imread("./images/puppy.bmp", IMREAD_GRAYSCALE);
	if (image.empty())
	{
		cout << "Error reading image..." << endl;
		return 0;
	}

	imshow("image", image);

	cout << "This image is " << image.rows << "x" << image.cols << "y" << endl;
	cout << "This image has" << image.channels() << "channel(s)" << endl;

	setMouseCallback("image", onMouse, reinterpret_cast<void*>(&image));

	Mat result;
	flip(image, result, 1);

	imshow("flip", result);

	circle(image, Point(155, 100), 65, 0, 3);

	putText(image, "SHA_CUN", Point(40, 200), FONT_HERSHEY_PLAIN, 2.0, 255, 2);

	imshow("sha_cun",image);*/

	//2.感兴趣区域
	Mat image = imread("./images/puppy.bmp");
	Mat logo = imread("./images/smalllogo.png");

	imshow("image", image);
	imshow("logo", logo);
	cout << "logo : " << logo.channels() << endl;

	Mat imageROI(image, Rect(image.cols - logo.cols, image.rows - logo.rows, logo.cols, logo.rows));
	logo.copyTo(imageROI);

	imshow("Image_logo", image);

	image = imread("./images/puppy.bmp");
	imageROI = image(Rect(image.cols - logo.cols, image.rows - logo.rows, logo.cols, logo.rows));
	
	Mat mask(logo); //必须是灰度图,只复制值不为0的部分(0为黑色, 255为白色)
	logo.copyTo(imageROI, mask);
	imshow("image_mask", image);


	waitKey(0);
	return 0;
}

图像像素的遍历:.at()、迭代器、指针

  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
//1.at()方法实现图像像素的遍历
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
  Mat grayim(600, 800, CV_8UC1);
  Mat colorim(600, 800, CV_8UC3);
  
  for(int i = 0; i < grayim.rows; i++)
  {
    for(int j = 0; j < grayim.cols; j++)
	{
	  grayim.at<uchar>(i,j) = (i + j) % 255;
	}
  }

  for(int i = 0; i < colorim.rows; i++)
  {
     for(int j = 0; j < colorim.cols; j++)
	 {
	   Vec3b pixel;
	   pixel[0] = i % 255; //Blue
	   pixel[1] = i % 255; //Green
	   pixel[2] = 0;       //Red
	   
	   colorim.at<Vec3b>(i, j) = pixel;
	 }
  }
  
   imshow("grayim", grayim);
   imshow("colorim", colorim);

   waitKey(0);
   return 0;
}

//2.简单的二值化处理代码
//如果,我们只需要图像的轮廓或者边缘的时候,那么其他像素是不是就可以认为噪声,
//为了减少数据量和方便计算,此时,我们就可以进行二值化
//而且二值化,还能去用来进行图像分割,前景后景分割
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
  Mat srcImage;
  srcImage = imread("4.jpg");
  imshow("srcImage1",srcImage);
  
  for(int i = 0; i < srcImage.rows; i++)
  {
    for(int j = 0; j < srcImage.cols; j++)
	{
	   Vec3b value = srcImage.at<Vec3b>(i,j);
	   
	   for(int h = 0; h < 3; h++)
	   {
	    if(value[h] > 128)
		   value[h] = 255;
		else
		   value[h] = 0;
	   }
	   
	   
	}
	
	srcImage.at<Vec3b>(i, j) = value;
  }
	
  imshow("srcImage2", srcImage);

  waitKey(0);

  return 0;
}
//3.使用迭代器遍历矩阵
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat grayim(800, 600, CV_8UC1);
	Mat colorim(800, 600, CV_8UC3);

	MatIterator_<uchar> grayit, grayend;
	for (grayit = grayim.begin<uchar>(), grayend = grayim.end<uchar>(); 
		grayit != grayend; grayit++)
		*grayit = rand() % 255;

	MatIterator_<Vec3b> colorit, colorend;
	for (colorit = colorim.begin<Vec3b>(), colorend = colorim.end<Vec3b>();
		colorit != colorend; colorit++)
		(*colorit)[0] = rand() % 255; //Blue
	(*colorit)[1] = rand() % 255; //Green
	(*colorit)[2] = rand() % 255; //Red

	imshow("grayim", grayim);
	imshow("colorim", colorim);

	waitKey(0);
	return 0;
}
//4.使用指针来遍历
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main()
{
	Mat grayim(800, 600, CV_8UC1);
	Mat colorim(800, 600, CV_8UC3);

	for (int i = 0; i < grayim.rows; i++)
	{
		uchar *p = grayim.ptr<uchar>(i);

		for (int j = 0; j < grayim.cols; j++)
		{
			p[j] = (i + j) % 255;
		}
	}

	for (int i = 0; i < colorim.rows; i++)
	{
		Vec3b *p = colorim.ptr<Vec3b>(i);

		for (int j = 0; j < colorim.cols; j++)
		{
			p[j][0] = i % 255;
			p[j][1] = j % 255;
			p[j][2] = 0;
		}
	}

	imshow("grayim", grayim);
	imshow("colorim", colorim);

	waitKey(0);
	return 0;
}

Chapter_02 : 操作像素

1.创建椒盐噪声 2.创建波浪影响remap() —- 重映射 3.扫描图像并访问相邻像素(1、代码的运行时间 2、锐化) 4.增加图片 5.减少图片中颜色的数量(没敲,太麻烦

  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

#include <iostream>
#include <opencv2/opencv.hpp>
//5.减少图片中颜色的数量
#define NTESTS 15
#define NITERATIONS 10

using namespace std;
using namespace cv;

//2.创建波浪影响remap()
void wave( const Mat &image, Mat &result)
{
	//the map functiuons
	Mat srcX(image.rows, image.cols, CV_32F);
	Mat srcY(image.rows, image.cols, CV_32F);

	//creating the mapping
	for (int i = 0; i < image.rows; i++)
	{
		for (int j = 0; j < image.cols; j++)
		{
			srcX.at<float>(i, j) = j;
			srcY.at<float>(i, j) = i + 3 * sin(j / 6.0);

			// horizontal flipping
			// srcX.at<float>(i,j)= image.cols-j-1;
			// srcY.at<float>(i,j)= i;
		}
	}

	//applying the mapping
	remap(image, result, srcX, srcY, INTER_LINEAR);
}

//3.扫描图像并访问相邻像素
void sharpen(const Mat &image, Mat &result)
{
	result.create(image.size(), image.type());
	int nchannels = image.channels();
	//处理所有的行,(除了第一行和最后一行)
	for (int j = 1; j < image.rows - 1; j++)
	{
		const uchar *previous = image.ptr<const uchar>(j - 1);  //previous row
		const uchar *current = image.ptr<const uchar>(j);       //current row
		const uchar *next = image.ptr<const uchar>(j + 1);      //next row

		uchar *output = result.ptr<uchar>(j);                   //output row

		for (int i = nchannels; i < (image.cols - 1) * nchannels; i++)
		{
			//应用锐化算子
			*output++ = saturate_cast<uchar>(5 * current[i]   -   current[i-nchannels]
				                             - current[i+nchannels] - previous[i] - next[i]);
		}
	}

	//把未处理的像素设置为0
	result.row(0).setTo(Scalar(0));
	result.row(result.rows - 1).setTo(Scalar(0));
	result.col(0).setTo(Scalar(0));
	result.col(result.cols - 1).setTo(Scalar(0));
}

//和sharpen一样的功能,只不过用了迭代
void sharpenIterator(const Mat &image, Mat &result)
{
	//must be a gray-level image
	CV_Assert(image.type() == CV_8UC1);

	//initialize iterator at row 1
	Mat_<uchar> :: const_iterator it = image.begin<uchar>() + image.cols;
	Mat_<uchar> :: const_iterator itend = image.end<uchar>() - image.cols;
	Mat_<uchar> :: const_iterator itup = image.begin<uchar>();
	Mat_<uchar> ::const_iterator itdown = image.begin<uchar>() + 2 * image.cols;

	//setup output image and iterator
	result.create(image.size(), image.type());   //allocate if necessary
	Mat_<uchar> ::iterator itout = result.begin<uchar>() + result.cols;

	for (; it != itend; ++it, ++itout, ++itup, ++itdown)
	{
		*itout = cv::saturate_cast<uchar>(*it * 5 - *(it - 1) - *(it + 1) - *itup - *itdown);
	}

	// Set the unprocessed pixels to 0
	result.row(0).setTo(cv::Scalar(0));
	result.row(result.rows - 1).setTo(cv::Scalar(0));
	result.col(0).setTo(cv::Scalar(0));
	result.col(result.cols - 1).setTo(cv::Scalar(0));
}

void sharpen2D(const Mat &image, Mat &result)
{
	//构造内核(所有入口都初始化为0)
	Mat kernel(3, 3, CV_32F, Scalar(0));
	//对内核赋值
	kernel.at<float>(1, 1) = 5.0;
	kernel.at<float>(0, 1) = -1.0;
	kernel.at<float>(2, 1) = -1.0;
	kernel.at<float>(1, 0) = -1.0;
	kernel.at<float>(1, 2) = -1.0;

	//对图像滤波
	filter2D(image, result, image.depth(), kernel);
}

//5.减少图片中颜色的数量
// 1st version
// see recipe Scanning an image with pointers
void colorReduce(Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols * image.channels(); // total number of elements per line

	for (int j = 0; j<nl; j++) {

		// get the address of row j
		uchar* data = image.ptr<uchar>(j);

		for (int i = 0; i<nc; i++) {

			// process each pixel ---------------------

			data[i] = data[i] / div*div + div / 2;

			// end of pixel processing ----------------

		} // end of line
	}
}

// version with input/ouput images
// see recipe Scanning an image with pointers
void colorReduceIO(const cv::Mat &image, // input image
	cv::Mat &result,      // output image
	int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols; // number of columns
	int nchannels = image.channels(); // number of channels

									  // allocate output image if necessary
	result.create(image.rows, image.cols, image.type());

	for (int j = 0; j<nl; j++) {

		// get the addresses of input and output row j
		const uchar* data_in = image.ptr<uchar>(j);
		uchar* data_out = result.ptr<uchar>(j);

		for (int i = 0; i<nc*nchannels; i++) {

			// process each pixel ---------------------

			data_out[i] = data_in[i] / div*div + div / 2;

			// end of pixel processing ----------------

		} // end of line
	}
}

// Test 1
// this version uses the dereference operator *
void colorReduce1(cv::Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols * image.channels(); // total number of elements per line
	uchar div2 = div >> 1; // div2 = div/2

	for (int j = 0; j<nl; j++) {

		uchar* data = image.ptr<uchar>(j);

		for (int i = 0; i<nc; i++) {


			// process each pixel ---------------------

			*data++ = *data / div*div + div2;

			// end of pixel processing ----------------

		} // end of line
	}
}

// Test 2
// this version uses the modulo operator
void colorReduce2(cv::Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols * image.channels(); // total number of elements per line
	uchar div2 = div >> 1; // div2 = div/2

	for (int j = 0; j<nl; j++) {

		uchar* data = image.ptr<uchar>(j);

		for (int i = 0; i<nc; i++) {

			// process each pixel ---------------------

			int v = *data;
			*data++ = v - v%div + div2;

			// end of pixel processing ----------------

		} // end of line
	}
}

// Test 3
// this version uses a binary mask
void colorReduce3(cv::Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols * image.channels(); // total number of elements per line
	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0) + 0.5);
	// mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0
	uchar div2 = 1 << (n - 1); // div2 = div/2

	for (int j = 0; j<nl; j++) {

		uchar* data = image.ptr<uchar>(j);

		for (int i = 0; i < nc; i++) {

			// process each pixel ---------------------

			*data &= mask;     // masking
			*data++ |= div2;   // add div/2

							   // end of pixel processing ----------------

		} // end of line
	}
}


// Test 4
// this version uses direct pointer arithmetic with a binary mask
void colorReduce4(cv::Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols * image.channels(); // total number of elements per line
	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0) + 0.5);
	int step = image.step; // effective width
						   // mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0
	uchar div2 = div >> 1; // div2 = div/2

						   // get the pointer to the image buffer
	uchar *data = image.data;

	for (int j = 0; j<nl; j++) {

		for (int i = 0; i<nc; i++) {

			// process each pixel ---------------------

			*(data + i) &= mask;
			*(data + i) += div2;

			// end of pixel processing ----------------

		} // end of line

		data += step;  // next line
	}
}

// Test 5
// this version recomputes row size each time
void colorReduce5(cv::Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0) + 0.5);
	// mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0

	for (int j = 0; j<nl; j++) {

		uchar* data = image.ptr<uchar>(j);

		for (int i = 0; i<image.cols * image.channels(); i++) {

			// process each pixel ---------------------

			*data &= mask;
			*data++ += div / 2;

			// end of pixel processing ----------------

		} // end of line
	}
}

// Test 6
// this version optimizes the case of continuous image
void colorReduce6(cv::Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols * image.channels(); // total number of elements per line

	if (image.isContinuous()) {
		// then no padded pixels
		nc = nc*nl;
		nl = 1;  // it is now a 1D array
	}

	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0) + 0.5);
	// mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0
	uchar div2 = div >> 1; // div2 = div/2

						   // this loop is executed only once
						   // in case of continuous images
	for (int j = 0; j<nl; j++) {

		uchar* data = image.ptr<uchar>(j);

		for (int i = 0; i<nc; i++) {

			// process each pixel ---------------------

			*data &= mask;
			*data++ += div2;

			// end of pixel processing ----------------

		} // end of line
	}
}

// Test 7
// this versions applies reshape on continuous image
void colorReduce7(cv::Mat image, int div = 64) {

	if (image.isContinuous()) {
		// no padded pixels
		image.reshape(1,   // new number of channels
			1); // new number of rows
	}
	// number of columns set accordingly

	int nl = image.rows; // number of lines
	int nc = image.cols*image.channels(); // number of columns

	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0) + 0.5);
	// mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0
	uchar div2 = div >> 1; // div2 = div/2

	for (int j = 0; j<nl; j++) {

		uchar* data = image.ptr<uchar>(j);

		for (int i = 0; i<nc; i++) {

			// process each pixel ---------------------

			*data &= mask;
			*data++ += div2;

			// end of pixel processing ----------------

		} // end of line
	}
}

// Test 8
// this version processes the 3 channels inside the loop with Mat_ iterators
void colorReduce8(cv::Mat image, int div = 64) {

	// get iterators
	cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
	cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
	uchar div2 = div >> 1; // div2 = div/2

	for (; it != itend; ++it) {

		// process each pixel ---------------------

		(*it)[0] = (*it)[0] / div*div + div2;
		(*it)[1] = (*it)[1] / div*div + div2;
		(*it)[2] = (*it)[2] / div*div + div2;

		// end of pixel processing ----------------
	}
}

// Test 9
// this version uses iterators on Vec3b
void colorReduce9(cv::Mat image, int div = 64) {

	// get iterators
	cv::MatIterator_<cv::Vec3b> it = image.begin<cv::Vec3b>();
	cv::MatIterator_<cv::Vec3b> itend = image.end<cv::Vec3b>();

	const cv::Vec3b offset(div / 2, div / 2, div / 2);

	for (; it != itend; ++it) {

		// process each pixel ---------------------

		*it = *it / div*div + offset;
		// end of pixel processing ----------------
	}
}

// Test 10
// this version uses iterators with a binary mask
void colorReduce10(cv::Mat image, int div = 64) {

	// div must be a power of 2
	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0) + 0.5);
	// mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0
	uchar div2 = div >> 1; // div2 = div/2

						   // get iterators
	cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
	cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();

	// scan all pixels
	for (; it != itend; ++it) {

		// process each pixel ---------------------

		(*it)[0] &= mask;
		(*it)[0] += div2;
		(*it)[1] &= mask;
		(*it)[1] += div2;
		(*it)[2] &= mask;
		(*it)[2] += div2;

		// end of pixel processing ----------------
	}
}

// Test 11
// this versions uses ierators from Mat_ 
void colorReduce11(cv::Mat image, int div = 64) {

	// get iterators
	cv::Mat_<cv::Vec3b> cimage = image;
	cv::Mat_<cv::Vec3b>::iterator it = cimage.begin();
	cv::Mat_<cv::Vec3b>::iterator itend = cimage.end();
	uchar div2 = div >> 1; // div2 = div/2

	for (; it != itend; it++) {

		// process each pixel ---------------------

		(*it)[0] = (*it)[0] / div*div + div2;
		(*it)[1] = (*it)[1] / div*div + div2;
		(*it)[2] = (*it)[2] / div*div + div2;

		// end of pixel processing ----------------
	}
}


// Test 12
// this version uses the at method
void colorReduce12(cv::Mat image, int div = 64) {

	int nl = image.rows; // number of lines
	int nc = image.cols; // number of columns
	uchar div2 = div >> 1; // div2 = div/2

	for (int j = 0; j<nl; j++) {
		for (int i = 0; i<nc; i++) {

			// process each pixel ---------------------

			image.at<cv::Vec3b>(j, i)[0] = image.at<cv::Vec3b>(j, i)[0] / div*div + div2;
			image.at<cv::Vec3b>(j, i)[1] = image.at<cv::Vec3b>(j, i)[1] / div*div + div2;
			image.at<cv::Vec3b>(j, i)[2] = image.at<cv::Vec3b>(j, i)[2] / div*div + div2;

			// end of pixel processing ----------------

		} // end of line
	}
}


// Test 13
// this version uses Mat overloaded operators
void colorReduce13(cv::Mat image, int div = 64) {

	int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0) + 0.5);
	// mask used to round the pixel value
	uchar mask = 0xFF << n; // e.g. for div=16, mask= 0xF0

							// perform color reduction
	image = (image&cv::Scalar(mask, mask, mask)) + cv::Scalar(div / 2, div / 2, div / 2);
}

// Test 14
// this version uses a look up table
void colorReduce14(cv::Mat image, int div = 64) {

	cv::Mat lookup(1, 256, CV_8U);

	for (int i = 0; i<256; i++) {

		lookup.at<uchar>(i) = i / div*div + div / 2;
	}

	cv::LUT(image, lookup, image);
}
       
int main()
{
	//2.创建波浪影响remap()  ---- 重映射
	/*Mat image = imread("./images/boldt.jpg", 0);

	imshow("image", image);

	Mat result;
	wave(image, result);

	imshow("result", result);*/

	////3.扫描图像并访问相邻像素
	//Mat image = imread("./images/boldt.jpg");
	//if (!image.data)
	//{
	//	return 0;
	//}

	//imshow("image", image);

	////调用sharpen()
	//Mat result_sharpen;
	////用来测试函数或者代码段的运行时间
	////getTickCount()返回从最近一次计算机开机到当前的时钟周期数
	//double time = static_cast<double>(getTickCount());
	//sharpen(image, result_sharpen);
	////getTickFrequency()返回每秒的时钟数
	//time = (static_cast<double>(getTickCount()) - time) / getTickFrequency();
	//cout << "time_sharpen = " << time << endl;

	//imshow("sharpen_Image", result_sharpen);

	////调用sharpenIterator(要使用灰度图)
	//Mat image3 = imread("./images/boldt.jpg", 0);
	//if (!image3.data)
	//{
	//	return 0;
	//}

	//imshow("image3", image3);
	//Mat result_Iterator;
	//double time3 = static_cast<double>(getTickCount());
	//sharpenIterator(image3, result_Iterator);
	//time3 = (static_cast<double>(getTickCount()) - time3) / getTickFrequency();
	//cout << "time_Iterator = " << time3 << endl;

	//imshow("Iterator_Image", result_Iterator);
	////调用sharpen2D()
	//Mat result_sharpen2D;

	//double time2 = static_cast<double>(getTickCount());
	//sharpen2D(image, result_sharpen2D);

	//time2 = (static_cast<double>(getTickCount()) - time2) / getTickFrequency();
	//cout << "time_sharpen2D = " << time2 << endl;

	//imshow("sharpen2D_Image", result_sharpen2D);

	//4.增加图片
	//Mat image1;
	//Mat image2;

	//image1 = imread("./images/boldt.jpg");
	//image2 = imread("./images/rain.jpg");
	//if (!image1.data)
	//	return 0;
	//if (!image2.data)
	//	return 0;

	//imshow("Image1", image1);
	//imshow("Image2", image2);

	//Mat result;

	//addWeighted(image1, 0.7, image2, 0.9, 0, result);

	//imshow("result", result);

	////using over loaded operator
	////result = 0.7 * image1 + 0.9 * image2;
	////imshow("result with operators", result);

	//image2 = imread("./images/rain.jpg", 0);
	////imshow("image2_rain", image2);
	//vector<Mat> planes;

	//split(image1, planes);
	////aadd to blue channel
	//planes[0] += image2;
	////merge the 3 1-channel images into 1 3-channel image
	//merge(planes, result);

	//imshow("Blue channel", result);

	//5.减少图片中颜色的数量

	Mat image = imread("./images/boldt.jpg");

	// time and process the image
	const int64 start = getTickCount();
	colorReduce(image, 64);
	//Elapsed time in seconds
	double duration = (getTickCount() - start) / getTickFrequency();

	// display the image
	cout << "Duration= " << duration << "secs" << std::endl;
	namedWindow("Image");
	imshow("Image", image);

	// test different versions of the function

	int64 t[NTESTS], tinit;
	// timer values set to 0
	for (int i = 0; i<NTESTS; i++)
		t[i] = 0;

	Mat images[NTESTS];
	Mat result;

	// the versions to be tested
	typedef void(*FunctionPointer)(Mat, int);
	FunctionPointer functions[NTESTS] = { colorReduce, colorReduce1, colorReduce2, colorReduce3, colorReduce4,
		colorReduce5, colorReduce6, colorReduce7, colorReduce8, colorReduce9,
		colorReduce10, colorReduce11, colorReduce12, colorReduce13, colorReduce14 };
	// repeat the tests several times
	int n = NITERATIONS;
	for (int k = 0; k<n; k++) {

		cout << k << " of " << n << std::endl;

		// test each version
		for (int c = 0; c < NTESTS; c++) {

			images[c] = imread("./images/boldt.jpg");

			// set timer and call function
			tinit = getTickCount();
			functions[c](images[c], 64);
			t[c] += getTickCount() - tinit;

			cout << ".";
		}

		cout << endl;
	}

	// short description of each function
	string descriptions[NTESTS] = {
		"original version:",
		"with dereference operator:",
		"using modulo operator:",
		"using a binary mask:",
		"direct ptr arithmetic:",
		"row size recomputation:",
		"continuous image:",
		"reshape continuous image:",
		"with iterators:",
		"Vec3b iterators:",
		"iterators and mask:",
		"iterators from Mat_:",
		"at method:",
		"overloaded operators:",
		"look-up table:",
	};

	for (int i = 0; i < NTESTS; i++) {

		namedWindow(descriptions[i]);
		imshow(descriptions[i], images[i]);
	}

	// print average execution time
	cout << endl << "-------------------------------------------" << endl << endl;
	for (int i = 0; i < NTESTS; i++) {

		cout << i << ". " << descriptions[i] << 1000.*t[i] / getTickFrequency() / n << "ms" << endl;
	}


	waitKey(0);
	return 0;
}

1.使用flip来进行水平垂直方向的改变 2.使用putText添加文本 3.使用椒盐噪声 4.减色函数clolrReduce

  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

#include <iostream>
#include <opencv2/opencv.hpp>
#include <random>

using namespace std;
using namespace cv;

Mat srcImage;
Mat result_1, result_0, result;
Mat src_gray_img;

////3.椒盐噪声
//void salt(Mat image, int n)
//{
//	//C++11的随机生成器
//	default_random_engine generator;
//	uniform_int_distribution<int> randomRow(0, image.rows - 1);
//	uniform_int_distribution<int> randomCol(0, image.cols - 1);
//
//	int i, j;
//	for (int k = 0; k < n; k++)
//	{
//		//随机生成图形位置
//		i = randomCol(generator);
//		j = randomRow(generator);
//
//		if (image.type() == CV_8UC1) // 灰度图像
//		{
//			//单通道8位图像
//			image.at<uchar>(j, i) = 255;
//		}
//		else if (image.type() == CV_8UC3) //彩色图像
//		{
//			//3通道图像
//			image.at<Vec3b>(j, i)[0] = 255;
//			image.at<Vec3b>(j, i)[1] = 255;
//			image.at<Vec3b>(j, i)[2] = 255;
//		}
//	}
//}

//4.减色函数
void colorReduce(Mat image, int div = 64)
{
	int nr = image.rows; //行数
	//每一行的元素
	int nc = image.cols * image.channels();

	for (int j = 0; j < nr; j++)
	{
		//取的行j的地址
		uchar *data = image.ptr<uchar>(j);

		for (int i = 0; i < nc; i++)
		{
			//处理每一个元素
			data[i] = data[i] / div * div + div / 2;
		}
	}
}

int main()
{
	srcImage = imread("1.jpg");

	if (srcImage.empty())
	{
		cout << "srcImage is error !" << endl;
		return -1;
	}

	imshow("srcImage", srcImage);

	//3.调用椒盐噪声
	//salt(srcImage, 3000);
	//imshow("srcImage_salt", srcImage);

	//4.减色函数
	colorReduce(srcImage, 64);
	imshow("srcImage_colorreduce", srcImage);

	//src_gray_img = imread("1.jpg", IMREAD_GRAYSCALE);
	//imshow("src_grat_img", src_gray_img);

	//1.使用flip来进行水平垂直方向的改变
	//flip(srcImage, result_1, 1); //正数表示水平
	//flip(srcImage, result_0, 0); //0表示垂直
	//flip(srcImage, result, -1); //负数表示水平和垂直

	//2.使用putText添加文本
	//putText(result_1, "Wangdasha is ppp", Point(4, 200), FONT_HERSHEY_PLAIN, 2.0, 125, 2);

	//imshow("result_1", result_1);
	//imshow("result_0", result_0);
	//imshow("result", result);

	waitKey(0);

	return 0;
}

Chapter_03 : 处理图像的颜色

1.用策略设计模式比较颜色 2.用GrabCut算法分割图像 3.用色调、饱和度、和亮度表示颜色

  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
#include <iostream>
#include <opencv2/opencv.hpp>

#include "ColorDetector.h"

using namespace std;
using namespace cv;

//3.用色调、饱和度、和亮度表示颜色 ---- 检测肤色
       //输入图像  色调区间  饱和度区间  输出掩码
void detectHScolor(const Mat& image, double minHue, double maxHue, double minSat, double maxSat, Mat &mask)
{
	//转到HSV空间
	Mat hsv;
	cvtColor(image, hsv, CV_BGR2HSV);
	//将3通道分割成3幅图像
	vector<Mat> channels;
	split(hsv, channels);
	//色调掩码
	Mat mask1;  //小于maxHue
	threshold(channels[0], mask1, maxHue, 255, THRESH_BINARY_INV);
	Mat mask2; //大于minHue
	threshold(channels[0], mask2, minHue, 255, THRESH_BINARY);

	Mat hueMask; //色调掩码
	if (minHue < maxHue)
		hueMask = mask1 & mask2;
	else //如果区间穿越0度中轴线
		hueMask = mask1 | mask2;

	//饱和度掩码
	//从minSat 到 maxSat
	//threshold(channels[1], mask1, maxSat, 255, THRESH_BINARY_INV);
	//threshold(channels[1], mask2, minSat, 255, THRESH_BINARY);

	//Mat satMask;
	//satMask = mask1 & mask2;

	Mat satMask;
	inRange(channels[1], minSat, maxSat, satMask);

	//组合掩码
	mask = hueMask & satMask;
}

int main()
{
	////1.用策略设计模式比较颜色

	////创建图像处理器对象
	//ColorDetector cdetect;

	////读取图像
	//Mat image = imread("./images/boldt.jpg");
	//if (image.empty())
	//	return 0;

	//imshow("image", image);

	////设置输入参数
	//cdetect.setTargetColor(230, 190, 130); // 这里表示蓝天

	////处理图像并显示结果
	//Mat result = cdetect.process(image);

	//imshow("result", result);

	//// or using functor
	//// here distance is measured with the Lab color space
	//ColorDetector colordetector(230, 190, 130,  // color
	//	45, true); // Lab threshold
	//namedWindow("result (functor)");
	//result = colordetector(image);
	//imshow("result (functor)", result);

	//// testing floodfill
	//floodFill(image,            // input/ouput image
	//	Point(100, 50),         // seed point
	//	Scalar(255, 255, 255),  // repainted color
	//	(Rect*)0,  // bounding rectangle of the repainted pixel set
	//	Scalar(35, 35, 35),     // low and high difference threshold
	//	Scalar(35, 35, 35),     // most of the time will be identical
	//	FLOODFILL_FIXED_RANGE); // pixels are compared to seed color

	//namedWindow("Flood Fill result");
	//result = colordetector(image);
	//imshow("Flood Fill result", image);

	//// Creating artificial images to demonstrate color space properties
	//Mat colors(100, 300, CV_8UC3, Scalar(100, 200, 150));
	//Mat range = colors.colRange(0, 100);
	//range = range + Scalar(10, 10, 10);
	//range = colors.colRange(200, 300);
	//range = range + Scalar(-10, -10, 10);

	//namedWindow("3 colors");
	//imshow("3 colors", colors);

	//Mat labImage(100, 300, CV_8UC3, Scalar(100, 200, 150));
	//cvtColor(labImage, labImage, CV_BGR2Lab);
	//range = colors.colRange(0, 100);
	//range = range + Scalar(10, 10, 10);
	//range = colors.colRange(200, 300);
	//range = range + Scalar(-10, -10, 10);
	//cvtColor(labImage, labImage, CV_Lab2BGR);

	//namedWindow("3 colors (Lab)");
	//imshow("3 colors (Lab)", colors);

	//// brightness versus luminance
	//Mat grayLevels(100, 256, CV_8UC3);
	//for (int i = 0; i < 256; i++) {
	//	grayLevels.col(i) = Scalar(i, i, i);
	//}

	//range = grayLevels.rowRange(50, 100);
	//Mat channels[3];
	//split(range, channels);
	//channels[1] = 128;
	//channels[2] = 128;
	//merge(channels, 3, range);
	//cvtColor(range, range, CV_Lab2BGR);


	//namedWindow("Luminance vs Brightness");
	//imshow("Luminance vs Brightness", grayLevels);


	//2.用GrabCut算法分割图像
	//Mat image = imread("./images/boldt.jpg");
	//if (image.empty())
	//	return 0;

	//imshow("image", image);

	//Rect rectangle(5, 70, 260, 120);

	//Mat result;
	//Mat bg, fg;
	////GrabCut分割算法
	//grabCut(image, result, rectangle, bg, fg, 5, GC_INIT_WITH_RECT);
	////取得标记可能为前景的像素
	//compare(result, GC_PR_FGD, result, CMP_EQ);
	////生成输出图像
	//Mat foreground(image.size(), CV_8UC3, Scalar(255, 255, 255));
	//image.copyTo(foreground, result);        //A.copyTo(B, mask);

	//imshow("foreground", foreground);
	//imshow("result", result);
	//imshow("image_copy", image);

	//3.用色调、饱和度、和亮度表示颜色
	Mat image = imread("./images/boldt.jpg");
	if (image.empty())
		return 0;

	imshow("image", image);

	//转换成HSV
	Mat hsv;
	cvtColor(image, hsv, CV_BGR2HSV);

	imshow("hsv", hsv);
	
	//把三个通道放进三个图片中
	vector<Mat> channels;
	split(hsv, channels); //0-H, 1-S, 2-V

	imshow("H", channels[0]); //色调
	imshow("S", channels[1]); //饱和度
	imshow("V", channels[2]); //亮度

	Mat newImage;
	Mat tmp(channels[2].clone());

	//改变亮度(V)
	channels[2] = 255;
	merge(channels, hsv);
	cvtColor(hsv, newImage, CV_HSV2BGR);

	imshow("newImage_V", newImage);
	//改变饱和度(S)
	channels[1] = 255;
	channels[2] = tmp;
	merge(channels, hsv);
	cvtColor(hsv, newImage, CV_HSV2BGR);

	imshow("newImage_S", newImage);
    //亮度、饱和度一起改变
	channels[1] = 255;
	channels[2] = 255;
	merge(channels, hsv);
	cvtColor(hsv, newImage, CV_HSV2BGR);

	imshow("newImage_SV", newImage);

	//展现出所有的HS颜色
	Mat hs(128, 360, CV_8UC3);

	for (int h = 0; h < 360; h++)
	{
		for (int s = 0; s < 128; s++)
		{
			hs.at<Vec3b>(s, h)[0] = h / 2;
			hs.at<Vec3b>(s, h)[1] = 255 - s * 2; //从高到低
			hs.at<Vec3b>(s, h)[2] = 255;
		}
	}

	cvtColor(hs, newImage, CV_HSV2BGR);
	imshow("newImage_hs", newImage);

	//肤色检测
	image = cv::imread("./images/girl.jpg");
	if (!image.data)
		return 0;

	imshow("girl", image);

	//检测肤色
	Mat mask;
	detectHScolor(image, 160, 10, 25, 166, mask); //色调为320度- 20度 饱和度为~0.1 到 0.65

	//显示使用掩码后的图像
	Mat detected(image.size(), CV_8UC3, Scalar(0, 0, 0));
	image.copyTo(detected, mask);

	imshow("detected", detected);




	// A test comparing luminance and brightness

	// create linear intensity image
	Mat linear(100, 256, CV_8U);
	for (int i = 0; i<256; i++) {

		linear.col(i) = i;
	}

	// create a Lab image
	linear.copyTo(channels[0]);
	Mat constante(100, 256, CV_8U, Scalar(128));
	constante.copyTo(channels[1]);
	constante.copyTo(channels[2]);
	merge(channels, image);

	// convert back to BGR
	Mat brightness;
	cvtColor(image, brightness, CV_Lab2BGR);
	split(brightness, channels);

	// create combined image
	Mat combined(200, 256, CV_8U);
	Mat half1(combined, Rect(0, 0, 256, 100));
	linear.copyTo(half1);
	Mat half2(combined, Rect(0, 100, 256, 100));
	channels[0].copyTo(half2);

	namedWindow("Luminance vs Brightness");
	imshow("Luminance vs Brightness", combined);


	waitKey(0);
	return 0;
}



#pragma once
#if !defined COLORDETECT
#define COLORDETECT

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

class ColorDetector {
private:

	//允许的最小差距
	int maxDist;

	//目标颜色
	Vec3b target;

	//image containing color converted image
	Mat converted;
	bool useLab;

	//存储二值映射结果的图像
	Mat result;

public:
	//空的构造函数
	ColorDetector() : maxDist(100), target(0, 0, 0), useLab(false) {}

	ColorDetector(bool useLab) : maxDist(100), target(0, 0, 0), useLab(useLab) {}
	//另一只构造函数,使用目标颜色和颜色距离作为参数
	ColorDetector(uchar blue, uchar green, uchar red, int mxDist = 100, bool useLab = false) :
		maxDist(mxDist), useLab(useLab) {

		//target color 
		setTargetColor(blue, green, red);
	}

	//计算与目标颜色的差距
	int getDistanceToTargetColor(const Vec3b& color) const
	{
		return getColorDistance(color, target);
	}

	//计算两个颜色之间的城区距离
	int getColorDistance(const Vec3b& color1, const Vec3b &color2) const
	{
		return abs(color1[0] - color2[0]) + abs(color1[1] - color2[1]) + abs(color1[2] - color2[2]);
	}

	Mat process(const Mat &image);

	Mat operator() (const Mat &image) {

		Mat input;

		if (useLab)
		{
			cvtColor(image, input, CV_BGR2Lab);
		}
		else
		{
			input = image;
		}

		Mat output;
		//compute absolute difference with target color
		absdiff(input, Scalar(target), output);
		//split the channel into 3 images
		vector<Mat> images;
		split(output, images);
		//add the 3 channels (saturation might occurs here)
		output = images[0] + images[1] + images[2];
		//apply threshold
		threshold(output, output, maxDist, 255, THRESH_BINARY_INV);

		return output;
	}


	//Getters and setters

	//设置颜色差距的阈值
	//阈值必须是正数,否则就设置为0
	void setColorDistanceThreshold(int distance)
	{
		if (distance < 0)
			distance = 0;
		maxDist = distance;
	}

	//取得颜色差距的阈值
	int getColorDistanceThreshold() const
	{
		return maxDist;
	}

	//设置需要检测的颜色
	//given in BGR color space
	void setTargetColor(uchar blue, uchar green, uchar red)
	{
		//次序为BGR
		target = Vec3b(blue, green, red);

		if (useLab)
		{
			//Temporary 1-pixel image
			Mat tmp(1, 1, CV_8UC3);
			tmp.at<Vec3b>(0, 0) = Vec3b(blue, green, red);

			//Converting the target to Lab color space
			cvtColor(tmp, tmp, CV_BGR2Lab);

			target = tmp.at<Vec3b>(0, 0);
		}
	}

	//设置需要检测的颜色
	void setTargetColor(Vec3b color)
	{
		target = color;
	}

	//取得需要检测的颜色
	Vec3b getTargetColor() const
	{
		return target;
	}
};

#endif // !defined COLORDETECT





#include "ColorDetector.h"
#include <vector>

Mat ColorDetector::process(const Mat &image)
{
	//必要时重新分配二值映射
	//与输入图像的尺寸相同,不过是单通道
	result.create(image.size(), CV_8U);

	// Converting to Lab color space 
	if (useLab)
		cvtColor(image, converted, CV_BGR2Lab);

	// 取得迭代器
	Mat_<Vec3b>::const_iterator it = image.begin<Vec3b>();
	Mat_<Vec3b>::const_iterator itend = image.end<Vec3b>();
	Mat_<uchar>::iterator itout = result.begin<uchar>();

	// get the iterators of the converted image 
	if (useLab) {
		it = converted.begin<Vec3b>();
		itend = converted.end<Vec3b>();
	}

	// 对于每一个像素
	for (; it != itend; ++it, ++itout) {

		//比较与目标颜色的差距
		if (getDistanceToTargetColor(*it)<maxDist) {

			*itout = 255;

		}
		else {

			*itout = 0;
		}

		// end of pixel processing ----------------
	}

	return result;
}

Chapter_04 : 用直方图统计像素

计算图像直方图

  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
//#include "Histogram1D.h"
//#include "ContentFinder.h"
//#include  "ColorHistogram.h"
//
//
//int main()
//{
//	//读入图像,并转成灰度图
//	Mat image = imread("./images/waves.jpg", 0);
//	if (!image.data)
//		return 0;
//
//	imshow("image", image);
//
//	Mat imageROI;
//	imageROI = image(Rect(216, 33, 24, 30)); //设置感兴趣区域
//
//	imshow("imageROI", imageROI);
//
//	//直方图对象
//	//计算直方图
//	Histogram1D h;
//	//计算直方图
//	Mat hist = h.getHistogram(imageROI);
//	imshow("Histogram1D", h.getHistogramImage(imageROI));
//
//	//创建内容搜寻器
//	ContentFinder finder;
//	//设置用来反向投影的直方图
//
//	// set histogram to be back-projected
//	finder.setHistogram(hist);
//	finder.setThreshold(-1.0f);
//
//	// Get back-projection
//	cv::Mat result1;
//	result1 = finder.find(image);
//
//	// Create negative image and display result
//	cv::Mat tmp;
//	result1.convertTo(tmp, CV_8U, -1.0, 255.0);
//	cv::namedWindow("Backprojection result");
//	cv::imshow("Backprojection result", tmp);
//
//	// Get binary back-projection
//	finder.setThreshold(0.12f);
//	result1 = finder.find(image);
//
//	// Draw a rectangle around the reference area
//	cv::rectangle(image, cv::Rect(216, 33, 24, 30), cv::Scalar(0, 0, 0));
//
//	// Display image
//	cv::namedWindow("Image");
//	cv::imshow("Image", image);
//
//	// Display result
//	cv::namedWindow("Detection Result");
//	cv::imshow("Detection Result", result1);
//	
//
//
//	//装载彩色图片
//	ColorHistogram hc;
//	Mat color = imread("./images/waves.jpg");
//
//	imshow("color", color);
//
//	//提取ROI
//	imageROI = color(Rect(0, 0, 100, 45));  //蓝色天空的区域
//    //获取3D颜色直方图(每个通道适合8个箱子)
//	hc.setSize(8);        //8 * 8 * 8
//	Mat shist = hc.getHistogram(imageROI);
//	//创建内容搜寻器
//	//设置用来反向投影的直方图
//	finder.setHistogram(shist);
//	finder.setThreshold(0.05f);
//	//取得颜色直方图的反向投影
//	result1 = finder.find(color);
//
//	imshow("result1", result1);
//
//	//装载彩色图片
//	Mat color2 = imread("./images/dog.jpg");
//	imshow("color2", color2);
//
//	Mat result2 = finder.find(color2);
//
//	imshow("result2", result2);
//
//
//	// Get ab color histogram
//	hc.setSize(256); // 256x256
//	cv::Mat colorhist = hc.getabHistogram(imageROI);
//
//	// display 2D histogram
//	colorhist.convertTo(tmp, CV_8U, -1.0, 255.0);
//	cv::namedWindow("ab histogram");
//	cv::imshow("ab histogram", tmp);
//
//
//	// set histogram to be back - projected
//	finder.setHistogram(colorhist);
//	finder.setThreshold(0.05f);
//
//	// Convert to Lab space
//	cv::Mat lab;
//	cv::cvtColor(color, lab, CV_BGR2Lab);
//
//	// Get back-projection of ab histogram
//	int ch[2] = { 1,2 };
//	result1 = finder.find(lab, 0, 256.0f, ch);
//
//	cv::namedWindow("Result ab (1)");
//	cv::imshow("Result ab (1)", result1);
//
//
//
//	// Second colour image
//	cv::cvtColor(color2, lab, CV_BGR2Lab);
//
//	// Get back-projection of ab histogram
//	result2 = finder.find(lab, 0, 256.0, ch);
//
//	cv::namedWindow("Result ab (2)");
//	cv::imshow("Result ab (2)", result2);
//
//	// Draw a rectangle around the reference sky area
//	cv::rectangle(color, cv::Rect(0, 0, 100, 45), cv::Scalar(0, 0, 0));
//	cv::namedWindow("Color Image");
//	cv::imshow("Color Image", color);
//
//	// Get Hue colour histogram
//	hc.setSize(180); // 180 bins
//	colorhist = hc.getHueHistogram(imageROI);
//
//	// set histogram to be back-projected
//	finder.setHistogram(colorhist);
//
//
//
//	// Convert to HSV space
//	cv::Mat hsv;
//	cv::cvtColor(color, hsv, CV_BGR2HSV);
//	// Get back-projection of hue histogram
//	ch[0] = 0;
//	result1 = finder.find(hsv, 0.0f, 180.0f, ch);
//
//	cv::namedWindow("Result Hue (1)");
//	cv::imshow("Result Hue (1)", result1);
//
//	//// Second colour image
//	//color2 = cv::imread("./images/dog.jpg");
//
//	//// Convert to HSV space
//	//cv::cvtColor(color2, hsv, CV_BGR2HSV);
//
//	//// Get back-projection of hue histogram
//	//result2 = finder.find(hsv, 0.0f, 180.0f, ch);
//
//	//cv::namedWindow("Result Hue (2)");
//	//cv::imshow("Result Hue (2)", result2);
//
//
//	waitKey(0);
//	return 0;
//}


#pragma once
#if !defined HISTOGRAM
#define HISTOGRAM

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;


//创建灰度图像的直方图
class Histogram1D
{
private:
	int histSize[1];        //直方图中箱子的数量
	float hranges[2];       //值范围
	const float *ranges[1]; //值范围的指针
	int channels[1];        //要检查的通道数量

public:
	Histogram1D()
	{
		//准备一维直方图的默认参数
		histSize[0] = 256;    //256个箱子
		hranges[0] = 0.0;     //从0开始(包含0)
		hranges[1] = 256.0;   //到256(不包含256)
		ranges[0] = hranges;
		channels[0] = 0;      //先关注通道0
	}
	////
	//void setChannel(int c)
	//{
	//	channels[0] = c;
	//}
	////
	//int getChannel()
	//{
	//	return channels[0];
	//}
	////
	//void setRange(float minValue, float maxValue)
	//{
	//	hranges[0] = minValue;
	//	hranges[1] = maxValue;
	//}
	////
	//float getMinValue()
	//{
	//	return hranges[0];
	//}
	////
	//float getMaxValue()
	//{
	//	return hranges[1];
	//}
	////
	//void setNBins(int nbins)
	//{
	//	histSize[0] = nbins;
	//}
	////
	//int getNBins()
	//{
	//	return histSize[0];
	//}

	//计算一维直方图
	Mat getHistogram(const Mat &image)
	{
		Mat hist;
		//用calcHist函数计算一维直方图
		//一幅图像的直方图   一幅   使用的通道, 不使用掩码  作为结果的直方图  
		//这是一维的直方图  箱子数量  像素值的范围
		calcHist(&image, 1, channels, Mat(), hist, 1, histSize, ranges);

		return hist;
	}

	//计算一维直方图,并返回它的图像
	Mat getHistogramImage(const Mat &image, int zoom = 1)
	{
		//先计算直方图
		Mat hist = getHistogram(image);

		//创建图像
		return Histogram1D::getImageOfHistogram(hist, zoom);
	}


	//// Stretches the source image using min number of count in bins.
	//Mat stretch(const Mat &image, int minValue = 0) {

	//	// Compute histogram first
	//	Mat hist = getHistogram(image);

	//	// find left extremity of the histogram
	//	int imin = 0;
	//	for (; imin < histSize[0]; imin++) {
	//		// ignore bins with less than minValue entries
	//		if (hist.at<float>(imin) > minValue)
	//			break;
	//	}

	//	// find right extremity of the histogram
	//	int imax = histSize[0] - 1;
	//	for (; imax >= 0; imax--) {

	//		// ignore bins with less than minValue entries
	//		if (hist.at<float>(imax) > minValue)
	//			break;
	//	}

	//	// Create lookup table
	//	int dims[1] = { 256 };
	//	Mat lookup(1, dims, CV_8U);

	//	for (int i = 0; i<256; i++) {

	//		if (i < imin) lookup.at<uchar>(i) = 0;
	//		else if (i > imax) lookup.at<uchar>(i) = 255;
	//		else lookup.at<uchar>(i) = cvRound(255.0*(i - imin) / (imax - imin));
	//	}

	//	// Apply lookup table
	//	Mat result;
	//	result = applyLookUp(image, lookup);

	//	return result;
	//}

	//// Stretches the source image using percentile.
	//Mat stretch(const Mat &image, float percentile) {

	//	// number of pixels in percentile
	//	float number = image.total() * percentile;

	//	// Compute histogram first
	//	Mat hist = getHistogram(image);

	//	// find left extremity of the histogram
	//	int imin = 0;
	//	for (float count = 0.0; imin < 256; imin++) {
	//		// number of pixel at imin and below must be > number
	//		if ((count += hist.at<float>(imin)) >= number)
	//			break;
	//	}

	//	// find right extremity of the histogram
	//	int imax = 255;
	//	for (float count = 0.0; imax >= 0; imax--) {
	//		// number of pixel at imax and below must be > number
	//		if ((count += hist.at<float>(imax)) >= number)
	//			break;
	//	}

	//	// Create lookup table
	//	int dims[1] = { 256 };
	//	Mat lookup(1, dims, CV_8U);

	//	for (int i = 0; i<256; i++) {

	//		if (i < imin) lookup.at<uchar>(i) = 0;
	//		else if (i > imax) lookup.at<uchar>(i) = 255;
	//		else lookup.at<uchar>(i) = cvRound(255.0*(i - imin) / (imax - imin));
	//	}

	//	// Apply lookup table
	//	Mat result;
	//	result = applyLookUp(image, lookup);

	//	return result;
	//}


	//创建一个表示直方图的图像(静态方法)
	static Mat getImageOfHistogram(const Mat &hist, int zoom) {
		//取得箱子值的最大值和最小值
		double maxVal = 0;
		double minVal = 0;
		minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		//取得直方图的大小
		int histSize = hist.rows;
		//用于显示直方图的方形图像
		Mat histImg(histSize *zoom, histSize *zoom, CV_8U, Scalar(255));
		//设置最高点为90%(即图像高度)的箱子个数
		int hpt = static_cast<int>(0.9 * histSize);
		//为每个箱子画垂直线
		for (int h = 0; h < histSize; h++)
		{
			float binVal = hist.at<float>(h);

			if (binVal > 0)
			{
				int intensity = static_cast<int>(binVal * hpt / maxVal);
				line(histImg, Point(h * zoom, histSize * zoom),
					Point(h * zoom, (histSize - intensity) * zoom), Scalar(0), zoom);
			}
		}

		return histImg;
	}



	//// Equalizes the source image.
	//static Mat equalize(const Mat &image) {

	//	Mat result;
	//	equalizeHist(image, result);

	//	return result;
	//}


	//// Applies a lookup table transforming an input image into a 1-channel image
	//static Mat applyLookUp(const Mat &image, // input image
	//	const Mat &lookup) { // 1x256 uchar matrix

	//						 // the output image
	//	Mat result;

	//	// apply lookup table
	//	LUT(image, lookup, result);

	//	return result;
	//}

	//// Applies a lookup table transforming an input image into a 1-channel image
	//// this is a test version with iterator; always use function cv::LUT
	//static Mat applyLookUpWithIterator(const Mat& image, const Mat& lookup) {

	//	// Set output image (always 1-channel)
	//	Mat result(image.rows, image.cols, CV_8U);
	//	Mat_<uchar>::iterator itr = result.begin<uchar>();

	//	// Iterates over the input image
	//	Mat_<uchar>::const_iterator it = image.begin<uchar>();
	//	Mat_<uchar>::const_iterator itend = image.end<uchar>();

	//	// Applies lookup to each pixel
	//	for (; it != itend; ++it, ++itr) {

	//		*itr = lookup.at<uchar>(*it);
	//	}

	//	return result;
	//}
};
#endif // !defined HISTOGRAM


#pragma once
#if !define OFINDER
#define OFINDER

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>

using namespace std;
using namespace cv;

class ContentFinder
{
private:
	//直方图参数
	float hranges[2];
	const float *ranges[3];
	int channels[3];

	float threshold;    //判断阈值
	Mat histogram;      //输入直方图
	SparseMat shistogram;
	bool  isSparse;

public:

	ContentFinder() : threshold(0.1f), isSparse(false)
	{
		//本类中所有的通道的范围相同
		ranges[0] = hranges;
		ranges[1] = hranges;
		ranges[2] = hranges;
	}

	// Sets the threshold on histogram values [0,1]
	void setThreshold(float t) {

		threshold = t;
	}

	// Gets the threshold
	float getThreshold() {

		return threshold;
	}

	//设置应用的直方图
	void setHistogram(const Mat &h)
	{
		isSparse = false;
		normalize(h, histogram, 1.0);
	}
	//设置应用的直方图----SparseMat
	void setHistogram(const SparseMat &h)
	{
		isSparse = true;
		normalize(h, shistogram, 1.0, NORM_L2);
	}

	//使用全部通道,范围【0, 256】
	Mat find(const Mat &image)
	{
		Mat result;

		hranges[0] = 0.0;    //默认范围【0, 256】, hranges[1] = 256.0;
		hranges[1] = 256.0;
		channels[0] = 0;     //三个通道
		channels[1] = 1;
		channels[2] = 2;

		return find(image, hranges[0], hranges[1], channels);
	}

	//查找属于直方图的像素
	Mat find(const Mat& image, float minValue, float maxValue, int *channels)
	{
		Mat result;
		hranges[0] = minValue;
		hranges[1] = maxValue;

		if (isSparse)
		{
			for (int i = 0; i<shistogram.dims(); i++)
				this->channels[i] = channels[i];

			calcBackProject(&image,
				1,          //只使用一幅图像
				channels,   //通道
				histogram,  //直方图
				result,     //反向投影
				ranges,     //每个维度的值范围
				255.0       //选用的换算技术
							//把概率从1映射到255
			);
		}
		else {
			//直方图的维度数与通道列表一致
			for (int i = 0; i < histogram.dims; i++)
			{
				this->channels[i] = channels[i];
			}
			//calcBackProject函数和calcHist函数有些类似,而calcBackProject不会增加箱子的数量,而是从箱子读取的值赋给方向投影图像中对应的像素
			calcBackProject(&image,
				1,          //只使用一幅图像
				channels,   //通道
				histogram,  //直方图
				result,     //反向投影
				ranges,     //每个维度的值范围
				255.0       //选用的换算技术
							//把概率从1映射到255
			);
		}

		//对反射投影结果做阈值化,得到二值图像
		if (threshold > 0.0)

			cv::threshold(result, result, 255.0*threshold, 255.0, cv::THRESH_BINARY);

		return result;
	}
};

#endif // !define OFINDER




#pragma once
#if !define COLORHISTOGRAM
#define COLORHISTOGRAM

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

class ColorHistogram
{
private:

	int histSize[3];         //每个维度的大小
	float hranges[2];        //值的大小(三个维度用同一个值)
	const float* ranges[3];  //每个维度的范围
	int channels[3];         //需要处理的通道

public:

	ColorHistogram()
	{
		//准备用于彩色图像的默认参数
		//每个维度的范围和大小是相同的
		histSize[0] = histSize[1] = histSize[2] = 256;
		hranges[0] = 0.0;      // BGR范围为0-256
		hranges[1] = 256.0;
		ranges[0] = hranges;   //这个类中
		ranges[1] = hranges;   //所有通道的范围均相等
		ranges[2] = hranges; 
		channels[0] = 0;           //三个通道: B
		channels[1] = 1;           //G
		channels[2] = 2;           //R
	}

	//ÿ¸öά¶ÈµÃµ½Ö±·½Í¼µÄ´óС
	void setSize(int size)
	{
		//ÿ¸öά¶ÈµÄ´óС¶¼ÏàµÈ
		histSize[0] = histSize[1] = histSize[3] = size;
	}

	//计算直方图
	Mat getHistogram(const Mat &image)
	{
		Mat hist;

		
		calcHist(&image, 1, //单幅图像的直方图
			channels,  //用到的通道
			Mat(),     //不使用掩码
			hist,      //得到的直方图
			3,         //这是一个三维的直方图
			histSize,  //箱子数量
			ranges);   //像素值的范围

		return hist;
	}

	

	//计算直方图
	SparseMat getSparseHistogram(const Mat &image)
	{
		SparseMat hist(3,       //维数
			histSize,          //每个维度的大小
			CV_32F);

		//计算直方图
		calcHist(&image, 1, 
			channels,  
			Mat(),    
			hist,      
			3,         
			histSize, 
			ranges);  


		return hist;
	}

	//用均值平均算法查找目标
	//计算一维色调直方图
	//BGR 的原图转化为 HSV
	//忽略低饱和度的像素
	cv::Mat getHueHistogram(const cv::Mat &image,
		int minSaturation = 0) {

		cv::Mat hist;

		//转化为HSV空间
		cv::Mat hsv;
		cv::cvtColor(image, hsv, CV_BGR2HSV);

		// 掩码(可能用到,可能用不到)
		cv::Mat mask;
		//根据需要创建掩码
		if (minSaturation>0) {

			// 将3个通道分割进是3个图像
			std::vector<cv::Mat> v;
			cv::split(hsv, v);

			//屏蔽低饱和度的像素
			cv::threshold(v[1], mask, minSaturation, 255,
				cv::THRESH_BINARY);
		}

		// 准备一维色调直方图的参数
		hranges[0] = 0.0;    //范围为 0 - 180
		hranges[1] = 180.0;
		channels[0] = 0;    //色调通道

		// 计算直方图
		cv::calcHist(&hsv,
			1,			
			channels,	
			mask,		
			hist,		
			1,			
			histSize,	
			ranges		
		);

		return hist;
	}


	// Computes the 2D ab histogram.
	// BGR source image is converted to Lab
	cv::Mat getabHistogram(const cv::Mat &image) {

		cv::Mat hist;

		// Convert to Lab color space
		cv::Mat lab;
		cv::cvtColor(image, lab, CV_BGR2Lab);

		// Prepare arguments for a 2D color histogram
		hranges[0] = 0;
		hranges[1] = 256.0;
		channels[0] = 1; // the two channels used are ab 
		channels[1] = 2;

		// Compute histogram
		cv::calcHist(&lab,
			1,			// histogram of 1 image only
			channels,	// the channel used
			cv::Mat(),	// no mask is used
			hist,		// the resulting histogram
			2,			// it is a 2D histogram
			histSize,	// number of bins
			ranges		// pixel value range
		);

		return hist;
	}


};

#endif // !define COLORHISTOGRAM

Chapter_05 : 用形态学运算变换图像

1.腐蚀、膨胀、开始、闭合等形态学运算 2.MSER算法提取特征区域 3.用分水岭算法实现图像分割

  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

#include "WatershedSegmenter.h"

int main()
{
	////1.腐蚀、膨胀、开始、闭合等形态学运算
	//Mat image = imread("./images/binary.bmp");
	//if (!image.data)
	//	return 0;

	//imshow("image", image);


	//Mat eroded; 
	//erode(image, eroded, Mat());
	//imshow("erode", eroded);

	//Mat dilated; 
	//dilate(image, dilated, Mat());
	//imshow("dilate", dilated);


	//Mat element(7, 7, CV_8U, Scalar(1));

	//erode(image, eroded, element);
	//imshow("Erode-7*7", eroded);


	//erode(image, eroded, Mat(), Point(-1, -1), 3);
	//imshow("erode--3time", eroded);



	//Mat element5(5, 5, CV_8U, Scalar(1));
	//Mat closed;
	//morphologyEx(image, closed, MORPH_CLOSE, element5);
	//imshow("close", closed);


	//Mat opened;
	//morphologyEx(image, opened, MORPH_OPEN, element5);
	//imshow("open", opened);

	//// explicit closing
	//// 1. dilate original image
	//cv::Mat result;
	//cv::dilate(image, result, element5);
	//// 2. in-place erosion of the dilated image
	//cv::erode(result, result, element5);

	//// Display the closed image
	//cv::namedWindow("Closed Image (2)");
	//cv::imshow("Closed Image (2)", result);

	//// Close and Open the image
	//cv::morphologyEx(image, image, cv::MORPH_CLOSE, element5);
	//cv::morphologyEx(image, image, cv::MORPH_OPEN, element5);

	//// Display the close/opened image
	//cv::namedWindow("Closed|Opened Image");
	//cv::imshow("Closed|Opened Image", image);
	//cv::imwrite("binaryGroup.bmp", image);

	//// Read input image
	//image = cv::imread("./images/binary.bmp");

	//// Open and Close the image
	//cv::morphologyEx(image, image, cv::MORPH_OPEN, element5);
	//cv::morphologyEx(image, image, cv::MORPH_CLOSE, element5);

	//// Display the close/opened image
	//cv::namedWindow("Opened|Closed Image");
	//cv::imshow("Opened|Closed Image", image);


	//Mat image2 = imread("./images/boldt.jpg", 0);
	//if (!image2.data)
	//	return 0;
	//imshow("image2", image2);

	//morphologyEx(image2, result, MORPH_GRADIENT, Mat());
	//imshow("Edge_result", result);
	//imshow("Edge_255-result", 255 - result);

	//int threshold(80);
	//cv::threshold(result, result, threshold, 255, THRESH_BINARY);
	//imshow("threshold_result", result);

	
	//Mat image3 = imread("./images/book.jpg", 0);
	//if (!image3.data)
	//	return 0;
	//imshow("Image3", image3);

	//transpose(image3, image3);
	//imshow("transpose", image3);
	//flip(image3, image3, 0);
	//imshow("image3", image3);


	//Mat element7(7, 7, CV_8U, Scalar(1));
	//morphologyEx(image3, result, MORPH_BLACKHAT, element7);
	//imshow("Blackhat", result);

	//threshold = 25;
	//cv::threshold(result, result,
	//	threshold, 255, cv::THRESH_BINARY);

	//imshow("Thresholded Black Top-hat", 255 - result);


	////2.MSER算法提取特征区域
	//Mat image = imread("./images/building.jpg", 0);
	//if (!image.data)
	//	return 0;
	//imshow("image", image);

	////基本的MSER检测器
	//Ptr<MSER> ptrMSER = MSER::create(5,  //局部检测时使用的增量值
	//	200,                             //允许的最小面积
	//	2000);                           //允许的最大面积

	////点集的容器
	//vector<vector<Point> > points;
	////矩形的容器
	//vector<Rect> rects;
	////检测MSER特征 ----检测的结果放在两个区域,第一个是区域的容器,每个区域用组成它的像素点表示,
	////第二个是矩形的容器,每个矩形包围一个区域
	//ptrMSER->detectRegions(image, points, rects);

	//cout << points.size() << "MSERs detected" << endl;

	////创建白色区域
	//Mat output(image.size(), CV_8UC3);
	//output = Scalar(255, 255, 255);
 //   //Opencv随机数生成器
	//RNG rng;
	////针对每个检测到的特征区域, 在彩色区域显示MSER
	////反向排序,先显示较大的MSER
	//for (vector<vector<Point> > ::reverse_iterator it = points.rbegin(); it != points.rend(); ++it)
	//{
	//	//生成随机颜色
	//	Vec3b c(rng.uniform(0, 254), rng.uniform(0, 254), rng.uniform(0, 254));

	//	cout << "MSER size = " << it->size() << endl;

	//	//针对MSER集合中的每个点
	//	for (vector<Point> ::iterator itPts = it->begin(); itPts != it->end(); ++itPts)
	//	{
	//		//不重复MSER的像素
	//		if (output.at<Vec3b>(*itPts)[0] == 255)
	//		{
	//			output.at<Vec3b>(*itPts) = c;
	//		}
	//	}
	//}

	//imshow("MSER point sets", output);
	//imwrite("./images/mser.bmp", output);


	////提取并显示矩形的MSER
	//vector<Rect> ::iterator itr = rects.begin();
	//vector<vector<Point> > ::iterator  itp = points.begin();
	//for (; itr != rects.end(); ++itr, ++itp)
	//{
	//	//检查两者比例
	//	if (static_cast<double> (itp->size()) / itr->area() > 0.6)
	//		rectangle(image, *itr, Scalar(0, 0, 255), 2);
	//}

	////显示结果
	//imshow("Rectangle MSERs", image);

	////提取并显示椭圆形的MSER
	//image = imread("./images/building.jpg", 0);
	//if (!image.data)
	//	return 0;
	//for (vector<vector<Point> > ::iterator it = points.begin(); it != points.end(); ++it)
	//{
	//	//遍历MSER集合中的每一个点
	//	for (vector<Point> ::iterator itPts = it->begin(); itPts != it->end(); ++itPts)
	//	{
	//		//提取封闭的矩形
	//		RotatedRect rr = minAreaRect(*it);
	//		//检查椭圆得长宽比
	//		if (rr.size.height / rr.size.width > 0.2 || rr.size.height / rr.size.width < 1.6)
	//			ellipse(image, rr, Scalar(255), 2);
	//		
	//	}

	//}

	//imshow("MSER ellipses", image);


	//3.用分水岭算法实现图像分割
	Mat image = imread("./images/group.jpg");
	if (!image.data)
		return 0;

	imshow("image", image);

	Mat binary;
	binary = imread("./images/binary.bmp", 0);
	imshow("Binary Image", binary);

	//消除噪声和细小的物体
	Mat fg;
	erode(binary, fg, Mat(), Point(-1, -1), 4);
	imshow("erode", fg);
	//标识不含物体的图像像素
	Mat bg;
	dilate(binary, bg, Mat(), Point(-1, -1), 4);
	threshold(bg, bg, 1, 128, THRESH_BINARY_INV);
	imshow("dilate_threshold", bg);

	//创建标记图像
	Mat markers(binary.size(), CV_8U, Scalar(0));
	markers = fg + bg;
	imshow("Markers", markers);
	//创建分水岭分割类的对象
	WatershedSegmenter segmenter;
	//设置标记图像,然后执行分割过程
	segmenter.setMarkers(markers);
	segmenter.process(image);

	imshow("Segmentation", segmenter.getSegmentation());

	imshow("Watersheds", segmenter.getWatersheds());

	// Open another image
	image = cv::imread("./images/tower.jpg");

	// Identify background pixels
	cv::Mat imageMask(image.size(), CV_8U, cv::Scalar(0));
	cv::rectangle(imageMask, cv::Point(5, 5), cv::Point(image.cols - 5, image.rows - 5), cv::Scalar(255), 3);
	// Identify foreground pixels (in the middle of the image)
	cv::rectangle(imageMask, cv::Point(image.cols / 2 - 10, image.rows / 2 - 10),
		cv::Point(image.cols / 2 + 10, image.rows / 2 + 10), cv::Scalar(1), 10);

	// Set markers and process
	segmenter.setMarkers(imageMask);
	segmenter.process(image);

	// Display the image with markers
	cv::rectangle(image, cv::Point(5, 5), cv::Point(image.cols - 5, image.rows - 5), cv::Scalar(255, 255, 255), 3);
	cv::rectangle(image, cv::Point(image.cols / 2 - 10, image.rows / 2 - 10),
		cv::Point(image.cols / 2 + 10, image.rows / 2 + 10), cv::Scalar(1, 1, 1), 10);
	cv::namedWindow("Image with marker");
	cv::imshow("Image with marker", image);

	// Display watersheds
	cv::namedWindow("Watershed");
	cv::imshow("Watershed", segmenter.getWatersheds());

	waitKey(0);

	return 0;
}



#pragma once
#if !define WATERSHS
#define WATERSHS

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

class WatershedSegmenter {

private:
	Mat markers;

public:

	void setMarkers(const Mat &markerImage)
	{
		//转化为整数型图像
		markerImage.convertTo(markers, CV_32S);
	}

	Mat process(const Mat &image)
	{
		//应用分水岭
		watershed(image, markers);

		return markers;
	}
	//以图像的形式返回结果
	Mat getSegmentation()
	{
		Mat tmp;
		//所有标签值大于255的区域都赋值为255
		markers.convertTo(tmp, CV_8U);

		return tmp;
	}

	//以图像的形式返回分水岭
	Mat getWatersheds()
	{
		Mat tmp;
		markers.convertTo(tmp, CV_8U, 255, 255);

		return tmp;
	}

};

#endif // !define WATERSHS

Chapter_06 : 图像滤波

1.方块滤波、高斯滤波、resize(), pyrUp(), pyrDown() 2.Sobel滤波器 3.Laplacian算子 4.高斯差分

  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
//#include <iostream>
//#include <opencv2/opencv.hpp>
//
//#include "LaplacianZC.h"
//
//using namespace std;
//using namespace cv;
//
//int main()
//{
//	////1.方块滤波、高斯滤波、resize(), pyrUp(), pyrDown()
//	//Mat image = imread("./images/boldt.jpg", 0);
//	//if (!image.data)
//	//{
//	//	cout << "image is error" << endl;
//	//	return 0;
//	//}
//	//imshow("image", image);
//	//  
//	////方块滤波
//	//Mat result;
//	//blur(image, result, Size(5, 5));
//	//imshow("Mean Image", result);
// //   //Blur the image with a mean filter 9*9
//	//blur(image, result, Size(9, 9));
//	//imshow("Mean Image(9 * 9)", result);
//
//	////高斯滤波
//	//GaussianBlur(image, result, Size(5, 5), //滤波尺寸
//	//	1.5);                               //控制高斯曲线形状的参数
//	//imshow("Gaussian Filtered Image", result);
//
//	//// Display the blurred image
//	//cv::namedWindow("Gaussian filtered Image (9x9)");
//	//cv::imshow("Gaussian filtered Image (9x9)", result);
//
//	////Get the gaussian kernel (1.5)
//	//Mat gauss = getGaussianKernel(9, 1.5, CV_32F);
//
//	////Display kernal value 
//	//Mat_<float>::const_iterator it = gauss.begin<float>();
//	//Mat_<float>::const_iterator itend = gauss.end<float>();
//	//cout << "1.5 = [";
//	//for (; it != itend; ++it)
//	//{
//	//	cout << "]" << endl;
//	//}
//
//	////Get the gaussian kernel(0.5)
//	//gauss = getGaussianKernel(9, 0.5, CV_32F);
//	//// Display kernel values
//	//it = gauss.begin<float>();
//	//itend = gauss.end<float>();
//	//std::cout << "0.5 = [";
//	//for (; it != itend; ++it) {
//	//	std::cout << *it << " ";
//	//}
//	//std::cout << "]" << std::endl;
//
//	//// Get the gaussian kernel (2.5)
//	//gauss = cv::getGaussianKernel(9, 2.5, CV_32F);
//
//	//// Display kernel values
//	//it = gauss.begin<float>();
//	//itend = gauss.end<float>();
//	//std::cout << "2.5 = [";
//	//for (; it != itend; ++it) {
//	//	std::cout << *it << " ";
//	//}
//	//std::cout << "]" << std::endl;
//
//	//// Get the gaussian kernel(9 elements)
//	//gauss = cv::getGaussianKernel(9, -1, CV_32F);
//
//	//// Display kernel values
//	//it = gauss.begin<float>();
//	//itend = gauss.end<float>();
//	//std::cout << "9 = [";
//	//for (; it != itend; ++it) {
//	//	std::cout << *it << " ";
//	//}
//	//std::cout << "]" << std::endl;
//
//	//// Get the Deriv kernel (2.5)
//	//cv::Mat kx, ky;
//	//cv::getDerivKernels(kx, ky, 2, 2, 7, true);
//
//	//// Display kernel values
//	//cv::Mat_<float>::const_iterator kit = kx.begin<float>();
//	//cv::Mat_<float>::const_iterator kitend = kx.end<float>();
//	//std::cout << "[";
//	//for (; kit != kitend; ++kit) {
//	//	std::cout << *kit << " ";
//	//}
//	//std::cout << "]" << std::endl;
//
//	//// Read input image with salt&pepper noise
//	//image = cv::imread("./images/salted.bmp", 0);
//	//if (!image.data)
//	//	return 0;
//
//	//// Display the S&P image
//	//cv::namedWindow("S&P Image");
//	//cv::imshow("S&P Image", image);
//
//	//// Blur the image with a mean filter
//	//cv::blur(image, result, cv::Size(5, 5));
//
//	//// Display the blurred image
//	//cv::namedWindow("Mean filtered S&P Image");
//	//cv::imshow("Mean filtered S&P Image", result);
//
//	//// Applying a median filter
//	//cv::medianBlur(image, result, 5);
//
//	//// Display the blurred image
//	//cv::namedWindow("Median filtered Image");
//	//cv::imshow("Median filtered Image", result);
//
//	//// Reduce by 4 the size of the image (the wrong way)
//	////只保留每四个像素中的一个
//	//image = cv::imread("./images/boldt.jpg", 0);
//	//cv::Mat reduced(image.rows / 4, image.cols / 4, CV_8U);
//
//	//for (int i = 0; i<reduced.rows; i++)
//	//	for (int j = 0; j<reduced.cols; j++)
//	//		reduced.at<uchar>(i, j) = image.at<uchar>(i * 4, j * 4);
//
//	//// Display the reduced image
//	//cv::namedWindow("Badly reduced Image");
//	//cv::imshow("Badly reduced Image", reduced);
//
//	//cv::resize(reduced, reduced, cv::Size(), 4, 4, cv::INTER_NEAREST);
//
//	//// Display the (resized) reduced image
//	//cv::namedWindow("Badly reduced");
//	//cv::imshow("Badly reduced", reduced);
//
//	//cv::imwrite("badlyreducedimage.bmp", reduced);
//
//	//// first remove high frequency component
//	//cv::GaussianBlur(image, image, cv::Size(11, 11), 1.75);
//	//// keep only 1 of every 4 pixels
//	//cv::Mat reduced2(image.rows / 4, image.cols / 4, CV_8U);
//	//for (int i = 0; i<reduced2.rows; i++)
//	//	for (int j = 0; j<reduced2.cols; j++)
//	//		reduced2.at<uchar>(i, j) = image.at<uchar>(i * 4, j * 4);
//
//	//// Display the reduced image
//	//cv::namedWindow("Reduced Image, original size");
//	//cv::imshow("Reduced Image, original size", reduced2);
//
//	//cv::imwrite("reducedimage.bmp", reduced2);
//
//	//// resizing with NN
//	//cv::Mat newImage;
//	//cv::resize(reduced2, newImage, cv::Size(), 4, 4, cv::INTER_NEAREST);
//
//	//// Display the (resized) reduced image
//	//cv::namedWindow("Reduced Image");
//	//cv::imshow("Reduced Image", newImage);
//
//	//// resizing with bilinear
//	//cv::resize(reduced2, newImage, cv::Size(), 4, 4, cv::INTER_LINEAR);
//
//	//// Display the (resized) reduced image
//	//cv::namedWindow("Bilinear resizing");
//	//cv::imshow("Bilinear resizing", newImage);
//
//	//// Creating an image pyramid
//	//cv::Mat pyramid(image.rows, image.cols + image.cols / 2 + image.cols / 4 + image.cols / 8, CV_8U, cv::Scalar(255));
//	//image.copyTo(pyramid(cv::Rect(0, 0, image.cols, image.rows)));
//
//	//cv::pyrDown(image, reduced); // reduce image size by half
//	//reduced.copyTo(pyramid(cv::Rect(image.cols, image.rows / 2, image.cols / 2, image.rows / 2)));
//	//cv::pyrDown(reduced, reduced2); // reduce image size by another half
//	//reduced2.copyTo(pyramid(cv::Rect(image.cols + image.cols / 2, image.rows - image.rows / 4, image.cols / 4, image.rows / 4)));
//	//cv::pyrDown(reduced2, reduced); // reduce image size by another half
//	//reduced.copyTo(pyramid(cv::Rect(image.cols + image.cols / 2 + image.cols / 4, image.rows - image.rows / 8, image.cols / 8, image.rows / 8)));
//
//	//// Display the pyramid
//	//cv::namedWindow("Pyramid of images");
//	//cv::imshow("Pyramid of images", pyramid);
//
//
//	//2.Sobel滤波器
//    Mat image = imread("./images/boldt.jpg", 0);
//	if (!image.data)
//		return 0;
//	imshow("Image", image);
//
//	//计算Sobel滤波器的X方向
//	Mat sobelX;
//	Sobel(image,    //输入 
//		sobelX,     // 输出
//		CV_8U,      //图像类型
//		1, 0,       //内核规格
//		3,          //正方形内核的尺寸
//		0.4, 128);  // 比例和偏移量
//
//	imshow("Sobel X Image", sobelX);
//
//	//计算Sobel滤波器的Y方向
//	Mat sobelY;
//	Sobel(image,    //输入 
//		sobelY,     // 输出
//		CV_8U,      //图像类型
//		0, 1,       //内核规格
//		3,          //正方形内核的尺寸
//		0.4, 128);  // 比例和偏移量
//
//	imshow("Sobel Y Image", sobelY);
//
//	//组合这两个结果   计算Sobel滤波器的范数
//	Sobel(image, sobelX, CV_16S, 1, 0);
//	Sobel(image, sobelY, CV_16S, 0, 1);
//
//	Mat sobel;
//	//计算L1范数
//	sobel = abs(sobelX) + abs(sobelY);
//	//找到Sobel最大值
//	double sobmin, sobmax;
//	minMaxLoc(sobel, &sobmin, &sobmax);
//	cout << "sobel value range:" << sobmin << "  " << sobmax << endl;
//
//	// Compute Sobel X derivative (7x7)
//	cv::Sobel(image, sobelX, CV_8U, 1, 0, 7, 0.001, 128);
//
//	// Display the image
//	cv::namedWindow("Sobel X Image (7x7)");
//	cv::imshow("Sobel X Image (7x7)", sobelX);
//
//	//Print window pixel values
//	for (int i = 0; i < 12; i++)
//	{
//		for (int j = 0; j < 12; j++)
//		{
//			cout << setw(5) << static_cast<int>(sobel.at<short>(i + 79, j + 215)) << " ";
//		}
//		cout << endl;
//
//	}
//
//	cout << endl;
//	cout << endl;
//	cout << endl;
//
//	//转换成8位图像
//	Mat sobelImage;
//	sobel.convertTo(sobelImage, CV_8U, -255./sobmax, 255);
//
//	imshow("sobelImage", sobelImage);
//	//进行阈值化
//	Mat sobelThresholded;
//	threshold(sobelImage, sobelThresholded, 225, 255, THRESH_BINARY);
//	imshow("sobelThresholded", sobelThresholded);
//
//
//
//	//3.Laplacian算子
//	//默认的拉普拉斯算子的核的值的大小是 3 * 3
//	Mat laplace;
//	Laplacian(image, laplace, CV_8U, 1, 1, 128);
//
//	imshow("Laplacian Image", laplace);
//
//	int cx(238), cy(90);
//	int dx(12), dy(12);
//
//	//一个小的窗口
//	Mat window(image, Rect(cx, cy, dx, dy));
//	imshow("window", window);
//	imwrite("./images/window.bmp", window);
//
//	//用LaplacianZC类计算拉普拉斯
//	LaplactionZC laplacian;
//	laplacian.setAperture(7);
//	Mat flap = laplacian.computeLaplacian(image);
//
//	// display min max values of the lapalcian
//	double lapmin, lapmax;
//	cv::minMaxLoc(flap, &lapmin, &lapmax);
//	// display laplacian image
//	laplace = laplacian.getLaplacianImage();
//	cv::namedWindow("Laplacian Image (7x7)");
//	cv::imshow("Laplacian Image (7x7)", laplace);
//
//	// Print image values
//	std::cout << std::endl;
//	std::cout << "Image values:\n\n";
//	for (int i = 0; i<dx; i++) {
//		for (int j = 0; j<dy; j++)
//			std::cout << std::setw(5) << static_cast<int>(image.at<uchar>(i + cy, j + cx)) << " ";
//		std::cout << std::endl;
//	}
//	std::cout << std::endl;
//
//	// Print Laplacian values
//	std::cout << "Laplacian value range=[" << lapmin << "," << lapmax << "]\n";
//	std::cout << std::endl;
//	for (int i = 0; i<dx; i++) {
//		for (int j = 0; j<dy; j++)
//			std::cout << std::setw(5) << static_cast<int>(flap.at<float>(i + cy, j + cx) / 100) << " ";
//		std::cout << std::endl;
//	}
//	std::cout << std::endl;
//
//	// Compute and display the zero-crossing points
//	cv::Mat zeros;
//	zeros = laplacian.getZeroCrossings(flap);
//	cv::namedWindow("Zero-crossings");
//	cv::imshow("Zero-crossings", 255 - zeros);
//
//	// Print window pixel values
//	std::cout << "Zero values:\n\n";
//	for (int i = 0; i<dx; i++) {
//		for (int j = 0; j<dy; j++)
//			std::cout << std::setw(2) << static_cast<int>(zeros.at<uchar>(i + cy, j + cx)) / 255 << " ";
//		std::cout << std::endl;
//	}
//
//	// down-sample and up-sample the image
//	cv::Mat reduced, rescaled;
//	cv::pyrDown(image, reduced);
//	cv::pyrUp(reduced, rescaled);
//
//	// Display the rescaled image
//	cv::namedWindow("Rescaled Image");
//	cv::imshow("Rescaled Image", rescaled);
//
//
//	//4.高斯差分(DoG)
//	cv::Mat dog;
//	//计算高斯差分
//	cv::subtract(rescaled, image, dog, cv::Mat(), CV_16S);
//	cv::Mat dogImage;
//	dog.convertTo(dogImage, CV_8U, 1.0, 128);
//
//	// Display the DoG image
//	cv::namedWindow("DoG Image (from pyrdown/pyrup)");
//	cv::imshow("DoG Image (from pyrdown/pyrup)", dogImage);
//
//	// Apply two Gaussian filters
//	cv::Mat gauss05;
//	cv::Mat gauss15;
//	cv::GaussianBlur(image, gauss05, cv::Size(), 0.5);
//	cv::GaussianBlur(image, gauss15, cv::Size(), 1.5);
//
//	// compute a difference of Gaussians 
//	cv::subtract(gauss15, gauss05, dog, cv::Mat(), CV_16S);
//	dog.convertTo(dogImage, CV_8U, 2.0, 128);
//
//	// Display the DoG image
//	cv::namedWindow("DoG Image");
//	cv::imshow("DoG Image", dogImage);
//
//	// Apply two Gaussian filters
//	cv::Mat gauss20;
//	cv::GaussianBlur(image, gauss20, cv::Size(), 2.0);
//	cv::Mat gauss22;
//	cv::GaussianBlur(image, gauss22, cv::Size(), 2.2);
//
//	// compute a difference of Gaussians 
//	cv::subtract(gauss22, gauss20, dog, cv::Mat(), CV_32F);
//	dog.convertTo(dogImage, CV_8U, 10.0, 128);
//
//	// Display the DoG image
//	cv::namedWindow("DoG Image (2)");
//	cv::imshow("DoG Image (2)", dogImage);
//
//
//	// 计算高斯差分的过零点
//	zeros = laplacian.getZeroCrossings(dog);
//	cv::namedWindow("Zero-crossings of DoG");
//	cv::imshow("Zero-crossings of DoG", 255 - zeros);
//
//	// Display the image with window
//	cv::rectangle(image, cv::Rect(cx, cy, dx, dy), cv::Scalar(255, 255, 255));
//	cv::namedWindow("Original Image with window");
//	cv::imshow("Original Image with window", image);
//
//
//	waitKey(0);
//	return 0;
//}

Chapter_07 : 提取直线、轮廓和区域

1.Sobel算子的部分 Canny边缘提取 2.用霍夫变换检测直线 3.用概率霍夫变换检测直线 4.点集的直线拟合 5.提取连续区域

  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
//#include <iostream>
//#include <opencv2/opencv.hpp>
//#include <vector>
//
//#include "EdgeDetector.h"
//#include "LineFinder.h"
//
//using namespace std;
//using namespace cv;
//
//int main()
//{
//	//Mat image = imread("./images/road.jpg", 0);
//	//if (!image.data)
//	//	return 0;
//	//imshow("Image", image);
//
//	////计算Sobel
//	//EdgeDetector ed;
//	//ed.computeSobel(image);
//
//	////展示Sobel
//	//imshow("Sobel (orientation)", ed.getSobelOrientationImage());
//	//imwrite("./images/ori.bmp", ed.getSobelOrientationImage());
//	//// Display the Sobel low threshold
//	//cv::namedWindow("Sobel (low threshold)");
//	//cv::imshow("Sobel (low threshold)", ed.getBinaryMap(125));
//
//	//// Display the Sobel high threshold
//	//cv::namedWindow("Sobel (high threshold)");
//	//cv::imshow("Sobel (high threshold)", ed.getBinaryMap(350));
//
//	////运用Canny边缘算子
//	//Mat contours;
//	//Canny(image,             //灰度图像
//	//	contours,            //输出轮廓
//	//	125,                 //低阈值
//	//	350);                //高阈值
//
//	//imshow("Canny", 255 - contours);
//
//	////创建一个测试的图像
//	//Mat test(200, 200, CV_8U, Scalar(0));
//	//cv::line(test, cv::Point(100, 0), cv::Point(200, 200), cv::Scalar(255));
//	//cv::line(test, cv::Point(0, 50), cv::Point(200, 200), cv::Scalar(255));
//	//cv::line(test, cv::Point(0, 200), cv::Point(200, 0), cv::Scalar(255));
//	//cv::line(test, cv::Point(200, 0), cv::Point(0, 200), cv::Scalar(255));
//	//cv::line(test, cv::Point(100, 0), cv::Point(100, 200), cv::Scalar(255));
//	//cv::line(test, cv::Point(0, 100), cv::Point(200, 100), cv::Scalar(255));
//
//	//// Display the test image
//	//cv::namedWindow("Test Image");
//	//cv::imshow("Test Image", test);
//	//cv::imwrite("test.bmp", test);
//
//	////2.用霍夫变换检测直线
//	//vector<Vec2f> lines;
//	//HoughLines(contours, lines, 1,
//	//	PI/180,          //步长
//	//    60);             //最小投票数
//	////画线
//	//Mat result(contours.rows, contours.cols, CV_8U, Scalar(255));
//	//image.copyTo(result);
//
//	//cout << "Lines detected" << lines.size() << endl;
//
//	//vector<Vec2f>::const_iterator it = lines.begin();
//	//while (it != lines.end())
//	//{
//	//	float rho = (*it)[0];     //第一个元素是距离rho
//	//	float theta = (*it)[1];   //第二个元素是角度theta
//
//	//	if (theta < PI/4. || theta > 3.*PI / 4) //垂直线(大致)
//	//	{
//	//		//直线与第一行的交叉点
//	//		Point pt1(rho/cos(theta), 0);
//	//		//直线与最后一行的交叉点
//	//		Point pt2((rho - result.rows * sin(theta)) / cos(theta), result.rows);
//	//		//画白色的线
//	//		line(result, pt1, pt2, Scalar(255), 1);
//
//	//	}
//	//	else //水平线(大致)
//	//	{
//	//		//直线与第一列的交叉点
//	//		Point pt1(0, rho/sin(theta));
//	//		//直线与最后一列的交叉点
//	//		Point pt2(result.cols, (rho - result.cols * cos(theta)) / sin(theta));
//	//		//画白色的线
//	//		line(result, pt1, pt2, Scalar(255), 1);
//	//	}
//	//	cout << "line: (" << rho << "," << theta << ")\n";
//
//	//	++it;
//	//}
//	//imshow("HoughLines", result);
//	//
//	////3.用概率霍夫变换检测直线
//	////创建LineFinder类的实例
//	//LineFinder finder;
//	////设置概率霍夫变换的参数
//	//finder.setLineLengthAndGap(100, 20);
//	//finder.setMinVote(60);
//
//	////检测直线并画直线
//	//vector<Vec4i> linesp = finder.findLines(contours);
//	//finder.drawDetectedLines(image);
//
//	//std::vector<cv::Vec4i>::const_iterator it2 = linesp.begin();
//	//while (it2 != linesp.end()) {
//
//	//	std::cout << "(" << (*it2)[0] << "," << (*it2)[1] << ")-("
//	//		<< (*it2)[2] << "," << (*it2)[3] << ")" << std::endl;
//
//	//	++it2;
//	//}
//
//	//imshow("HoughLinesp", image);
//
//	////4.点集的直线拟合
//	//image = imread("./images/road.jpg", 0);
//
//	//int n = 0;  //选用直线0
//	//line(image, Point(linesp[n][0], linesp[n][1]), Point(linesp[n][2], linesp[n][3]),Scalar(255), 5);
//	//imshow("One line of the Image", image);
//	////黑白图像
//	//Mat oneline(image.size(), CV_8U, Scalar(0));
//	////白色直线
//	//line(oneline, Point(linesp[n][0], linesp[n][1]), Point(linesp[n][2], linesp[n][3]), Scalar(255), 3);
//	////轮廓与白色直线进行与&操作
//	//bitwise_and(contours, oneline, oneline);
//	//imshow("One line", 255 - oneline);
//
//	//vector<Point> points;
//	////迭代遍历像素,得到所有点的位置
//	//for (int y = 0; y < oneline.rows; y++)
//	//{
//	//	//行y
//	//	uchar *rowPtr = oneline.ptr<uchar>(y);
//	//	for (int x = 0; x < oneline.cols; x++)
//	//	{
//	//		//列x
//
//	//		//如果在轮廓上
//	//		if (rowPtr[x])
//	//		{
//	//			points.push_back(Point(x, y));
//	//		}
//	//	}
//	//}
//
//	////得到点集后, 利用这些点集拟合出直线, fitLine可以轻松的得到最优的拟合直线
//	//Vec4f line;
//	//fitLine(points, line,
//	//	DIST_L2,    //距离类型
//	//	0,          //L2距离不用这个参数
//	//	0.01, 0.01);//精度
//
//	//cout << "line: (" << line[0] << "," << line[1] << ")(" << line[2] << "," << line[3] << ")\n";
//
//	//int x0 = line[2];      //直线上一个点
//	//int y0 = line[3];      
//	//int x1 = x0 + 100 * line[0];  //加上长度为100的向量
//	//int y1 = y0 + 100 * line[1];  //(用单位向量生成)
//	//image = imread("./images/road.jpg", 0);
//	////绘制这条线
//	//cv::line(image, Point(x0, y0), Point(x1, y1), Scalar(0), 0.2); //颜色和宽度
//	//imshow("Fitted line", image);
//
//	//// eliminate inconsistent lines
//	//finder.removeLinesOfInconsistentOrientations(ed.getOrientation(), 0.4, 0.1);
//
//	//// Display the detected line image
//	//image = cv::imread("./images/road.jpg", 0);
//
//	//finder.drawDetectedLines(image);
//	//cv::namedWindow("Detected Lines (2)");
//	//cv::imshow("Detected Lines (2)", image);
//
//
//	////创建霍夫累加器
//	////这里的用的图像类型是uchar,实际类型应该是int
//	//cv::Mat acc(200, 180, CV_8U, cv::Scalar(0));
//
//	////选取一个像素点
//	//int x = 50, y = 30;
//
//	////循环遍历所有角度
//	//for (int i = 0; i<180; i++) {
//
//	//	double theta = i*PI / 180.;
//
//	//	//找到对应的rho值
//	//	double rho = x*std::cos(theta) + y*std::sin(theta);
//	//	//j对应-100到100的rho
//	//	int j = static_cast<int>(rho + 100.5);
//
//	//	std::cout << i << "," << j << std::endl;
//
//	//	//增加累加器
//	//	acc.at<uchar>(j, i)++;
//	//}
//
//	//// draw the axes
//	//cv::line(acc, cv::Point(0, 0), cv::Point(0, acc.rows - 1), 255);
//	//cv::line(acc, cv::Point(acc.cols - 1, acc.rows - 1), cv::Point(0, acc.rows - 1), 255);
//
//	//cv::imwrite("./images/hough1.bmp", 255 - (acc * 100));
//
//	//// Choose a second point
//	//x = 30, y = 10;
//
//	//// loop over all angles
//	//for (int i = 0; i<180; i++) {
//
//	//	double theta = i*PI / 180.;
//	//	double rho = x*cos(theta) + y*sin(theta);
//	//	int j = static_cast<int>(rho + 100.5);
//
//	//	acc.at<uchar>(j, i)++;
//	//}
//
//	//cv::namedWindow("Hough Accumulator");
//	//cv::imshow("Hough Accumulator", acc * 100);
//	//cv::imwrite("./images/hough2.bmp", 255 - (acc * 100));
//
//	////检测圆
//	//image = cv::imread("./images/chariot.jpg", 0);
//
//	//cv::GaussianBlur(image, image, cv::Size(5, 5), 1.5);
//	//std::vector<cv::Vec3f> circles;
//	//cv::HoughCircles(image, circles, cv::HOUGH_GRADIENT,
//	//	2,   // 累加器分辨率(图像尺寸 / 2) 
//	//	20,  // 两个圆之间的最小距离
//	//	200, // Canny算子的高阈值
//	//	60, // 最小投票数
//	//	15, 50); // 最小和最大半径
//
//	//std::cout << "Circles: " << circles.size() << std::endl;
//
//	//// Draw the circles
//	//image = cv::imread("./images/chariot.jpg", 0);
//
//	//std::vector<cv::Vec3f>::const_iterator itc = circles.begin();
//
//	//while (itc != circles.end()) {
//
//	//	cv::circle(image,
//	//		cv::Point((*itc)[0], (*itc)[1]), // 圆心
//	//		(*itc)[2], //半径
//	//		cv::Scalar(255), // 颜色
//	//		2); // 厚度
//
//	//	++itc;
//	//}
//
//	//cv::namedWindow("Detected Circles");
//	//cv::imshow("Detected Circles", image);
//
//	//5.提取连续区域
//    Mat image = imread("./images/binaryGroup.bmp", 0);
//	if (!image.data)
//		return 0;
//	imshow("Binary Image", image);
//	//用于存储轮廓的向量
//	vector<vector<Point> > contours;
//	findContours(image,
//		contours,           //存储轮廓的向量
//		RETR_EXTERNAL,      //检查外部轮廓
//		CHAIN_APPROX_NONE); //每个轮廓的全部像素
//
//	//查看轮廓得数量
//	cout << "Contours: " << contours.size() << endl;
//	vector<vector<Point> >::const_iterator itContours = contours.begin();
//	for (; itContours != contours.end(); ++itContours)
//	{
//		cout << "Size: " << itContours->size() << endl;
//	}
//	//在白色图像上画黑色轮廓
//	Mat result(image.size(), CV_8U, Scalar(255));
//	drawContours(result, contours,
//		-1,  //画出全部轮廓
//		0,   //用黑色画
//		2    //厚度为2
//	);
//	imshow("Contours", result);
//	//删除太短或者太长的轮廓
//	int cmin = 50;
//	int cmax = 500;
//	vector< vector<Point> > ::iterator itc = contours.begin();
//	while (itc != contours.end())
//	{
//		if (itc->size() < cmin || itc->size() > cmax)
//			itc = contours.erase(itc);
//		else
//			++itc;
//
//	}
//
//	// draw contours on the original image
//	cv::Mat original = cv::imread("./images/group.jpg");
//
//	cv::drawContours(original, contours,
//		-1, // draw all contours
//		cv::Scalar(255, 255, 255), // in white
//		2); // with a thickness of 2
//
//	cv::namedWindow("Contours on Animals");
//	cv::imshow("Contours on Animals", original);
//
//	result.setTo(Scalar(255));
//	drawContours(result, contours, -1, 0, 1);
//
//	image = imread("./images/binaryGroup.bmp", 0);
//
//	//需要思考的是这个二维的矩阵,行代表什么,列代表什么?
//	//首先说,行:代表了一个轮廓,每行的数据是一个数组,数组里面是一系列的点
//	//这些点代表了这个轮廓的位置
//	//所以,contours[0],代表了第一个轮廓,contours[0][0],代表了第一个轮廓的某个点
//
//	//A.测试边界框
//	Rect r0 = boundingRect(contours[0]);
//	//画矩形
//	rectangle(result, r0, 0, 2);
//	//B.测试覆盖圆
//	float radius;
//	Point2f center;
//	minEnclosingCircle(contours[1], center, radius);
//	//画圆型
//	circle(result, center, static_cast<int>(radius), 0, 2);
//	//C.测试多边形逼近
//	vector<Point> poly;
//	approxPolyDP(contours[2], poly, 5, true);
//	//画多边形
//	polylines(result, poly, true, 0, 2);
//
//	cout << "Polygon size: " << poly.size() << endl;
//	//D.测试凸包(另一种形式的多边形逼近)
//	std::vector<cv::Point> hull;
//	cv::convexHull(contours[3], hull);
//	//画多边形
//	cv::polylines(result, hull, true, 0, 2);
//	//检测轮廓矩
//	//迭代遍历所有轮廓
//	//在所有区域内部画出重心
//	itc = contours.begin();
//	while (itc != contours.end()) {
//
//		//计算所有矩形轮廓
//		cv::Moments mom = cv::moments(*itc++);
//
//		//画重心
//		cv::circle(result,
//			// 将重心位置转换成整数
//			cv::Point(mom.m10 / mom.m00, mom.m01 / mom.m00),
//			2, cv::Scalar(0), 2); // 画黑点
//	}
//
//	cv::namedWindow("Some Shape descriptors");
//	cv::imshow("Some Shape descriptors", result);
//
//	// 打开一副新的图像
//	image = cv::imread("./images/binaryGroup.bmp", 0);
//
//
//	cv::findContours(image,
//		contours, 
//		cv::RETR_LIST, 
//		cv::CHAIN_APPROX_NONE); 
//
//	result.setTo(255);
//	cv::drawContours(result, contours,
//		-1, 
//		0,  
//		2); 
//	cv::namedWindow("All Contours");
//	cv::imshow("All Contours", result);
//
//	// get a MSER image
//	cv::Mat components;
//	components = cv::imread("./images/mser.bmp", 0);
//
//	// create a binary version
//	components = components == 255;
//	// open the image (white background)
//	cv::morphologyEx(components, components, cv::MORPH_OPEN, cv::Mat(), cv::Point(-1, -1), 3);
//
//	cv::namedWindow("MSER image");
//	cv::imshow("MSER image", components);
//
//	contours.clear();
//	//翻转图像 (background must be black)
//	cv::Mat componentsInv = 255 - components;
//	//得到连续区域的轮廓
//	cv::findContours(componentsInv,
//		contours, 
//		cv::RETR_EXTERNAL, //检索外部向量
//		cv::CHAIN_APPROX_NONE); 
//
//	// white image
//	cv::Mat quadri(components.size(), CV_8U, 255);
//
//	//针对全部轮廓
//	std::vector<std::vector<cv::Point> >::iterator it = contours.begin();
//	while (it != contours.end()) {
//		poly.clear();
//		// 多边形逼近轮廓
//		cv::approxPolyDP(*it, poly, 5, true);
//
//		// 是否为四边形
//		if (poly.size() == 4) {
//			// 画出来
//			cv::polylines(quadri, poly, true, 0, 2);
//		}
//
//		++it;
//	}
//
//	cv::namedWindow("MSER quadrilateral");
//	cv::imshow("MSER quadrilateral", quadri);
//
//
//	waitKey(0);
//	return 0;
//}




#pragma once
#if !defineed SOBELEDGES
#define SOBELEDGES

#define PI 3.1415926

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

class EdgeDetector {
private:

	// original image
	cv::Mat img;

	// 16-bit signed int image
	cv::Mat sobel;

	// Aperture size of the Sobel kernel
	int aperture;

	// Sobel magnitude
	cv::Mat sobelMagnitude;

	// Sobel orientation
	cv::Mat sobelOrientation;

public:

	EdgeDetector() : aperture(3) {}

	// Set the aperture size of the kernel
	void setAperture(int a) {

		aperture = a;
	}

	// Get the aperture size of the kernel
	int getAperture() const {

		return aperture;
	}

	// Compute the Sobel
	void computeSobel(const cv::Mat& image) {

		cv::Mat sobelX;
		cv::Mat sobelY;

		// Compute Sobel
		cv::Sobel(image, sobelX, CV_32F, 1, 0, aperture);
		cv::Sobel(image, sobelY, CV_32F, 0, 1, aperture);

		// 将笛卡尔坐标系转化为极坐标,得到幅值和角度
		cv::cartToPolar(sobelX, sobelY, sobelMagnitude, sobelOrientation);
	}

	// Compute the Sobel
	void computeSobel(const cv::Mat& image, cv::Mat &sobelX, cv::Mat &sobelY) {

		// Compute Sobel
		cv::Sobel(image, sobelX, CV_32F, 1, 0, aperture);
		cv::Sobel(image, sobelY, CV_32F, 0, 1, aperture);

		// Compute magnitude and orientation
		cv::cartToPolar(sobelX, sobelY, sobelMagnitude, sobelOrientation);
	}

	// Get Sobel magnitude
	cv::Mat getMagnitude() {

		return sobelMagnitude;
	}

	// Get Sobel orientation
	cv::Mat getOrientation() {

		return sobelOrientation;
	}

	// Get a thresholded binary map
	cv::Mat getBinaryMap(double threshold) {

		cv::Mat bin;
		cv::threshold(sobelMagnitude, bin, threshold, 255, cv::THRESH_BINARY_INV);

		return bin;
	}

	// Get a CV_8U image of the Sobel
	cv::Mat getSobelImage() {

		cv::Mat bin;

		double minval, maxval;
		cv::minMaxLoc(sobelMagnitude, &minval, &maxval);
		sobelMagnitude.convertTo(bin, CV_8U, 255 / maxval);

		return bin;
	}

	// Get a CV_8U image of the Sobel orientation
	// 1 gray-level = 2 degrees
	cv::Mat getSobelOrientationImage() {

		cv::Mat bin;

		sobelOrientation.convertTo(bin, CV_8U, 90 / PI);

		return bin;
	}

};

#endif // !defineed SOBELEDGES





#pragma once
#if !define LINEF
#define LINEF

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;
#define PI 3.1415926

class LineFinder
{
private:
	//原始图像
	Mat img;
	//包含被检测直线的端点的向量
	vector<Vec4i> lines;
	//累加器分辨率参数
	double deltaRho;
	double deltaTheta;
	//确认直线之前必须收到的最小投票数
	int minVote;
	//直线的最小长度
	double minLength;
	//直线上允许的最大空隙
	double maxGap;

public:
	//默认累加器分辨率是1像素,1度
	//没有空隙,没有最小长度
	LineFinder() :deltaRho(1), deltaTheta(PI / 180), minVote(10), minLength(0.), maxGap(0.) {}

	//设置累加器的分辨率
	void setAccResolution(double dRho, double dTheta)
	{
		deltaRho = dRho;
		deltaTheta = dTheta;
	}
	//设置最小投票数
	void setMinVote(int minv) {

		minVote = minv;
	}

	//设置直线长度和空隙
	void setLineLengthAndGap(double length, double gap) {

		minLength = length;
		maxGap = gap;
	}
	//应用概率霍夫变换
	vector<Vec4i> findLines(Mat &binary)
	{
		lines.clear();
		HoughLinesP(binary, lines, deltaRho, deltaTheta, minVote, minLength, maxGap);

		return lines;
	}

	//在图像上绘制出检测出来的直线
	void drawDetectedLines(Mat &image, Scalar color = Scalar(255, 255, 255))
	{
		//画直线
		vector<Vec4i>::const_iterator it2 = lines.begin();

		while (it2 != lines.end())
		{
			Point pt1((*it2)[0], (*it2)[1]);
			Point pt2((*it2)[2], (*it2)[3]);

			line(image, pt1, pt2, color);

			++it2;
		}

	}

	// Eliminates lines that do not have an orientation equals to
	// the ones specified in the input matrix of orientations
	// At least the given percentage of pixels on the line must 
	// be within plus or minus delta of the corresponding orientation
	std::vector<cv::Vec4i> removeLinesOfInconsistentOrientations(
		const cv::Mat &orientations, double percentage, double delta) {

		std::vector<cv::Vec4i>::iterator it = lines.begin();

		// check all lines
		while (it != lines.end()) {

			// end points
			int x1 = (*it)[0];
			int y1 = (*it)[1];
			int x2 = (*it)[2];
			int y2 = (*it)[3];

			// line orientation + 90o to get the parallel line
			double ori1 = atan2(static_cast<double>(y1 - y2), static_cast<double>(x1 - x2)) + PI / 2;
			if (ori1>PI) ori1 = ori1 - 2 * PI;

			double ori2 = atan2(static_cast<double>(y2 - y1), static_cast<double>(x2 - x1)) + PI / 2;
			if (ori2>PI) ori2 = ori2 - 2 * PI;

			// for all points on the line
			cv::LineIterator lit(orientations, cv::Point(x1, y1), cv::Point(x2, y2));
			int i, count = 0;
			for (i = 0, count = 0; i < lit.count; i++, ++lit) {

				float ori = *(reinterpret_cast<float *>(*lit));

				// is line orientation similar to gradient orientation ?
				if (std::min(fabs(ori - ori1), fabs(ori - ori2))<delta)
					count++;

			}

			double consistency = count / static_cast<double>(i);

			// set to zero lines of inconsistent orientation
			if (consistency < percentage) {

				(*it)[0] = (*it)[1] = (*it)[2] = (*it)[3] = 0;

			}

			++it;
		}

		return lines;
	}

};


#endif // !define LINEF

Chapter_08 : 检测兴趣点

1.Harris角点检测 2.GFTT(good-features-to-track) 3.drawKeypoints() 换关键点的通用函数 4.FAST角点检测 5.SURF尺度不变的特征检测 6.SIFT尺度不变特征转换 7.BRISK(二元稳健恒定可扩展关键点)检测法 8.ORB特征检测算法

  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

//#include <iostream>
//#include <opencv2/opencv.hpp>
//#include <opencv2/core.hpp>
//#include <opencv2/features2d.hpp>
//#include <opencv2/imgproc.hpp>
//#include <opencv2/highgui.hpp>
//#include <opencv2/xfeatures2d.hpp>
//
//#include "HarrisDetector.h"
//
//using namespace std;
//using namespace cv;
//
//int main()
//{
//	//1.Harris角点检测
//	Mat image = imread("./images/church01.jpg", 0);
//	if (!image.data)
//		return 0;
//	//变成水平的
//	transpose(image, image);
//	flip(image, image, 0);
//
//	imshow("Image", image);
//
//	//检测Harris角点
//	Mat cornerStrength;
//	cornerHarris(image, cornerStrength, 
//		         3,     //领域尺寸
//		         3,     //口径尺寸
//		         0.01); //Harris参数
//	//对角点强度进行阈值化
//	Mat harrisCorners;
//	double threshold = 0.0001;
//	cv::threshold(cornerStrength, harrisCorners, threshold, 255, THRESH_BINARY_INV);
//	imshow("Harris_threshold", harrisCorners);
//
//	//用一个类去封装Harris
//	HarrisDetected harris;
//	//计算Harris值
//	harris.detect(image);
//	//检测Harris角点
//	vector<Point> pts;
//	harris.getCorners(pts, 0.02);
//	//画出Harris角点
//	harris.drawOnImage(image, pts);
//
//	imshow("HarrisDetected", image);
//
//	//2.GFTT(good-features-to-track)
//	image = imread("./images/church01.jpg", 0);
//	// rotate the image (to produce a horizontal image)
//	cv::transpose(image, image);
//	cv::flip(image, image, 0);
//	//计算适合跟踪的特征
//	vector<KeyPoint> keypoints;
//	//GFTT检测器
//	Ptr<GFTTDetector> ptrGFTT = GFTTDetector::create(
//		500,   //关键点的最大值
//		0.01,  //质量等级
//		10     //角点之间允许的最短距离
//	);
//	//检测GFTT
//	ptrGFTT->detect(image, keypoints);
//	//展示所有关键点
//	vector<KeyPoint>::const_iterator it = keypoints.begin();
//	while (it != keypoints.end())
//	{
//		//对每个关键点画圆
//		circle(image, it->pt, 3, Scalar(255, 255, 255), 1);
//		++it;
//	}
//
//	imshow("GFTT", image);
//	//3.drawKeypoints() 换关键点的通用函数
//	// Read input image
//	image = cv::imread("./images/church01.jpg", 0);
//	// rotate the image (to produce a horizontal image)
//	cv::transpose(image, image);
//	cv::flip(image, image, 0);
//
//	// Opencv也提供了在图像上画关键点的通用函数
//	cv::drawKeypoints(image,		// 原始图像
//		keypoints,					// 关键点的向量
//		image,						// 输出图像
//		cv::Scalar(255, 255, 255),	// 关键点颜色
//		cv::DrawMatchesFlags::DRAW_OVER_OUTIMG); //画图标志
//
//	// Display the keypoints
//	cv::namedWindow("Good Features to Track Detector");
//	cv::imshow("Good Features to Track Detector", image);
//
//	//4.FAST角点检测
//	image = imread("./images/church01.jpg", 0);
//	transpose(image, image);
//	flip(image, image, 0);
//	//最终的关键点容器
//	keypoints.clear();
//	//FAST特征检测器,阈值为40
//	Ptr<FastFeatureDetector> ptrFAST = FastFeatureDetector::create(40);
//	//检测关键点
//	ptrFAST->detect(image, keypoints);
//	//画关键点
//	drawKeypoints(image, keypoints, image, Scalar(255, 255,255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
//	cout << "Number of keypoints(FAST): " << keypoints.size() << endl;
//
//	imshow("FAST_01", image);
//	//FAST_02----非极大抑制
//	image = imread("./images/church01.jpg", 0);
//	transpose(image, image);
//	flip(image, image, 0);
//
//	keypoints.clear();
//	//检测关键点
//	ptrFAST->setNonmaxSuppression(false);
//	ptrFAST->detect(image, keypoints);
//	//画出关键点
//	drawKeypoints(image, keypoints, image, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_OVER_OUTIMG);
//	imshow("FAST FEATURES(ALL)", image);
//	//FAST_03----Grid
//	image = imread("./images/church01.jpg", 0);
//	transpose(image, image);
//	flip(image, image, 0);
//
//	int total(100);
//	int hstep(5), vstep(3);
//	int hsize(image.cols / hstep), vsize(image.rows / vstep);
//	int subtotal(total / (hstep * vstep));
//
//	Mat imageROI;
//	vector<KeyPoint> gridpoints;
//	cout << "Grid of" << vstep << "by" << hstep << "each of size" << vsize << "by" << hsize << endl;
//
//	//检测低阈值
//	ptrFAST->setThreshold(20);
//	//非极大抑制
//	ptrFAST->setNonmaxSuppression(true);
//	//最终的关键点容器
//	keypoints.clear();
//	//检测每一个网格
//	for (int i = 0; i < vstep; i++)
//	{
//		for (int j = 0; j < hstep; j++)
//		{
//			//在当前网格创建ROI
//			imageROI = image(Rect(j * hsize, i * vsize, hsize, vsize));
//			//在网格中检测关键点
//			gridpoints.clear();
//			ptrFAST->detect(imageROI, gridpoints);
//			cout << "Number of FAST in grid" << i << "," << j << ":" << gridpoints.size() << endl;
//			if (gridpoints.size() > subtotal)
//			{
//				for (auto it = gridpoints.begin(); it != gridpoints.begin() + subtotal; ++it)
//				{
//					std::cout << "  " << it->response << std::endl;
//				}
//			}
//			//获取最大强度的FAST特征
//			auto itEnd(gridpoints.end());
//			if (gridpoints.size() > subtotal)
//			{
//				//选取最强的特征
//				nth_element(gridpoints.begin(), gridpoints.begin() + subtotal, gridpoints.end(),
//					[](KeyPoint &a, KeyPoint &b) {return a.response > b.response; });
//
//				itEnd = gridpoints.begin() + subtotal;
//			}
//			//加入全局特征容器
//			for (auto it = gridpoints.begin(); it != itEnd; ++it)
//			{
//				//转换成图上的坐标
//				it->pt += Point2f(j * hsize, i * vsize);
//				keypoints.push_back(*it);
//				cout << "  " << it->response << std::endl;
//			}
//		}
//	}
//	//画关键点
//	drawKeypoints(image, keypoints, image, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_OVER_OUTIMG);
//
//	imshow("FAST Features (grid)", image);
//
//	//5.SURF尺度不变的特征检测
//	image = imread("./images/church01.jpg", 0);
//	transpose(image, image);
//	flip(image, image, 0);
//   
//	keypoints.clear();
//	//创建SURF特征检测器对象
//	Ptr<xfeatures2d::SurfFeatureDetector> ptrSURF = xfeatures2d::SurfFeatureDetector::create(2000.0);
//	//检测关键点
//	ptrSURF->detect(image, keypoints);
//	//画出关键点,包括尺度和方向信息
//	Mat featureImage;
//	drawKeypoints(image, keypoints, featureImage, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//
//	imshow("SURF", featureImage);
//
//	cout << "Number of SURF keypoints: " << keypoints.size() << endl;
//
//	// Read a second input image
//	image = cv::imread("./images/church03.jpg", cv::IMREAD_GRAYSCALE);
//	// rotate the image (to produce a horizontal image)
//	cv::transpose(image, image);
//	cv::flip(image, image, 0);
//
//	// Detect the SURF features
//	ptrSURF->detect(image, keypoints);
//
//	cv::drawKeypoints(image, keypoints, featureImage, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//
//	// Display the keypoints
//	cv::namedWindow("SURF (2)");
//	cv::imshow("SURF (2)", featureImage);
//
//	//6.SIFT尺度不变特征转换
//	image = imread("./images/church01.jpg", IMREAD_GRAYSCALE);
//	transpose(image, image);
//	flip(image, image, 0);
//
//	keypoints.clear();
//	Ptr<xfeatures2d::SiftFeatureDetector> ptrSIFT = xfeatures2d::SiftFeatureDetector::create();
//	ptrSIFT->detect(image, keypoints);
//	//画关键点
//	drawKeypoints(image, keypoints, featureImage, Scalar(255, 255,255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//	
//	imshow("SIFT", featureImage);
//	cout << "Number of SIFT keypoints: " << keypoints.size() << endl;
//
//
//	//7.BRISK(二元稳健恒定可扩展关键点)检测法
//	image = imread("./images/church01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//	transpose(image, image);
//	flip(image, image, 0);
//
//	keypoints.clear();
//	//构造BRISK特征检测器对象
//	Ptr<BRISK> ptrBRISK = BRISK::create(60, 5);
//	//检测关键点
//	ptrBRISK->detect(image, keypoints);
//	drawKeypoints(image, keypoints, featureImage, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//	imshow("BRISK", featureImage);
//	cout << "Number of BRISK keypoints: " << keypoints.size() << endl;
//
//	//8.ORB特征检测算法
//	image = imread("./images/church01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//	transpose(image, image);
//	flip(image, image, 0);
//
//	keypoints.clear();
//	//构造ORB特征检测器
//	Ptr<ORB> ptrORB = ORB::create(75, 1.2, 8);
//	//检测关键点
//	ptrORB->detect(image, keypoints);
//	//画出关键点
//	drawKeypoints(image, keypoints, featureImage, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//
//	imshow("ORB", featureImage);
//	cout << "Number of ORB keypoints: " << keypoints.size() << endl;
//
//
//
//
//
//	waitKey(0);
//	return 0;
//}





#pragma once
#if !define HARRISD
#define HARRISD

#include <iostream>
#include <opencv2/opencv.hpp>
#include <vector>

using namespace std;
using namespace cv;

class HarrisDetected
{
private:
	//32位浮点整数的角点强度图像
	Mat cornerStrength;
	//32位浮点整数的阈值化角点图像
	Mat cornerTh;
	//局部最大值图像(内部)
	Mat localMax;
	//平滑导数的领域尺寸
	int neighborhood;
	//梯度计算的口径
	int aperture;
	//Harris参数
	double k;
	//阈值计算的最大强度
	double maxStrength;
	//计算得到的阈值(内部)
	double threshold;
	//非最大值抑制的领域尺寸
	double nonMaxSize;
	//非最大值抑制的内核
	Mat kernel;

public:
	HarrisDetected() : neighborhood(3), aperture(3), k(0.1), maxStrength(0.0), threshold(0.01), nonMaxSize(3)
	{
		setLocalMaxWindowSize(nonMaxSize);
	}
	//创建用于抑制非最大值的内核
	void setLocalMaxWindowSize(int size)
	{
		nonMaxSize = size;
		kernel.create(nonMaxSize, nonMaxSize, CV_8U);
	}
	//计算Harris角点
	void detect(const Mat& image)
	{
		//计算Harris
		cornerHarris(image, cornerStrength, neighborhood, aperture, k);
		//计算内部阈值
		minMaxLoc(cornerStrength, 0, &maxStrength);
		//检测局部最大值
		Mat dilated;  //临时图像
		dilate(cornerStrength, dilated, Mat());
		compare(cornerStrength, dilated, localMax, CMP_EQ);
	}

	//用Harris值得到角点分布图
	Mat getCornerMap(double qualityLeval)
	{
		Mat cornerMap;
		//对角点强度进行阈值化
		threshold = qualityLeval * maxStrength;
		cv::threshold(cornerStrength, cornerTh, threshold, 255, THRESH_BINARY);
		//转换成8位图像
		cornerTh.convertTo(cornerMap, CV_8U);
		//非最大值抑制
		bitwise_and(cornerMap, localMax, cornerMap);

		return cornerMap;

	}

	//用Harris值得到特征点
	void getCorners(vector<Point> &points, double qualityLevel)
	{
		//获得角点分布图
		Mat cornerMap = getCornerMap(qualityLevel);
		//获得角点
		getCorners(points, cornerMap);
	}
	//用角点分布图得到特征点
	void getCorners(vector<Point> &points, const Mat &cornerMap)
	{
		//迭代遍历像素,得到所有特征
		for (int y = 0; y < cornerMap.rows; y++)
		{
			const uchar *rowPtr = cornerMap.ptr<uchar>(y);

			for (int x = 0; x < cornerMap.cols; x++)
			{
				//如果他是一个特征点
				if (rowPtr[x])
				{
					points.push_back(Point(x, y));
				}
			}
		}
	}

	//在特征点的位置画圆
	void drawOnImage(Mat &image, const vector<Point> &points, Scalar color = Scalar(255, 255, 255), int radius = 3, int thickness = 1)
	{
		vector <Point> ::const_iterator it = points.begin();
		//针对所有角点
		while (it != points.end())
		{
			//在每个角点位置画圆
			circle(image, *it, radius, color, thickness);
			++it;
		}
	}
};

#endif // !define HARRISD

Chapter_09 : 描述和匹配兴趣点

1.局部模板匹配—-两个图像的匹配和找一样的模板 2.描述并匹配局部强度值模式—-SURF和SIFT 3.用二值描述子匹配关键点

  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
//#include <iostream>
//#include <opencv2/core.hpp>
//#include <opencv2/imgproc.hpp>
//#include <opencv2/highgui.hpp>
//#include <opencv2/features2d.hpp>
//#include <opencv2/objdetect.hpp>
//#include <opencv2/xfeatures2d.hpp>
//
//using namespace std;
//using namespace cv;
//
//int main()
//{
//	////1.局部模板匹配 ---- 两个图像的匹配
//	//Mat image1 = imread("./images/church01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//	//Mat image2 = imread("./images/church02.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//
//	//imshow("image1", image1);
//	//imshow("image2", image2);
//	////定义关键点容器
//	//vector<KeyPoint> keypoints1;
//	//vector<KeyPoint> keypoints2;
//	////定义特征检测器
//	//Ptr<FeatureDetector> ptrDetector;  //泛型检测器指针
//	////这里使用FAST检测器
//	//ptrDetector = FastFeatureDetector::create(80);
//	////检测关键点
//	//ptrDetector->detect(image1, keypoints1);
//	//ptrDetector->detect(image2, keypoints2);
//
//	//std::cout << "Number of keypoints (image 1): " << keypoints1.size() << std::endl;
//	//std::cout << "Number of keypoints (image 2): " << keypoints2.size() << std::endl;
//
//	////定义一个特定大小的矩形(11 * 11), 用于表示每个关键点周围的图像块
//	////定义正方形的领域
//	//const int nsize(11);
//	//Rect neighborhood(0, 0, nsize, nsize); //11 * 11
//	//Mat patch1;
//	//Mat patch2;
//	////将一副图像的关键点与另一幅图像的所有关键点进行比较,找出最相似的
//	////在第二幅图像中找出与第一幅图像中的每个关键点最匹配的
//	//Mat result;
//	//vector<DMatch> matches;
//	////针对图像一的全部关键点
//	//for (int i = 0; i < keypoints1.size(); i++)
//	//{
//	//	//定义图像块
//	//	neighborhood.x = keypoints1[i].pt.x - nsize / 2;
//	//	neighborhood.y = keypoints1[i].pt.y - nsize / 2;
//	//	//如果领域超出图像范围,就继续处理下一个点
//	//	if (neighborhood.x < 0 || neighborhood.y < 0 || 
//	//		neighborhood.x + nsize >= image1.cols || neighborhood.y + nsize >= image1.rows)
//	//		continue;
//	//	//第一幅图像的块
//	//	patch1 = image1(neighborhood);
//	//	//存放最匹配的值
//	//	DMatch bestMatch;
//	//	//针对第二幅图像的全部关键点
//	//	for (int j = 0; j < keypoints2.size(); j++)
//	//	{
//	//		//定义图像块
//	//		neighborhood.x = keypoints2[j].pt.x - nsize / 2;
//	//		neighborhood.y = keypoints2[j].pt.y - nsize / 2;
//
//	//		//如果领域超出图像范围,就继续处理下一个点
//	//		if (neighborhood.x < 0 || neighborhood.y < 0 ||
//	//			neighborhood.x + nsize >= image2.cols || neighborhood.y + nsize >= image2.rows)
//	//			continue;
//	//		//第二幅图像的块
//	//		patch2 = image2(neighborhood);
//
//	//	    //匹配两个图像块
//	//		matchTemplate(patch1, patch2, result, TM_SQDIFF);
//	//		//cv::matchTemplate(patch1, patch2, result, cv::TM_SQDIFF);
//	//		//检查是否为最佳匹配
//	//		if (result.at<float>(0, 0) < bestMatch.distance)
//	//		{
//	//			bestMatch.distance = result.at <float>(0, 0);
//	//			bestMatch.queryIdx = i;
//	//			bestMatch.trainIdx = j;
//	//		}
//	//	}
//	//	//添加最佳匹配
//	//	matches.push_back(bestMatch);
//	//}
//
//	//std::cout << "Number of matches: " << matches.size() << std::endl;
//	////提取50个最佳匹配项
//	//nth_element(matches.begin(), matches.begin() + 50, matches.end());
//	//matches.erase(matches.begin() + 50, matches.end());
//
//	//std::cout << "Number of matches (after): " << matches.size() << std::endl;
//
//	////画出匹配结果
//	//Mat matchImage;
//	//drawMatches(image1, keypoints1, image2, keypoints2, matches, matchImage, Scalar(255, 255, 255), Scalar(255, 255, 255));
//
//	//imshow("Matches", matchImage);
//
//	////模板匹配----寻找一样的部分
//	////定义一个模板
//	//Mat target(image1, Rect(80, 105, 30, 30));
//	////展示模板
//	//imshow("Template", target);
//	////定义搜索区域-----这里用图像的上半部分
//	//Mat roi(image2, Rect(0, 0, image2.cols, image2.rows / 2));
//	////进行模板匹配
//	//matchTemplate(roi,  //搜索区域
//	//	target,         //模板
//	//	result,         //结果
//	//	TM_SQDIFF);     //相似度
//	////找到最相似的位置
//	//double minVal, maxVal;
//	//Point minPt, maxPt;
//	//minMaxLoc(result, &minVal, &maxVal, &minPt, &maxPt);
//	////在相似度最高的位置画矩形框
//	////本例为minPt
//	//rectangle(roi, Rect(minPt.x, minPt.y, target.cols, target.rows), 255);
//	////展示模板
//	//imshow("Best", image2);
//
//
//	//////2.描述并匹配局部强度值模式----SURF和SIFT
//	////SURF
//	////读图片
//	//Mat image1 = imread("./images/church01.jpg", IMREAD_GRAYSCALE);
//	//Mat image2 = imread("./images/church02.jpg", IMREAD_GRAYSCALE);
//
//	//if (!image1.data || !image2.data)
//	//	return 0;
//	//imshow("image1", image1);
//	//imshow("image2", image2);
//	////定义关键点的检测器
//	//vector<KeyPoint> keypoints1;
//	//vector<KeyPoint> keypoints2;
//	////定义特征检测器----SURF
//	//Ptr<Feature2D> ptrFeature2D = xfeatures2d::SURF::create(2000.0);
//	////检测关键点
//	//ptrFeature2D->detect(image1, keypoints1);
//	//ptrFeature2D->detect(image2, keypoints2);
//
//	////画出特征点
//	//cv::Mat featureImage;
//	//cv::drawKeypoints(image1, keypoints1, featureImage, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//
//	//cv::namedWindow("SURF");
//	//cv::imshow("SURF", featureImage);
//
//	//std::cout << "Number of SURF keypoints (image 1): " << keypoints1.size() << std::endl;
//	//std::cout << "Number of SURF keypoints (image 2): " << keypoints2.size() << std::endl;
//
//	////提取描述子
//	//Mat descriptors1;
//	//Mat descriptors2;
//	//ptrFeature2D->compute(image1, keypoints1, descriptors1);
//	//ptrFeature2D->compute(image2, keypoints2, descriptors2);
//	////构造匹配器
//	//BFMatcher matcher(NORM_L2);  //度量距离
//	////匹配两幅图片的描述子
//	//vector<DMatch> matches;
//	//matcher.match(descriptors1, descriptors2, matches);
//	////画出匹配的描述子
//	//Mat imageMatches;
//	//drawMatches(image1, keypoints1, image2, keypoints2, matches, imageMatches, 
//	//	Scalar(255, 255, 255), Scalar(255, 255, 255), vector<char>(), 
//	//	DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//	//imshow("SURF Matches", imageMatches);
//	//cout << "Number of matches: " << matches.size() << endl;
//
//	////对于每个关键点找出最符合的2个匹配点 ----比率检验法
//	//std::vector<std::vector<cv::DMatch> > matches2;
//	//matcher.knnMatch(descriptors1, descriptors2,
//	//	matches2,
//	//	2); //找出k个最佳匹配项
//	//matches.clear();
//
//	//// 执行比率检验法
//	//double ratioMax = 0.6;
//	//std::vector<std::vector<cv::DMatch> >::iterator it;
//	//for (it = matches2.begin(); it != matches2.end(); ++it) {
//	//	//  第一个最佳匹配项/第二个最佳匹配项
//	//	if ((*it)[0].distance / (*it)[1].distance < ratioMax) {
//	//		// 这个匹配项可以接收
//	//		matches.push_back((*it)[0]);
//
//	//	}
//	//}
//	//// 
//	//cv::drawMatches(
//	//	image1, keypoints1, // 1st image and its keypoints
//	//	image2, keypoints2, // 2nd image and its keypoints
//	//	matches,           // the matches
//	//	imageMatches,      // the image produced
//	//	cv::Scalar(255, 255, 255),  // color of lines
//	//	cv::Scalar(255, 255, 255),  // color of points
//	//	std::vector< char >(),    // masks if any 
//	//	cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//
//	//std::cout << "Number of matches (after ratio test): " << matches.size() << std::endl;
//
//	//cv::namedWindow("SURF Matches (ratio test at 0.6)");
//	//cv::imshow("SURF Matches (ratio test at 0.6)", imageMatches);
//
//	////匹配差值的阈值化
//	//// 指定范围的匹配
//	//float maxDist = 0.3;
//	//matches2.clear();
//	//matcher.radiusMatch(descriptors1, descriptors2, matches2,maxDist); //两个描述子之间的最大允许差值
//	//			
//	//cv::drawMatches(
//	//	image1, keypoints1, // 1st image and its keypoints
//	//	image2, keypoints2, // 2nd image and its keypoints
//	//	matches2,          // the matches
//	//	imageMatches,      // the image produced
//	//	cv::Scalar(255, 255, 255),  // color of lines
//	//	cv::Scalar(255, 255, 255),  // color of points
//	//	std::vector<std::vector< char >>(),    // masks if any 
//	//	cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//
//	//int nmatches = 0;
//	//for (int i = 0; i< matches2.size(); i++)
//	//	nmatches += matches2[i].size();
//
//	//std::cout << "Number of matches (with max radius): " << nmatches << std::endl;
//
//	//cv::namedWindow("SURF Matches (with max radius)");
//	//cv::imshow("SURF Matches (with max radius)", imageMatches);
//
//
//	////SIFT
//	//image1 = cv::imread("./images/church01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//	//image2 = cv::imread("./images/church03.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//
//	//std::cout << "Number of SIFT keypoints (image 1): " << keypoints1.size() << std::endl;
//	//std::cout << "Number of SIFT keypoints (image 2): " << keypoints2.size() << std::endl;
//
//	//imshow("image1_sift", image1);
//	//imshow("image2_sift", image2);
//
//	//ptrFeature2D = xfeatures2d::SIFT::create();
//	//ptrFeature2D->detectAndCompute(image1, noArray(), keypoints1, descriptors1);
//	//ptrFeature2D->detectAndCompute(image2, noArray(), keypoints2, descriptors2);
//
//	//matcher.match(descriptors1, descriptors2, matches);
//
//	//// extract the 50 best matches
//	//std::nth_element(matches.begin(), matches.begin() + 50, matches.end());
//	//matches.erase(matches.begin() + 50, matches.end());
//
//	//// draw matches
//	//cv::drawMatches(
//	//	image1, keypoints1, // 1st image and its keypoints
//	//	image2, keypoints2, // 2nd image and its keypoints
//	//	matches,            // the matches
//	//	imageMatches,      // the image produced
//	//	cv::Scalar(255, 255, 255),  // color of lines
//	//	cv::Scalar(255, 255, 255), // color of points
//	//	std::vector<char>(), cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//	//// Display the image of matches
//	//cv::namedWindow("Multi-scale SIFT Matches");
//	//cv::imshow("Multi-scale SIFT Matches", imageMatches);
//
//	//std::cout << "Number of matches: " << matches.size() << std::endl;
//
//
//
//
//
//	//3.用二值描述子匹配关键点
//    Mat image1 = imread("./images/church01.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//	Mat image2 = imread("./images/church02.jpg", CV_LOAD_IMAGE_GRAYSCALE);
//
//	//定义关键点容器和描述子
//	vector<KeyPoint> keypoints1;
//	vector<KeyPoint> keypoints2;
//	Mat descriptors1;
//	Mat descriptors2;
//	//定义特征检测器/描述子
//	Ptr<Feature2D> feature = ORB::create(60);   //大约60个特征点
//	//检测并描述关键点
//	//检测ORB特征
//	feature->detectAndCompute(image1, noArray(), keypoints1, descriptors1);
//	feature->detectAndCompute(image2, noArray(), keypoints2, descriptors2);
//	
//	Mat featureImage;
//	drawKeypoints(image1, keypoints1, featureImage, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//	//展示角点
//	imshow("ORB", featureImage);
//
//	std::cout << "Number of ORB keypoints (image 1): " << keypoints1.size() << std::endl;
//	std::cout << "Number of ORB keypoints (image 2): " << keypoints2.size() << std::endl;
//
//	//构建匹配器
//	BFMatcher matcher(NORM_HAMMING);
//	//匹配两幅图像的描述子
//	vector<DMatch> matches;
//	matcher.match(descriptors1, descriptors2, matches);
//
//	cv::Mat imageMatches;
//	cv::drawMatches(
//		image1, keypoints1, // 1st image and its keypoints
//		image2, keypoints2, // 2nd image and its keypoints
//		matches,           // the matches
//		imageMatches,      // the image produced
//		cv::Scalar(255, 255, 255),  // color of lines
//		cv::Scalar(255, 255, 255),  // color of points
//		std::vector< char >(),    // masks if any 
//		cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS | cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//
//	cv::imshow("ORB Matches", imageMatches);
//	std::cout << "Number of matches: " << matches.size() << std::endl;
//
//
//	waitKey(0);
//	return 0;
//}

Chapter_10 : 估算图像之间的投影关系

1.计算图像对的基础矩阵 2.用RANSAC算法匹配图像 3.计算两幅图像之间的单应矩阵—-找到对应的点和拼接两幅图像 4.检测图像中的平面目标

   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
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
//#include <iostream>
//#include <vector>
//#include <opencv2/core.hpp>
//#include <opencv2/imgproc.hpp>
//#include <opencv2/highgui.hpp>
//#include <opencv2/features2d.hpp>
//#include <opencv2/calib3d.hpp>
//#include <opencv2/objdetect.hpp>
//#include <opencv2/xfeatures2d.hpp>
//#include <opencv2/stitching.hpp>
//
//#include "RobustMatcher.h"
//#include "TargetMatcher.h"
//
//using namespace std;
//using namespace cv;
//
//int main()
//{
//	////1.计算图像对的基础矩阵
//	//Mat image1 = imread("./images/church01.jpg", 0);
//	//Mat image2 = imread("./images/church03.jpg", 0);
//	//if (!image1.data || !image2.data)
//	//	return 0;
//
//	//// Display the images
//	//cv::namedWindow("Right Image");
//	//cv::imshow("Right Image", image1);
//	//cv::namedWindow("Left Image");
//	//cv::imshow("Left Image", image2);
//
//	////定义关键点容器和描述子、
//	//vector<KeyPoint> keypoints1;
//	//vector<KeyPoint> keypoints2;
//	//Mat descriptors1, descriptors2;
//	////构建SIFT特征检测器
//	//Ptr<Feature2D> ptrFeature2D = xfeatures2d::SIFT::create(74);
//
//	//ptrFeature2D->detectAndCompute(image1, noArray(), keypoints1, descriptors1);
//	//ptrFeature2D->detectAndCompute(image2, noArray(), keypoints2, descriptors2);
//
//	//std::cout << "Number of SIFT points (1): " << keypoints1.size() << std::endl;
//	//std::cout << "Number of SIFT points (2): " << keypoints2.size() << std::endl;
//
//	////画关键点
//	//cv::Mat imageKP;
//	//cv::drawKeypoints(image1, keypoints1, imageKP, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//	//cv::namedWindow("Right SIFT Features");
//	//cv::imshow("Right SIFT Features", imageKP);
//	//cv::drawKeypoints(image2, keypoints2, imageKP, cv::Scalar(255, 255, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//	//cv::namedWindow("Left SIFT Features");
//	//cv::imshow("Left SIFT Features", imageKP);
//
//	////构建匹配类的实例
//	//BFMatcher matcher(NORM_L2, true);
//	////匹配描述子
//	//vector<DMatch> matches;
//	//matcher.match(descriptors1, descriptors2, matches);
//	//std::cout << "Number of matched points: " << matches.size() << std::endl;
//	////手动的选择一些匹配的描述子
//	//vector<DMatch> selMatches;
//	//// make sure to double-check if the selected matches are valid
//	//selMatches.push_back(matches[2]);
//	//selMatches.push_back(matches[5]);
//	//selMatches.push_back(matches[16]);
//	//selMatches.push_back(matches[19]);
//	//selMatches.push_back(matches[14]);
//	//selMatches.push_back(matches[34]);
//	//selMatches.push_back(matches[29]);
//
//	////画出选择的描述子
//	//cv::Mat imageMatches;
//	//cv::drawMatches(image1, keypoints1,  // 1st image and its keypoints
//	//	image2, keypoints2,  // 2nd image and its keypoints
//	//	selMatches,			// the selected matches
//	//	imageMatches,		// the image produced
//	//	cv::Scalar(255, 255, 255),
//	//	cv::Scalar(255, 255, 255),
//	//	std::vector<char>(),
//	//	2
//	//); // color of the lines
//	//cv::namedWindow("Matches");
//	//cv::imshow("Matches", imageMatches);
//	////将一维关键点转变为二维的点
//	//vector<int> pointIndexes1;
//	//vector<int> pointIndexes2;
//	//for (vector<DMatch>::const_iterator it = selMatches.begin(); it != selMatches.end(); ++it)
//	//{
//	//	pointIndexes1.push_back(it->queryIdx);
//	//	pointIndexes2.push_back(it->trainIdx);
//	//}
//	////为了在findFundamentalMat中使用,需要先把这些关键点转化为Point2f类型
//	//vector<Point2f> selPoints1, selPoints2;
//	//KeyPoint::convert(keypoints1, selPoints1, pointIndexes1);
//	//KeyPoint::convert(keypoints2, selPoints2, pointIndexes2);
//	////通过画点来检查
//	//vector<Point2f> ::const_iterator it = selPoints1.begin();
//	//while (it != selPoints1.end())
//	//{
//	//	//在每个角点位置画圆
//	//	circle(image1, *it, 3, Scalar(255, 255, 255), 2);
//	//	++it;
//	//}
//	//it = selPoints2.begin();
//	//while (it != selPoints2.end()) {
//
//	//	// draw a circle at each corner location
//	//	cv::circle(image2, *it, 3, cv::Scalar(255, 255, 255), 2);
//	//	++it;
//	//}
//	////用7对匹配项计算基础矩阵
//	//Mat fundamental = findFundamentalMat(
//	//    selPoints1, //第一幅图像的7个点
//	//    selPoints2, //第二幅图像的7个点
//	//	FM_7POINT); //7个点的方法
//	//cout << "F-Matrix size= " << fundamental.rows << "," << fundamental.cols << endl;
//	//Mat fund(fundamental, cv::Rect(0, 0, 3, 3));
//	////在右侧图像上画出对极线的左侧点
//	//vector<Vec3f> lines1;
//	//computeCorrespondEpilines(selPoints1, //图像点
//	//	1,                 //在第一副图像中(也可在第二幅图像中)
//	//	fund,       //基础矩阵
//	//	lines1);           //对极线的向量
//	//std::cout << "size of F matrix:" << fund.rows << "x" << fund.cols << std::endl;
//	////遍历全部对极线
//	//for (std::vector<cv::Vec3f>::const_iterator it = lines1.begin();
//	//	it != lines1.end(); ++it) {
//
//	//	// 画出第一列和最后一列之间的线条
//	//	cv::line(image2, cv::Point(0, -(*it)[2] / (*it)[1]),
//	//		cv::Point(image2.cols, -((*it)[2] + (*it)[0] * image2.cols) / (*it)[1]),
//	//		cv::Scalar(255, 255, 255));
//	//}
//
//	//// draw the left points corresponding epipolar lines in left image 
//	//std::vector<cv::Vec3f> lines2;
//	//cv::computeCorrespondEpilines(cv::Mat(selPoints2), 2, fund, lines2);
//	//for (std::vector<cv::Vec3f>::const_iterator it = lines2.begin();
//	//	it != lines2.end(); ++it) {
//
//	//	// draw the epipolar line between first and last column
//	//	cv::line(image1, cv::Point(0, -(*it)[2] / (*it)[1]),
//	//		cv::Point(image1.cols, -((*it)[2] + (*it)[0] * image1.cols) / (*it)[1]),
//	//		cv::Scalar(255, 255, 255));
//	//}
//
//	//// combine both images
//	//cv::Mat both(image1.rows, image1.cols + image2.cols, CV_8U);
//	//image1.copyTo(both.colRange(0, image1.cols));
//	//image2.copyTo(both.colRange(image1.cols, image1.cols + image2.cols));
//
//	//// Display the images with points and epipolar lines
//	//cv::namedWindow("Epilines");
//	//cv::imshow("Epilines", both);
//	///*
//	//// Convert keypoints into Point2f
//	//std::vector<cv::Point2f> points1, points2, newPoints1, newPoints2;
//	//cv::KeyPoint::convert(keypoints1, points1);
//	//cv::KeyPoint::convert(keypoints2, points2);
//	//cv::correctMatches(fund, points1, points2, newPoints1, newPoints2);
//	//cv::KeyPoint::convert(newPoints1, keypoints1);
//	//cv::KeyPoint::convert(newPoints2, keypoints2);
//
//	//cv::drawMatches(image1, keypoints1,  // 1st image and its keypoints
//	//image2, keypoints2,  // 2nd image and its keypoints
//	//matches,			// the matches
//	//imageMatches,		// the image produced
//	//cv::Scalar(255, 255, 255),
//	//cv::Scalar(255, 255, 255),
//	//std::vector<char>(),
//	//2
//	//); // color of the lines
//	//cv::namedWindow("Corrected matches");
//	//cv::imshow("Corrected matches", imageMatches);
//	//*/
//
//
//
//	//2.用RANSAC算法匹配图像
//    
//	// Read input images
// //   cv::Mat image1 = cv::imread("./images/church01.jpg", 0);
// //   cv::Mat image2 = cv::imread("./images/church03.jpg", 0);
//
// //   if (!image1.data || !image2.data)
// //        return 0;
//
// //   // Display the images
// //   cv::namedWindow("Right Image");
// //   cv::imshow("Right Image", image1);
// //   cv::namedWindow("Left Image");
//	//cv::imshow("Left Image", image2);
//
//	////准备匹配器(用默认参数)
//	////SIFT检测器和描述子
//	//RobustMatcher rmatcher(xfeatures2d::SIFT::create(250));
//	////匹配两幅图像
//	//vector<DMatch> matches;
//
//	//vector<KeyPoint> keypoints1, keypoints2;
//	//Mat fundamental = rmatcher.match(image1, image2, matches, keypoints1, keypoints2);
//
//	//Mat imageMatches;
//	//drawMatches(image1, keypoints1, image2, keypoints2, matches, imageMatches,
//	//	Scalar(255, 255, 255), Scalar(255, 255, 255), vector<char>(), 2);
//
//	//imshow("Matches", imageMatches);
//
//	//// Convert keypoints into Point2f	
//	//std::vector<cv::Point2f> points1, points2;
//
//	//for (std::vector<cv::DMatch>::const_iterator it = matches.begin();
//	//	it != matches.end(); ++it) {
//
//	//	// Get the position of left keypoints
//	//	float x = keypoints1[it->queryIdx].pt.x;
//	//	float y = keypoints1[it->queryIdx].pt.y;
//	//	points1.push_back(keypoints1[it->queryIdx].pt);
//	//	cv::circle(image1, cv::Point(x, y), 3, cv::Scalar(255, 255, 255), 3);
//	//	// Get the position of right keypoints
//	//	x = keypoints2[it->trainIdx].pt.x;
//	//	y = keypoints2[it->trainIdx].pt.y;
//	//	cv::circle(image2, cv::Point(x, y), 3, cv::Scalar(255, 255, 255), 3);
//	//	points2.push_back(keypoints2[it->trainIdx].pt);
//	//}
//
//	//// Draw the epipolar lines
//	//std::vector<cv::Vec3f> lines1;
//	//cv::computeCorrespondEpilines(points1, 1, fundamental, lines1);
//
//	//for (std::vector<cv::Vec3f>::const_iterator it = lines1.begin();
//	//	it != lines1.end(); ++it) {
//
//	//	cv::line(image2, cv::Point(0, -(*it)[2] / (*it)[1]),
//	//		cv::Point(image2.cols, -((*it)[2] + (*it)[0] * image2.cols) / (*it)[1]),
//	//		cv::Scalar(255, 255, 255));
//	//}
//
//	//std::vector<cv::Vec3f> lines2;
//	//cv::computeCorrespondEpilines(points2, 2, fundamental, lines2);
//
//	//for (std::vector<cv::Vec3f>::const_iterator it = lines2.begin();
//	//	it != lines2.end(); ++it) {
//
//	//	cv::line(image1, cv::Point(0, -(*it)[2] / (*it)[1]),
//	//		cv::Point(image1.cols, -((*it)[2] + (*it)[0] * image1.cols) / (*it)[1]),
//	//		cv::Scalar(255, 255, 255));
//	//}
//
//	//// Display the images with epipolar lines
//	//cv::namedWindow("Right Image Epilines (RANSAC)");
//	//cv::imshow("Right Image Epilines (RANSAC)", image1);
//	//cv::namedWindow("Left Image Epilines (RANSAC)");
//	//cv::imshow("Left Image Epilines (RANSAC)", image2);
//
//
//
//	////3.计算两幅图像之间的单应矩阵----找到对应的点和拼接两幅图像
//	//// Read input images
// //   cv::Mat image1 = cv::imread("./images/parliament1.jpg", 0);
// //   cv::Mat image2 = cv::imread("./images/parliament2.jpg", 0);
// //   if (!image1.data || !image2.data)
// //         return 0;
//
// //   // Display the images
// //   cv::namedWindow("Image 1");
// //   cv::imshow("Image 1", image1);
// //   cv::namedWindow("Image 2");
// //   cv::imshow("Image 2", image2);
//
//	////构建关键点容器和描述子
//	//vector<KeyPoint> keypoints1;
//	//vector<KeyPoint> keypoints2;
//	//Mat descriptors1, descriptors2;
// //   //构建SIFT特征检测器
//	//Ptr<Feature2D> ptrFeature2D = xfeatures2d::SIFT::create(74);
//	//ptrFeature2D->detectAndCompute(image1, noArray() ,keypoints1, descriptors1);
//	//ptrFeature2D->detectAndCompute(image2, noArray(), keypoints2, descriptors2);
//
//	//cout << " Number of feature points (1):" << keypoints1.size() << endl;
//	//cout << " Number of feature points (2):" << keypoints2.size() << endl;
//
//	//BFMatcher matcher(NORM_L2, true);
//	//vector<DMatch> matches;
//	//matcher.match(descriptors1, descriptors2, matches);
//
//	//Mat imageMatches;
//	//drawMatches(image1, keypoints1, image2, keypoints2, matches, imageMatches, 
//	//	Scalar(255, 255, 255), Scalar(255, 255, 255), vector<char>(), 2);
//	//imshow("Matches (pure rotation case)", imageMatches);
//
//	////接下来使用findHomography函数实现,和findFundamentalMat函数相似
//	////我们要将关键点转变为Point2f
//	//vector<Point2f> points1, points2;
//	//for (vector<DMatch>::const_iterator it = matches.begin(); it != matches.end(); ++it)
//	//{
//	//	//获得左边关键点的位置
//	//	float x = keypoints1[it->queryIdx].pt.x;
//	//	float y = keypoints1[it->queryIdx].pt.y;
//	//	points1.push_back(Point2f(x, y));
//	//	//获得右边关键点的位置
//	//    x = keypoints2[it->trainIdx].pt.x;
//	//	y = keypoints2[it->trainIdx].pt.y;
//	//	points2.push_back(Point2f(x, y));
//	//}
//
//	//cout << points1.size() << " " << points2.size() << endl;
//	////找到第一幅图像和第二幅图像之间的单应矩阵
//	//vector<char> inliers;
//	//Mat homography = findHomography(
//	//    points1, points2,   //对应的点
//	//	inliers,            //输出的局部匹配项
//	//	RANSAC,             //RANSAC方法
//	//	1.);                //到重复投影点最大的距离
//	//						// Draw the inlier points
//	//cv::drawMatches(image1, keypoints1,  // 1st image and its keypoints
//	//	image2, keypoints2,  // 2nd image and its keypoints
//	//	matches,			// the matches
//	//	imageMatches,		// the image produced
//	//	cv::Scalar(255, 255, 255),  // color of the lines
//	//	cv::Scalar(255, 255, 255),  // color of the keypoints
//	//	inliers,
//	//	2);
//	//cv::namedWindow("Homography inlier points");
//	//cv::imshow("Homography inlier points", imageMatches);
//
//	////将第一幅图像扭曲到第二幅图像----实现两幅图像的拼接
//	//Mat result;
//	//warpPerspective(image1,     //输入图像
//	//	result,                 //输出图像
//	//	homography,             //单应矩阵
//	//	Size(2 * image1.cols, image1.rows)); //输出图像的尺寸
//
//	////把第一幅图像复制到完整图像的第一个半边
//	//Mat harf(result, Rect(0, 0, image2.cols, image2.rows));
//	//image2.copyTo(harf);  //把image2复制到image1的感兴趣区域
//
//	//imshow("Image mosaic", result);
//
//	////图像拼接技术----用Stitcher生成全景图
//	////Mat img1 = imread("./images/parliament1.jpg");
//	////Mat img2 = imread("./images/parliament2.jpg");
//	////imshow("img1", img1);
//	////imshow("img2", img2);
//	////vector<Mat> images;
//	////images.push_back(img1);
//	////images.push_back(img2);
//
//	//vector<Mat> images;
//	//images.push_back(imread("./images/parliament1_1.jpg"));
//	//images.push_back(imread("./images/parliament2_1.jpg"));
//
//	//Mat panorama;  //输出的全景图
//	////创建拼接器
//	//Stitcher stitcher = Stitcher::createDefault();
//	////拼接图像
//	//Stitcher::Status status = stitcher.stitch(images, panorama);
//
//	//if (status == cv::Stitcher::OK) // success?
//	//{
//	//
// //    	cv::namedWindow("Panorama");
//	//	cv::imshow("Panorama", panorama);
//	//}
//
//	
//
//	//4.检测图像中的平面目标
//	// Read input images
//    cv::Mat target = cv::imread("./images/cookbook1.bmp", 0);
//    cv::Mat image = cv::imread("./images/objects.jpg", 0);
//    if (!target.data || !image.data)
//      return 0;
//
//    // Display the images
//    cv::namedWindow("Target");
//    cv::imshow("Target", target);
//    cv::namedWindow("Image");
//    cv::imshow("Image", image);
//    
//	//初始化匹配器
//	TargetMatcher tmatcher(FastFeatureDetector::create(10), BRISK::create());
//	tmatcher.setNormType(NORM_HAMMING);
//	//定义输出数据
//	vector<DMatch> matches;
//	vector<KeyPoint> keypoints1, keypoints2;
//	vector<Point2f> corners;
//	//设定目标图像
//	tmatcher.setTarget(target);
//	//匹配目标图像
//	tmatcher.detectTarget(image, corners);
//	//画出目标角点
//	if (corners.size() == 4) { //已获得检测结果
//
//		cv::line(image, cv::Point(corners[0]), cv::Point(corners[1]), cv::Scalar(255, 255, 255), 6);
//		cv::line(image, cv::Point(corners[1]), cv::Point(corners[2]), cv::Scalar(255, 255, 255), 6);
//		cv::line(image, cv::Point(corners[2]), cv::Point(corners[3]), cv::Scalar(255, 255, 255), 6);
//		cv::line(image, cv::Point(corners[3]), cv::Point(corners[0]), cv::Scalar(255, 255, 255), 6);
//	}
//
//	cv::namedWindow("Target detection");
//	cv::imshow("Target detection", image);
//
//	waitKey(0);
//	return 0;
//}



#pragma once
#if !defined MATCHER
#define MATCHER

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/xfeatures2d.hpp>

using namespace std;
using namespace cv;

#define NOCHECK      0
#define CROSSCHECK   1
#define RATIOCHECK   2
#define BOTHCHECK    3


class RobustMatcher
{
private:
	//特征点检测器对象的指针
	Ptr<FeatureDetector> detector;
	//特征描述子提取器对象的指针
	Ptr<DescriptorExtractor> descriptor;
	int normType;
	float ratio;   //第一个和第二个NN之间的最大比率
	bool refineF;  //如果等于true,则会优化基础矩阵
	bool refineM;  //如果等于true,则会优化匹配结果
	double distance;    //到极点的最小距离
	double confidence;  //可信度(概率)
public:

	RobustMatcher(const cv::Ptr<cv::FeatureDetector> &detector,
		const cv::Ptr<cv::DescriptorExtractor> &descriptor = cv::Ptr<cv::DescriptorExtractor>())
		: detector(detector), descriptor(descriptor), normType(cv::NORM_L2),
		ratio(0.8f), refineF(true), refineM(true), confidence(0.98), distance(1.0)
	{

		//这里使用关联描述子
		if (!this->descriptor) {
			this->descriptor = this->detector;
		}
    }


	// Set the feature detector
	void setFeatureDetector(const cv::Ptr<cv::FeatureDetector>& detect) {

		this->detector = detect;
	}

	// Set descriptor extractor
	void setDescriptorExtractor(const cv::Ptr<cv::DescriptorExtractor>& desc) {

		this->descriptor = desc;
	}

	// Set the norm to be used for matching
	void setNormType(int norm) {

		normType = norm;
	}

	// Set the minimum distance to epipolar in RANSAC
	void setMinDistanceToEpipolar(double d) {

		distance = d;
	}

	// Set confidence level in RANSAC
	void setConfidenceLevel(double c) {

		confidence = c;
	}

	// Set the NN ratio
	void setRatio(float r) {

		ratio = r;
	}

	// if you want the F matrix to be recalculated
	void refineFundamental(bool flag) {

		refineF = flag;
	}

	// if you want the matches to be refined using F
	void refineMatches(bool flag) {

		refineM = flag;
	}



	// Clear matches for which NN ratio is > than threshold
	// return the number of removed points 
	// (corresponding entries being cleared, i.e. size will be 0)
	int ratioTest(const std::vector<std::vector<cv::DMatch> >& inputMatches,
		std::vector<cv::DMatch>& outputMatches) {

		int removed = 0;

		// for all matches
		for (std::vector<std::vector<cv::DMatch> >::const_iterator matchIterator = inputMatches.begin();
			matchIterator != inputMatches.end(); ++matchIterator) {

			//   first best match/second best match
			if ((matchIterator->size() > 1) && // if 2 NN has been identified 
				(*matchIterator)[0].distance / (*matchIterator)[1].distance < ratio) {

				// it is an acceptable match
				outputMatches.push_back((*matchIterator)[0]);

			}
			else {

				removed++;
			}
		}

		return removed;
	}

	// Insert symmetrical matches in symMatches vector
	void symmetryTest(const std::vector<cv::DMatch>& matches1,
		const std::vector<cv::DMatch>& matches2,
		std::vector<cv::DMatch>& symMatches) {

		// for all matches image 1 -> image 2
		for (std::vector<cv::DMatch>::const_iterator matchIterator1 = matches1.begin();
			matchIterator1 != matches1.end(); ++matchIterator1) {

			// for all matches image 2 -> image 1
			for (std::vector<cv::DMatch>::const_iterator matchIterator2 = matches2.begin();
				matchIterator2 != matches2.end(); ++matchIterator2) {

				// Match symmetry test
				if (matchIterator1->queryIdx == matchIterator2->trainIdx  &&
					matchIterator2->queryIdx == matchIterator1->trainIdx) {

					// add symmetrical match
					symMatches.push_back(*matchIterator1);
					break; // next match in image 1 -> image 2
				}
			}
		}
	}

	// Apply both ratio and symmetry test
	// (often an over-kill)
	void ratioAndSymmetryTest(const std::vector<std::vector<cv::DMatch> >& matches1,
		const std::vector<std::vector<cv::DMatch> >& matches2,
		std::vector<cv::DMatch>& outputMatches) {

		// Remove matches for which NN ratio is > than threshold

		// clean image 1 -> image 2 matches
		std::vector<cv::DMatch> ratioMatches1;
		int removed = ratioTest(matches1, ratioMatches1);
		std::cout << "Number of matched points 1->2 (ratio test) : " << ratioMatches1.size() << std::endl;
		// clean image 2 -> image 1 matches
		std::vector<cv::DMatch> ratioMatches2;
		removed = ratioTest(matches2, ratioMatches2);
		std::cout << "Number of matched points 1->2 (ratio test) : " << ratioMatches2.size() << std::endl;

		// Remove non-symmetrical matches
		symmetryTest(ratioMatches1, ratioMatches2, outputMatches);

		std::cout << "Number of matched points (symmetry test): " << outputMatches.size() << std::endl;
	}

	// 用RANSAC算法获取优质匹配项
	// 返回基础矩阵和匹配项
	cv::Mat ransacTest(const std::vector<cv::DMatch>& matches,
		std::vector<cv::KeyPoint>& keypoints1,
		std::vector<cv::KeyPoint>& keypoints2,
		std::vector<cv::DMatch>& outMatches) {

		//把关键点转变为Point2f类型	
		std::vector<cv::Point2f> points1, points2;

		for (std::vector<cv::DMatch>::const_iterator it = matches.begin();
			it != matches.end(); ++it) {

			//获取左侧关键点的位置
			points1.push_back(keypoints1[it->queryIdx].pt);
			//获取右侧关键点的位置
			points2.push_back(keypoints2[it->trainIdx].pt);
		}

		// 用 RANSAC 计算F矩阵
		std::vector<uchar> inliers(points1.size(), 0);
		cv::Mat fundamental = cv::findFundamentalMat(
			points1, points2, //匹配像素点
			inliers,         //匹配状态(inlier or outlier)  
			cv::FM_RANSAC,   // RANSAC 算法
			distance,        // 到对极线的距离
			confidence);     // 置信度

		 //取下剩下的 (inliers) 匹配项
		std::vector<uchar>::const_iterator itIn = inliers.begin();
		std::vector<cv::DMatch>::const_iterator itM = matches.begin();
		// 遍历所有的匹配项
		for (; itIn != inliers.end(); ++itIn, ++itM) {

			if (*itIn) { // it is a valid match

				outMatches.push_back(*itM);
			}
		}

		if (refineF || refineM) {
			// The F matrix will be recomputed with all accepted matches

			// Convert keypoints into Point2f for final F computation	
			points1.clear();
			points2.clear();

			for (std::vector<cv::DMatch>::const_iterator it = outMatches.begin();
				it != outMatches.end(); ++it) {

				// Get the position of left keypoints
				points1.push_back(keypoints1[it->queryIdx].pt);
				// Get the position of right keypoints
				points2.push_back(keypoints2[it->trainIdx].pt);
			}

			// Compute 8-point F from all accepted matches
			fundamental = cv::findFundamentalMat(
				points1, points2, // matching points
				cv::FM_8POINT); // 8-point method

			if (refineM) {

				std::vector<cv::Point2f> newPoints1, newPoints2;
				// refine the matches
				correctMatches(fundamental,             // F matrix
					points1, points2,        // original position
					newPoints1, newPoints2); // new position
				for (int i = 0; i< points1.size(); i++) {

					std::cout << "(" << keypoints1[outMatches[i].queryIdx].pt.x
						<< "," << keypoints1[outMatches[i].queryIdx].pt.y
						<< ") -> ";
					std::cout << "(" << newPoints1[i].x
						<< "," << newPoints1[i].y << std::endl;
					std::cout << "(" << keypoints2[outMatches[i].trainIdx].pt.x
						<< "," << keypoints2[outMatches[i].trainIdx].pt.y
						<< ") -> ";
					std::cout << "(" << newPoints2[i].x
						<< "," << newPoints2[i].y << std::endl;

					keypoints1[outMatches[i].queryIdx].pt.x = newPoints1[i].x;
					keypoints1[outMatches[i].queryIdx].pt.y = newPoints1[i].y;
					keypoints2[outMatches[i].trainIdx].pt.x = newPoints2[i].x;
					keypoints2[outMatches[i].trainIdx].pt.y = newPoints2[i].y;
				}
			}
		}


		return fundamental;
	}








	//用RANSAC算法匹配特征点
	//返回基础矩阵和输出的匹配项
	//这是一个简单的展示,和书上的一样,下面有个比较复杂的
	Mat matchBook(Mat &image1, Mat &image2,  //输入图像
		vector<DMatch> &matches,         //输出匹配项
		vector<KeyPoint> &keypoints1,    //输出关键点
		vector<KeyPoint> &keypoints2)
	{
		//1.检测特征点
		detector->detect(image1, keypoints1);
		detector->detect(image2, keypoints2);
		//2.提取特征描述子
		Mat descriptors1, descriptors2;
		descriptor->compute(image1, keypoints1, descriptors1);
		descriptor->compute(image2, keypoints2, descriptors2);
		//3.匹配两幅图像描述子
		//(用于部分检测方法)
		//构造匹配类的实例(带交叉检查)
		BFMatcher matcher(normType,  //差距衡量
			true);                   //交叉检查标志
		//匹配描述子
		vector<DMatch> outputMatches;
		matcher.match(descriptors1, descriptors2, outputMatches);
		//4.用RANSAC算法验证匹配项
		Mat fundamental = ransacTest(outputMatches, keypoints1, keypoints2, matches);
		//返回基础矩阵
		return fundamental;
	}


	//用RANSAC算法匹配特征点
	//返回基础矩阵和输出的匹配项
	cv::Mat match(cv::Mat& image1, cv::Mat& image2, // 输入图像
		std::vector<cv::DMatch>& matches, // 输出匹配项和关键点
		std::vector<cv::KeyPoint>& keypoints1, std::vector<cv::KeyPoint>& keypoints2,
		int check = CROSSCHECK) {  // check type (symmetry or ratio or none or both)

		//检测特征点
		detector->detect(image1, keypoints1);
		detector->detect(image2, keypoints2);

		std::cout << "Number of feature points (1): " << keypoints1.size() << std::endl;
		std::cout << "Number of feature points (2): " << keypoints2.size() << std::endl;

		// 提取特征描述子
		cv::Mat descriptors1, descriptors2;
		descriptor->compute(image1, keypoints1, descriptors1);
		descriptor->compute(image2, keypoints2, descriptors2);

		std::cout << "descriptor matrix size: " << descriptors1.rows << " by " << descriptors1.cols << std::endl;

		// 3. Match the two image descriptors
		//    (optionaly apply some checking method)

		// Construction of the matcher with crosscheck 
		cv::BFMatcher matcher(normType,            //distance measure
			check == CROSSCHECK);  // crosscheck flag

								   // vectors of matches
		std::vector<std::vector<cv::DMatch> > matches1;
		std::vector<std::vector<cv::DMatch> > matches2;
		std::vector<cv::DMatch> outputMatches;

		// call knnMatch if ratio check is required
		if (check == RATIOCHECK || check == BOTHCHECK) {
			// from image 1 to image 2
			// based on k nearest neighbours (with k=2)
			matcher.knnMatch(descriptors1, descriptors2,
				matches1, // vector of matches (up to 2 per entry) 
				2);		  // return 2 nearest neighbours

			std::cout << "Number of matched points 1->2: " << matches1.size() << std::endl;

			if (check == BOTHCHECK) {
				// from image 2 to image 1
				// based on k nearest neighbours (with k=2)
				matcher.knnMatch(descriptors2, descriptors1,
					matches2, // vector of matches (up to 2 per entry) 
					2);		  // return 2 nearest neighbours

				std::cout << "Number of matched points 2->1: " << matches2.size() << std::endl;
			}

		}

		// select check method
		switch (check) {

		case CROSSCHECK:
			matcher.match(descriptors1, descriptors2, outputMatches);
			std::cout << "Number of matched points 1->2 (after cross-check): " << outputMatches.size() << std::endl;
			break;
		case RATIOCHECK:
			ratioTest(matches1, outputMatches);
			std::cout << "Number of matched points 1->2 (after ratio test): " << outputMatches.size() << std::endl;
			break;
		case BOTHCHECK:
			ratioAndSymmetryTest(matches1, matches2, outputMatches);
			std::cout << "Number of matched points 1->2 (after ratio and cross-check): " << outputMatches.size() << std::endl;
			break;
		case NOCHECK:
		default:
			matcher.match(descriptors1, descriptors2, outputMatches);
			std::cout << "Number of matched points 1->2: " << outputMatches.size() << std::endl;
			break;
		}

		// 4. Validate matches using RANSAC
		cv::Mat fundamental = ransacTest(outputMatches, keypoints1, keypoints2, matches);
		std::cout << "Number of matched points (after RANSAC): " << matches.size() << std::endl;

		// return the found fundamental matrix
		return fundamental;
	}
};

#endif // !define MATCHER




#pragma once
#if !defined TMATCHER
#define TMATCHER

#define VERBOSE 1

#include <iostream>
#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/calib3d.hpp>
#include <opencv2/features2d.hpp>

using namespace std;
using namespace cv;

class TargetMatcher
{
private:
	//特征点检测器对象的指针
	Ptr<FeatureDetector> detector;
	//特征描述子提取器对象的指针
	Ptr<DescriptorExtractor> descriptor;
	//目标图像
	Mat target;
	//比较描述子容器
	int normType;
	//最小重投影误差
	double distance;
	//金字塔形图像的数量
	int numberOfLevels;
	//层级之间的范围
	double scaleFactor;
	//目标图像构建的金字塔以及它的关键点
	vector<Mat> pyramid;
	vector<vector<KeyPoint> > pyrKeypoints;
	vector<Mat> pyrDescriptors;

	// 创建目标图像的金字塔
	void createPyramid() {

		// 创建目标图像的金字塔
		pyramid.clear();
		cv::Mat layer(target);
		for (int i = 0; i < numberOfLevels; i++) { // 逐层缩小
			pyramid.push_back(target.clone());
			resize(target, target, cv::Size(), scaleFactor, scaleFactor);
		}

		pyrKeypoints.clear();
		pyrDescriptors.clear();
		// 逐层检测关键点和描述子
		for (int i = 0; i < numberOfLevels; i++) {
			// 在第i层检测目标关键点
			pyrKeypoints.push_back(std::vector<cv::KeyPoint>());
			detector->detect(pyramid[i], pyrKeypoints[i]);
			if (VERBOSE)
				std::cout << "Interest points: target=" << pyrKeypoints[i].size() << std::endl;
			//在第i层计算描述子
			pyrDescriptors.push_back(cv::Mat());
			descriptor->compute(pyramid[i], pyrKeypoints[i], pyrDescriptors[i]);
		}
	}
public:
	TargetMatcher(const cv::Ptr<cv::FeatureDetector> &detector,
		const cv::Ptr<cv::DescriptorExtractor> &descriptor = cv::Ptr<cv::DescriptorExtractor>(),
		int numberOfLevels = 8, double scaleFactor = 0.9)
		: detector(detector), descriptor(descriptor), normType(cv::NORM_L2), distance(1.0),
		numberOfLevels(numberOfLevels), scaleFactor(scaleFactor) {

		// in this case use the associated descriptor
		if (!this->descriptor) {
			this->descriptor = this->detector;
		}
	}

	// Set the norm to be used for matching
	void setNormType(int norm) {

		normType = norm;
	}

	// Set the minimum reprojection distance
	void setReprojectionDistance(double d) {

		distance = d;
	}

	//设置目标图像
	void setTarget(const Mat t)
	{
		if (VERBOSE)
			cv::imshow("Target", t);
		target = t;
		createPyramid();
	}



	// Identify good matches using RANSAC
	// Return homography matrix and output matches
	//下面的是更好的
	cv::Mat ransacTest(const std::vector<cv::DMatch>& matches,
		std::vector<cv::KeyPoint>& keypoints1,
		std::vector<cv::KeyPoint>& keypoints2,
		std::vector<cv::DMatch>& outMatches) {

		// Convert keypoints into Point2f	
		std::vector<cv::Point2f> points1, points2;
		outMatches.clear();
		for (std::vector<cv::DMatch>::const_iterator it = matches.begin();
			it != matches.end(); ++it) {

			// Get the position of left keypoints
			points1.push_back(keypoints1[it->queryIdx].pt);
			// Get the position of right keypoints
			points2.push_back(keypoints2[it->trainIdx].pt);
		}

		// Find the homography between image 1 and image 2
		std::vector<uchar> inliers(points1.size(), 0);
		cv::Mat homography = cv::findHomography(
			points1, points2, // corresponding points
			inliers,         // match status (inlier or outlier)  
			cv::RHO,	     // RHO method
			distance);       // max distance to reprojection point

							 // extract the surviving (inliers) matches
		std::vector<uchar>::const_iterator itIn = inliers.begin();
		std::vector<cv::DMatch>::const_iterator itM = matches.begin();
		// for all matches
		for (; itIn != inliers.end(); ++itIn, ++itM) {

			if (*itIn) { // it is a valid match

				outMatches.push_back(*itM);
			}
		}

		return homography;
	}

	// 检测预先定义的平面目标
	// 返回单应矩阵和检测到的目标的4个角点
	cv::Mat detectTarget(const cv::Mat& image,
		// 目标角点的坐标(顺时针方向)
		std::vector<cv::Point2f>& detectedCorners) {

		// 1. 检测图像的关键点
		std::vector<cv::KeyPoint> keypoints;
		detector->detect(image, keypoints);
		if (VERBOSE)
			std::cout << "Interest points: image=" << keypoints.size() << std::endl;
		// 计算描述子
		cv::Mat descriptors;
		descriptor->compute(image, keypoints, descriptors);
		std::vector<cv::DMatch> matches;

		cv::Mat bestHomography;
		cv::Size bestSize;
		int maxInliers = 0;
		cv::Mat homography;

		// 构建匹配器
		cv::BFMatcher matcher(normType);

		// 2. 对金字塔的每层, 鲁棒匹配单应矩阵
		for (int i = 0; i < numberOfLevels; i++) {
			// 在目标和图像之间发现RANSAC单应矩阵
			matches.clear();

			// 匹配描述子
			matcher.match(pyrDescriptors[i], descriptors, matches);
			if (VERBOSE)
				std::cout << "Number of matches (level " << i << ")=" << matches.size() << std::endl;
			// 用RANSAC验证匹配项
			std::vector<cv::DMatch> inliers;
			homography = ransacTest(matches, pyrKeypoints[i], keypoints, inliers);
			if (VERBOSE)
				std::cout << "Number of inliers=" << inliers.size() << std::endl;

			if (inliers.size() > maxInliers) { // 有更好的 H
				maxInliers = inliers.size();
				bestHomography = homography;
				bestSize = pyramid[i].size();
			}

			if (VERBOSE) {
				cv::Mat imageMatches;
				cv::drawMatches(target, pyrKeypoints[i],  // 1st image and its keypoints
					image, keypoints,  // 2nd image and its keypoints
					inliers,			// the matches
					imageMatches,		// the image produced
					cv::Scalar(255, 255, 255),  // color of the lines
					cv::Scalar(255, 255, 255),  // color of the keypoints
					std::vector<char>(),
					2);
				cv::imshow("Target matches", imageMatches);
				cv::waitKey();
			}
		}

		// 3. 用最佳单应矩阵找出角点坐标
		if (maxInliers > 8) { // 估计值有效

			//最佳尺寸的目标角点
			std::vector<cv::Point2f> corners;
			corners.push_back(cv::Point2f(0, 0));
			corners.push_back(cv::Point2f(bestSize.width - 1, 0));
			corners.push_back(cv::Point2f(bestSize.width - 1, bestSize.height - 1));
			corners.push_back(cv::Point2f(0, bestSize.height - 1));

			// 重新投影目标角点
			cv::perspectiveTransform(corners, detectedCorners, bestHomography);
		}

		if (VERBOSE)
			std::cout << "Best number of inliers=" << maxInliers << std::endl;
		return bestHomography;
	}

};

#endif // !defined TMATCHER

Chapter_11 : 三维重建

1.相机标定 2.相机姿态还原 3.用标定相机实现三维重建 4.计算立体图像的深度

  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

//#include <iostream>
//#include <iomanip>
//#include <vector>
//
//#include <opencv2/core.hpp>
//#include <opencv2/imgproc.hpp>
//#include <opencv2/highgui.hpp>
//#include <opencv2/features2d.hpp>
//
//#include <opencv2/viz.hpp>
//#include <opencv2/calib3d.hpp>
//
//#include "CameraCalibrator.h"
//
//int main()
//{
//	////1.相机标定
//	//cv::Mat image;
//	//std::vector<std::string> filelist;
//
//	//// generate list of chessboard image filename
//	//// named chessboard01 to chessboard27 in chessboard sub-dir
//	//for (int i = 1; i <= 27; i++) {
//
//	//	std::stringstream str;
//	//	str << "images/chessboards/chessboard" << std::setw(2) << std::setfill('0') << i << ".jpg";
//	//	std::cout << str.str() << std::endl;
//
//	//	filelist.push_back(str.str());
//	//	image = cv::imread(str.str(), 0);
//
//	//	// cv::imshow("Board Image",image);	
//	//	// cv::waitKey(100);
//	//}
//
//	//// Create calibrator object
//	//CameraCalibrator cameraCalibrator;
//	//// add the corners from the chessboard
//	//cv::Size boardSize(7, 5);
//	//cameraCalibrator.addChessboardPoints(
//	//	filelist,	// filenames of chessboard image
//	//	boardSize, "Detected points");	// size of chessboard
//
//	//									// calibrate the camera
//	//cameraCalibrator.setCalibrationFlag(true, true);
//	//cameraCalibrator.calibrate(image.size());
//
//	//// Exampple of Image Undistortion
//	//image = cv::imread(filelist[14], 0);
//	//cv::Size newSize(static_cast<int>(image.cols*1.5), static_cast<int>(image.rows*1.5));
//	//cv::Mat uImage = cameraCalibrator.remap(image, newSize);
//
//	//// display camera matrix
//	//cv::Mat cameraMatrix = cameraCalibrator.getCameraMatrix();
//	//std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
//	//std::cout << cameraMatrix.at<double>(0, 0) << " " << cameraMatrix.at<double>(0, 1) << " " << cameraMatrix.at<double>(0, 2) << std::endl;
//	//std::cout << cameraMatrix.at<double>(1, 0) << " " << cameraMatrix.at<double>(1, 1) << " " << cameraMatrix.at<double>(1, 2) << std::endl;
//	//std::cout << cameraMatrix.at<double>(2, 0) << " " << cameraMatrix.at<double>(2, 1) << " " << cameraMatrix.at<double>(2, 2) << std::endl;
//
//	//cv::namedWindow("Original Image");
//	//cv::imshow("Original Image", image);
//	//cv::namedWindow("Undistorted Image");
//	//cv::imshow("Undistorted Image", uImage);
//
//	//// Store everything in a xml file
//	//cv::FileStorage fs("calib.xml", cv::FileStorage::WRITE);
//	//fs << "Intrinsic" << cameraMatrix;
//	//fs << "Distortion" << cameraCalibrator.getDistCoeffs();
//
//
//
//
//	// Read the camera calibration parameters
//	cv::Mat cameraMatrix;
//	cv::Mat cameraDistCoeffs;
//	cv::FileStorage fs("calib.xml", cv::FileStorage::READ);
//	fs["Intrinsic"] >> cameraMatrix;
//	fs["Distortion"] >> cameraDistCoeffs;
//	std::cout << " Camera intrinsic: " << cameraMatrix.rows << "x" << cameraMatrix.cols << std::endl;
//	std::cout << cameraMatrix.at<double>(0, 0) << " " << cameraMatrix.at<double>(0, 1) << " " << cameraMatrix.at<double>(0, 2) << std::endl;
//	std::cout << cameraMatrix.at<double>(1, 0) << " " << cameraMatrix.at<double>(1, 1) << " " << cameraMatrix.at<double>(1, 2) << std::endl;
//	std::cout << cameraMatrix.at<double>(2, 0) << " " << cameraMatrix.at<double>(2, 1) << " " << cameraMatrix.at<double>(2, 2) << std::endl << std::endl;
//	cv::Matx33d cMatrix(cameraMatrix);
//
//	// Input image points
//	std::vector<cv::Point2f> imagePoints;
//	imagePoints.push_back(cv::Point2f(136, 113));
//	imagePoints.push_back(cv::Point2f(379, 114));
//	imagePoints.push_back(cv::Point2f(379, 150));
//	imagePoints.push_back(cv::Point2f(138, 135));
//	imagePoints.push_back(cv::Point2f(143, 146));
//	imagePoints.push_back(cv::Point2f(381, 166));
//	imagePoints.push_back(cv::Point2f(345, 194));
//	imagePoints.push_back(cv::Point2f(103, 161));
//
//	// Input object points
//	std::vector<cv::Point3f> objectPoints;
//	objectPoints.push_back(cv::Point3f(0, 45, 0));
//	objectPoints.push_back(cv::Point3f(242.5, 45, 0));
//	objectPoints.push_back(cv::Point3f(242.5, 21, 0));
//	objectPoints.push_back(cv::Point3f(0, 21, 0));
//	objectPoints.push_back(cv::Point3f(0, 9, -9));
//	objectPoints.push_back(cv::Point3f(242.5, 9, -9));
//	objectPoints.push_back(cv::Point3f(242.5, 9, 44.5));
//	objectPoints.push_back(cv::Point3f(0, 9, 44.5));
//
//	// Read image
//	cv::Mat image = cv::imread("./images/bench2.jpg");
//	// Draw image points
//	for (int i = 0; i < 8; i++) {
//		cv::circle(image, imagePoints[i], 3, cv::Scalar(0, 0, 0), 2);
//	}
//	cv::imshow("An image of a bench", image);
//
//	// Create a viz window
//	cv::viz::Viz3d visualizer("Viz window");
//	visualizer.setBackgroundColor(cv::viz::Color::white());
//
//	/// Construct the scene
//	// Create a virtual camera
//	cv::viz::WCameraPosition cam(cMatrix,  // matrix of intrinsics
//		image,    // image displayed on the plane
//		30.0,     // scale factor
//		cv::viz::Color::black());
//	// Create a virtual bench from cuboids
//	cv::viz::WCube plane1(cv::Point3f(0.0, 45.0, 0.0),
//		cv::Point3f(242.5, 21.0, -9.0),
//		true,  // show wire frame 
//		cv::viz::Color::blue());
//	plane1.setRenderingProperty(cv::viz::LINE_WIDTH, 4.0);
//	cv::viz::WCube plane2(cv::Point3f(0.0, 9.0, -9.0),
//		cv::Point3f(242.5, 0.0, 44.5),
//		true,  // show wire frame 
//		cv::viz::Color::blue());
//	plane2.setRenderingProperty(cv::viz::LINE_WIDTH, 4.0);
//	// Add the virtual objects to the environment
//	visualizer.showWidget("top", plane1);
//	visualizer.showWidget("bottom", plane2);
//	visualizer.showWidget("Camera", cam);
//
//	// Get the camera pose from 3D/2D points
//	cv::Mat rvec, tvec;
//	cv::solvePnP(objectPoints, imagePoints,      // corresponding 3D/2D pts 
//		cameraMatrix, cameraDistCoeffs, // calibration 
//		rvec, tvec);                    // output pose
//	std::cout << " rvec: " << rvec.rows << "x" << rvec.cols << std::endl;
//	std::cout << " tvec: " << tvec.rows << "x" << tvec.cols << std::endl;
//
//	cv::Mat rotation;
//	// convert vector-3 rotation
//	// to a 3x3 rotation matrix
//	cv::Rodrigues(rvec, rotation);
//
//	// Move the bench	
//	cv::Affine3d pose(rotation, tvec);
//	visualizer.setWidgetPose("top", pose);
//	visualizer.setWidgetPose("bottom", pose);
//
//	// visualization loop
//	while (cv::waitKey(100) == -1 && !visualizer.wasStopped())
//	{
//
//		visualizer.spinOnce(1,     // pause 1ms 
//			true); // redraw
//	}
//
//
//
//
//
//
//
//
//	waitKey(0);
//	return 0;
//}






#pragma once
#if !defined CAMERACALIBRATOR_H
#define CAMERACALIBRATOR_H

#include <iostream>
#include <vector>

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

class CameraCalibrator
{
private:
	//输入点:
	//世界坐标系中的点
	//(每个正方形为一个单位)
	vector<vector<Point3f> > objectPoints;
	//点在图像中的位置(以像素为单位)
	vector<vector<Point2f> > imagePoints;
	//输出矩阵
	Mat cameraMatrix;
	Mat distCoeffs;
	//指定标定方式的标志
	int flag;
	//用于图像不失真
	Mat map1, map2;
	bool mustInitUndistort;

public:
	CameraCalibrator() : flag(0), mustInitUndistort(true){}
	
	//打开棋盘图像,提取角点
	int addChessboardPoints(const vector<string> &filelist, Size &boardSize, string windowName = "");
	// Add scene points and corresponding image points
	void addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners);
	//标定相机
	double calibrate(const Size imageSize);
	// Set the calibration flag
	void setCalibrationFlag(bool radial8CoeffEnabled = false, bool tangentialParamEnabled = false);
	//去除图像中的畸变(标定后)
	Mat remap(const Mat &image, Size &outputSize = Size(-1, -1));

	// Getters
	cv::Mat getCameraMatrix() { return cameraMatrix; }
	cv::Mat getDistCoeffs() { return distCoeffs; }

};


#endif // !defined CAMERACALIBRATOR_H





#include "CameraCalibrator.h"

//打开棋盘图像,提取角点
int CameraCalibrator::addChessboardPoints(
	const vector<string> &filelist,   //文件名列表
	Size &boardSize,                  //标定面板的大小
	string windowName                 //展示窗口的名字
)
{
	//棋盘上的角点
	vector<Point2f> imageCorners;
	vector<Point3f> objectCorners;
	//场景中的三维点
	//在棋盘坐标系中, 初始化棋盘中的角点
	//角点的三维坐标(X, Y,Z)= (i,j,0)
	for (int i = 0; i < boardSize.height; i++)
	{
		for (int j = 0; j < boardSize.width; j++)
		{
			objectCorners.push_back(Point3f(i, j, 0.0f));
		}
	}
	//图像上的二维点
	Mat image;  //用于存储棋盘图像
	int successes = 0;
	//处理所有视角
	for (int i = 0; i < filelist.size(); i++)
	{
		//打开图像
		image = imread(filelist[i], 0);
		//取得棋盘中的角点
		bool found = findChessboardCorners(image,       //包含棋盘图案的图像
			                               boardSize,   //图案的大小
			                               imageCorners //检测到角点的列表
		                                   );
		//取得角点上的亚像素级精度
		if (found)
		{
			cornerSubPix(image, imageCorners,
				Size(5, 5),
				Size(-1, -1),
				TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS,
					30,   //最大迭代次数
					0.1));//最小精度
			//如果棋盘是完好的,就把它加入结果
			if (imageCorners.size() == boardSize.area())
			{
				//加入从同一个视角得到的图像和场景点
				addPoints(imageCorners, objectCorners);
				successes++;
			}
		}
		if (windowName.length() > 0 && imageCorners.size() == boardSize.area())
		{
			//画角点
			drawChessboardCorners(image, boardSize, imageCorners, found);
			imshow(windowName, image);
			waitKey(100);
		}
	}

	return successes;
}


// Add scene points and corresponding image points
void CameraCalibrator::addPoints(const std::vector<cv::Point2f>& imageCorners, const std::vector<cv::Point3f>& objectCorners) {

	// 2D image points from one view
	imagePoints.push_back(imageCorners);
	// corresponding 3D scene points
	objectPoints.push_back(objectCorners);
}

// 标定相机
// 返回重投影误差
double CameraCalibrator::calibrate(const cv::Size imageSize)
{
	// undistorter must be reinitialized
	mustInitUndistort = true;

	//输出旋转量和平移量
	std::vector<cv::Mat> rvecs, tvecs;

	// 开始标定
	return
		calibrateCamera(objectPoints, // 三维点
			imagePoints,  // 图像点
			imageSize,    // 图像尺寸
			cameraMatrix, // 输出相机矩阵
			distCoeffs,   // 输出畸变矩阵
			rvecs, tvecs, // Rs, Ts 
			flag);        // 设置选项,CV_CALIB_USE_INTRINSIC_GUESS);

}

// 去除图像中的畸变(标定后)
cv::Mat CameraCalibrator::remap(const cv::Mat &image, cv::Size &outputSize) {

	cv::Mat undistorted;

	if (outputSize.height == -1)
		outputSize = image.size();

	if (mustInitUndistort) { // 每个标定过程调用一次

		cv::initUndistortRectifyMap(
			cameraMatrix,  // 计算得到的相机矩阵
			distCoeffs,    // 计算得到的畸变矩阵
			cv::Mat(),     // 可选矫正项 (无) 
			cv::Mat(),     // 生成无畸变的相机矩阵
			outputSize,    // 无畸变图像的尺寸
			CV_32FC1,      // 输出图片的类型
			map1, map2);   // x 和 y 映射功能

		mustInitUndistort = false;
	}

	// 映射功能
	cv::remap(image, undistorted, map1, map2,
		cv::INTER_LINEAR); // 插值类型

	return undistorted;
}


// Set the calibration options
// 8radialCoeffEnabled should be true if 8 radial coefficients are required (5 is default)
// tangentialParamEnabled should be true if tangeantial distortion is present
void CameraCalibrator::setCalibrationFlag(bool radial8CoeffEnabled, bool tangentialParamEnabled) {

	// Set the flag used in cv::calibrateCamera()
	flag = 0;
	if (!tangentialParamEnabled) flag += CV_CALIB_ZERO_TANGENT_DIST;
	if (radial8CoeffEnabled) flag += CV_CALIB_RATIONAL_MODEL;
}

Chapter_12 : 处理视频序列

1.读取视频处理 2.处理视频帧 3.提取视频中的眼前物体

  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
#include <opencv2/bgsegm.hpp>

#include "VideoProcessor.h"
#include "BGFGSegmentor.h"

void draw(const cv::Mat& img, cv::Mat& out) {

	img.copyTo(out);
	cv::circle(out, cv::Point(100, 100), 5, cv::Scalar(255, 0, 0), 2);
}

void canny(cv::Mat& img, cv::Mat& out) {

	// 转换为灰度
	if (img.channels() == 3)
		cv::cvtColor(img, out, cv::COLOR_BGR2GRAY);
	// 计算Canny边缘
	cv::Canny(out, out, 100, 200);
	// 反转图像
	cv::threshold(out, out, 128, 255, cv::THRESH_BINARY_INV);
}


int main()
{
	////1.读取视频处理
 //   //打开视频文件
	//VideoCapture capture("./images/bike.avi");
	////检查视频是否打开成功
	//if (!capture.isOpened() )
	//	return 1;
	////取得帧速率
	//double rate = capture.get(CV_CAP_PROP_FPS);

	//bool stop(false);
	//Mat frame;  //当前视频帧
	//namedWindow("Extracted Frame");

	////根据帧速率计算帧之间的等待时间, 单位为ms
	//int delay = 1000 / rate;
	////循环遍历视频中的全部帧
	//while (!stop)
	//{
	//	//读取下一帧(如果有)
	//	if (!capture.read(frame) )
	//		break;

	//	imshow("Extracted Frame", frame);

	//	//等待一段时间,或者通过按键停止
	//	if (waitKey(delay) >= 0)
	//		stop = true;
	//	
	//}

	////关闭视频文件
	////不是必须的,因为类的析构函数会调用
	//capture.release();


	////2.处理视频帧
	////打开视频文件
	//VideoCapture capture("./images/bike.avi");
	////检查视频是否打开
	//if (!capture.isOpened())
	//	return 1;
	////获取帧速率
	//double rate = capture.get(CV_CAP_PROP_FPS);
	//cout << "Frame rate: " << rate << "fps" << endl;

	//double stop(false);
	//Mat frame;
	////根据帧速率计算镇之间的等待时间,单位为ms
	//int delay = 1000 / rate;
	//long long i = 0;
	//string b = "bike";
	//string ext = ".bmp";



	////循环遍历视频中的全部帧
	//while (!stop)
	//{
	//	//读取下一帧(如果有)
	//	if (!capture.read(frame))
	//		break;
	//	imshow("Extracted Frame", frame);

	//	string name(b);
	//	ostringstream ss;
	//	ss << setfill('0') << setw(3) << i;
	//	name += ss.str();
	//	i++;
	//	name += ext;

	//	cout << name << endl;

	//	Mat test;

	//	//等待一段时间, 或者通过按键停止
	//	if (waitKey(delay) >= 0)
	//		stop = true;
	//}

	//capture.release();


	//waitKey();


	////现在创建一个自定义类VideoProcessor完整的封装了视频处理任务
	//VideoProcessor processor;
	////打开视频文件
	//processor.setInput("./images/bike.avi");
	////声明显示视频的窗口
	//processor.displayInput("Input Video");
	//processor.displayOutput("Output Video");
	////用原始帧速率播放视频
	//processor.setDelay(1000. / processor.getFrameRate());
	////设置处理帧的回调函数
	//processor.setFrameProcessor(canny);
	////输出视频
	//processor.setOutput("./images/bikeCanny.avi", -1, 15);
	////在当前帧停止处理
	//processor.stopAtFrameNo(51);
	////开始处理
	//processor.run();

	//waitKey();


	//3.提取视频中的眼前物体
	// 打开视频
	cv::VideoCapture capture("./images/bike.avi");
	// 检查是否打开成功
	if (!capture.isOpened())
		return 0;

	// 当前视频帧
	cv::Mat frame;
	// 前景的二值图像
	cv::Mat foreground;
	// 背景图
	cv::Mat background;

	cv::namedWindow("Extracted Foreground");

	// 混合高斯模型类的对象,全部采用默认参数
	cv::Ptr<cv::BackgroundSubtractor> ptrMOG = cv::bgsegm::createBackgroundSubtractorMOG();

	bool stop(false);
	// 遍历视频中的所有帧
	while (!stop)
	{

		// 读取下一帧(如果有)
		if (!capture.read(frame))
			break;

		// 更新背景并返回前景
		ptrMOG->apply(frame, foreground, 0.01);

		// 改进图像效果
		cv::threshold(foreground, foreground, 128, 255, cv::THRESH_BINARY_INV);

		// 显示前景
		cv::imshow("Extracted Foreground", foreground);

		// 产生延时或者按键结束
		if (cv::waitKey(10) >= 0)
			stop = true;
	}
	cv::waitKey();

	//// Create video procesor instance
	//VideoProcessor processor;

	//// Create background/foreground segmentor 
	//BGFGSegmentor segmentor;
	//segmentor.setThreshold(25);

	//// Open video file
	//processor.setInput("./images/bike.avi");

	//// set frame processor
	//processor.setFrameProcessor(&segmentor);

	//// Declare a window to display the video
	//processor.displayOutput("Extracted Foreground");

	//// Play the video at the original frame rate
	//processor.setDelay(1000. / processor.getFrameRate());

	//// Start the process
	//processor.run();

	//cv::waitKey();


	return 0;
}






#pragma once
#if !defined VPROCESSOR
#define VPROCESSOR

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//帧处理的接口
class FrameProcessor
{
public:
	//处理方法
	virtual void process(Mat &input, Mat &output) = 0;
};

class VideoProcessor
{
private:
	//Opencv视频捕获对象
	VideoCapture capture;
	//处理每一帧时都会调用的回调函数
	void(*process) (Mat&, Mat&);
	FrameProcessor *frameProcessor;
	//布尔型变量,表示该回调函数是否会被调用
	bool callIt;
	//输入窗口的显示名称
	string windowNameInput;
	//输出窗口的显示名称
	string windowNameOutput;
	//帧之间的延时
	int delay;
	//已经处理的帧数
	long fnumber;
	//达到这个帧数时结束
	long frameToStop;
	//结束处理
	bool stop;

	//用作输入图像的图像文件名向量
	vector<string> images;
	//图像容器的迭代器
	vector<string>::const_iterator itImg;
	//Opencv写视频对象
	VideoWriter writer;
	//输出文件
	string outputFile;
	//输出图像的当前序号
	int currentIndex;
	//输出图像文件名中数字的位数
	int digits;
	//输出图像的扩展名
	string extension;

	//取得下一帧
	//可以是视频文件、摄像机、图像向量
	bool readNextFrame(Mat &frame)
	{
		if (images.size()== 0)
			return capture.read(frame);
		else
		{
			if (itImg != images.end() )
			{
				frame = imread(*itImg);
				itImg++;
				return frame.data != 0;
			}

			return false;
		}
	}
		//写输出的帧
		//可以是视频文件或者图像组
		void writeNextFrame(Mat &frame)
		{
			if (extension.length())  //写入到图像组
			{
				stringstream ss;
				ss << outputFile << setfill('0') << setw(digits) << currentIndex++ << extension;
				imwrite(ss.str(), frame);
			}
			else   //写入到视频文件
			{
				writer.write(frame);
			}
		}
	
public:

	// 构造函数设置默认的值
	VideoProcessor() : callIt(false), delay(-1),fnumber(0), stop(false), digits(0), frameToStop(-1),process(0), frameProcessor(0) {}



	// 设置视频文件的名称
	bool setInput(std::string filename) {

		fnumber = 0;
		// 防止已经有资源和VideoCapture实例关联
		capture.release();
		images.clear();

		//打开视频文件
		return capture.open(filename);
	}

	// 设置相机id
	bool setInput(int id) {

		fnumber = 0;
		// 防止已经有资源和VideoCapture实例关联
		capture.release();
		images.clear();

		// 打开视频文件
		return capture.open(id);
	}

	// 设置输入图像的向量
	bool setInput(const std::vector<std::string>& imgs) {

		fnumber = 0;
		// 防止已经有资源和VideoCapture实例关联
		capture.release();

		// 将这个图像向量作为输入对象
		images = imgs;
		itImg = images.begin();

		return true;
	}

	// 设置输出视频文件
	// 默认情况下会使用与输入视频相同的参数
	bool setOutput(const std::string &filename, int codec = 0, double framerate = 0.0, bool isColor = true) {

		outputFile = filename;
		extension.clear();

		if (framerate == 0.0)
			framerate = getFrameRate(); // 与输入相同

		char c[4];
		//使用与输入相同的编解码器
		if (codec == 0) {
			codec = getCodec(c);
		}

		//打开输出视频
		return writer.open(outputFile, //文件名
			codec, // 所用的编解码器
			framerate,      // 视频的帧速率
			getFrameSize(), // 帧的尺寸
			isColor);       // 彩色视频?
	}

	// 设置输出一系列图像文件
	// 扩展名是 ".jpg",或者".bmp" ...
	bool setOutput(const std::string &filename, // 前缀
		const std::string &ext, // 图像文件的扩展名
		int numberOfDigits = 3,   //数字的位数
		int startIndex = 0) {     // 开始序号

	    // 数字的位数必须是正数
		if (numberOfDigits<0)
			return false;

		// 文件名和常用的扩展名
		outputFile = filename;
		extension = ext;

		// 文件编号方案中数字的位数
		digits = numberOfDigits;
		// 从这个序号开始编码
		currentIndex = startIndex;

		return true;
	}

	// set the callback function that will be called for each frame
	void setFrameProcessor(void(*frameProcessingCallback)(cv::Mat&, cv::Mat&)) {

		// 使回调函数失效
		frameProcessor = 0;
		// 这个就是即将被调用的帧处理实例
		process = frameProcessingCallback;
		callProcess();
	}

	// 设置实现 FrameProcessor接口的实例
	void setFrameProcessor(FrameProcessor* frameProcessorPtr) {

		// 使回调函数失效
		process = 0;
		// 这个就是即将被调用的帧处理实例
		frameProcessor = frameProcessorPtr;
		callProcess();
	}

	// stop streaming at this frame number
	void stopAtFrameNo(long frame) {

		frameToStop = frame;
	}

	//需要调用回调函数
	void callProcess() {

		callIt = true;
	}

	//不需要调用回调函数
	void dontCallProcess() {

		callIt = false;
	}

	//用于显示输入的帧
	void displayInput(std::string wn) {

		windowNameInput = wn;
		cv::namedWindow(windowNameInput);
	}

	//用于显示处理过的帧
	void displayOutput(std::string wn) {

		windowNameOutput = wn;
		cv::namedWindow(windowNameOutput);
	}

	// do not display the processed frames
	void dontDisplay() {

		cv::destroyWindow(windowNameInput);
		cv::destroyWindow(windowNameOutput);
		windowNameInput.clear();
		windowNameOutput.clear();
	}

	// 设置帧之间的延迟
	// 0 表示每一帧都在等待
	// 负数表示不延时
	void setDelay(int d) {

		delay = d;
	}

	// a count is kept of the processed frames
	long getNumberOfProcessedFrames() {

		return fnumber;
	}

	// return the size of the video frame
	cv::Size getFrameSize() {

		if (images.size() == 0) {

			// get size of from the capture device
			int w = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_WIDTH));
			int h = static_cast<int>(capture.get(cv::CAP_PROP_FRAME_HEIGHT));

			return cv::Size(w, h);

		}
		else { // if input is vector of images

			cv::Mat tmp = cv::imread(images[0]);
			if (!tmp.data) return cv::Size(0, 0);
			else return tmp.size();
		}
	}

	// 返回下一帧的编号
	long getFrameNumber() {

		if (images.size() == 0) {

			//从捕获设备获取信息
			long f = static_cast<long>(capture.get(cv::CAP_PROP_POS_FRAMES));
			return f;

		}
		else { // if input is vector of images

			return static_cast<long>(itImg - images.begin());
		}
	}

	// return the position in ms
	double getPositionMS() {

		// undefined for vector of images
		if (images.size() != 0) return 0.0;

		double t = capture.get(cv::CAP_PROP_POS_MSEC);
		return t;
	}

	// return the frame rate
	double getFrameRate() {

		// undefined for vector of images
		if (images.size() != 0) return 0;

		double r = capture.get(cv::CAP_PROP_FPS);
		return r;
	}

	// return the number of frames in video
	long getTotalFrameCount() {

		// for vector of images
		if (images.size() != 0) return images.size();

		long t = capture.get(cv::CAP_PROP_FRAME_COUNT);
		return t;
	}

	// get the codec of input video
	int getCodec(char codec[4]) {

		// undefined for vector of images
		if (images.size() != 0) return -1;

		union {
			int value;
			char code[4];
		} returned;

		returned.value = static_cast<int>(capture.get(cv::CAP_PROP_FOURCC));

		codec[0] = returned.code[0];
		codec[1] = returned.code[1];
		codec[2] = returned.code[2];
		codec[3] = returned.code[3];

		return returned.value;
	}

	// go to this frame number
	bool setFrameNumber(long pos) {

		// for vector of images
		if (images.size() != 0) {

			// move to position in vector
			itImg = images.begin() + pos;
			// is it a valid position?
			if (pos < images.size())
				return true;
			else
				return false;

		}
		else { // if input is a capture device

			return capture.set(cv::CAP_PROP_POS_FRAMES, pos);
		}
	}

	// go to this position
	bool setPositionMS(double pos) {

		// not defined in vector of images
		if (images.size() != 0)
			return false;
		else
			return capture.set(cv::CAP_PROP_POS_MSEC, pos);
	}

	// go to this position expressed in fraction of total film length
	bool setRelativePosition(double pos) {

		// for vector of images
		if (images.size() != 0) {

			// move to position in vector
			long posI = static_cast<long>(pos*images.size() + 0.5);
			itImg = images.begin() + posI;
			// is it a valid position?
			if (posI < images.size())
				return true;
			else
				return false;

		}
		else { // if input is a capture device

			return capture.set(cv::CAP_PROP_POS_AVI_RATIO, pos);
		}
	}

	//结束处理
	void stopIt() {

		stop = true;
	}

	// 处理过程是否已经停止?
	bool isStopped() {

		return stop;
	}

	// 捕获设备是否已经打开?
	bool isOpened() {

		return capture.isOpened() || !images.empty();
	}

	// 抓取并处理序列中的帧
	void run() {

		//当前帧
		cv::Mat frame;
		//输出帧
		cv::Mat output;

		//如果没有设置捕获设备
		if (!isOpened())
			return;

		stop = false;

		while (!isStopped()) {

			// 读取下一帧(如果有)
			if (!readNextFrame(frame))
				break;

			// 显示输入的帧
			if (windowNameInput.length() != 0)
				cv::imshow(windowNameInput, frame);

			// 调用处理函数
			if (callIt) {

				// 处理帧
				if (process)
					process(frame, output);
				else if (frameProcessor)
					frameProcessor->process(frame, output);
				// 递增帧数
				fnumber++;

			}
			else {
				//没有处理
				output = frame;
			}

			// 写入输出序列
			if (outputFile.length() != 0)
				writeNextFrame(output);

			// 显示输出的帧
			if (windowNameOutput.length() != 0)
				cv::imshow(windowNameOutput, output);

			// 产生延迟
			if (delay >= 0 && cv::waitKey(delay) >= 0)
				stopIt();

			// 检查是否需要结束
			if (frameToStop >= 0 && getFrameNumber() == frameToStop)
				stopIt();
		}
	}
};



#endif // !defined VPROCESSOR






#pragma once
#if !defined BGFGSeg
#define BGFGSeg

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include "videoprocessor.h"

class BGFGSegmentor : public FrameProcessor {

	cv::Mat gray;			// 当前灰度图像
	cv::Mat background;		// 累积的背景
	cv::Mat backImage;		// 当前背景图像
	cv::Mat foreground;		// 前景图像
	double learningRate;    // 累积背景时使用的学习效率
	int threshold;			// 提取前景的阈值

public:

	BGFGSegmentor() : threshold(10), learningRate(0.01) {}

	// Set the threshold used to declare a foreground
	void setThreshold(int t) {

		threshold = t;
	}

	// Set the learning rate
	void setLearningRate(double r) {

		learningRate = r;
	}

	// 处理方法
	void process(cv::Mat &frame, cv::Mat &output) {

		// 转换为灰度图
		cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);

		// 采用第一针初始化背景
		if (background.empty())
			gray.convertTo(background, CV_32F);

		// 将背景转化为 8U类型
		background.convertTo(backImage, CV_8U);

		// 计算图像与背景之间的差异
		cv::absdiff(backImage, gray, foreground);

		// 在前景图像上应用
		cv::threshold(foreground, output, threshold, 255, cv::THRESH_BINARY_INV);

		// 积累背景
		cv::accumulateWeighted(gray, background,
			// alpha*gray + (1-alpha)*background
			learningRate,  // 学习速率
			output);       // 掩码
	}
};

#endif

Chapter_13 : 跟踪运动物体

1.跟踪视频中的特征点 2.估算光流 3.跟踪视频中的物体

  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
#include "FeatureTracker.h"
#include "VideoProcessor.h"
#include "VisualTracker.h"

//绘制光流向量图
void drawOpticalFlow(const Mat& oflow, //光流
	                 Mat &flowImage,   //绘制的图像
	                 int  stride,      //显示向量的步长
	                 float scale,      //放大因子
	                 const Scalar& color)  //显示向量的颜色
{
	//必要时创建图像
	if (flowImage.size() != oflow.size())
	{
		flowImage.create(oflow.size(), CV_8UC3);
		flowImage = Vec3i(255, 255, 255);
	}
	//对所有向量, 以stride作为步长
	for (int y = 0; y < oflow.rows; y += stride)
	{
		for (int x = 0; x < oflow.cols; x += stride) 
		{
			// 获取向量	
			Point2f vector = oflow.at< Point2f>(y, x);
			// 画线条
			line(flowImage, Point(x, y),
				Point(static_cast<int>(x + scale*vector.x + 0.5),
					static_cast<int>(y + scale*vector.y + 0.5)), color);
			// 画顶端圆圈	
			circle(flowImage, Point(static_cast<int>(x + scale*vector.x + 0.5),
				static_cast<int>(y + scale*vector.y + 0.5)), 1, color, -1);
		}
   }
}


int main()
{
	////1.跟踪视频中的特征点
	////创建视频处理类的实例
	//VideoProcessor processor;
	////创建特征跟踪类的实例
	//FeatureTracker tracker;
	////打开视频文件
	//processor.setInput("./images/bike.avi");
	////设置帧处理类
	//processor.setFrameProcessor(&tracker);
	////声明显示视频的窗口
	//processor.displayOutput("Tracked Feature");
	////以原始帧速率播放视频
	//processor.setDelay(1000. / processor.getFrameRate());

	//processor.stopAtFrameNo(90);
	////开始处理
	//processor.run();
	//
	//waitKey();


	////2.估算光流
	//Mat frame1 = imread("./images/goose/goose230.bmp", 0);
	//Mat frame2 = imread("./images/goose/goose237.bmp", 0);

	//imshow("frame1", frame1);
	//imshow("frame2", frame2);

	//// Combined display
	//Mat combined(frame1.rows, frame1.cols + frame2.cols, CV_8U);
	//frame1.copyTo(combined.colRange(0, frame1.cols));
	//frame2.copyTo(combined.colRange(frame1.cols, frame1.cols + frame2.cols));
	//imshow("Frames", combined);

	//// 创建光流算法
	//Ptr<DualTVL1OpticalFlow> tvl1 = createOptFlow_DualTVL1();

	//cout << "regularization coeeficient: " << tvl1->getLambda() << endl; // the smaller the soomther
	//cout << "Number of scales: " << tvl1->getScalesNumber() << endl; // number of scales
	//cout << "Scale step: " << tvl1->getScaleStep() << endl; // size between scales
	//cout << "Number of warpings: " << tvl1->getWarpingsNumber() << endl; // size between scales
	//cout << "Stopping criteria: " << tvl1->getEpsilon() << " and " << tvl1->getOuterIterations() << endl; // size between scales
	//														// compute the optical flow between 2 frames
	//Mat oflow; // 二维光流向量的图像
	//// 计算frame1和frame2之间的光流
	//tvl1->calc(frame1, frame2, oflow);

	//// 绘制光流
	//Mat flowImage;
	//drawOpticalFlow(oflow,     // 输入光流向量 
	//	flowImage, // 生成的图像
	//	8,         // 每隔8个像素显示一个向量
	//	2,         // 长度延长2倍
	//	Scalar(0, 0, 0)); // 向量颜色

	//imshow("Optical Flow", flowImage);

	//// 计算两个帧之间更加光滑的光流
	//tvl1->setLambda(0.075);
	//tvl1->calc(frame1, frame2, oflow);

	//// Draw the optical flow image
	//Mat flowImage2;
	//drawOpticalFlow(oflow,     // input flow vectors 
	//	flowImage2, // image to be generated
	//	8,         // display vectors every 8 pixels
	//	2,         // multiply size of vectors by 2
	//	Scalar(0, 0, 0)); // vector color

	//imshow("Smoother Optical Flow", flowImage2);
 //   waitKey();


   
	//3.跟踪视频中的物体
	// 创建视频处理器实例
	VideoProcessor processor;

	// 生成文件名
	std::vector<std::string> imgs;
	std::string prefix = "images/goose/goose";
	std::string ext = ".bmp";

	// 添加用于跟踪的图像名称
	for (long i = 130; i < 317; i++) {

		string name(prefix);
		ostringstream ss;
		ss << setfill('0') << setw(3) << i;
		name += ss.str();
		name += ext;

		cout << name << endl;
		imgs.push_back(name);
	}

	// 创建特征提取器实例
	Ptr<TrackerMedianFlow> ptr = TrackerMedianFlow::create();
	VisualTracker tracker(ptr);
	// VisualTracker tracker(TrackerKCF::createTracker());

	// 打开视频文件
	processor.setInput(imgs);

	// 设置帧处理器
	processor.setFrameProcessor(&tracker);

	// 声明显示视频的窗口
	processor.displayOutput("Tracked object");

	// 定义显示的帧速率
	processor.setDelay(50);

	// 指定初始目标位置
	cv::Rect bb(290, 100, 65, 40);
	tracker.setBoundingBox(bb);

	// 开始跟踪
	processor.run();

	cv::waitKey();

	// 中值流量跟踪算法
	Mat image1 = imread("./images/goose/goose130.bmp", ImreadModes::IMREAD_GRAYSCALE);

	// 定义一个10 * 10 的网格
	vector<Point2f> grid;
	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < 10; j++) {
			Point2f p(bb.x + i*bb.width / 10., bb.y + j*bb.height / 10);
			grid.push_back(p);
		}
	}

	// track in next image
	Mat image2 = imread("./images/goose/goose131.bmp", ImreadModes::IMREAD_GRAYSCALE);
	vector<Point2f> newPoints;
	vector<uchar> status; // status of tracked features
	vector<float> err;    // error in tracking

							   // track the points
	calcOpticalFlowPyrLK(image1, image2, // 2 consecutive images
		grid,      // input point position in first image
		newPoints, // output point postion in the second image
		status,    // tracking success
		err);      // tracking error

				   // Draw the points
	for (Point2f p : grid)
	{

	   circle(image1, p, 1, Scalar(255, 255, 255), -1);
	}
	imshow("Initial points", image1);

	for (Point2f p : newPoints) {

		circle(image2, p, 1, Scalar(255, 255, 255), -1);
	}
	imshow("Tracked points", image2);

	waitKey();

	return 0;
}




#pragma once
#if !defined FTRACKER
#define FTRACKER

#include <iostream>
#include <string>
#include <vector>

#include <opencv2/opencv.hpp>

#include "VideoProcessor.h"


class FeatureTracker :public FrameProcessor
{
private:
	//当前的灰度图像
	Mat gray;
	//上一个灰度图像
	Mat gray_prev;
	//被跟踪的特征,从0到1
	vector<Point2f> points[2];
	//被跟踪特征点的初始位置
	vector<Point2f> initial;
	//被监测的特征
	vector<Point2f> features;
	//检测特征点的最大个数
	int max_count;
	//检测特征点的质量等级
	double qlevel;
	//两个特征点之间的最小差距
	double minDist;
	//被跟踪特征的状态
	vector<uchar> status;
	//跟踪中出现的误差
	vector<float> err;
public:

	FeatureTracker():max_count(500), qlevel(0.01), minDist(10.){}

	//处理方法
	void process(Mat &frame, Mat &output)
	{
		//装换成灰度图
		cvtColor(frame, gray, CV_BGR2GRAY);
		frame.copyTo(output);
		//1.如果必须添加新的特征点
		if (addNewPoints())
		{
			//检测特征点
			detectFeaturePoints();
			//在当前跟踪列表中添加检测的特征点
			points[0].insert(points[0].end(), features.begin(), features.end());
			initial.insert(initial.end(), features.begin(), features.end());
		}
		//对于序列中的第一幅图像
		if (gray_prev.empty())
			gray.copyTo(gray_prev);
		//2.跟踪特征
		calcOpticalFlowPyrLK(gray_prev, gray, //两个连续图像
			points[0],       //输入第一幅图像的特征点位置
			points[1],       //输入第二幅图像的特征点位置
			status,          //跟踪成功
			err);              //跟踪误差
		//3.循环检查被跟踪的特征点,剔除部分特征点
		int k = 0;
		for (int i = 0; i < points[1].size(); i++)
		{
			//是否保留这个特征点
			if (acceptTrackedPoint(i))
			{
				//在向量中保留这个特征点
				initial[k] = initial[i];
				points[1][k++] = points[1][i];
			}
		}
		//删除跟踪失败的特征点
		points[1].resize(k);
		initial.resize(k);
		//4.处理已经认可的被跟踪特征点
		handleTrackedPoints(frame, output);
		//5.让当前特征点和图像变成前一个
		swap(points[1], points[0]);
		swap(gray_prev, gray);
	}

	//特征点检测方法
	void detectFeaturePoints()
	{
		//检测特征点
		goodFeaturesToTrack(gray,  //图像
			features,  //输出检测到的特征点
			max_count, //特征点的最大数量
			qlevel,    //质量等级
			minDist);   //特征点之间的最小差距
	}

	//判断是否需要添加新的特征点
	bool addNewPoints()
	{
		//如果特征点数量太少
		return points[0].size() <= 10;
	}

	//判断需要保留的特征点
	bool acceptTrackedPoint(int i)
	{
		return status[i] &&
			//如果特征点已将移动
			(abs(points[0][i].x - points[1][i].x) +
			(abs(points[0][i].y - points[1][i].y)) > 2);
	}

	//处理当前跟踪的特征点
	void handleTrackedPoints(Mat &frame, Mat &output)
	{
		//遍历所有特征点
		for (int i = 0; i < points[1].size(); i++)
		{
			//画线和圆
			line(output, initial[i], points[1][i], Scalar(255, 255, 255));
			circle(output, points[1][i], 3, Scalar(255, 255, 255), -1);
		}		
	
	}
};

#endif // !defined FTRACKER




#pragma once
#if !defined VTRACKER
#define VTRACKER

#include <iostream>

#include <opencv2/opencv.hpp>
#include <opencv2/tracking/tracker.hpp>

#include "VideoProcessor.h"

using namespace std;
using namespace cv;


class VisualTracker : public FrameProcessor
{
private:
	Ptr<Tracker> tracker;
	Rect2d box;
	bool reset;
public:
	//构造函数指定选用的跟踪器
	VisualTracker(Ptr<Tracker> tracker) :reset(true), tracker(tracker) {}
	
	//设置矩形,以启动跟踪过程
	void setBoundingBox(const Rect2d& bb) {

		box = bb;
		reset = true;
	}
	//回调函数
	void process(Mat &frame, Mat &output)
	{
		if (reset)
		{ 
			// 新跟踪会话
			reset = false;
			tracker->init(frame, box);
		}
		else 
		{ 
			// 更新目标位置
			tracker->update(frame, box);
		}

		// 在当前帧中绘制矩形
		frame.copyTo(output);
		rectangle(output, box, Scalar(255, 255, 255), 2);
	}
};

#endif // !defined VTRACKER

Chapter_14 : 实用案列

1.用最邻近局部二值模式实现人脸识别 2.通过级联Haar特征实现物体和人脸定位 3.用支持向量机和方向梯度直方图实现物体与行人检测

  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
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/face.hpp>
#include <iomanip>
#include <opencv2/objdetect.hpp>
#include <opencv2/ml.hpp>

using namespace std;
using namespace cv;


//1.用最邻近局部二值模式实现人脸识别
//计算灰度图像的局部二值模式
void lbp(const Mat &image, Mat &result)
{
	assert(image.channels() == 1); // input image must be gray scale

	result.create(image.size(), CV_8U); // 必要时分配空间
										
     // 逐行处理,除了第一行和最后一行
	for (int j = 1; j<image.rows - 1; j++)
	{
		// 输入行的指针
		const uchar* previous = image.ptr<const uchar>(j - 1);
		const uchar* current = image.ptr<const uchar>(j);	 
		const uchar* next = image.ptr<const uchar>(j + 1); 

		uchar* output = result.ptr<uchar>(j);	// 输出行

		for (int i = 1; i<image.cols - 1; i++) {

			// 构建局部二值模式
			*output = previous[i - 1] > current[i] ? 1 : 0;
			*output |= previous[i] > current[i] ? 2 : 0;
			*output |= previous[i + 1] > current[i] ? 4 : 0;

			*output |= current[i - 1] > current[i] ? 8 : 0;
			*output |= current[i + 1] > current[i] ? 16 : 0;

			*output |= next[i - 1] > current[i] ? 32 : 0;
			*output |= next[i] > current[i] ? 64 : 0;
			*output |= next[i + 1] > current[i] ? 128 : 0;

			output++; // 下一个像素
		}
	}

	// 未处理的设置为0
	result.row(0).setTo(Scalar(0));
	result.row(result.rows - 1).setTo(Scalar(0));
	result.col(0).setTo(Scalar(0));
	result.col(result.cols - 1).setTo(Scalar(0));
}


//3.用支持向量机和方向梯度直方图实现物体与行人检测
// 画出一个单元格的HOG
void drawHOG(vector<float>::const_iterator hog, // HOG迭代器
	         int numberOfBins,        // HOG中的箱子数量
	         Mat &image,              // 单元格的图像
	         float scale = 1.0)       // 长度缩放比例
{       

	const float PI = 3.1415927;
	float binStep = PI / numberOfBins;
	float maxLength = image.rows;
	float cx = image.cols / 2.;
	float cy = image.rows / 2.;

	// 逐个箱子
	for (int bin = 0; bin < numberOfBins; bin++) {

		// 箱子方向
		float angle = bin*binStep;
		float dirX = cos(angle);
		float dirY = sin(angle);
		// 线条长度, 与箱子大小成正比
		float length = 0.5*maxLength* *(hog + bin);

		// 画线条
		float x1 = cx - dirX * length * scale;
		float y1 = cy - dirY * length * scale;
		float x2 = cx + dirX * length * scale;
		float y2 = cy + dirY * length * scale;
		line(image, Point(x1, y1), Point(x2, y2), CV_RGB(255, 255, 255), 1);
	}
}

// 在图像上绘制 HOG
void drawHOGDescriptors(const Mat &image,  // 输入图像
	                    Mat &hogImage,     // 结果HOG图像
	                    Size cellSize,     // 每个单元格的大小(忽略区块)
	                    int nBins)         // 箱子数量
{                             

	// 区块大小等于图像大小
	HOGDescriptor hog(Size( (image.cols / cellSize.width) * cellSize.width,(image.rows / cellSize.height) * cellSize.height),
		              Size( (image.cols / cellSize.width) * cellSize.width,(image.rows / cellSize.height) * cellSize.height),
		              cellSize,    // 区块步长(这里只有一个区块)
		              cellSize,    // 单元格大小
		              nBins);      // 箱子数量

	// 计算 HOG
	vector<float> descriptors;
	hog.compute(image, descriptors);

	float scale = 2.0 / * max_element(descriptors.begin(), descriptors.end());

	hogImage.create(image.rows, image.cols, CV_8U);

	vector<float>::const_iterator itDesc = descriptors.begin();

	for (int i = 0; i < image.rows / cellSize.height; i++) {
		for (int j = 0; j < image.cols / cellSize.width; j++) {
			// 画出每个单元格
			hogImage(Rect(j*cellSize.width, i*cellSize.height, cellSize.width, cellSize.height));
			drawHOG(itDesc, nBins,
				    hogImage(Rect(j*cellSize.width, i*cellSize.height,cellSize.width, cellSize.height)), scale);
			itDesc += nBins;
		}
	}
}



int main()
{

	////1.用最邻近局部二值模式实现人脸识别
	//Mat image = imread("./images/girl.jpg", IMREAD_GRAYSCALE);

	//imshow("Original image", image);

	//Mat lbpImage;
	//lbp(image, lbpImage);

	//imshow("LBP image", lbpImage);

	//Ptr<face::FaceRecognizer> recognizer =  face::LBPHFaceRecognizer::create(1,   //LBP模式的半径
	//	8,     //使用邻近像素的数量
	//	8, 8,  //网格大小
	//	200.); //最邻近的距离阈值

	////参考图像和标签的向量 
	//vector<Mat> referenceImages;
	//vector<int> labels;
	////打开参考图像
	//referenceImages.push_back(imread("./images/face0_1.png", IMREAD_GRAYSCALE));
	//labels.push_back(0); // 编号为0的人
	//referenceImages.push_back(imread("./images/face0_2.png", IMREAD_GRAYSCALE));
	//labels.push_back(0); // 编号为0的人
	//referenceImages.push_back(imread("./images/face0_2.png", IMREAD_GRAYSCALE));
	//labels.push_back(0); // 编号为0的人
	//referenceImages.push_back(imread("./images/face1_1.png", IMREAD_GRAYSCALE));
	//labels.push_back(1); // 编号为1的人
	//referenceImages.push_back(imread("./images/face1_2.png", IMREAD_GRAYSCALE));
	//labels.push_back(1); // 编号为1的人
	//referenceImages.push_back(imread("./images/face1_2.png", IMREAD_GRAYSCALE));
	//labels.push_back(1); // 编号为1的人


	///*Mat i1 = imread("./images/face0_1.png", IMREAD_GRAYSCALE);
	//Mat i2 = imread("./images/face0_2.png", IMREAD_GRAYSCALE);
	//imshow("i1", i1);
	//imshow("i2", i2);*/

	//// the 6 positive samples
	//Mat faceImages(2 * referenceImages[0].rows, 3 * referenceImages[0].cols, CV_8U);
	//for (int i = 0; i < 2; i++)
	//	for (int j = 0; j < 3; j++) {

	//		referenceImages[i * 2 + j].copyTo(faceImages(Rect(j*referenceImages[i * 2 + j].cols, i*referenceImages[i * 2 + j].rows, referenceImages[i * 2 + j].cols, referenceImages[i * 2 + j].rows)));
	//	}

	//resize(faceImages, faceImages, Size(), 0.5, 0.5);
	//imshow("Reference faces", faceImages);

	////通过计算LBPH进行训练
	//recognizer->train(referenceImages, labels);

	//int predictedLabel = -1;
	//double confidence = 0.0;

	//// Extract a face image
	//Mat inputImage;
	//resize(image(Rect(160, 75, 90, 90)), inputImage, Size(256, 256));
	//imshow("Input image", inputImage);

	//// 识别图像对应的编号
	//recognizer->predict(inputImage,     // 人脸图像 
	//	predictedLabel, // 识别结果
	//	confidence);    // 置信度

	//cout << "Image label= " << predictedLabel << " (" << confidence << ")" << endl;



	////2.通过级联Haar特征实现物体和人脸定位
	//// open the positive sample images
	//std::vector<cv::Mat> referenceImages;
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop00.png"));
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop01.png"));
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop02.png"));
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop03.png"));
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop04.png"));
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop05.png"));
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop06.png"));
	//referenceImages.push_back(cv::imread("./images/stopSamples/stop07.png"));

	//// create a composite image
	//cv::Mat positveImages(2 * referenceImages[0].rows, 4 * referenceImages[0].cols, CV_8UC3);
	//for (int i = 0; i < 2; i++)
	//	for (int j = 0; j < 4; j++) {

	//		referenceImages[i * 2 + j].copyTo(positveImages(cv::Rect(j*referenceImages[i * 2 + j].cols, i*referenceImages[i * 2 + j].rows, referenceImages[i * 2 + j].cols, referenceImages[i * 2 + j].rows)));
	//	}

	//cv::imshow("Positive samples", positveImages);

	//cv::Mat negative = cv::imread("./images/stopSamples/bg01.jpg");
	//cv::resize(negative, negative, cv::Size(), 0.33, 0.33);
	//cv::imshow("One negative sample", negative);

	//cv::Mat inputImage = cv::imread("./images/stopSamples/stop9.jpg");
	//cv::resize(inputImage, inputImage, cv::Size(), 0.5, 0.5);

	//cv::CascadeClassifier cascade;
	//if (!cascade.load("./images/stopSamples/classifier/cascade.xml")) {
	//	std::cout << "Error when loading the cascade classfier!" << std::endl;
	//	return -1;
	//}

	//// predict the label of this image
	//std::vector<cv::Rect> detections;

	//cascade.detectMultiScale(inputImage, // 输入图像
	//	detections, // 检测结果
	//	1.1,        // 缩小比例
	//	1,          // 所需近邻数量
	//	0,          // 标志位(不用)
	//	cv::Size(48, 48),    // 检测对象的最小尺寸
	//	cv::Size(128, 128)); // 检测对象的最大尺寸

	//std::cout << "detections= " << detections.size() << std::endl;
	//for (int i = 0; i < detections.size(); i++)
	//	cv::rectangle(inputImage, detections[i], cv::Scalar(255, 255, 255), 2);

	//cv::imshow("Stop sign detection", inputImage);

	//// Detecting faces
	//cv::Mat picture = cv::imread("./images/girl.jpg");
	//cv::CascadeClassifier faceCascade;
	//if (!faceCascade.load("D:\\Project_OpenCV\\Cmake\\install\\etc\\haarcascades\\haarcascade_frontalface_default.xml")) {
	//	std::cout << "Error when loading the face cascade classfier!" << std::endl;
	//	return -1;
	//}

	//faceCascade.detectMultiScale(picture, // input image 
	//	detections, // detection results
	//	1.1,        // scale reduction factor
	//	3,          // number of required neighbor detections
	//	0,          // flags (not used)
	//	cv::Size(48, 48),    // minimum object size to be detected
	//	cv::Size(128, 128)); // maximum object size to be detected

	//std::cout << "detections= " << detections.size() << std::endl;
	//// draw detections on image
	//for (int i = 0; i < detections.size(); i++)
	//	cv::rectangle(picture, detections[i], cv::Scalar(255, 255, 255), 2);

	//// Detecting eyes
	//cv::CascadeClassifier eyeCascade;
	//if (!eyeCascade.load("D:\\Project_OpenCV\\Cmake\\install\\etc\\haarcascades\\haarcascade_eye.xml")) {
	//	std::cout << "Error when loading the eye cascade classfier!" << std::endl;
	//	return -1;
	//}

	//eyeCascade.detectMultiScale(picture, // input image 
	//	detections, // detection results
	//	1.1,        // scale reduction factor
	//	3,          // number of required neighbor detections
	//	0,          // flags (not used)
	//	cv::Size(24, 24),    // minimum object size to be detected
	//	cv::Size(64, 64)); // maximum object size to be detected

	//std::cout << "detections= " << detections.size() << std::endl;
	//// draw detections on image
	//for (int i = 0; i < detections.size(); i++)
	//	cv::rectangle(picture, detections[i], cv::Scalar(0, 0, 0), 2);

	//cv::imshow("Detection results", picture);


	//3.用支持向量机和方向梯度直方图实现物体与行人检测
    Mat image = imread("./images/girl.jpg", IMREAD_GRAYSCALE);
    imshow("Original image", image);

    HOGDescriptor hog(Size((image.cols / 16) * 16, (image.rows / 16) * 16), // 窗口大小
	                  Size(16, 16),    // 区块大小
	                  Size(16, 16),    // 区块步长
	                  Size(4, 4),      // 单元格大小
                      9);              // 箱子数量

    vector<float> descriptors;

    // 调用 drawHOGDescriptors函数,在图像上绘制HOG
    Mat hogImage = image.clone();
    drawHOGDescriptors(image, hogImage, Size(16, 16), 9); 
	imshow("HOG image", hogImage);

    // generate the filename
    vector<string> imgs;
    string prefix = "./images/stopSamples/stop";
    string ext = ".png";

    // loading 8 positive samples
    vector<Mat> positives;

    for (long i = 0; i < 8; i++) 
	{

	     string name(prefix);
	     ostringstream ss; 
		 ss << setfill('0') << setw(2) << i; 
		 name += ss.str();
	     name += ext;

	     positives.push_back(imread(name, IMREAD_GRAYSCALE));
    }

    // the first 8 positive samples
    Mat posSamples(2 * positives[0].rows, 4 * positives[0].cols, CV_8U);
    for (int i = 0; i < 2; i++)
	  for (int j = 0; j < 4; j++)
	  {

		positives[i * 4 + j].copyTo(posSamples(Rect(j*positives[i * 4 + j].cols, i*positives[i * 4 + j].rows, positives[i * 4 + j].cols, positives[i * 4 + j].rows)));

	  }

    imshow("Positive samples", posSamples);


    // loading 8 negative samples
    string nprefix = "./images/stopSamples/neg";
    vector<Mat> negatives;

    for (long i = 0; i < 8; i++) 
	{

	     string name(nprefix);
	     ostringstream ss;
		 ss << setfill('0') << setw(2) << i;
		 name += ss.str();
	     name += ext;

	     negatives.push_back(imread(name, IMREAD_GRAYSCALE));
    }

    // the first 8 negative samples
    Mat negSamples(2 * negatives[0].rows, 4 * negatives[0].cols, CV_8U);
    for (int i = 0; i < 2; i++)
	for (int j = 0; j < 4; j++)
	{

		negatives[i * 4 + j].copyTo(negSamples(cv::Rect(j*negatives[i * 4 + j].cols, i*negatives[i * 4 + j].rows, negatives[i * 4 + j].cols, negatives[i * 4 + j].rows)));
	}

    imshow("Negative samples", negSamples);

    // The HOG descriptor for stop sign detection
    HOGDescriptor hogDesc(positives[0].size(), // 窗口大小
	                      Size(8, 8),    // 区块大小
	                      Size(4, 4),    // 区块步长
	                      Size(4, 4),    // 单元格大小
	                      9);            //  箱子数量

    // 计算第一个描述子
    vector<float> desc;
    hogDesc.compute(positives[0], desc);

    cout << "Positive sample size: " << positives[0].rows << "x" << positives[0].cols << endl;
    cout << "HOG descriptor size: " << desc.size() << endl;

    // 样本描述子矩阵
    int featureSize = desc.size();
    int numberOfSamples = positives.size() + negatives.size();
    // 创建存储样本HOG的矩阵
    Mat samples(numberOfSamples, featureSize, CV_32FC1);

    // 用第一个描述子填第一行
    for (int i = 0; i < featureSize; i++)
	    samples.ptr<float>(0)[i] = desc[i];

    // 计算正样本的描述子
    for (int j = 1; j < positives.size(); j++) 
	{
	   hogDesc.compute(positives[j], desc);
	   // 用当前描述子填下一行
	   for (int i = 0; i < featureSize; i++)
		    samples.ptr<float>(j)[i] = desc[i];
     }

     // 计算负样本的描述子
     for (int j = 0; j < negatives.size(); j++) 
	 {
	     hogDesc.compute(negatives[j], desc);
	     // 用当前描述子填下一行
	     for (int i = 0; i < featureSize; i++)
		    samples.ptr<float>(j + positives.size())[i] = desc[i];
      }

     // 创建标签
     Mat labels(numberOfSamples, 1, CV_32SC1);
     // 正样本的标签
     labels.rowRange(0, positives.size()) = 1.0;
     // 负样本的标签
     labels.rowRange(positives.size(), numberOfSamples) = -1.0;

    // 创建SVM分类器
    Ptr<ml::SVM> svm = ml::SVM::create();
    svm->setType(ml::SVM::C_SVC);
    svm->setKernel(ml::SVM::LINEAR);

    // 准备训练数据
    Ptr<ml::TrainData> trainingData =
    ml::TrainData::create(samples, ml::SampleTypes::ROW_SAMPLE, labels);

    // SVM 训练
    svm->train(trainingData);

    Mat queries(4, featureSize, CV_32FC1);

    // 每行填入查询描述子
    hogDesc.compute(imread("./images/stopSamples/stop08.png", IMREAD_GRAYSCALE), desc);
    for (int i = 0; i < featureSize; i++)
	     queries.ptr<float>(0)[i] = desc[i];
         hogDesc.compute(imread("./images/stopSamples/stop09.png", IMREAD_GRAYSCALE), desc);
    for (int i = 0; i < featureSize; i++)
	     queries.ptr<float>(1)[i] = desc[i];
         hogDesc.compute(imread("./images/stopSamples/neg08.png", IMREAD_GRAYSCALE), desc);
    for (int i = 0; i < featureSize; i++)
	    queries.ptr<float>(2)[i] = desc[i];
        hogDesc.compute(imread("./images/stopSamples/neg09.png", IMREAD_GRAYSCALE), desc);
    for (int i = 0; i < featureSize; i++)
	    queries.ptr<float>(3)[i] = desc[i];
    Mat predictions;

    // 测试分类器
    svm->predict(queries, predictions);

    for (int i = 0; i < 4; i++)
	    cout << "query: " << i << ": " << ((predictions.at<float>(i) < 0.0) ? "Negative" : "Positive") << endl;

   // 行人检测
   Mat myImage = imread("./images/person.jpg", IMREAD_GRAYSCALE);

   // 创建检测器
   vector<Rect> peoples;
   HOGDescriptor peopleHog;
   peopleHog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
   // 检测图像中的行人
   peopleHog.detectMultiScale(myImage, // 输入图像
	                          peoples, // 输出矩形列表 
	                          0,       // 判断检测结果是否有效的阈值
	                          Size(4, 4),       // 窗口步长
	                          Size(32, 32),     // 填充图像
	                          1.1,              // 缩放比列
	                          2);               // 分组阈值

   // draw detections on image
   cout << "Number of peoples detected: " << peoples.size() << endl;
   for (int i = 0; i < peoples.size(); i++)
	  rectangle(myImage, peoples[i], Scalar(255, 255, 255), 2);

   imshow("People detection", myImage);


	waitKey(0);
	return 0;
}