现有如下一张图片,发现其图案与图片并不平行,故希望先通过滑动条旋转图片角度使图案水平,再将旋转好后的图片截图保存下来。
测试图片:
初始版本:实现图片旋转功能。
缺陷:旋转时超出图片大小的部分无法显示。
#include
#include
#include
#include
// 全局变量
cv::Mat g_image_original, g_image_result; // 原始图片,处理后的图片
// 滑动条回调函数
void trackbar_callback(int angle, void *)
{
// 旋转中心,旋转角度,缩放比例
cv::Mat img = cv::getRotationMatrix2D(cv::Point2f(g_image_original.cols * 0.5f, g_image_original.rows * 0.5f), angle, 1);
// 旋转图片
warpAffine(g_image_original, g_image_result, img, g_image_original.size());
}
int main()
{
const std::string window_name = "image";
const std::string path = "E:/VSCode/git/my_program/image/5_1.PNG";
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
g_image_original = cv::imread(path);
if (g_image_original.empty())
{
std::cout << "can not open image!" << std::endl;
return -1;
}
// 进度条
int angle = 0; // 图片旋转的角度
cv::createTrackbar("angle", window_name, &angle, 360, trackbar_callback);
while (true)
{
trackbar_callback(angle, 0); // 进度条回调函数
cv::imshow(window_name, g_image_result);
if (cv::waitKey(10) >= 0)
break;
}
cv::destroyAllWindows();
return 0;
}
旋转前:
旋转后:
中间版本:解决图片旋转后无法全部显示的问题。
实现方法:旋转前,放大图片使其大小足以容纳原图旋转。旋转时,将已放大的图片缩小至原大小。
缺陷:无参照物以确保图案平行。
#include
#include
#include
#include
// 全局变量
cv::Mat g_image_original, g_image_result; // 原始图片,处理后的图片
// 滑动条回调函数
void trackbar_callback(int angle, void *enlarge)
{
double m = *(double *)enlarge;
// 旋转中心,旋转角度,缩放比例
cv::Mat img = cv::getRotationMatrix2D(cv::Point2f(g_image_original.cols * 0.5f, g_image_original.rows * 0.5f), angle, 1 / m); // 缩小图片
// 旋转图片
warpAffine(g_image_original, g_image_result, img, g_image_original.size());
}
int main()
{
const std::string window_name = "image";
const std::string path = "E:/VSCode/git/my_program/image/5_1.PNG";
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
g_image_original = cv::imread(path);
if (g_image_original.empty())
{
std::cout << "can not open image!" << std::endl;
return -1;
}
// 放大图片
double m1 = std::sqrt(std::pow(g_image_original.cols, 2) + std::pow(g_image_original.rows, 2)); //计算对角线长度
double m2 = g_image_original.cols > g_image_original.rows ? g_image_original.rows : g_image_original.cols;
double m = m1 / m2; // 放大比例
cv::resize(g_image_original, g_image_original, cv::Size(g_image_original.cols * m, g_image_original.rows * m));
// 进度条
int angle = 0; // 图片旋转的角度
cv::createTrackbar("angle", window_name, &angle, 360, trackbar_callback, &m);
while (true)
{
trackbar_callback(angle, &m); // 进度条回调函数
cv::imshow(window_name, g_image_result);
if (cv::waitKey(10) >= 0)
break;
}
cv::destroyAllWindows();
return 0;
}
旋转前:
旋转后:
最终版本:通过鼠标调整4条直线使其分布在图案四周,方便确定图案已平行,同时也可用于截取图案。
操作方法:首先按下鼠标左键选择任意一条绿线,然后一直按住左键移动鼠标使绿线接近图案。同样方法移动其它3条绿线,使其包围住图案。通过滑动条旋转角度使图案与绿线平行,按s
键保存4条绿线包围的矩形图片。
#include
#include
#include
#include
// 全局变量
cv::Mat g_image_original, g_image_result; // 原始图片,处理后的图片
cv::Rect g_image_xywh; // 可通过鼠标移动由4条直线组成的矩形
// 画4条直线
void draw_lines(cv::Mat img, cv::Rect rect)
{
cv::Scalar color(0, 255, 0); // 颜色
int thickness = 2; // 厚度
// 按顺时针画4条线
cv::line(img, cv::Point(0, rect.y), cv::Point(img.cols, rect.y), color, thickness); // 上边
cv::line(img, cv::Point(rect.x + rect.width, 0), cv::Point(rect.x + rect.width, img.rows), color, thickness); // 右边
cv::line(img, cv::Point(0, rect.y + rect.height), cv::Point(img.cols, rect.y + rect.height), color, thickness); // 下边
cv::line(img, cv::Point(rect.x, 0), cv::Point(rect.x, img.rows), color, thickness); // 左边
}
// 滑动条的回调函数,用于旋转图片
void trackbar_callback(int angle, void *enlarge)
{
double m = *(double *)enlarge;
// 旋转中心,旋转角度,缩放比例
cv::Mat img = cv::getRotationMatrix2D(cv::Point2f(g_image_original.cols * 0.5f, g_image_original.rows * 0.5f), angle, 1 / m); // 缩小图片
// 旋转图片
warpAffine(g_image_original, g_image_result, img, g_image_original.size());
// 画4条直线
draw_lines(g_image_result, g_image_xywh);
}
// 确定鼠标所点击在那一条线上
int mouse_location(cv::Rect rect, cv::Point p)
{
static int n = 10;
if (p.y > rect.y - n && p.y < rect.y + n) //上边
return 1;
else if (p.x > rect.x + rect.width - n && p.x < rect.x + rect.width + n) //右边
return 2;
else if (p.y > rect.y + rect.height - n && p.y < rect.y + rect.height + n) //下边
return 3;
else if (p.x > rect.x - n && p.x < rect.x + n) //左边
return 4;
else
return -1;
}
// 矫正坐标
void correct(int &m, int n1, int n2)
{
if (m < n1)
m = n1;
else if (m > n2)
m = n2;
}
// 鼠标事件的回调函数,用于移动4条直线以判断图片是否平行,按's'键截取图片并保存
void mouse_callback(int event, int x, int y, int flags, void *param)
{
static cv::Rect rect(0, 0, 0, 0); // 暂存g_image_xywh
static cv::Point p1(0, 0), p2(0, 0); // 鼠标左键点击的点、鼠标按住左键移动的点
static int location = -1; // 鼠标左键点击落在那条直线上
cv::Size size(0, 0); // 鼠标移动距离
switch (event)
{
case cv::EVENT_LBUTTONDOWN: // 鼠标左键点击
rect = g_image_xywh;
p1 = cv::Point(x, y);
location = mouse_location(g_image_xywh, p1);
break;
case cv::EVENT_MOUSEMOVE: // 鼠标移动
p2 = cv::Point(x, y);
size = cv::Size(p2.x - p1.x, p2.y - p1.y);
if (location == 1) //上边
{
g_image_xywh.y = rect.y + size.height;
correct(g_image_xywh.y, 0, g_image_result.rows); // 矫正坐标
g_image_xywh.height = rect.y + rect.height - g_image_xywh.y;
}
else if (location == 2) //右边
{
g_image_xywh.width = rect.width + size.width;
correct(g_image_xywh.width, g_image_xywh.width < -rect.x, g_image_result.cols - rect.x); // 矫正坐标
}
else if (location == 3) //下边
{
g_image_xywh.height = rect.height + size.height;
correct(g_image_xywh.height, -rect.y, g_image_result.rows - rect.y); // 矫正坐标
}
else if (location == 4) //左边
{
g_image_xywh.x = rect.x + size.width;
correct(g_image_xywh.x, 0, g_image_result.cols); // 矫正坐标
g_image_xywh.width = rect.x + rect.width - g_image_xywh.x;
}
draw_lines(g_image_result, g_image_xywh);
break;
case cv::EVENT_LBUTTONUP: // 鼠标左键释放
rect = cv::Rect(0, 0, 0, 0);
p1 = cv::Point(0, 0);
p2 = cv::Point(0, 0);
location = -1;
break;
default:
break;
}
}
int main()
{
const std::string window_name = "image";
const std::string path = "E:/VSCode/git/my_program/image/5_1.PNG";
int angle = 0; //图片旋转的角度
g_image_original = cv::imread(path);
if (g_image_original.empty())
{
std::cout << "can not open image!" << std::endl;
return -1;
}
cv::namedWindow(window_name, cv::WINDOW_AUTOSIZE);
// 初始化g_image_xywh
g_image_xywh.width = g_image_original.cols;
g_image_xywh.height = g_image_original.rows;
//放大图片,以确保旋转图片时图片保持放大前的尺寸
double m1 = std::sqrt(std::pow(g_image_original.cols, 2) + std::pow(g_image_original.rows, 2)); //计算对角线长度
double m2 = g_image_original.cols > g_image_original.rows ? g_image_original.rows : g_image_original.cols;
double m = m1 / m2; // 放大比例
cv::resize(g_image_original, g_image_original, cv::Size(g_image_original.cols * m, g_image_original.rows * m));
// 初始化g_image_xywh
g_image_xywh.x = (g_image_original.cols - g_image_xywh.width) / 2;
g_image_xywh.y = (g_image_original.rows - g_image_xywh.height) / 2;
//鼠标回调函数
cv::setMouseCallback(window_name, mouse_callback);
// 进度条回调函数
cv::createTrackbar(" angle:", window_name, &angle, 180, trackbar_callback, &m);
while (true)
{
trackbar_callback(angle, &m);
cv::imshow(window_name, g_image_result);
char c = cv::waitKey(10);
if (c == 's') // 按's'保存图片
{
int x1 = g_image_xywh.width > 0 ? g_image_xywh.x : g_image_xywh.x + g_image_xywh.width;
int y1 = g_image_xywh.height > 0 ? g_image_xywh.y : g_image_xywh.y + g_image_xywh.height;
int thickness = 2; // 由于在图片上画线,截取时需减去线的厚度
cv::Rect rect(x1 + thickness, y1 + thickness, abs(g_image_xywh.width) - 2 * thickness, abs(g_image_xywh.height) - 2 * thickness);
cv::imwrite("1.png", g_image_result(rect));
}
else if (c > 0 && c != 's') // 按除's'外的键以退出循环
break;
}
cv::destroyAllWindows();
return 0;
}
旋转前:
旋转后:
截取的图片: