OpenCV C++案例实战四《图像透视矫正》
- 前言
- 一、图像预处理
- 二、轮廓提取
-
- 1.提取最外轮廓
- 2.提取矩形四个角点
- 3.将矩形角点排序
- 三、透视矫正
- 四、源码
- 总结
前言 本文将使用OpenCV C++ 进行图像透视矫正。
一、图像预处理 【OpenCV|OpenCV C++案例实战四《图像透视矫正》】
文章图片
原图如图所示。首先进行图像预处理。将图像进行灰度、滤波、二值化、形态学等操作,目的是为了下面的轮廓提取。在这里我还使用了形态学开、闭操作,目的是使整个二值图像连在一起。大家在做图像预处理时,可以根据图像特征自行处理。
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat gaussian;
GaussianBlur(gray, gaussian, Size(3, 3), 0);
Mat thresh;
threshold(gaussian, thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
Mat open;
morphologyEx(thresh, open, MORPH_OPEN, kernel);
Mat kernel1 = getStructuringElement(MORPH_RECT, Size(7, 7));
Mat close;
morphologyEx(open, close, MORPH_CLOSE, kernel1);
文章图片
如图就是经过图像预处理得到的二值图像。
二、轮廓提取 1.提取最外轮廓
vectorcontours;
findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
使用findContours、RETR_EXTERNAL就可以提取出物体最外轮廓。
2.提取矩形四个角点 接下来将使用approxPolyDP进行多边形轮廓拟合,目的是为了找到矩形的四个角点。关于approxPolyDP API大家可以自行百度查看其用法。
vectorconPoly(contours.size());
vectorsrcPts;
for (int i = 0;
i < contours.size();
i++)
{
double area = contourArea(contours[i]);
if (area > 10000)
{
double peri = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);
//获取矩形四个角点
srcPts = { conPoly[i][0],conPoly[i][1],conPoly[i][2],conPoly[i][3] };
} }
3.将矩形角点排序 由于我们之前使用的approxPolyDP获取的角点是无序的,所以我们得确定各角点所在的位置。在这里我使用的算法是根据其角点所在图像位置特征确定左上、左下、右下、右上四个点。
int width = src.cols / 2;
int height = src.rows / 2;
int T_L, T_R, B_R, B_L;
for (int i = 0;
i < srcPts.size();
i++)
{
if (srcPts[i].x < width && srcPts[i].y < height)
{
T_L = i;
}
if (srcPts[i].x > width && srcPts[i].y < height)
{
T_R = i;
}
if (srcPts[i].x > width && srcPts[i].y > height)
{
B_R = i;
}
if (srcPts[i].x < width && srcPts[i].y > height)
{
B_L = i;
} }
文章图片
如图所示。至此已经完成了矩形四个角点的定位。接下来就可以使用透视变换进行图像矫正了。
三、透视矫正 在这里我们需要知道透视变换一个原理:
变换后,图像的长和宽应该变为:
长 = max(变换前左边长,变换前右边长)
宽 = max(变换前上边长,变换前下边长)
设变换后图像的左上角位置为原点位置。
double LeftHeight = EuDis(srcPts[T_L], srcPts[B_L])
double RightHeight = EuDis(srcPts[T_R], srcPts[B_R]);
double MaxHeight = max(LeftHeight, RightHeight);
double UpWidth = EuDis(srcPts[T_L], srcPts[T_R]);
double DownWidth = EuDis(srcPts[B_L], srcPts[B_R]);
double MaxWidth = max(UpWidth, DownWidth);
确定变换后的长宽之后,就可以使用getPerspectiveTransform、warpPerspective进行透视矫正了。
//这里使用的顺序是左上、右上、右下、左下顺时针顺序。SrcAffinePts、DstAffinePts要一一对应
Point2f SrcAffinePts[4] = { Point2f(srcPts[T_L]),Point2f(srcPts[T_R]) ,Point2f(srcPts[B_R]) ,Point2f(srcPts[B_L]) };
Point2f DstAffinePts[4] = { Point2f(0,0),Point2f(MaxWidth,0),Point2f(MaxWidth,MaxHeight),Point2f(0,MaxHeight) };
Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);
Mat DstImg;
warpPerspective(src, DstImg, M, Point(MaxWidth, MaxHeight));
文章图片
这就是进行透视矫正之后的效果。
四、源码
#include
#includeusing namespace std;
using namespace cv;
double EuDis(Point pt1, Point pt2)
{
return sqrt((pt2.x - pt1.x)*(pt2.x - pt1.x) + (pt2.y - pt1.y)*(pt2.y - pt1.y));
}int main()
{ Mat src = https://www.it610.com/article/imread("1.jpg");
if (src.empty())
{
cout << "No Image!" << endl;
system("pause");
return -1;
} Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat gaussian;
GaussianBlur(gray, gaussian, Size(3, 3), 0);
Mat thresh;
threshold(gaussian, thresh, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
Mat open;
morphologyEx(thresh, open, MORPH_OPEN, kernel);
Mat kernel1 = getStructuringElement(MORPH_RECT, Size(7, 7));
Mat close;
morphologyEx(open, close, MORPH_CLOSE, kernel1);
vectorcontours;
findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
vectorconPoly(contours.size());
vectorsrcPts;
for (int i = 0;
i < contours.size();
i++)
{
double area = contourArea(contours[i]);
if (area > 10000)
{
double peri = arcLength(contours[i], true);
approxPolyDP(contours[i], conPoly[i], 0.02*peri, true);
srcPts = { conPoly[i][0],conPoly[i][1],conPoly[i][2],conPoly[i][3] };
} } int width = src.cols / 2;
int height = src.rows / 2;
int T_L, T_R, B_R, B_L;
for (int i = 0;
i < srcPts.size();
i++)
{
if (srcPts[i].x < width && srcPts[i].y < height)
{
T_L = i;
}
if (srcPts[i].x > width && srcPts[i].y < height)
{
T_R = i;
}
if (srcPts[i].x > width && srcPts[i].y > height)
{
B_R = i;
}
if (srcPts[i].x < width && srcPts[i].y > height)
{
B_L = i;
} } //circle(src, srcPts[T_L], 10, Scalar(0, 0, 255), -1);
//circle(src, srcPts[T_R], 10, Scalar(0, 255, 255), -1);
//circle(src, srcPts[B_R], 10, Scalar(255, 0, 0), -1);
//circle(src, srcPts[B_L], 10, Scalar(0, 255, 0), -1);
/*
变换后,图像的长和宽应该变为:
长 = max(变换前左边长,变换前右边长)
宽 = max(变换前上边长,变换前下边长)
设变换后图像的左上角位置为原点位置。
*/ double LeftHeight = EuDis(srcPts[T_L], srcPts[B_L]);
double RightHeight = EuDis(srcPts[T_R], srcPts[B_R]);
double MaxHeight = max(LeftHeight, RightHeight);
double UpWidth = EuDis(srcPts[T_L], srcPts[T_R]);
double DownWidth = EuDis(srcPts[B_L], srcPts[B_R]);
double MaxWidth = max(UpWidth, DownWidth);
Point2f SrcAffinePts[4] = { Point2f(srcPts[T_L]),Point2f(srcPts[T_R]) ,Point2f(srcPts[B_R]) ,Point2f(srcPts[B_L]) };
Point2f DstAffinePts[4] = { Point2f(0,0),Point2f(MaxWidth,0),Point2f(MaxWidth,MaxHeight),Point2f(0,MaxHeight) };
Mat M = getPerspectiveTransform(SrcAffinePts, DstAffinePts);
Mat DstImg;
warpPerspective(src, DstImg, M, Point(MaxWidth, MaxHeight));
//imshow("Dst", DstImg);
imshow("src", src);
waitKey(0);
destroyAllWindows();
system("pause");
return 0;
}
总结 本文使用OpenCV C++ 进行图像透视矫正,关键步骤有以下几点。
1、图像预处理,获取二值图像。
2、将二值图像进行轮廓提取,定位矩形四个角点,并确定其位置。
3、确定图像变换后的长、宽。并将SrcAffinePts、DstAffinePts一一对应之后进行透视变换。
若文章有存在不足的地方,欢迎大家交流指正!!!
推荐阅读
- pytorch深度学习实战|Mask R-CNN详解(图文并茂)
- 备战蓝桥|【备战蓝桥,冲击省一】高精度算法实现加减乘除
- 备战蓝桥|备战蓝桥,冲击省一 进制转换 你不会还不会吧()
- 备战蓝桥|备战蓝桥,冲击省一 二分查找法 看完你就会了
- 备战蓝桥|【2021年蓝桥省赛真题】赛前最后冲刺,省一我来啦
- C++常见面试题|顺网科技面试题 select poll epoll多路io复用都说一说
- 程序员|Linux之父炮轰C++(糟糕程序员的垃圾语言)
- C++笔记|C++ 内存管理 —— 第一講(C++ 內存構件)
- C语言编程学习指导|C语言进阶(指针进阶)