2016-06-10 57 views
1

一個出入記錄輸出下面是一個功能我在我的源文件檢測在谷歌的UnitTest

cv::Mat* Retina::Preprocessing::create_mask(const cv::Mat *img, const uint8_t threshold) { 

    LOG_IF(ERROR, img->empty()) << "The input image is empty. Terminating Now!!"; 

    cv::Mat green_channel(img->rows(), img->cols(), CV_8UC1); /*!< Green channel. */ 

    /// Check number of channels in img and based on that find out the green channel. 
    switch (img->channels()){ 
     /// For 3 channels, it is an RGB image and we use cv::mixChannels(). 
     case(3): { 
      int from_to[] = {1,0}; 
      cv::mixChannels(img, 1, &green_channel, 1, from_to, 1); 
      break; 
     } 
     /// For a single channel, we assume that it is the green channel. 
     case(1): { 
      green_channel = *img; 
      break; 
     } 
     /// Otherwise we are out of clue and throw an error. 
     default: { 
      LOG(ERROR)<<"Number of image channels found = "<< img->channels()<<". Terminating Now!!"; 
      return nullptr; /*!< (unreachable code) Only given for completion */ 
     } 

    } 

    cv::Mat mask(img->rows(), img->cols(), CV_8UC1);/*!< Empty mask image */ 

    if (threshold == 0)/// Otsu's threshold is used 
     cv::threshold(green, mask, threshold, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); 
    else /// We can use the provided threshold value 
     cv::threshold(green, mask, threshold, 255, CV_THRESH_BINARY); 

    LOG_IF(ERROR, mask.empty())<< "After thresholding, image became empty. Terminating Now!!"; 



    std::vector<std::vector<cv::Point>> contours; 

    /// Get the contours in the binary mask. 
    cv::findContours(mask, *contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE); 
    size_t max_index{}; 
    double prev_area{}; 
    size_t index{}; 

    /// Lambda function for a convenient one-liner std::for_each. SEE BELOW. 
    lambda_max_index = [max_index, prev_area, index] (std::vector<cv::Point> c){ 
     double new_area { cv::contourArea(c) }; 
     max_index = (new_area > prev_area) ? max_index = index : max_index; 
     ++index; 
    }; 

    /// For each contour compute its area, and over the loop find out the largest contour. 
    std::for_each(contours.begin(), contours.end(), lambda_max_index()); 

    /// Iterate over each point and test if it lies inside the contour, and drive in it. 
    for(size_t row_pt = 0; row_pt < mask_out.rows(); ++row_pt){ 
     for(size_t col_pt = 0; col_pt < mask_out.cols(); ++col_pt){ 
      if (cv::pointPolygonTest(contours[max_index], cv::Point(row_pt, col_pt))>=0) 
       mask[row_pt, col_pt] = 255; 
      else 
       mask[row_pt, col_pt] = 0; 
     } 
    } 
    return &mask; 
} 

以下是我已經寫了一個谷歌的單元測試文件。這是我第一次嘗試GoogleTest。

#include"PreProcessing.hxx" 
#include<opencv2/highgui/highgui.hpp> 
#include<gtest/gtest.h> 

TEST(Fundus_Mask_Creation, NO_THRESHOLD) { 

    cv::Mat img(cv::imread(std::string("../data/test_img.jpg"))); 

    cv::Mat* mask = Retina::Preprocessing::create_mask(&img); 

    ASSERT_TRUE(!(mask->empty())); 

    std::string winname("Input Image"); 
    cv::namedWindow(winname); 

    cv::imshow(winname, img); 
    cv::waitKey(800); 

    cv::destroyWindow(winname); 

    winname = "Fundus Mask"; 
    cv::namedWindow(winname); 

    cv::imshow(winname, *mask); 
    cv::waitKey(800); 
} 

    TEST(Fundus_Mask_Creation, THRESHOLD) { 

     cv::Mat img(cv::imread(std::string("../data/test_img.jpg"))); 

     std::uint8_t threshold{10}; 
     cv::Mat* mask = Retina::Preprocessing::create_mask(&img, threshold); 

     ASSERT_TRUE(!(mask->empty())); 

     std::string winname("Input Image"); 
     cv::namedWindow(winname); 

     cv::imshow(winname, img); 
     cv::waitKey(800); 

     cv::destroyWindow(winname); 

     winname = "Fundus Mask"; 
     cv::namedWindow(winname); 

     cv::imshow(winname, *mask); 
     cv::waitKey(800); 
    } 

我也想測試的情況下,在考勤記錄日誌,這些日誌"Number of image channels found = "<< img->channels()<<". Terminating Now!!!";

我如何寫一個單元測試,這將成功運行(即原來的源文件會記錄一個致命錯誤) ,當我提供一個導致上述消息正確記錄的輸入時?

回答

2

你需要嘲笑(googlemock)來實現這一點。要啓用嘲笑,請創建一個薄包裝類,以及該類將繼承的接口(您需要使用此接口來啓用模擬)以調用glog調用。您可以使用此作爲一個準則:

class IGlogWrapper { 
public: 
    ... 
    virtual void LogIf(int logMessageType, bool condition, const std::string &message) = 0; 
    ... 
}; 

class GlogWrapper : public IGlogWrapper { 
public: 
    ... 
    void LogIf(int logMessageType, bool condition, const std::string &message) override { 
     LOG_IF(logMessageType, condition) << message; 
    } 
    ... 
}; 

現在創建一個模擬類:

class GlogWrapperMock : public IGlogWrapper { 
public: 
    ... 
    MOCK_METHOD3(LogIf, void(int, bool, const std::string &)); 
    ... 
}; 

,讓您的Retina::Preprocessing類持有一個指向IGlogWrapper,並通過該接口記錄的調用。現在,您可以使用依賴注入傳遞Retina::Preprocessing類指向實際記錄器類GlogWrapper的指針,該指針將使用glog,而在測試中,將指針傳遞給模擬對象(GlogWrapperMock的實例)。通過這種方式,您可以在測試中創建一個具有兩個通道的圖像,並在該模擬對象上設置期望值,以便在該情況下調用函數LogIf。下面的示例使用一個公共的方法來注入一個記錄器使用:

using namespace testing; 

TEST(Fundus_Mask_Creation, FAILS_FOR_TWO_CHANNEL_IMAGES) { 

    // create image with two channels 
    cv::Mat img(...); 

    GlogWrapperMock glogMock; 
    Retina::Preprocessing::setLogger(&glogMock); 

    std::string expectedMessage = "Number of image channels found = 2. Terminating Now!!"; 
    EXPECT_CALL(glogMock, LogIf(ERROR, _, expectedMessage).WillOnce(Return()); 

    cv::Mat* mask = Retina::Preprocessing::create_mask(&img); 

    // Make additional assertions if necessary 
    ... 
} 

約嘲諷檢查文檔的更多信息:https://github.com/google/googletest/blob/master/googlemock/docs/ForDummies.md#getting-started 另外,請注意,您已經發布了兩個單元測試沒有太大的當您從磁盤加載圖像並在斷言之後還進行其他調用時會發生意外。單元測試應該簡潔,快速並且與環境隔離。我會建議關於單元測試的額外閱讀,互聯網充滿了資源。希望這可以幫助!

+0

是的。它做了。很感謝你。我學到了很多。 –

+1

@UjjwalAryan不客氣!爲了表示讚賞,請考慮提供答案,如果有幫助的話。 –

+0

這是我會寫的答案:),但我會指出另一種攔截和檢測API調用的方法是使用鏈接時間模擬。這些函數具有與常規日誌API相同的簽名,但具有不同的實現,類似於模擬:它記錄交互並提供預期結果。鏈接時嘲諷更像是一種C語言技術,而不是C++,我認爲接口可以更好地使用該語言。 – legalize