代码
#include "../common/common.hpp"
static Mat src, gray, dst;
static int threshold_value = 100;
static int max_level = 255;
static const char* title = "Contours Result";
static const char* roi_win = "Final Result";
static void findROI(int, void*);
static void m_rotate();
void main(int argc, char** argv)
{
src = imread(getCVImagesPath("images/case3-1r.png"));
imshow("src3-1", src);
m_rotate();
namedWindow(title, CV_WINDOW_AUTOSIZE);
createTrackbar("Threshold:", title, &threshold_value, max_level, findROI);
findROI(0, 0);
waitKey(0);
}
void findROI(int, void*) // 发现感兴趣区域,即最大外接矩形,然后截取该区域图像显示
{
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat canny;
Canny(gray, canny, threshold_value, threshold_value * 2, 3, false);
vector<vector<Point>> contours;
vector<Vec4i> hireachy;
findContours(canny, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
cout << "contours.size=" << contours.size() << endl;
float radio = 0.5; // 0.75
int minW = src.cols*radio; // 最大外接矩形的宽高最小值
int minH = src.rows*radio;
RNG rng(12345);
Mat drawImg = Mat::zeros(src.size(), CV_8UC3);
Rect bbox;
for (size_t i = 0; i < contours.size(); i++)
{
RotatedRect minRect = minAreaRect(contours[i]); // 得到轮廓最小的矩形,旋转的
float degree = abs(minRect.angle); // 旋转的角度
// 此算法不是特别准确,可以优化,只是懒了。。
if (minRect.size.width>minW && minRect.size.height>minH && minRect.size.width<src.cols-5) // 轮廓所在矩形大于最小宽高
{
Point2f pts[4];
minRect.points(pts);
bbox = minRect.boundingRect(); // 获取轮廓所在的最小矩形在图像中的位置
cout << i << ".degree=" << degree << ", width=" << bbox.width << ", height=" << bbox.height << ", x=" << bbox.x << ", y=" << bbox.y << endl;
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (size_t i = 0; i < 4; i++)
{
line(drawImg, pts[i], pts[(i + 1) % 4], color, 1, 8, 0);
}
}
}
imshow(title, drawImg);
if (bbox.width>0 && bbox.height>0)
{
Mat roiImg = src(bbox); // ()运算符重载,在图像src上截取rect所在区域的部分
imshow(roi_win, roiImg);
}
}
void m_rotate() // 旋转图像,摆正
{
cvtColor(src, gray, COLOR_BGR2GRAY);
Mat canny;
Canny(gray, canny, threshold_value, threshold_value * 2, 3, false);
vector<vector<Point>> contours;
vector<Vec4i> hireachy;
findContours(canny, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
cout << "m_rotate contours.size=" << contours.size() << endl;
Mat drawImg = Mat::zeros(src.size(), CV_8UC3);
float maxW = 0;
float maxH = 0;
double degree = 0;
for (size_t i = 0; i < contours.size(); i++)
{
RotatedRect minRect = minAreaRect(contours[i]); // 得到轮廓最小的矩形,旋转的
degree = abs(minRect.angle); // 旋转的角度
if (degree > 0)
{
maxW = max(maxW, minRect.size.width); // 获取最大矩形的宽高
maxH = max(maxH, minRect.size.height);
}
}
RNG rng(12345);
for (size_t i = 0; i < contours.size(); i++)
{
RotatedRect minRect = minAreaRect(contours[i]); // 得到轮廓最小的矩形,旋转的
if (maxW==minRect.size.width && maxH==minRect.size.height)
{
degree = minRect.angle;
Point2f pts[4];
minRect.points(pts);
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
for (size_t i = 0; i < 4; i++)
{
line(drawImg, pts[i], pts[(i + 1) % 4], color, 1, 8, 0);
}
}
}
cout << "maxW=" << maxW << ", maxH=" << maxH << ", degree=" << degree << endl;
imshow("drawImg", drawImg);
Point2f center(src.cols / 2, src.rows / 2); // 旋转图像时的锚点
Mat rotm = getRotationMatrix2D(center, degree, 1.0); // 根据角度锚点获取旋转矩阵
// rotm depth=6, type=6, cols=3, rows=2 CV_64F 单通道
cout << "rotm depth=" << rotm.depth() << ", type=" << rotm.type() << ", cols=" << rotm.cols << ", rows=" << rotm.rows << endl;
/*
0.707107, -0.707107, 387.414,
0.707107, 0.707107, -159.301,
*/
for (int row = 0; row < rotm.rows; row++)
{
for (int col = 0; col < rotm.cols; col++)
{
cout << rotm.at<double>(row, col) << ", ";
}
cout << endl;
}
Mat dst;
// INTER_LINEAR 表示双线性插值生成旋转后的图像,Scalar(0) 表示旋转后的非输入图像的部分用该颜色填充
warpAffine(src, dst, rotm, src.size(), INTER_LINEAR, 0, Scalar(0)); // 旋转图像,旋转后的图像的宽高深度通道数与输入图像一致
imshow("Correct Image", dst);
dst.copyTo(src);
}
效果图