一翘骂、背景
??現(xiàn)有如下圖片驱闷,希望能用鼠標(biāo)畫出矩形,在矩形中計算出圖片的傾斜角度敲董,并由此自動旋轉(zhuǎn)使圖片水平。
二慰安、實(shí)現(xiàn)
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <cmath>
using namespace std;
using namespace cv;
// 全局變量
Mat g_image_original, g_image_gray, g_image_rect; // 原始圖片腋寨,灰度圖,鼠標(biāo)畫出的矩形圖片
Rect g_rect; // 鼠標(biāo)畫出的矩形的坐標(biāo)
Vec4d g_max_line; // g_rect的最長的線段化焕,用于計算g_angle
double g_angle; // 圖片旋轉(zhuǎn)角度
// 鼠標(biāo)
size_t g_step = 0; // 操作步驟的標(biāo)志
// 進(jìn)度條
int g_Canny_th = 255, g_HoughLinesP_th = 0, g_HoughLinesP_ml = 0, g_HoughLinesP_mg = 0;
// 畫圖函數(shù)
void draw(const string name, const Mat img)
{
Mat img1 = img.clone();
if (g_step == 1 || g_step == 2) // 鼠標(biāo)左鍵點(diǎn)擊或按住左鍵移動時畫出矩形
{
rectangle(img1, g_rect, Scalar(0, 255, 0), 2);
}
if (g_step == 2) // 鼠標(biāo)按住左鍵移動(矩形已畫出)時
{
if (g_max_line != Vec4d(0, 0, 0, 0))
{
// 畫出識別到的最長的線段
line(g_image_rect, Point(g_max_line[0], g_max_line[1]), Point(g_max_line[2], g_max_line[3]), Scalar(0, 0, 255), 2);
}
// img1(g_rect) = g_image_rect.clone(); // 錯誤:clone會img1(g_rect)重新分配內(nèi)存萄窜,修改img1(g_rect)不會改變img
g_image_rect.copyTo(img1(g_rect));
}
if (g_step == 3) // 奇數(shù)次按下'1'鍵時
{
cv::Mat img2 = cv::getRotationMatrix2D(cv::Point2f(img.cols * 0.5f, img.rows * 0.5f), g_angle, 1); // 旋轉(zhuǎn)中心,旋轉(zhuǎn)角度,縮放比例
warpAffine(img1, img1, img2, img1.size()); // 旋轉(zhuǎn)圖片
}
imshow(name, img1);
}
// 滑動條的回調(diào)函數(shù)
void trackbar_callback(int pos, void *)
{
if (g_step == 2) // 鼠標(biāo)已畫出矩形后查刻,才會去識別矩形中最長的線段
{
g_max_line = Vec4d(0, 0, 0, 0);
Mat img;
Canny(g_image_gray, img, g_Canny_th, 3 * g_Canny_th); // 邊緣檢測
//直線檢測键兜,參數(shù):rho,theta穗泵,閾值普气,線段最小長度,共線線段之間的最小間隔
vector<Vec4d> lines;
HoughLinesP(img, lines, 1, CV_PI / 180, g_HoughLinesP_th, g_image_gray.cols * g_HoughLinesP_ml / 100, g_image_gray.cols * g_HoughLinesP_mg / 100);
//獲取最長線段的角度
double max_lenght = 0; //線段最大長度
int n = 2;
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
if (l[0] > n && l[0] < img.cols - n && l[1] > n && l[1] < img.rows - n) //去除圖片邊框上的線段
{
int length = g_max_line.rows * g_max_line.rows + g_max_line.cols * g_max_line.cols;
if (max_lenght < length) // 尋找最長線段
{
max_lenght = length;
g_max_line = l;
}
}
}
//計算傾斜角度
if (g_max_line[0] != g_max_line[2])
g_angle = atan((g_max_line[3] - g_max_line[1]) / (g_max_line[2] - g_max_line[0])) * 180 / CV_PI;
else
g_angle = 90;
cvtColor(img, g_image_rect, COLOR_GRAY2BGR); // 灰度圖轉(zhuǎn)彩圖佃延,方便畫彩線
}
}
// 鼠標(biāo)事件的回調(diào)函數(shù)
void mouse_callback(int event, int x, int y, int flags, void *param)
{
switch (event)
{
case EVENT_LBUTTONDOWN: // 鼠標(biāo)左鍵點(diǎn)擊
if (g_step == 0)
{
g_step = 1;
g_rect.x = x;
g_rect.y = y;
}
break;
case EVENT_MOUSEMOVE: // 鼠標(biāo)移動
if (g_step == 1)
{
g_rect.width = x - g_rect.x;
g_rect.height = y - g_rect.y;
}
break;
case EVENT_LBUTTONUP: // 鼠標(biāo)左鍵釋放
g_step = 2;
g_image_rect = g_image_original(g_rect).clone();
cvtColor(g_image_rect, g_image_gray, COLOR_BGR2GRAY); // 灰度圖
break;
case EVENT_RBUTTONDOWN: // 鼠標(biāo)右鍵點(diǎn)擊现诀,清除內(nèi)容
g_step = 0;
g_rect = Rect(0, 0, 0, 0);
g_max_line = Vec4d(0, 0, 0, 0);
break;
default:
break;
}
}
int main()
{
const string name = "image";
namedWindow(name, WINDOW_AUTOSIZE);
g_image_original = imread("E:/VSCode/git/my_program/image/1.PNG");
if (g_image_original.empty())
{
cout << "can not open image!" << endl;
return -1;
}
setMouseCallback(name, mouse_callback); // 鼠標(biāo)
createTrackbar("threshold1", name, &g_Canny_th, 255, trackbar_callback); // 進(jìn)度條
createTrackbar("threshold2", name, &g_HoughLinesP_th, 255, trackbar_callback);
createTrackbar("min_length", name, &g_HoughLinesP_ml, 100, trackbar_callback);
createTrackbar(" max_gap", name, &g_HoughLinesP_mg, 100, trackbar_callback);
while (true)
{
trackbar_callback(0, 0); // 進(jìn)度條函數(shù)
draw(name, g_image_original); // 畫圖
char c = waitKey(10);
if (c == '1') // 按'1'第奇數(shù)次,旋轉(zhuǎn)圖片履肃;按'1'第偶數(shù)次仔沿,還原圖片
{
if (g_step == 2) // 旋轉(zhuǎn)圖片的命令標(biāo)志
g_step = 3;
else if (g_step == 3) // 還原圖片的命令標(biāo)志
g_step = 2;
}
else if (c > 0 && c != '1') // 退出循環(huán)
break;
}
destroyAllWindows();
return 0;
}
滑動條參數(shù):
// 邊緣檢測
Canny(g_image_gray, img, g_Canny_th, 3 * g_Canny_th);
// 直線檢測
HoughLinesP(img, lines, 1, CV_PI / 180, g_HoughLinesP_th, g_image_gray.cols * g_HoughLinesP_ml / 100, g_image_gray.cols * g_HoughLinesP_mg / 100);
threshold1
:
??對應(yīng)g_Canny_th,是檢測物體邊緣的閾值尺棋。
threshold2
:
??對應(yīng)g_HoughLinesP_th封锉,是提取直線的閾值。
min_length
:
??對應(yīng)g_HoughLinesP_ml膘螟,由于鼠標(biāo)畫出的矩形成福,其寬度不固定,故使用百分比萍鲸。只識別長度大于g_image_gray.cols * g_HoughLinesP_ml / 100的直線闷叉。
max_gap
:
??對應(yīng)g_HoughLinesP_mg,也是使用百分比脊阴。若兩條小線段在同一條直線上握侧,且其間隔小于g_image_gray.cols * g_HoughLinesP_mg / 100,則將其連接成一條直線嘿期,否則將其看作兩條直線品擎。
操作方法:
??運(yùn)行程序,在空白圖片上點(diǎn)擊鼠標(biāo)左鍵并按住移動备徐,由此畫出矩形萄传。設(shè)置窗口中的滑動條參數(shù)使矩形中出現(xiàn)最長的紅線,由此自動計算出傾斜角度蜜猾。在鍵盤按下1
鍵可旋轉(zhuǎn)圖片使其水平秀菱,再按1
鍵一次,可使其復(fù)原蹭睡。點(diǎn)擊鼠標(biāo)右鍵能夠擦除已畫好的矩形衍菱,由此可以重新作畫。按鍵盤除1
以外的鍵可自動退出程序肩豁。
運(yùn)行結(jié)果: