---
tags: C++, OpenCV
disqus: HackMD
---
電腦視覺 Homework #1
===
A. 附圖Image_a.jpg 為一張具有車道隔線之道路圖,請利用Hough Transform方法將白色車道隔線取出。參考步驟如下:1. 將圖轉為灰階圖,然後利用適當之Edge detector取得Edge map;2. 利用Line Hough Transform轉換產生參數空間之統計陣列 (Accumulator array),並將此陣列以圖形呈現;3. 由參數空間之統計陣列找出屬於車道隔線之Lines;4. 將上步驟之結果套至原圖中,以輸出白色車道隔線。(30%)
---
原圖:

Accumulator array圖與隔線位置圖:


對照原圖之檢出的線:


---
A1. 5. 若透過車子上之攝影設備取得連續之畫面,說明如何將它應用到車子輔助駕駛
中之是否偏離車道的判斷?(15%)
---
將影片取出一張一張的照片,找出白色隔線,並判斷車頭左右邊各取一個定位點,通過定位點與白色隔線距離判斷是否偏離車道。
---
B. 附圖Image_b.jpg 為一張同時具有多枚台幣1元,5元,10元 與50元之圖,請利用Hough
Transform 方法來算算看各個幣別之個數。參考步驟如下:1. 將圖轉為灰階圖,然後利用適當之Edge detector取得Edge map;2. 利用Circle Hough Transform 轉換產生參數空間之統計陣列(Accumulator array),並將此陣列以圖形呈現;3. 由參數空間之統計陣列找出屬於各個幣別之Circles;4. 將上步驟之結果套至原圖中,只取出各個幣別之圖輸出。(40%)
---
原圖:

Accumulator array圖與隔線位置圖:


對照原圖之檢出的圓:


紅色:一元
黃色:五元
紫色:十元
藍色:五十元
判斷幣別方式:
先找到圓的位置,再判斷各圓之半徑
```cpp=
int radius = c[2];
if (radius < 65)
{
return "NT$1";
}
else if (radius >= 65 && radius < 75)
{
return "NT$5";
}
else if (radius >= 75 && radius < 85)
{
return "NT$10";
}
else if (radius >= 85)
{
return "NT$50";
}
```
```
X:732 Y:214 radius:87 NT$50
X:696 Y:692 radius:80 NT$10
X:406 Y:726 radius:86 NT$50
X:870 Y:420 radius:80 NT$10
X:280 Y:384 radius:80 NT$10
X:636 Y:450 radius:82 NT$10
X:306 Y:174 radius:66 NT$5
X:430 Y:340 radius:62 NT$1
X:948 Y:608 radius:63 NT$1
X:402 Y:528 radius:61 NT$1
X:1032 Y:246 radius:70 NT$5
```
---
C. 將B.中利用Hough Transform Circles公式檢測的做法改為透過建立 R-table方法實現之。(15%)
截取硬幣圖,使用template matching的方式找出圓



---
程式原始碼
---
```cpp=
#include <iostream>
#include <thread>
#include <chrono>
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/ximgproc.hpp"
using namespace std;
using namespace cv;
using namespace cv::ximgproc;
Mat dstLines; Mat dstCircles; Mat dstHoughTransformLines; Mat dstHoughTransformCircles;
Mat dstHoughTransformLinesSpace; Mat dstHoughTransformCirclesSpace;
const char* image_window = "Source Image";
const char* result_window = "Result window";
int match(string filename, string templatename);
void display_template_matching();
void MatchingMethod( int, void* );
class HoughTrans
{
private:
thread* t;
string window_name;
Mat src; Mat dstHoughTransform; Mat srctemplate;
int DELAY_CAPTION = 1500;
void display_caption(Mat dst, const char* caption)
{
Mat src = Mat::zeros(dst.size(), dst.type());
putText(src, caption,
Point(src.cols/4, src.rows/2),
FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255));
display_dst(src, DELAY_CAPTION);
}
void display_dst(Mat dst, int delay)
{
imshow(window_name, dst);
waitKey(delay);
}
void display_hough_lines()
{
// 圖片深複製,放在新的記憶體空間
Mat dst = src.clone();
Mat orignImg = src.clone();
// 將圖片轉灰階圖
cvtColor(dst, dst, COLOR_BGR2GRAY);
// GaussianBlur Image
GaussianBlur(dst, dst, Size(3, 3), 0, 0);
// Edge Image
int low_threshold = 50;
int high_threshold = 150;
Canny(dst, dst, low_threshold, high_threshold);
// Masked Image, 將車道視線範圍外的線條遮蓋
Mat MaskImg = Mat::zeros(dst.size(), CV_8UC1);
int ignore_mask_color = 255;
Point rook_points[1][4];
rook_points[0][0] = Point(0, dst.rows);
rook_points[0][1] = Point(400, 350);
rook_points[0][2] = Point(550, 350);
rook_points[0][3] = Point(dst.cols, dst.rows);
const Point* ppt[1] = { rook_points[0] };
int npt[] = { 4 };
fillPoly(MaskImg, ppt, npt, 1, Scalar(ignore_mask_color), LINE_8, 0);
bitwise_and(dst, MaskImg, dst);
// Draw lines
vector<Vec4f> lines;
HoughLinesP(dst, lines, 1, M_PI/180, 20, 5, 150);
dstHoughTransform = Mat::zeros(src.size(), src.type());
for (size_t i = 0; i < lines.size(); i++)
{
Vec4i l = lines[i];
line(orignImg, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 12, LINE_AA);
line(dstHoughTransform, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0, 0, 255), 12, LINE_AA);
}
dstLines = orignImg.clone();
}
void display_hough_circles()
{
// 圖片深複製,放在新的記憶體空間
Mat dst = src.clone();
Mat orignImg = src.clone();
// 將圖片轉灰階圖
cvtColor(dst, dst, COLOR_BGR2GRAY);
// MedianBlur Image
medianBlur(dst, dst, 5);
// Edge Image
int low_threshold = 50;
int high_threshold = 100;
Canny(dst, dst, low_threshold, high_threshold);
// Draw Circles
vector<Vec3f> circles;
HoughCircles(dst, circles, HOUGH_GRADIENT, 1,
dst.rows/8, // change this value to detect circles with different distances to each other
100, 30, 50, 150 // change the last two parameters
);
dstHoughTransform = Mat::zeros(src.size(), src.type());
for( size_t i = 0; i < circles.size(); i++ )
{
Vec3i c = circles[i];
Point center = Point(c[0], c[1]);
// circle center
circle(src, center, 1, Scalar(0,100,100), 3, LINE_AA);
// circle outline
int radius = c[2];
if (radius < 65)
{
circle(src, center, radius, Scalar(0, 0, 255), 3, LINE_AA);
circle(dstHoughTransform, center, radius, Scalar(0, 0, 255), 3, LINE_AA);
cout << "X:" << c[0] << " " << "Y:" << c[1] << " " << "radius:" << c[2] << " " << "NT$1" << endl;
}
else if (radius >= 65 && radius < 75)
{
circle(src, center, radius, Scalar(0, 255, 255), 3, LINE_AA);
circle(dstHoughTransform, center, radius, Scalar(0, 255, 255), 3, LINE_AA);
cout << "X:" << c[0] << " " << "Y:" << c[1] << " " << "radius:" << c[2] << " " << "NT$5" << endl;
}
else if (radius >= 75 && radius < 85)
{
circle(src, center, radius, Scalar(255, 0, 255), 3, LINE_AA);
circle(dstHoughTransform, center, radius, Scalar(255, 0, 255), 3, LINE_AA);
cout << "X:" << c[0] << " " << "Y:" << c[1] << " " << "radius:" << c[2] << " " << "NT$10" << endl;
}
else if (radius >= 85)
{
circle(src, center, radius, Scalar(255, 255, 0), 3, LINE_AA);
circle(dstHoughTransform, center, radius, Scalar(255, 255, 0), 3, LINE_AA);
cout << "X:" << c[0] << " " << "Y:" << c[1] << " " << "radius:" << c[2] << " " << "NT$50" << endl;
}
}
dstCircles = src.clone();
}
void houghtransform(Mat const &fht)
{
double minv(0),maxv(0);
minMaxLoc(fht,&minv,&maxv);
Mat ucharFht;
fht.convertTo(ucharFht, CV_MAKETYPE(CV_8U, fht.channels()), 255.0/(maxv+minv), minv/(maxv+minv));
dstHoughTransform = ucharFht.clone();
}
public:
HoughTrans(string _window_name, Mat _src)
{
HoughTrans::window_name = _window_name;
HoughTrans::src = _src;
}
~HoughTrans()
{
cv::destroyAllWindows();
}
void Run()
{
if (window_name == "HoughLines")
{
display_hough_lines();
}
else if (window_name == "HoughCircles")
{
display_hough_circles();
}
}
void ShowHoughTransform(Mat *dst, Mat *dstSpace)
{
*dst = dstHoughTransform.clone();
Mat hough;
ximgproc::FastHoughTransform(dstHoughTransform, hough, CV_32S, 6, 2, 1); // dst is source image
houghtransform(hough);
*dstSpace = dstHoughTransform;
}
};
int main()
{
char window_HoughLines[] = "HoughLines";
char window_HoughCircles[] = "HoughCircles";
char window_HoughTransformLinesSpace[] = "HoughTransformLinesSpace";
char window_HoughTransformCirclesSpace[] = "HoughTransformCirclesSpace";
char window_HoughTransformLines[] = "HoughTransformLines";
char window_HoughTransformCircles[] = "HoughTransformCircles";
char window_TemplateMatching[] = "TemplateMatching";
// 載入圖片到src
const char* filename = "image_a.jpg";
Mat src = imread(samples::findFile( filename ), IMREAD_COLOR);
if (src.empty())
{
cout << "Error opening image" << endl;
return EXIT_FAILURE;
}
// 載入圖片到src2
const char* filename2 = "image_b.jpg";
Mat src2 = imread(samples::findFile( filename2 ), IMREAD_COLOR);
if (src2.empty())
{
cout << "Error opening image" << endl;
return EXIT_FAILURE;
}
// 載入圖片到src3
const char* filename3 = "image_b_cut.jpg";
Mat src3 = imread(samples::findFile( filename3 ), IMREAD_COLOR);
if (src3.empty())
{
cout << "Error opening image" << endl;
return EXIT_FAILURE;
}
std::cout << "Waiting..." << std::endl;
namedWindow(window_HoughLines, WINDOW_AUTOSIZE);
namedWindow(window_HoughCircles, WINDOW_AUTOSIZE);
namedWindow(window_HoughTransformLinesSpace, WINDOW_AUTOSIZE);
namedWindow(window_HoughTransformCirclesSpace, WINDOW_AUTOSIZE);
namedWindow(window_HoughTransformLines, WINDOW_AUTOSIZE);
namedWindow(window_HoughTransformCircles, WINDOW_AUTOSIZE);
namedWindow(window_TemplateMatching, WINDOW_AUTOSIZE);
imshow(window_HoughLines, src);
imshow(window_HoughCircles, src2);
imshow(window_TemplateMatching, src3);
HoughTrans demo1(window_HoughLines, src);
HoughTrans demo2(window_HoughCircles, src2);
demo1.Run();
demo1.ShowHoughTransform(&dstHoughTransformLines, &dstHoughTransformLinesSpace);
demo2.Run();
demo2.ShowHoughTransform(&dstHoughTransformCircles, &dstHoughTransformCirclesSpace);
//display_template_matching();
match(filename2, filename3);
std::cout << "Finished." << std::endl;
imshow(window_HoughLines, dstLines);
imshow(window_HoughCircles, dstCircles);
imshow(window_HoughTransformLinesSpace, dstHoughTransformLinesSpace);
imshow(window_HoughTransformCirclesSpace, dstHoughTransformCirclesSpace);
imshow(window_HoughTransformLines, dstHoughTransformLines);
imshow(window_HoughTransformCircles, dstHoughTransformCircles);
waitKey();
return EXIT_SUCCESS;
}
int match(string filename, string templatename)
{
Mat ref = cv::imread(filename);
Mat tpl = cv::imread(templatename);
Mat dst = ref.clone();
Mat dstTpl = tpl.clone();
// 將圖片轉灰階圖
cvtColor(dst, dst, COLOR_BGR2GRAY);
cvtColor(dstTpl, dstTpl, COLOR_BGR2GRAY);
//medianBlur(dst, dst, 5);
GaussianBlur(dst, dst, Size(3, 3), 0, 0);
int low_threshold = 150;
int high_threshold = 200;
Canny(dst, dst, low_threshold, high_threshold);
Mat sx, sy;
Sobel(dst, sx, -1, 1, 0);
Sobel(dst, sy, -1, 0, 1);
Mat image_input = abs(sx) + abs(sy);
//medianBlur(dstTpl, dstTpl, 5);
GaussianBlur(dstTpl, dstTpl, Size(3, 3), 0, 0);
Canny(dstTpl, dstTpl, low_threshold, high_threshold);
Mat sx2, sy2;
Sobel(dstTpl, sx2, -1, 1, 0);
Sobel(dstTpl, sy2, -1, 0, 1);
Mat template_input = abs(sx2) + abs(sy2);
Mat match_result;
matchTemplate(image_input, template_input, match_result, 5);
Mat thresholded;
threshold(match_result, thresholded, 0.2, 1, THRESH_BINARY);
while (true)
{
double minval, maxval, threshold = 0.9;
Point minloc, maxloc;
minMaxLoc(thresholded, &minval, &maxval, &minloc, &maxloc);
if (maxval >= threshold)
{
rectangle(ref, maxloc, Point(maxloc.x + tpl.cols, maxloc.y + tpl.rows), CV_RGB(0,255,0), 2);
floodFill(thresholded, maxloc, 0); //mark drawn blob
}
else
break;
}
imshow("final", ref);
return 0;
}
```