課程名稱:線性代數 CS233 B
授課教師:簡廷因
發放時間:2020-11-02
截止時間:2020-11-30 23:59:59


題目說明

給一張圖片 ( jpg ),偵測出圖片中最長的直線,逆時針旋轉直到直線垂直於水平線,並將旋轉後多出來的部份塗黑,之後輸出成圖片檔 ( jpg )。

本次作業可使用 OpenCV 套件

參數設定:

Canny 使用 500,100
HoughLinesP 使用 1, CV_PI / 180, 250, 200, 10

OpenCV 設定

在Visual Studio 2019上使用OpenCV : 先依照這篇設定,之後會遇到錯誤類似於 LNK1104 無法開啟檔案 'opencv_world401d.lib'
[圖文] OpenCV 4.0.1 安裝配置在 Visual Studio 2019 : 之後再依照這篇設定環境參數即可。

解題思路

先使用 imread() 讀取圖片,之後使用 Canny() 完成邊緣偵測,再使用 HoughLinesP() 完成霍夫直線判斷,得到圖片中所有直線的陣列,之後遍歷陣列取得最長邊的斜率 ( 長度可使用 norm() 進行計算 ),並算出圖片需要旋轉的角度 ( 在 getSlope() 中 return 的地方,我也不知道為什麼要這樣弄,在網路上找的資料拼拼湊湊弄出來的 ),之後找出圖片的中心點 ( 長度寬度各除以二 ),並使用 getRotationMatrix2D() 找出旋轉矩陣,最後使用 warpAffine() 取得圖片旋轉後的結果並輸出檔案 ( 使用 imwrite() ) 即可。

請先看更新:點我

參考解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

/*
OpenCV setup
https://ppt.cc/fdePvx : 先依照這篇設定,之後會遇到錯誤類似於 LNK1104 無法開啟檔案 'opencv_world401d.lib'
https://ppt.cc/fda4tx : 之後再依照這篇設定環境參數即可
*/

/*
Reference:
霍夫直線檢測: https://ppt.cc/fSXfIx
計算斜率: https://www.itread01.com/content/1547635163.html
旋轉圖片: https://www.itread01.com/articles/1476043874.html
*/

// get longest line's slope
double getSlope(vector<Vec4i>& lines)
{
double _max = 0, tmp;
Vec4i V;

for (auto& l : lines)
{
tmp = norm(Point(l[2], l[3]) - Point(l[0], l[1]));
if (tmp > _max) _max = tmp, V = l;
}

return atan((double)(V[3] - V[1]) / (double)(V[2] - V[0])) * 180 / CV_PI + 90;
}

int main(int argc, char* argv[])
{
if (argc < 3) return -1;

// read image
Mat Image = imread(argv[1], -1);
if (Image.empty()) return -1;

// get Canny matrix
Mat canny;
Canny(Image, canny, 500, 100, 3);

// get hough lines
vector<Vec4i> Lines;
HoughLinesP(canny, Lines, 1, CV_PI / 180, 250, 200, 10);

auto slope = getSlope(Lines);

// get center and rotation matrix
Size sz = Image.size();
Point2f center(sz.width / 2., sz.height / 2.);
Mat rotMat = getRotationMatrix2D(center, slope, 1.0);

// rotate
Mat result;
warpAffine(Image, result, rotMat, sz);

imwrite(argv[2], result);
}

參考資料

Opencv霍夫直线检测
opencv計算直線的斜率、截距,與水平線弧度值、角度值
用OpenCV實現Photoshop算法(一): 圖像旋轉


2020/12/01 00:39

老師系統好像出問題,抓錯資料導致測資錯誤,後來系統弄好後只剩 10 分,然後多了一些需要處理的情況。

還有原本是 逆時針旋轉,後來改成 朝最接近垂直線的角度旋轉

  1. 修改旋轉角度: 原本是逆時針旋轉所以 slope 直接加 90,後來加上判斷,若順時針旋轉的角度較小則順時針旋轉,減 90。修改這邊後是 50 分。

  2. 彩色圖片: 原本的測資好像都是黑白的圖片,所以那時候沒有加上轉灰階,新測資的 6 ~ 10 是彩色的圖片,需要轉灰階才能成功偵測。

修改後的 Code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>

using namespace std;
using namespace cv;

/*
Reference:
霍夫直線檢測: https://ppt.cc/fSXfIx
計算斜率: https://www.itread01.com/content/1547635163.html
旋轉圖片: https://www.itread01.com/articles/1476043874.html
*/

// get longest line's slope
double getSlope(vector<Vec4i>& lines)
{
double _max = 0, tmp;
Vec4i V;

for (auto& l : lines)
{
tmp = norm(Point(l[2], l[3]) - Point(l[0], l[1]));
if (tmp > _max) _max = tmp, V = l;
}

return atan((double)(V[3] - V[1]) / (double)(V[2] - V[0])) * 180 / CV_PI;
}

int main(int argc, char* argv[])
{
if (argc < 3) return -1;

// read image
Mat Image = imread(argv[1], -1);
if (Image.empty()) return -1;

// 轉成灰階,彩色圖片沒有這一步會錯誤
Mat gray;
cvtColor(Image, gray, CV_BGR2GRAY);

// get canny matrix
Mat canny;
Canny(gray, canny, 500, 100, 3);

// get hough lines
vector<Vec4i> Lines;
HoughLinesP(canny, Lines, 1, CV_PI / 180, 250, 200, 10);

double slope = getSlope(Lines);
slope += (slope > 0) ? -90 : 90;

// get center and rotation matrix
Size sz = Image.size();
Point2f center(sz.width / 2., sz.height / 2.);
Mat rotMat = getRotationMatrix2D(center, slope, 1.0);

// rotate
Mat result;
warpAffine(Image, result, rotMat, sz);

imwrite(argv[2], result);
}