1. 程式人生 > >影象演算法中的設計模式(一):使用策略模式設計演算法

影象演算法中的設計模式(一):使用策略模式設計演算法

設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。總體來說,一個設計模式就是一個可重用的、讓程式碼更容易被他人理解的、可靠性的解決方案。
策略設計模式的目的就是把演算法封裝進類。封裝後,演算法之間互相替換,或者把幾個演算法組合起來進行更復雜的處理,都會更加容易。而且這種模式能夠儘可能地將演算法的複雜性隱藏在一個直觀的程式設計介面之後,因而有利於演算法的部署。

本文以一個簡單的演算法設計為例:識別影象中具有某種顏色的所有畫素。演算法原理很簡單,即對於一幅彩色影象,設其中某畫素點的值為color1,待參考的目標顏色為color2;color1與color2都是具有3個元素的向量,求color1與color2之間的歐式距離distance;當distance小於一定閾值maxDist時認為該畫素和參考目標畫素相同,在輸出結果影象中將其設為白色,反之設為黑色。

僅考慮演算法實現,而不考慮設計模式,程式碼如下:

#include"opencv2/opencv.hpp"
#include"opencv2/core/core.hpp"
#include"opencv2/highgui/highgui.hpp"
#include<iostream>

//計算兩個顏色之間的歐式距離
float getColorDistance(const cv::Vec3b& color1,const cv::Vec3b& color2) {
    return cv::norm(color1,color2,cv::NORM_L2);
}

int main() {
    //開啟影象並顯示
cv::Mat image; image = cv::imread("56.jpg"); if (image.empty()) { //錯誤處理 std::cout << "影象打開發生錯誤"<<std::endl; return -1; } cv::namedWindow("Image"); cv::imshow("Image", image); //使用迭代器逐點比較與目標顏色的差距,併產生結果影象 cv::Mat result; result.create(image.
size(), CV_8UC1); cv::Mat_<cv::Vec3b>::const_iterator it = image.begin<cv::Vec3b>(); cv::Mat_<cv::Vec3b>::const_iterator itend = image.end<cv::Vec3b>(); cv::Mat_<uchar>::iterator itout = result.begin<uchar>(); int MaxDist = 100; cv::Vec3b targetColor = cv::Vec3b(0, 150, 240); for (; it != itend; it++,itout++) { if (getColorDistance(*it, targetColor) <= MaxDist) { *itout = 255; } else *itout = 0; } //顯示結果 cv::namedWindow("Result"); cv::imshow("Result", result); cv::waitKey(); return 0; }

執行程式的結果:
輸入影象
這裡寫圖片描述
結果影象:
這裡寫圖片描述

從這裡開始引入“策略模式”。將上述顏色檢測演算法封裝進一個策略類ColorDetector,這裡先看一下封裝完成後,部署和使用它的方法。

int main() {
    //1.建立顏色檢測演算法類物件
    ColorDetector cdetect;
    //2.讀取輸入影象
    cv::Mat image;
    image = cv::imread("56.jpg");
    if (image.empty()) {  //錯誤處理
        std::cout << "影象打開發生錯誤" << std::endl;
        return -1;
    }

    //3.設定輸入引數
    cdetect.setTargetColor(0,150,240);
    cdetect.setMaxDist(100);


    //4.處理影象並顯示結果
    cv::Mat result;
    result = cdetect.process(image);
    cv::namedWindow("Result");
    cv::imshow("Result", result);
    cv::waitKey();
    return 0;
}

可以看出,層次清晰了很多,使用的時候只是呼叫了類物件的3個方法。
下面展示這個策略類的結構:

class ColorDetector{
private:
    cv::Vec3b targetColor;//目標顏色
    int maxDist;//距離閾值
    cv::Mat result;//結果影象

public:
    ColorDetector();
    void setTargetColor(uchar g,uchar b,uchar r);//設定目標顏色
    void setMaxDist(int dist);//設定距離閾值
    float getColorDistance(const cv::Vec3b& color);//計算兩個顏色之間的歐式距離
    cv::Mat process(const cv::Mat& image);//封裝整個演算法處理過程
};

建構函式:

ColorDetector::ColorDetector():targetColor(cv::Vec3b(0,0,0)),maxDist(100) {}

在執行建構函式時,類的例項會用預設值初始化演算法的各種引數,以便它能立即進入可用狀態。
還可以編寫set和get方法來讀寫演算法的引數值:

//設定目標顏色
void ColorDetector::setTargetColor(uchar g,uchar b,uchar r) {
        targetColor = cv::Vec3b(g,b,r);
    }
//設定距離閾值
void ColorDetector::setMaxDist(int dist) {
        if (dist < 0)
            dist = 0;
        maxDist = dist;
    }

將演算法執行過程封裝進process方法:

//計算兩個顏色之間的歐式距離
float ColorDetector::getColorDistance(const cv::Vec3b& color) {
    return cv::norm(color, targetColor, cv::NORM_L2);
}
cv::Mat ColorDetector::process(const cv::Mat& image) {
    result.create(image.size(), CV_8UC1);
    cv::Mat_<cv::Vec3b>::const_iterator it = image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::const_iterator itend = image.end<cv::Vec3b>();
    cv::Mat_<uchar>::iterator itout = result.begin<uchar>();
    for (; it != itend; it++, itout++) {
        if (getColorDistance(*it) <= maxDist) {
            *itout = 255;
        }
        else
            *itout = 0;
    }
    return result;
}

可以看出,封裝進這個類的演算法相對簡單。當演算法的實現過程更加複雜,步驟繁多,並且包含多個引數時,策略設計模式會真正顯示出強大的功能。