OpenCV 2 学习笔记(10): 算法的基本设计模式<1>:策略模式(strategy pattern)

本文介绍了如何使用策略模式在OpenCV中封装颜色检测算法。通过创建一个名为ColorDetector的类,该类接受图像、颜色和幅度范围作为输入,输出为包含给定颜色的二值图像。文章探讨了类的设计,包括构造器、属性设置方法以及避免数值溢出的问题。同时,还提供了一个main方法的示例,展示如何使用该类。

           

策略模式的用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。本文只是简单的提及,想了解比较详细,请看:

http://www.cnblogs.com/zhenyulu/articles/82017.html

  C++最大的优势是什么,就是面向对象,我们可以把属性和方法都封装到一个类里面。这样我们很容易变换一种方法或者可以更容易的创建复杂的程序。而且这样可以将复杂的程序封装起来,只留一个简单易用的接口。

       下面我们想创建一个简单的算法,它会确定图像中有给定颜色的所有像素。算法中图像和颜色作为输入,输出为表示有给定颜色的一个二值图像。还有一个给定的幅度范围。

// get the iterators
     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 each pixel
     for ( ; it!= itend; ++it, ++itout) {
        
   // process each pixel ---------------------            
// compute distance from target color
   if (getDistance(*it)<minDist) {
           *itout= 255;
        } else {
           *itout= 0;
        }
        // end of pixel processing ----------------     }

// Computes the distance from target color.
     int getDistance(const cv::Vec3b& color) const {
        return abs(color[0]-target[0])+
               abs(color[1]-target[1])+
               abs(color[2]-target[2]);
     }

首先就是创建迭代器,然后扫描每一个像素和目标像素的差值,如果小于则创建255像素,否则是0像素,这里的计算距离是去差值的绝对值之和。显然target是一个类属性。现在让我们把它封装起来:

cv::Mat ColorDetector::process(const cv::Mat &image) {
   
     // re-allocate binary map if necessary
     // same size as input image, but 1-channel
     result.create(image.rows,image.cols,CV_8U);
上述循环代码
      ...
     return result;
}

我们刚开始就创建了输出图像,如果result的大小和深度和源图像不相同,那么result就会被从新分配。核心的代码我们已经写好了,让我们写一下剩下的部分。

class ColorDetector {
  private:
     // minimum acceptable distance
     int minDist; 
     // target color
     cv::Vec3b target; 
     // image containing resulting binary map
     cv::Mat result;

ColorDetector就是我们的类名,但是为了初始化我们的类,需要一个初始化构造器,设置属性的默认值:

     // empty constructor
     ColorDetector() : minDist(100) { 
        // default parameter initialization here
        target[0]= target[1]= target[2]= 0;
     }

这样即使在程序运行时没有初始化类,也可以合法的运行并且返回一个正确的结果,这也是一个面向对象的策略模式,也就是运行时让参数保持合法值。当然用户设定初始值也是必不可少的,我们需要提供set和get方法:

// Sets the color distance threshold.
     // Threshold must be positive, 
     // otherwise distance threshold is set to 0.
     void setColorDistanceThreshold(int distance) {
        if (distance<0)
           distance=0;
        minDist= distance;
     }
     // Gets the color distance threshold
     int getColorDistanceThreshold() const {
        return minDist;
     }

// Sets the color distance threshold.
     // Threshold must be positive, 
     // otherwise distance threshold is set to 0.
     void setColorDistanceThreshold(int distance) {
        if (distance<0)
           distance=0;
        minDist= distance;
     }
     // Gets the color distance threshold
     int getColorDistanceThreshold() const {
        return minDist;
     }

从上述代码可以看到,我们利用了面向对象的多态性,有两个setTargetColor方法。

         下面就写一个main方法来使用这个类。

int main()
{
   // 1. Create image processor object
   ColorDetector cdetect;
   // 2. Read input image
   cv::Mat image= cv::imread("boldt.jpg");
if (!image.data)
      return 0; 
   // 3. Set input parameters
   cdetect.setTargetColor(130,190,230); // here blue sky
   cv::namedWindow("result");
   // 4. Process the image and display the result
   cv::imshow("result",cdetect.process(image));
   cv::waitKey();
   return 0;
}

当然我们的算法很简单。在这种模式下适合处理更复杂、更多参数的问题。在计算像素值的差时,OpenCV提供了一个计算向量欧几里得范数的函数,所以也可以写成这样:

Return
static_cast<int>(
cv::norm<int,3>(cv::Vec3i(color[0]-target[0],
color[1]-target[1],
color[2]-target[2]);

另外我们也知道,OpenCV中的大部分数据结构都对基础的算术符进行了重载,所以我们还可以把距离计算写成:

return static_cast<int>(cv::norm<uchar,3>(color-target);

这个式子看起来是对的,但是他是错误的。大家知道在这个算式当中隐含了saturate_cast方法,uchar取值范围很小,容易超出范围,如果得出的是负值,那么直接被置为0,那么显然结果错了。


下面给出完整代码:

colordetector.h

#if  !defined COLORDETECT		
#define COLORDETECT

#include <opencv2/core/core.hpp>
using namespace cv;
class ColorDetector 
{
private :
	int minDist ;

	Vec3b target;

	Mat result;

	int getDistance(const Vec3b& color) const 
	{
		return abs(color[0]-target[0])+abs(color[1]-target[1])+abs(color[2]-target[2]);
	}
public:

	 ColorDetector() 
	 { 
		 minDist = 150;
		  target[0]= target[1]= target[2]= 0;
	  }

	void setColorDistanceThreshold(int distance)
	{
		if (distance<0)
			distance = 0;
		minDist = distance;
	}

	int getColorDistanceThreshold() 
	{
		return minDist;
	}

	void setTargetColor(unsigned char red,unsigned char green,unsigned char blue)
	{
		target[2] = red;
		target[1] = green;
		target[0] = blue;

	}

	void setTargetColor(Vec3b color)
	{
		target = color;
	}

	const Vec3b getTargetColor() 
	{
		return target;
	}

	Mat process(const Mat &image);
}; //类声明最后面有个分号!!!!!!!!!!!!!!!!!!!!!!!!

#endif
colordetector.cpp
#include "colordetector.h"
	
Mat ColorDetector::process(const Mat &image) {
	
	  // re-allocate binary map if necessary
	  // same size as input image, but 1-channel
	  result.create(image.rows,image.cols,CV_8U);

	  // get the iterators
	  Mat_<Vec3b>::const_iterator it= image.begin<Vec3b>();
	  Mat_<Vec3b>::const_iterator itend= image.end<Vec3b>();
	  cv::Mat_<uchar>::iterator itout= result.begin<uchar>();

	  // for each pixel
	  for ( ; it!= itend; ++it, ++itout) {
        
		// process each pixel ---------------------

		  // compute distance from target color
		  if (getDistance(*it)<minDist) {

			  *itout= 255;

		  } else {

			  *itout= 0;
		  }

        // end of pixel processing ----------------
	  }

	  return result;
}
colorDetection.cpp

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "colordetector.h"


 int main()
{
	// Create image processor object
	ColorDetector cdetect;

	// Read input image
	Mat image= imread("pascal.jpg");
	if (!image.data)
		return 0; 

   // set input parameters
	cdetect.setTargetColor(130,190,230); // here blue sky

   // Read image, process it and display the result
	namedWindow("result");
	imshow("result",cdetect.process(image));

	waitKey();

	return 0;
}
这是处理结果,左边是原图:

                            







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值