今天(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>

image

分別點actionEdgeImage1和actionEdgeImage2, 跳至信號槽加入對應的回呼函式

image

接續上一個步驟, 在對應的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: }

另外,新增一個<邊緣偵測>頁面, 元件名稱如下所示

image

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

image

同時,新增一個函式定義actionDetectEdges(int index)

image

對應的實作如下:

   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: }

操作展示:

image

====================================================================

角點偵測(Detecting Corners)

新增頁面<邊緣偵測>, 對應的元件名稱如下:

image

MenuBar加入<角點偵測>及下一層<Image1>和<Image2>

image

<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

image

   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符合該位置的上下左右都是暗都是亮, 可以特別被凸顯出來<+>

image

步驟2<Dilate with a cross>後, 接著進行<Erode with a diamond>

image

步驟3: 進行<<Dilate with a x shape>目前的pixel符合該位置的上下左右都是暗都是亮, 可以特別被凸顯出來<x>

image

步驟4<Dilate with a x shape>後, 接著進行<Erode with a square>

image

步驟5 將步驟4的結果和步驟2的結果進行絕對誤差(absdiff)運算

image

image

步驟6 將上一步驟的結果進行二值化門檻

image

步驟7 在步驟6中絕對誤差後像素點大於門檻的位置繪製一個圓圈

image

==========================================================================

角點偵測以棋盤格為例

imageimage

imageimage

imageimage

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 me1237guy 的頭像
    me1237guy

    天天向上

    me1237guy 發表在 痞客邦 留言(0) 人氣()