• 相机匹配点的选取(基于竖直直杆)


    上一篇博客已经进行霍夫直线检测的过程,那么之前的霍夫直线检测出来的直线进行拟合到一条直线之后有什么用呢?
    我的想法是进行直线拟合,就可以知道这条直线的表达式,通过提取相机匹配图像之中的匹配点距离这条直线的距离,从而提取出来最佳的匹配点.(当然这里只是我需要进行的,如果不是截取距离一条直线的距离,就不能这么用)

    最初的想法

    下面的代码的思路就是将左边提取出来的特征点,依次计算他们距离拟合直线的距离,得到的距离从小到达进行排序,依据排除的顺序,选取第一个和第八个大小的数值,作为提取出来的特殊匹配点,进行下面的操作.(整好跟之前的项目进行接轨)

    代码如下所示:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. using namespace cv;
    11. using namespace std;
    12. using namespace cv::xfeatures2d;
    13. float K = 7.23106;
    14. float B = -8556.64;
    15. int main()
    16. {
    17. //创建检测器
    18. //cv::Ptr detector = SiftFeatureDetector::create();//sift
    19. Ptr detector = SurfFeatureDetector::create(100);//创建SIFT特征检测器,可改成SURF/ORB
    20. //Ptr descriptor_extractor = DescriptorExtractor::create("SURF");//创建特征向量生成器,可改成SURF/ORB
    21. Ptr descriptor_matcher = DescriptorMatcher::create("BruteForce");//创建特征匹配器
    22. if (detector.empty())
    23. cout << "fail to create detector!";
    24. //读入图像
    25. Mat img1 = imread("左相机123.bmp", 0);
    26. Mat img2 = imread("右相机123.bmp", 0);
    27. //特征点检测
    28. vector m_LeftKey, m_RightKey;
    29. detector->detect(img1, m_LeftKey);//检测img1中的SIFT特征点,存储到m_LeftKey中
    30. detector->detect(img2, m_RightKey);
    31. cout << "图像1特征点个数:" << m_LeftKey.size() << endl;
    32. cout << "图像2特征点个数:" << m_RightKey.size() << endl;
    33. vector key_points_1, key_points_2;
    34. cv::Mat dstImage1, dstImage2;
    35. detector->detectAndCompute(img1, Mat(), key_points_1, dstImage1);
    36. detector->detectAndCompute(img2, Mat(), key_points_2, dstImage2);
    37. //根据特征点计算特征描述子矩阵,即特征向量矩阵
    38. double t = getTickCount();//当前滴答数
    39. t = ((double)getTickCount() - t) / getTickFrequency();
    40. cout << "SIFT算法用时:" << t << "秒" << endl;
    41. cout << "图像1特征描述矩阵大小:" << dstImage1.size()
    42. << ",特征向量个数:" << dstImage1.rows << ",维数:" << dstImage1.cols << endl;
    43. cout << "图像2特征描述矩阵大小:" << dstImage2.size()
    44. << ",特征向量个数:" << dstImage2.rows << ",维数:" << dstImage2.cols << endl;
    45. //画出特征点
    46. Mat img_m_LeftKey, img_m_RightKey;
    47. drawKeypoints(img1, m_LeftKey, img_m_LeftKey, Scalar::all(-1), 0);
    48. drawKeypoints(img2, m_RightKey, img_m_RightKey, Scalar::all(-1), 0);
    49. //imshow("Src1",img_m_LeftKey);
    50. //imshow("Src2",img_m_RightKey);
    51. //特征匹配
    52. vector matches;//匹配结果
    53. descriptor_matcher->match(dstImage1, dstImage2, matches);//匹配两个图像的特征矩阵
    54. cout << "Match个数:" << matches.size() << endl;
    55. //计算匹配结果中距离的最大和最小值
    56. //距离是指两个特征向量间的欧式距离,表明两个特征的差异,值越小表明两个特征点越接近
    57. double max_dist = 0;
    58. double min_dist = 100;
    59. for (int i = 0; i < matches.size(); i++)
    60. {
    61. double dist = matches[i].distance;
    62. if (dist < min_dist) min_dist = dist;
    63. if (dist > max_dist) max_dist = dist;
    64. }
    65. cout << "最大距离:" << max_dist << endl;
    66. cout << "最小距离:" << min_dist << endl;
    67. //筛选出较好的匹配点
    68. vector goodMatches;
    69. for (int i = 0; i < matches.size(); i++)
    70. {
    71. if (matches[i].distance < 0.2 * max_dist)
    72. {
    73. goodMatches.push_back(matches[i]);
    74. }
    75. }
    76. cout << "goodMatch个数:" << goodMatches.size() << endl;
    77. //画出匹配结果
    78. Mat img_matches;
    79. //红色连接的是匹配的特征点对,绿色是未匹配的特征点
    80. drawMatches(img1, m_LeftKey, img2, m_RightKey, goodMatches, img_matches,
    81. Scalar::all(-1), CV_RGB(0, 255, 0), Mat(), 2);
    82. imshow("MatchSIFT", img_matches);
    83. imwrite("E:/项目使用版/合并版/Sift+RANSAC/Sift+RANSAC/MatchSIFT.jpg", img_matches);
    84. IplImage result = img_matches;
    85. waitKey(0);
    86. //RANSAC匹配过程
    87. vector m_Matches = goodMatches;
    88. // 分配空间
    89. int ptCount = (int)m_Matches.size();
    90. Mat p1(ptCount, 2, CV_32F);
    91. Mat p2(ptCount, 2, CV_32F);
    92. // 把Keypoint转换为Mat
    93. Point2f pt;
    94. for (int i = 0; i < ptCount; i++)
    95. {
    96. pt = m_LeftKey[m_Matches[i].queryIdx].pt;
    97. p1.at<float>(i, 0) = pt.x;
    98. p1.at<float>(i, 1) = pt.y;
    99. pt = m_RightKey[m_Matches[i].trainIdx].pt;
    100. p2.at<float>(i, 0) = pt.x;
    101. p2.at<float>(i, 1) = pt.y;
    102. }
    103. // 用RANSAC方法计算F
    104. Mat m_Fundamental;
    105. vector m_RANSACStatus; // 这个变量用于存储RANSAC后每个点的状态
    106. findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);
    107. // 计算野点个数
    108. int OutlinerCount = 0;
    109. for (int i = 0; i < ptCount; i++)
    110. {
    111. if (m_RANSACStatus[i] == 0) // 状态为0表示野点
    112. {
    113. OutlinerCount++;
    114. }
    115. }
    116. int InlinerCount = ptCount - OutlinerCount; // 计算内点
    117. cout << "内点数为:" << InlinerCount << endl;
    118. // 这三个变量用于保存内点和匹配关系
    119. vector m_LeftInlier;
    120. vector m_RightInlier;
    121. vector m_InlierMatches;
    122. m_InlierMatches.resize(InlinerCount);
    123. m_LeftInlier.resize(InlinerCount);
    124. m_RightInlier.resize(InlinerCount);
    125. InlinerCount = 0;
    126. float inlier_minRx = img1.cols; //用于存储内点中右图最小横坐标,以便后续融合
    127. float distance;//点到直线的距离公式
    128. for (int i = 0, j = 0; i < ptCount; i++)
    129. {
    130. if (m_RANSACStatus[i] != 0)
    131. {
    132. m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
    133. m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
    134. m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
    135. m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
    136. m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
    137. m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
    138. cout << j << "左图像:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    139. cout << j << "右图像:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    140. j++;
    141. if (m_RightInlier[InlinerCount].x < inlier_minRx) inlier_minRx = m_RightInlier[InlinerCount].x; //存储内点中右图最小横坐标
    142. InlinerCount++;
    143. }
    144. }
    145. // 把内点转换为drawMatches可以使用的格式
    146. vector key1(InlinerCount);
    147. vector key2(InlinerCount);
    148. KeyPoint::convert(m_LeftInlier, key1);
    149. KeyPoint::convert(m_RightInlier, key2);
    150. // 显示计算F过后的内点匹配
    151. Mat OutImage;
    152. drawMatches(img1, key1, img2, key2, m_InlierMatches, OutImage);
    153. cvNamedWindow("Match features", 1);
    154. cvShowImage("Match features", &IplImage(OutImage));
    155. waitKey(0);
    156. return 0;
    157. }

    进行匹配之后的图像:

    挑选的自己想要的点:看到上面的点就是出现了问题,就是匹配的时候,针对于自己想要的点没有匹配好.

    这个地方对上一个博客之中那个竖直检测的代码进行了修改,发现了一些问题.

    对相机的焦距进行了调整之后,可以得到如下匹配图,效果就是跟之前相比,匹配点是多了很多.

    但是发现上上面的那个图并不是我们想要的点,因为对比下面的这条直线,明显结果是错误的.

    点到直线的距离公式如下所示:

    对于点到直线的距离对于特征点进行评价,找出符合要求自己想要的点.对于算法再次进行改进,分为垂直和不垂直两种情况进行分别探讨.使用一个vertical标志进行表征,相应的代码如下所示:

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. using namespace cv;
    11. using namespace std;
    12. using namespace cv::xfeatures2d;
    13. /*
    14. void BubbleSort1(float arr[], int length) {//从大到小排序
    15. float temp;
    16. for (int i = 0; i < length - 1; i++) {//最外层循环,一共运行数组长度-1次,代表了一共要比较的次数
    17. for (int j = length - 1; j > i; j--) {//每次循环代表了找出每次的最大值。
    18. if (arr[j] > arr[j - 1]) {
    19. temp = arr[j];
    20. arr[j] = arr[j - 1];
    21. arr[j - 1] = temp;
    22. }
    23. }
    24. }
    25. }
    26. */
    27. void BubbleSort1(float arr[], int length) {//从小到大排序
    28. float temp;
    29. for (int i = 0; i < length - 1; i++) {
    30. for (int j = length - 1; j > i; j--) {
    31. if (arr[j] < arr[j - 1]) {
    32. temp = arr[j];
    33. arr[j] = arr[j - 1];
    34. arr[j - 1] = temp;
    35. }
    36. }
    37. }
    38. }
    39. bool vertical = true;
    40. float K = 10000000;
    41. float B = 1763;
    42. int main()
    43. {
    44. //创建检测器
    45. //cv::Ptr detector = SiftFeatureDetector::create();//sift
    46. Ptr detector = SurfFeatureDetector::create(100);//创建SIFT特征检测器,可改成SURF/ORB
    47. //Ptr descriptor_extractor = DescriptorExtractor::create("SURF");//创建特征向量生成器,可改成SURF/ORB
    48. Ptr descriptor_matcher = DescriptorMatcher::create("BruteForce");//创建特征匹配器
    49. if (detector.empty())
    50. cout << "fail to create detector!";
    51. //读入图像
    52. Mat img1 = imread("左相机123.bmp", 0);
    53. Mat img2 = imread("右相机123.bmp", 0);
    54. Mat combine;
    55. hconcat(img1, img2, combine); //横向拼接
    56. //特征点检测
    57. vector m_LeftKey, m_RightKey;
    58. detector->detect(img1, m_LeftKey);//检测img1中的SIFT特征点,存储到m_LeftKey中
    59. detector->detect(img2, m_RightKey);
    60. cout << "图像1特征点个数:" << m_LeftKey.size() << endl;
    61. cout << "图像2特征点个数:" << m_RightKey.size() << endl;
    62. vector key_points_1, key_points_2;
    63. cv::Mat dstImage1, dstImage2;
    64. detector->detectAndCompute(img1, Mat(), key_points_1, dstImage1);
    65. detector->detectAndCompute(img2, Mat(), key_points_2, dstImage2);
    66. //根据特征点计算特征描述子矩阵,即特征向量矩阵
    67. double t = getTickCount();//当前滴答数
    68. t = ((double)getTickCount() - t) / getTickFrequency();
    69. cout << "SIFT算法用时:" << t << "秒" << endl;
    70. cout << "图像1特征描述矩阵大小:" << dstImage1.size()
    71. << ",特征向量个数:" << dstImage1.rows << ",维数:" << dstImage1.cols << endl;
    72. cout << "图像2特征描述矩阵大小:" << dstImage2.size()
    73. << ",特征向量个数:" << dstImage2.rows << ",维数:" << dstImage2.cols << endl;
    74. //画出特征点
    75. Mat img_m_LeftKey, img_m_RightKey;
    76. drawKeypoints(img1, m_LeftKey, img_m_LeftKey, Scalar::all(-1), 0);
    77. drawKeypoints(img2, m_RightKey, img_m_RightKey, Scalar::all(-1), 0);
    78. //imshow("Src1",img_m_LeftKey);
    79. //imshow("Src2",img_m_RightKey);
    80. //特征匹配
    81. vector matches;//匹配结果
    82. descriptor_matcher->match(dstImage1, dstImage2, matches);//匹配两个图像的特征矩阵
    83. cout << "Match个数:" << matches.size() << endl;
    84. //计算匹配结果中距离的最大和最小值
    85. //距离是指两个特征向量间的欧式距离,表明两个特征的差异,值越小表明两个特征点越接近
    86. double max_dist = 0;
    87. double min_dist = 300;
    88. for (int i = 0; i < matches.size(); i++)
    89. {
    90. double dist = matches[i].distance;
    91. if (dist < min_dist) min_dist = dist;
    92. if (dist > max_dist) max_dist = dist;
    93. }
    94. cout << "最大距离:" << max_dist << endl;
    95. cout << "最小距离:" << min_dist << endl;
    96. //筛选出较好的匹配点
    97. vector goodMatches;
    98. for (int i = 0; i < matches.size(); i++)
    99. {
    100. if (matches[i].distance < 0.2 * max_dist)
    101. {
    102. goodMatches.push_back(matches[i]);
    103. }
    104. }
    105. cout << "goodMatch个数:" << goodMatches.size() << endl;
    106. //画出匹配结果
    107. Mat img_matches;
    108. //红色连接的是匹配的特征点对,绿色是未匹配的特征点
    109. drawMatches(img1, m_LeftKey, img2, m_RightKey, goodMatches, img_matches,
    110. Scalar::all(-1), CV_RGB(0, 255, 0), Mat(), 2);
    111. imshow("MatchSIFT", img_matches);
    112. imwrite("MatchSIFT.jpg", img_matches);
    113. IplImage result = img_matches;
    114. waitKey(0);
    115. //RANSAC匹配过程
    116. vector m_Matches = goodMatches;
    117. // 分配空间
    118. int ptCount = (int)m_Matches.size();
    119. Mat p1(ptCount, 2, CV_32F);
    120. Mat p2(ptCount, 2, CV_32F);
    121. // 把Keypoint转换为Mat
    122. Point2f pt;
    123. for (int i = 0; i < ptCount; i++)
    124. {
    125. pt = m_LeftKey[m_Matches[i].queryIdx].pt;
    126. p1.at<float>(i, 0) = pt.x;
    127. p1.at<float>(i, 1) = pt.y;
    128. pt = m_RightKey[m_Matches[i].trainIdx].pt;
    129. p2.at<float>(i, 0) = pt.x;
    130. p2.at<float>(i, 1) = pt.y;
    131. }
    132. // 用RANSAC方法计算F
    133. Mat m_Fundamental;
    134. vector m_RANSACStatus; // 这个变量用于存储RANSAC后每个点的状态
    135. findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);
    136. // 计算野点个数
    137. int OutlinerCount = 0;
    138. for (int i = 0; i < ptCount; i++)
    139. {
    140. if (m_RANSACStatus[i] == 0) // 状态为0表示野点
    141. {
    142. OutlinerCount++;
    143. }
    144. }
    145. int InlinerCount = ptCount - OutlinerCount; // 计算内点
    146. cout << "内点数为:" << InlinerCount << endl;
    147. // 这三个变量用于保存内点和匹配关系
    148. vector m_LeftInlier;
    149. vector m_RightInlier;
    150. vector m_InlierMatches;
    151. m_InlierMatches.resize(InlinerCount);
    152. m_LeftInlier.resize(InlinerCount);
    153. m_RightInlier.resize(InlinerCount);
    154. InlinerCount = 0;
    155. float inlier_minRx = img1.cols; //用于存储内点中右图最小横坐标,以便后续融合
    156. float distance = 0;//点到直线的距离公式
    157. float point[80];//声明一个数组,用来存放80个数组,也就是80个数值距离,里面可能是存在0元素的
    158. for (int i = 0, j = 0; i < ptCount; i++)
    159. {
    160. if (m_RANSACStatus[i] != 0)
    161. {
    162. m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
    163. m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
    164. m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
    165. m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
    166. m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
    167. m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
    168. cout << j << "左图像:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    169. cout << j << "右图像:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    170. if ( vertical == false )
    171. {
    172. if (j < 80)
    173. {
    174. cout << "j:" << j << endl;
    175. point[j] = (float)abs((K * m_LeftInlier[InlinerCount].x + B - m_LeftInlier[InlinerCount].y) / sqrt(K * K + 1 * 1));
    176. cout << "(K * m_LeftInlier[InlinerCount].x + B - m_LeftInlier[InlinerCount].y)" << (K * m_LeftInlier[InlinerCount].x + B - m_LeftInlier[InlinerCount].y) << endl;
    177. cout << "sqrt(K * K + 1 * 1)" << sqrt(K * K + 1 * 1) << endl;
    178. cout << "point[j]:" << point[j] << endl;
    179. }
    180. }
    181. else {
    182. if (j < 80)
    183. {
    184. cout << "j:" << j << endl;
    185. point[j] = (float)abs(( m_LeftInlier[InlinerCount].x - B));
    186. cout << "(m_LeftInlier[InlinerCount].x)" << m_LeftInlier[InlinerCount].x << endl;
    187. cout << "point[j]:" << point[j] << endl;
    188. }
    189. }
    190. j++;
    191. if (m_RightInlier[InlinerCount].x < inlier_minRx) inlier_minRx = m_RightInlier[InlinerCount].x; //存储内点中右图最小横坐标
    192. InlinerCount++;
    193. }
    194. }
    195. // 把内点转换为drawMatches可以使用的格式
    196. vector key1(InlinerCount);
    197. vector key2(InlinerCount);
    198. KeyPoint::convert(m_LeftInlier, key1);
    199. KeyPoint::convert(m_RightInlier, key2);
    200. // 显示计算F过后的内点匹配
    201. Mat OutImage;
    202. drawMatches(img1, key1, img2, key2, m_InlierMatches, OutImage);
    203. imwrite("test1.bmp", OutImage);
    204. cvNamedWindow("Match features better", 1);
    205. cvShowImage("Match features better ", &IplImage(OutImage));
    206. waitKey(0);
    207. BubbleSort1(point, 80);//将上面的距离进行了排序,从小进行排序
    208. float pointselect[8];//
    209. int select = 0;
    210. for (int i = 0; i < 80; i++)//提出为0的距离,从小到大选出8个数值
    211. {
    212. if (point[i] > 0.5 )
    213. {
    214. pointselect[select] = point[i];
    215. cout << "select元素:" << pointselect[select] << endl;
    216. select++;
    217. if (select == 8)
    218. {
    219. break;
    220. }
    221. }
    222. }
    223. cout << "select:" << select << endl;
    224. /****************************************************************************************************/
    225. //这里我们接下来就是选取几个数值之中距离最小和最大的.
    226. m_InlierMatches.resize(InlinerCount);
    227. m_LeftInlier.resize(InlinerCount);
    228. m_RightInlier.resize(InlinerCount);
    229. InlinerCount = 0;
    230. inlier_minRx = img1.cols; //用于存储内点中右图最小横坐标,以便后续融合
    231. for (int i = 0, j = 0; i < ptCount; i++)
    232. {
    233. if (m_RANSACStatus[i] != 0)
    234. {
    235. m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
    236. m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
    237. m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
    238. m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
    239. m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
    240. m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
    241. //cout << j << "左图像:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    242. //cout << j << "右图像:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    243. j++;
    244. if (vertical == false)
    245. {
    246. if ((float)abs((K*m_LeftInlier[InlinerCount].x + B - m_LeftInlier[InlinerCount].y) / sqrt(K * K + 1 * 1)) == pointselect[0])//最小点
    247. {
    248. cout << j << "左图像选出的点1:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    249. cout << j << "右图像选出的点1:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    250. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    251. }
    252. if ((float)abs((K*m_LeftInlier[InlinerCount].x + B - m_LeftInlier[InlinerCount].y) / sqrt(K * K + 1 * 1)) == pointselect[7])//最大点
    253. {
    254. cout << j << "左图像选出的点2:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    255. cout << j << "右图像选出的点2:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    256. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    257. }
    258. }
    259. else
    260. {
    261. if ((float)abs(m_LeftInlier[InlinerCount].x - B) == pointselect[0])//最小点
    262. {
    263. cout << j << "左图像选出的点1:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    264. cout << j << "右图像选出的点1:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    265. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    266. }
    267. if ((float)abs(m_LeftInlier[InlinerCount].x - B)== pointselect[7])//最大点
    268. {
    269. cout << j << "左图像选出的点2:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    270. cout << j << "右图像选出的点2:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    271. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    272. }
    273. }
    274. if (m_RightInlier[InlinerCount].x < inlier_minRx) inlier_minRx = m_RightInlier[InlinerCount].x; //存储内点中右图最小横坐标
    275. InlinerCount++;
    276. }
    277. }
    278. imwrite("combine.bmp",combine);
    279. resize(combine, combine, Size(1024, 540));//显示改变大小
    280. imshow("输出想要的匹配点", combine);
    281. waitKey(0);
    282. return 0;
    283. }

    最终得到的输出点是如下所示:

    可见上面的点是比较正常的.

    提取点到直线的垂足

    (对上面的代码再次进行迭代)
    点到直线的垂足公式,这个地方是比较容易推导的,下面是我自己进行推导的过程.

    还是要提前进行情况的分析,①不垂直的情况是可以用上面的公式进行表征;②垂直的情况, 

    基于上述的代码再次进行分析. 

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. using namespace cv;
    11. using namespace std;
    12. using namespace cv::xfeatures2d;
    13. /*
    14. void BubbleSort1(float arr[], int length) {//从大到小排序
    15. float temp;
    16. for (int i = 0; i < length - 1; i++) {//最外层循环,一共运行数组长度-1次,代表了一共要比较的次数
    17. for (int j = length - 1; j > i; j--) {//每次循环代表了找出每次的最大值。
    18. if (arr[j] > arr[j - 1]) {
    19. temp = arr[j];
    20. arr[j] = arr[j - 1];
    21. arr[j - 1] = temp;
    22. }
    23. }
    24. }
    25. }
    26. */
    27. void BubbleSort1(float arr[], int length) {//从小到大排序
    28. float temp;
    29. for (int i = 0; i < length - 1; i++) {
    30. for (int j = length - 1; j > i; j--) {
    31. if (arr[j] < arr[j - 1]) {
    32. temp = arr[j];
    33. arr[j] = arr[j - 1];
    34. arr[j - 1] = temp;
    35. }
    36. }
    37. }
    38. }
    39. float xll1, xrr1, yll1, yrr1;
    40. float xll2, xrr2, yll2, yrr2;
    41. bool vertical1 = true;
    42. float K1 = 10000000;
    43. float B1 = 1763;
    44. bool vertical2 = false;
    45. float K2= -6.11746;
    46. float B2 = 4138.73;
    47. int main()
    48. {
    49. //创建检测器
    50. //cv::Ptr detector = SiftFeatureDetector::create();//sift
    51. Ptr detector = SurfFeatureDetector::create(100);//创建SIFT特征检测器,可改成SURF/ORB
    52. //Ptr descriptor_extractor = DescriptorExtractor::create("SURF");//创建特征向量生成器,可改成SURF/ORB
    53. Ptr descriptor_matcher = DescriptorMatcher::create("BruteForce");//创建特征匹配器
    54. if (detector.empty())
    55. cout << "fail to create detector!";
    56. //读入图像
    57. Mat img1 = imread("左相机123.bmp", 0);
    58. Mat img2 = imread("右相机123.bmp", 0);
    59. Mat combine;
    60. hconcat(img1, img2, combine); //横向拼接
    61. //特征点检测
    62. vector m_LeftKey, m_RightKey;
    63. detector->detect(img1, m_LeftKey);//检测img1中的SIFT特征点,存储到m_LeftKey中
    64. detector->detect(img2, m_RightKey);
    65. cout << "图像1特征点个数:" << m_LeftKey.size() << endl;
    66. cout << "图像2特征点个数:" << m_RightKey.size() << endl;
    67. vector key_points_1, key_points_2;
    68. cv::Mat dstImage1, dstImage2;
    69. detector->detectAndCompute(img1, Mat(), key_points_1, dstImage1);
    70. detector->detectAndCompute(img2, Mat(), key_points_2, dstImage2);
    71. //根据特征点计算特征描述子矩阵,即特征向量矩阵
    72. double t = getTickCount();//当前滴答数
    73. t = ((double)getTickCount() - t) / getTickFrequency();
    74. cout << "SIFT算法用时:" << t << "秒" << endl;
    75. cout << "图像1特征描述矩阵大小:" << dstImage1.size()
    76. << ",特征向量个数:" << dstImage1.rows << ",维数:" << dstImage1.cols << endl;
    77. cout << "图像2特征描述矩阵大小:" << dstImage2.size()
    78. << ",特征向量个数:" << dstImage2.rows << ",维数:" << dstImage2.cols << endl;
    79. //画出特征点
    80. Mat img_m_LeftKey, img_m_RightKey;
    81. drawKeypoints(img1, m_LeftKey, img_m_LeftKey, Scalar::all(-1), 0);
    82. drawKeypoints(img2, m_RightKey, img_m_RightKey, Scalar::all(-1), 0);
    83. //imshow("Src1",img_m_LeftKey);
    84. //imshow("Src2",img_m_RightKey);
    85. //特征匹配
    86. vector matches;//匹配结果
    87. descriptor_matcher->match(dstImage1, dstImage2, matches);//匹配两个图像的特征矩阵
    88. cout << "Match个数:" << matches.size() << endl;
    89. //计算匹配结果中距离的最大和最小值
    90. //距离是指两个特征向量间的欧式距离,表明两个特征的差异,值越小表明两个特征点越接近
    91. double max_dist = 0;
    92. double min_dist = 300;
    93. for (int i = 0; i < matches.size(); i++)
    94. {
    95. double dist = matches[i].distance;
    96. if (dist < min_dist) min_dist = dist;
    97. if (dist > max_dist) max_dist = dist;
    98. }
    99. cout << "最大距离:" << max_dist << endl;
    100. cout << "最小距离:" << min_dist << endl;
    101. //筛选出较好的匹配点
    102. vector goodMatches;
    103. for (int i = 0; i < matches.size(); i++)
    104. {
    105. if (matches[i].distance < 0.2 * max_dist)
    106. {
    107. goodMatches.push_back(matches[i]);
    108. }
    109. }
    110. cout << "goodMatch个数:" << goodMatches.size() << endl;
    111. //画出匹配结果
    112. Mat img_matches;
    113. //红色连接的是匹配的特征点对,绿色是未匹配的特征点
    114. drawMatches(img1, m_LeftKey, img2, m_RightKey, goodMatches, img_matches,
    115. Scalar::all(-1), CV_RGB(0, 255, 0), Mat(), 2);
    116. imshow("MatchSIFT", img_matches);
    117. imwrite("MatchSIFT.jpg", img_matches);
    118. IplImage result = img_matches;
    119. waitKey(0);
    120. //RANSAC匹配过程
    121. vector m_Matches = goodMatches;
    122. // 分配空间
    123. int ptCount = (int)m_Matches.size();
    124. Mat p1(ptCount, 2, CV_32F);
    125. Mat p2(ptCount, 2, CV_32F);
    126. // 把Keypoint转换为Mat
    127. Point2f pt;
    128. for (int i = 0; i < ptCount; i++)
    129. {
    130. pt = m_LeftKey[m_Matches[i].queryIdx].pt;
    131. p1.at<float>(i, 0) = pt.x;
    132. p1.at<float>(i, 1) = pt.y;
    133. pt = m_RightKey[m_Matches[i].trainIdx].pt;
    134. p2.at<float>(i, 0) = pt.x;
    135. p2.at<float>(i, 1) = pt.y;
    136. }
    137. // 用RANSAC方法计算F
    138. Mat m_Fundamental;
    139. vector m_RANSACStatus; // 这个变量用于存储RANSAC后每个点的状态
    140. findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);
    141. // 计算野点个数
    142. int OutlinerCount = 0;
    143. for (int i = 0; i < ptCount; i++)
    144. {
    145. if (m_RANSACStatus[i] == 0) // 状态为0表示野点
    146. {
    147. OutlinerCount++;
    148. }
    149. }
    150. int InlinerCount = ptCount - OutlinerCount; // 计算内点
    151. cout << "内点数为:" << InlinerCount << endl;
    152. // 这三个变量用于保存内点和匹配关系
    153. vector m_LeftInlier;
    154. vector m_RightInlier;
    155. vector m_InlierMatches;
    156. m_InlierMatches.resize(InlinerCount);
    157. m_LeftInlier.resize(InlinerCount);
    158. m_RightInlier.resize(InlinerCount);
    159. InlinerCount = 0;
    160. float inlier_minRx = img1.cols; //用于存储内点中右图最小横坐标,以便后续融合
    161. float distance = 0;//点到直线的距离公式
    162. float point[80];//声明一个数组,用来存放80个数组,也就是80个数值距离,里面可能是存在0元素的
    163. for (int i = 0, j = 0; i < ptCount; i++)
    164. {
    165. if (m_RANSACStatus[i] != 0)
    166. {
    167. m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
    168. m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
    169. m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
    170. m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
    171. m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
    172. m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
    173. cout << j << "左图像:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    174. cout << j << "右图像:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    175. if ( vertical1 == false )
    176. {
    177. if (j < 80)
    178. {
    179. cout << "j:" << j << endl;
    180. point[j] = (float)abs((K1 * m_LeftInlier[InlinerCount].x + B1 - m_LeftInlier[InlinerCount].y) / sqrt(K1 * K1 + 1 * 1));
    181. cout << "(K1 * m_LeftInlier[InlinerCount].x + B1 - m_LeftInlier[InlinerCount].y)" << (K1 * m_LeftInlier[InlinerCount].x + B1 - m_LeftInlier[InlinerCount].y) << endl;
    182. cout << "sqrt(K1 * K1 + 1 * 1)" << sqrt(K1 * K1 + 1 * 1) << endl;
    183. cout << "point[j]:" << point[j] << endl;
    184. }
    185. }
    186. else {
    187. if (j < 80)
    188. {
    189. cout << "j:" << j << endl;
    190. point[j] = (float)abs(( m_LeftInlier[InlinerCount].x - B1));
    191. cout << "(m_LeftInlier[InlinerCount].x)" << m_LeftInlier[InlinerCount].x << endl;
    192. cout << "point[j]:" << point[j] << endl;
    193. }
    194. }
    195. j++;
    196. if (m_RightInlier[InlinerCount].x < inlier_minRx) inlier_minRx = m_RightInlier[InlinerCount].x; //存储内点中右图最小横坐标
    197. InlinerCount++;
    198. }
    199. }
    200. // 把内点转换为drawMatches可以使用的格式
    201. vector key1(InlinerCount);
    202. vector key2(InlinerCount);
    203. KeyPoint::convert(m_LeftInlier, key1);
    204. KeyPoint::convert(m_RightInlier, key2);
    205. // 显示计算F过后的内点匹配
    206. Mat OutImage;
    207. drawMatches(img1, key1, img2, key2, m_InlierMatches, OutImage);
    208. imwrite("test1.bmp", OutImage);
    209. cvNamedWindow("Match features better", 1);
    210. cvShowImage("Match features better ", &IplImage(OutImage));
    211. waitKey(0);
    212. BubbleSort1(point, 80);//将上面的距离进行了排序,从小进行排序
    213. float pointselect[8];//
    214. int select = 0;
    215. for (int i = 0; i < 80; i++)//提出为0的距离,从小到大选出8个数值
    216. {
    217. if (point[i] > 0.5 )
    218. {
    219. pointselect[select] = point[i];
    220. cout << "select元素:" << pointselect[select] << endl;
    221. select++;
    222. if (select == 8)
    223. {
    224. break;
    225. }
    226. }
    227. }
    228. cout << "select:" << select << endl;
    229. /****************************************************************************************************/
    230. //这里我们接下来就是选取几个数值之中距离最小和最大的.
    231. m_InlierMatches.resize(InlinerCount);
    232. m_LeftInlier.resize(InlinerCount);
    233. m_RightInlier.resize(InlinerCount);
    234. InlinerCount = 0;
    235. inlier_minRx = img1.cols; //用于存储内点中右图最小横坐标,以便后续融合
    236. for (int i = 0, j = 0; i < ptCount; i++)
    237. {
    238. if (m_RANSACStatus[i] != 0)
    239. {
    240. m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
    241. m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
    242. m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
    243. m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
    244. m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
    245. m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
    246. //cout << j << "左图像:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    247. //cout << j << "右图像:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    248. j++;
    249. if (vertical1 == false && vertical2 == false)//直线1 和直线2都不垂直
    250. {
    251. if ((float)abs((K1*m_LeftInlier[InlinerCount].x + B1 - m_LeftInlier[InlinerCount].y) / sqrt(K1 * K1 + 1 * 1)) == pointselect[0])//最小点
    252. {
    253. //匹配点
    254. cout << j << "左图像选出的点1:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    255. cout << j << "右图像选出的点1:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    256. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    257. //垂足
    258. xll1 = (float)(m_LeftInlier[InlinerCount].x + K1 * m_LeftInlier[InlinerCount].y - K1 * B1) / (K1 * K1 + 1);
    259. yll1 = (float)(K1 * m_LeftInlier[InlinerCount].x + K1 * K1 * m_LeftInlier[InlinerCount].y + B1) / (K1 * K1 + 1);
    260. xrr1 = (float)(m_RightInlier[InlinerCount].x + K2 * m_RightInlier[InlinerCount].y - K2 * B2) / (K2 * K2 + 1);
    261. yrr1 = (float)(K2 * m_RightInlier[InlinerCount].x + K2 * K2 * m_RightInlier[InlinerCount].y + B2) / (K2 * K2 + 1);
    262. }
    263. if ((float)abs((K1*m_LeftInlier[InlinerCount].x + B1 - m_LeftInlier[InlinerCount].y) / sqrt(K1 * K1 + 1 * 1)) == pointselect[7])//最大点
    264. {
    265. cout << j << "左图像选出的点2:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    266. cout << j << "右图像选出的点2:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    267. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    268. //垂足
    269. xll2 = (float)(m_LeftInlier[InlinerCount].x + K1 * m_LeftInlier[InlinerCount].y - K1 * B1) / (K1 * K1 + 1);
    270. yll2 = (float)(K1 * m_LeftInlier[InlinerCount].x + K1 * K1 * m_LeftInlier[InlinerCount].y + B1) / (K1 * K1 + 1);
    271. xrr2 = (float)(m_RightInlier[InlinerCount].x + K2 * m_RightInlier[InlinerCount].y - K2 * B2) / (K2 * K2 + 1);
    272. yrr2 = (float)(K2 * m_RightInlier[InlinerCount].x + K2 * K2 * m_RightInlier[InlinerCount].y + B2) / (K2 * K2 + 1);
    273. }
    274. }
    275. else if (vertical1 == true && vertical2 == true) //直线1和直线2都垂直
    276. {
    277. if ((float)abs(m_LeftInlier[InlinerCount].x - B1) == pointselect[0])//最小点x
    278. {
    279. cout << j << "左图像选出的点1:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    280. cout << j << "右图像选出的点1:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    281. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    282. //垂足
    283. xll1 = (float)(B1);
    284. yll1 = (float)(m_LeftInlier[InlinerCount].y);
    285. xrr1 = (float)(B2);
    286. yrr1 = (float)(m_RightInlier[InlinerCount].y);
    287. }
    288. if ((float)abs(m_LeftInlier[InlinerCount].x - B1)== pointselect[7])//最大点
    289. {
    290. cout << j << "左图像选出的点2:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    291. cout << j << "右图像选出的点2:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    292. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    293. //垂足
    294. xll2 = (float)(B1);
    295. yll2 = (float)(m_LeftInlier[InlinerCount].y);
    296. xrr2 = (float)(B2);
    297. yrr2 = (float)(m_RightInlier[InlinerCount].y);
    298. }
    299. }
    300. else if (vertical1 == true && vertical2 == false)//直线1垂直 直线二不垂直
    301. {
    302. if ((float)abs(m_LeftInlier[InlinerCount].x - B1) == pointselect[0])//最小点x
    303. {
    304. cout << j << "左图像选出的点1:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    305. cout << j << "右图像选出的点1:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    306. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    307. //垂足
    308. xll1 = (float)(B1);
    309. yll1 = (float)(m_LeftInlier[InlinerCount].y);
    310. xrr1 = (float)(m_RightInlier[InlinerCount].x + K2 * m_RightInlier[InlinerCount].y - K2 * B2) / (K2 * K2 + 1);
    311. yrr1 = (float)(K2 * m_RightInlier[InlinerCount].x + K2 * K2 * m_RightInlier[InlinerCount].y + B2) / (K2 * K2 + 1);
    312. }
    313. if ((float)abs(m_LeftInlier[InlinerCount].x - B1) == pointselect[7])//最大点
    314. {
    315. cout << j << "左图像选出的点2:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    316. cout << j << "右图像选出的点2:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    317. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    318. //垂足
    319. xll2 = (float)(B1);
    320. yll2 = (float)(m_LeftInlier[InlinerCount].y);
    321. xrr2 = (float)(m_RightInlier[InlinerCount].x + K2 * m_RightInlier[InlinerCount].y - K2 * B2) / (K2 * K2 + 1);
    322. yrr2 = (float)(K2 * m_RightInlier[InlinerCount].x + K2 * K2 * m_RightInlier[InlinerCount].y + B2) / (K2 * K2 + 1);
    323. }
    324. }
    325. if (m_RightInlier[InlinerCount].x < inlier_minRx) inlier_minRx = m_RightInlier[InlinerCount].x; //存储内点中右图最小横坐标
    326. InlinerCount++;
    327. }
    328. if (vertical1 == false && vertical2 == false)//直线1不垂直 直线2垂直
    329. {
    330. if ((float)abs((K1*m_LeftInlier[InlinerCount].x + B1 - m_LeftInlier[InlinerCount].y) / sqrt(K1 * K1 + 1 * 1)) == pointselect[0])//最小点
    331. {
    332. //匹配点
    333. cout << j << "左图像选出的点1:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    334. cout << j << "右图像选出的点1:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    335. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    336. //垂足
    337. xll1 = (float)(m_LeftInlier[InlinerCount].x + K1 * m_LeftInlier[InlinerCount].y - K1 * B1) / (K1 * K1 + 1);
    338. yll1 = (float)(K1 * m_LeftInlier[InlinerCount].x + K1 * K1 * m_LeftInlier[InlinerCount].y + B1) / (K1 * K1 + 1);
    339. xrr1 = (float)(B2);
    340. yrr1 = (float)(m_RightInlier[InlinerCount].y);
    341. }
    342. if ((float)abs((K1*m_LeftInlier[InlinerCount].x + B1 - m_LeftInlier[InlinerCount].y) / sqrt(K1 * K1 + 1 * 1)) == pointselect[7])//最大点
    343. {
    344. cout << j << "左图像选出的点2:" << m_LeftInlier[InlinerCount].x << " " << m_LeftInlier[InlinerCount].y << endl;
    345. cout << j << "右图像选出的点2:" << m_RightInlier[InlinerCount].x << " " << m_RightInlier[InlinerCount].y << endl;
    346. line(combine, Point2d(m_LeftInlier[InlinerCount].x, m_LeftInlier[InlinerCount].y), Point2d(4096 + m_RightInlier[InlinerCount].x, m_RightInlier[InlinerCount].y), Scalar(0, 255, 255), 2);
    347. //垂足
    348. xll2 = (float)(m_LeftInlier[InlinerCount].x + K1 * m_LeftInlier[InlinerCount].y - K1 * B1) / (K1 * K1 + 1);
    349. yll2 = (float)(K1 * m_LeftInlier[InlinerCount].x + K1 * K1 * m_LeftInlier[InlinerCount].y + B1) / (K1 * K1 + 1);
    350. xrr2 = (float)(B2);
    351. yrr2 = (float)(m_RightInlier[InlinerCount].y);
    352. }
    353. }
    354. }
    355. cout << "直线1垂足①:" << "x轴:" << xll1 << " " << "y轴:" << yll1 << endl;
    356. cout << "直线1垂足②:" << "x轴:" << xll2 << " " << "y轴:" << yll2 << endl;
    357. cout << "直线2垂足①:" << "x轴:" << xrr1 << " " << "y轴:" << yrr1 << endl;
    358. cout << "直线2垂足②:" << "x轴:" << xrr2 << " " << "y轴:" << yrr2 << endl;
    359. imwrite("combine.bmp",combine);
    360. resize(combine, combine, Size(1024, 540));//显示改变大小
    361. imshow("输出想要的匹配点", combine);
    362. waitKey(0);
    363. return 0;
    364. }

    可以得到如下的垂足

    说明结果正确. (代码写的太烂了,我自己都不想看了,不想改了)

  • 相关阅读:
    第十四届蓝桥杯大赛B组 JAVA 蜗牛 (递归剪枝)
    Halo-Theme-Hao文档:如何设置导航栏?
    OpenGL ES之深入解析PBO、UBO与TBO的功能和使用
    微信小程序开发:精细化处理人像动漫化调用之前的人像修复增强
    Meta因露骨AI图片陷入困境
    Hive insert插入数据与with子查询
    Android PreferenceScreen的使用和详解(设置页面)
    记一次vue3页面倒计时(定时器)切换页面问题
    Unity | unity&C++内存共享及中文乱码解决方式
    Java数组案例
  • 原文地址:https://blog.csdn.net/m0_47489229/article/details/127693065