今天(2013/11/18)我才知道可以用Windows Live Writer來寫部落格,不過Better late than never 搭配
Code Snippet plugin for Windows Live Writer可以貼出漂亮的程式碼,
之前我都是直接用網頁搭配編輯器撰寫部落格, 一方面撰寫完上傳速度慢到讓你覺得當機;
另一方面, 編輯工具實在不太好用!還是需要有一個專門的應用程式比較強大
回到正題, 這次要整合一個邊緣及角點偵測的程式碼, 不同以往方式這次採用c++ class來定義, 以下程式碼為參照
國外一本書名為 OpenCV 2 Computer Vision Application Programming Cookbook, 部分註解是我自己加上去的
ImageUtility.h加入新的類別MorphFeatures, 程式碼如下:
1: class MorphoFeatures{
2: private:
3: int threshold; // threshold to produce binary image
4: // structuring elements used in corner detection
5: cv::Mat cross; // 十字形狀
6: cv::Mat diamond; // 鑽石形狀
7: cv::Mat square; // 方塊形狀
8: cv::Mat x; // 畫叉形狀
9: //---------------------------------------------------------------------------------------
10: public:
11: // 預設建構子, 其中:後面為初始化成員變數
12: MorphoFeatures() : threshold(-1),
13: cross(5, 5, CV_8U, cv::Scalar(0)),
14: diamond(5, 5,CV_8U,cv::Scalar(1)),
15: square(5, 5, CV_8U,cv::Scalar(1)),
16: x(5, 5, CV_8U, cv::Scalar(0)){
17: // (1) Creating the cross-shaped structuring element
18: /* 0 0 1 0 0
19: * 0 0 1 0 0
20: * 1 1 1 1 1
21: * 0 0 1 0 0
22: * 0 0 1 0 0
23: */
24: for (int i=0; i<5; i++) {
25: cross.at<uchar>(2,i)= 1;
26: cross.at<uchar>(i,2)= 1;
27: }
28:
29: // (2) Creating the diamond-shaped structuring element
30: /* 0 0 1 0 0
31: * 0 1 1 1 0
32: * 1 1 1 1 1
33: * 0 1 1 1 0
34: * 0 0 1 0 0
35: */
36: // 左上
37: diamond.at<uchar>(0,0)= 0;
38: diamond.at<uchar>(0,1)= 0;
39: diamond.at<uchar>(1,0)= 0;
40: // 右下
41: diamond.at<uchar>(4,4)= 0;
42: diamond.at<uchar>(3,4)= 0;
43: diamond.at<uchar>(4,3)= 0;
44: // 左下
45: diamond.at<uchar>(4,0)= 0;
46: diamond.at<uchar>(4,1)= 0;
47: diamond.at<uchar>(3,0)= 0;
48: // 右上
49: diamond.at<uchar>(0,4)= 0;
50: diamond.at<uchar>(0,3)= 0;
51: diamond.at<uchar>(1,4)= 0;
52:
53: // (4) Creating the x-shaped structuring element
54: /* 1 0 0 0 1
55: * 0 1 0 1 0
56: * 0 0 1 0 0
57: * 0 1 0 1 0
58: * 1 0 0 0 1
59: */
60: for (int i=0; i<5; i++) {
61: x.at<uchar>(i,i)= 1;
62: x.at<uchar>(4-i,i)= 1;
63: }
64: }
65: //---------------------------------------------------------------------------------------
66: public:
67: cv::Mat getEdges(const cv::Mat &image); // 取得邊緣
68: void setThreshold(int value){ threshold = value; }; // 設定門檻值
69: cv::Mat getCorners(const cv::Mat &image); // 取得轉角
70: void drawOnImage(const cv::Mat& binary, cv::Mat& image); // 轉角點位置畫圓圈
71: void MorphoFeatures::applyThreshold(cv::Mat& result); // 絕對影像差值 > 門檻值為255, 否則為0
72: };
接下來, 實作對應的成員函式, 開啟ImageUtility.cpp
1: cv::Mat MorphoFeatures::getEdges(const cv::Mat &image)
2: {
3: // Get the gradient image
4: cv::Mat result;
5: cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());
6: // Apply threshold to obtain a binary image
7: applyThreshold(result);
8: return result;
9: }
morpholgyEx可以搭配不同的enum for morphological operator進行濾波
開啟C:\OpenCV-2.4.6\opencv\include\opencv2\imgproc\imgproc.hpp已有定義許多好用的morphological operator
1: //! type of morphological operation
2: enum { MORPH_ERODE=CV_MOP_ERODE, MORPH_DILATE=CV_MOP_DILATE,
3: MORPH_OPEN=CV_MOP_OPEN, MORPH_CLOSE=CV_MOP_CLOSE,
4: MORPH_GRADIENT=CV_MOP_GRADIENT, MORPH_TOPHAT=CV_MOP_TOPHAT,
5: MORPH_BLACKHAT=CV_MOP_BLACKHAT };
開啟mainwindow.ui加入<邊緣偵測><Image1><Image2>
分別點actionEdgeImage1和actionEdgeImage2, 跳至信號槽加入對應的回呼函式
接續上一個步驟, 在對應的triggered()回呼函式加入下列內容, 其中actionDetectEdge()為自訂函式
1: void MainWindow::on_actionEdgeImage1_triggered()
2: {
3: ui->tabWidget->setCurrentIndex(3);
4: ui->tabWidget_2->setCurrentIndex(9);
5: ui->radioButton_19->setChecked(true);
6: actionDetectEdges(0);
7: }
8:
9: void MainWindow::on_actionEdgeImage2_triggered()
10: {
11: ui->tabWidget->setCurrentIndex(3);
12: ui->tabWidget_2->setCurrentIndex(9);
13: ui->radioButton_20->setChecked(true);
14: actionDetectEdges(1);
15: }
另外,新增一個<邊緣偵測>頁面, 元件名稱如下所示
在mainwindow.cpp中void MainWindow::defineEventCallbackFunction(void)函式加入下列定義
1: connect(ui->horizontalSlider_10, SIGNAL(valueChanged(int)), ui->lcdNumber_10, SLOT(display(int))); // 邊緣偵測(Detect Edges)
接著, 在mainwindow.h新增一個MorphFeatures物件morpho
同時,新增一個函式定義actionDetectEdges(int index)
對應的實作如下:
1: // 邊緣偵測(Detecting Edges)
2: void MainWindow::actionDetectEdges(int index)
3: {
4: double tic,toc,tcost; // 計時開始, 計時結束, 花費時間
5: if( index==0 )
6: {
7: if(img_rgb1.empty()) return;
8: cv::cvtColor(img_rgb1_cpy, img_gray, CV_BGR2GRAY); // 彩色轉灰階
9: }else{
10: if(img_rgb2.empty()) return;
11: cv::cvtColor(img_rgb2_cpy, img_gray, CV_BGR2GRAY); // 彩色轉灰階
12: }
13:
14: tic = static_cast<double>(cv::getTickCount()); // tic
15: int thresholded = ui->horizontalSlider_10->value(); // 二值化門檻
16: morpho.setThreshold(thresholded);
17: cv::Mat edges;
18: edges = morpho.getEdges(img_gray);
19:
20: // 動態顯示像素值(處理後的結果)
21: if(index==0){
22: cv::cvtColor(edges, img_rgb1, CV_GRAY2BGR); // 灰階轉彩色
23: }else{
24: cv::cvtColor(edges, img_rgb2, CV_GRAY2BGR); // 灰階轉彩色
25: }
26: toc = static_cast<double>(cv::getTickCount()); // toc
27: tcost = (toc - tic)/cv::getTickFrequency(); // 花費時間
28:
29: QImage qimg_edge;
30: if(index==0){
31: qimg_edge = me::Mat2QImage(img_rgb1); // Mat2QImage
32: }else{
33: qimg_edge = me::Mat2QImage(img_rgb2); // Mat2QImage
34: }
35: if( index==0 )
36: {
37: me::imshow(ui->label,qimg_edge,0); //顯示影像(原始大圖)
38: }else{
39: me::imshow(ui->label_12,qimg_edge,0); //顯示影像(原始大圖)
40: }
41: me::imshow(ui->label_29,qimg_edge,1); //顯示影像(小圖)
42: if( edges.cols > ui->tab_4->width() |edges.rows > ui->tab_4->height())
43: {
44: ui->label_29->setScaledContents(true); //stretched 效果
45: }else{
46: ui->label_29->setScaledContents(false); //non-stretched 效果
47: }
48: }
操作展示:
====================================================================
角點偵測(Detecting Corners)
新增頁面<邊緣偵測>, 對應的元件名稱如下:
MenuBar加入<角點偵測>及下一層<Image1>和<Image2>
<Image1>和<Image2>對應triggered()函式如下, 其中actionDetectCorners為自訂函式
1: void MainWindow::on_actionDetectCornersImage1_triggered()
2: {
3: ui->tabWidget->setCurrentIndex(3);
4: ui->tabWidget_2->setCurrentIndex(10);
5: ui->radioButton_21->setChecked(true);
6: actionDetectCorners(0);
7: }
8:
9: void MainWindow::on_actionDetectCornersImage2_triggered()
10: {
11: ui->tabWidget->setCurrentIndex(3);
12: ui->tabWidget_2->setCurrentIndex(10);
13: ui->radioButton_22->setChecked(true);
14: actionDetectCorners(1);
15: }
actionDetectCorners
1: // 角點偵測(Detecting Corners)
2: void MainWindow::actionDetectCorners(int index)
3: {
4: double tic,toc,tcost; // 計時開始, 計時結束, 花費時間
5: if( index==0 )
6: {
7: if(img_rgb1.empty()) return;
8: cv::cvtColor(img_rgb1_cpy, img_gray, CV_BGR2GRAY); // 彩色轉灰階
9: }else{
10: if(img_rgb2.empty()) return;
11: cv::cvtColor(img_rgb2_cpy, img_gray, CV_BGR2GRAY); // 彩色轉灰階
12: }
13:
14: tic = static_cast<double>(cv::getTickCount()); // tic
15: int thresholded = ui->horizontalSlider_11->value(); // 二值化門檻
16: morpho.setThreshold(thresholded);
17:
18: cv::Mat corners;
19: corners= morpho.getCorners(img_gray);
20: cv::namedWindow("corners");
21: cv::imshow("corners", corners);
22: morpho.applyThreshold(corners);
23: morpho.drawOnImage(corners,img_gray); // Display the corner on the image
24:
25: // 動態顯示像素值(處理後的結果)
26: if(index==0){
27: cv::cvtColor(img_gray, img_rgb1, CV_GRAY2BGR); // 灰階轉彩色
28: }else{
29: cv::cvtColor(img_gray, img_rgb2, CV_GRAY2BGR); // 灰階轉彩色
30: }
31: toc = static_cast<double>(cv::getTickCount()); // toc
32: tcost = (toc - tic)/cv::getTickFrequency(); // 花費時間
33: //setWindowTitle("影像Opening花費時間: " + QString::number(tcost) + " sec");
34: QImage qimg_edge;
35: if(index==0){
36: qimg_edge = me::Mat2QImage(img_rgb1); // Mat2QImage
37: }else{
38: qimg_edge = me::Mat2QImage(img_rgb2); // Mat2QImage
39: }
40: if( index==0 )
41: {
42: me::imshow(ui->label,qimg_edge, 0); //顯示影像(原始大圖)
43: }else{
44: me::imshow(ui->label_12,qimg_edge, 0); //顯示影像(原始大圖)
45: }
46: me::imshow(ui->label_30, qimg_edge,1); //顯示影像(小圖)
47: if( img_gray.cols > ui->tab_4->width() | img_gray.rows > ui->tab_4->height())
48: {
49: ui->label_30->setScaledContents(true); //stretched 效果
50: }else{
51: ui->label_30->setScaledContents(false); //non-stretched 效果
52: }
53: }
載入sample.jpg
1: // (1) Creating the cross-shaped structuring element
2: /* 0 0 1 0 0
3: * 0 0 1 0 0
4: * 1 1 1 1 1
5: * 0 0 1 0 0
6: * 0 0 1 0 0
7: */
8: for (int i=0; i<5; i++) {
9: cross.at<uchar>(2,i)= 1;
10: cross.at<uchar>(i,2)= 1;
11: }
步驟1: 進行<<Dilate with a cross>>目前的pixel符合該位置的上下左右都是暗或都是亮, 可以特別被凸顯出來<+>
步驟2<Dilate with a cross>後, 接著進行<Erode with a diamond>
步驟3: 進行<<Dilate with a x shape>目前的pixel符合該位置的上下左右都是暗或都是亮, 可以特別被凸顯出來<x>
步驟4<Dilate with a x shape>後, 接著進行<Erode with a square>
步驟5 將步驟4的結果和步驟2的結果進行絕對誤差(absdiff)運算
步驟6 將上一步驟的結果進行二值化門檻
步驟7 在步驟6中絕對誤差後像素點大於門檻的位置繪製一個圓圈
==========================================================================
角點偵測以棋盤格為例
留言列表