【OpenCV学习笔记 006】基于类的图像处理

本篇博文将展示一些面向对象编程的原理,充分利用他们有助于构建高质量的代码,我们介绍一些重要的设计模式,能够帮助大家构建易测试、易维护、可重用的代码库。

一、在算法设计中使用策略(Strategy)模式

1.概念及原理

(1)策略设计模式的目标是将算法封装在类中,这样可以更容易地替换一个现有的算法,或者组合使用多个算法以拥有更复杂的处理逻辑。此外,该模式将算法的复杂度隐藏在易用的编程接口背后,降低了算法的部署难度。

(2)一旦使用策略模式将算法分封装在类中,可以通过创建它的实例来使用它,通常而言,实例都在程序初始化时被创建。算法参数的默认值可以被读取并显示。在拥有GUI的应用程序中,参数值可以通过不同的控件进行操作(文本框、滚动条等),这样用户可以方便地进行调整。

2.实验

鉴别出图像中含有给定颜色的所有像素。该算法输入的是图像及颜色,并返回表示含有指定颜色的像素的二值图像,还需指定另外一个参数,即对颜色偏差的容忍度。

源码

#include    
#include     
#include  

class ColorDetector{

private:
	//最小可接受距离
	int minDist;
	//目标色
	cv::Vec3b target;
	//结果图像
	cv::Mat result;

public:
	//空构造函数
	ColorDetector() :minDist(300){
		//初始化默认参数
		target[0] = target[1] = target[2] = 0;
	}

	int getDistance(const cv::Vec3b& color);
	cv::Mat process(const cv::Mat &image);
	void setTargetColor(unsigned char blue, unsigned char green, unsigned char red);
};

/*
计算与目标颜色的距离 
方法一:计算由RGB三分量的欧拉公式 
方法二:为了计算简单高效,我们仅仅是累加了RGB分量插值的绝对值(这也被称为街区距离)
*/
int ColorDetector::getDistance(const cv::Vec3b& color){

	return abs(color[0] - target[0]) + abs(color[1] - target[1]) + abs(color[2] - target[2]);
}

cv::Mat ColorDetector::process(const cv::Mat &image){
	//按需重新分配二值图像
	//与输入图像的尺寸相同,但是只有一个通道
	result.create(image.rows, image.cols, CV_8U);
	//得到迭代器
	cv::Mat_::const_iterator it = image.begin();
	cv::Mat_::const_iterator itend = image.end();
	cv::Mat_::iterator itout = result.begin();

	//对于每个像素
	for (; it != itend; ++it, ++itout){
	
		//处理每个像素
		//计算离目标颜色的距离
		if (getDistance(*it) < minDist){
			
			*itout = 255;
		}
		else
		{
			*itout = 0;
		}
	}

	return result;
}

//设置需检测的颜色  也可以使用cv::Vec3b来保存颜色值
void ColorDetector::setTargetColor(unsigned char blue, unsigned char green, unsigned char red){

	//BGR顺序
	target[0] = blue;
	target[1] = green;
	target[2] = red;
}

int main(){

	//1.创建图像处理的对象
	ColorDetector cdetect;
	//2.装载图像
	cv::Mat srcImage = cv::imread("picture.jpg");
	if (!srcImage.data) return 0;
	//3.设置输入参数
	cdetect.setTargetColor(130, 190, 230);//蓝天的颜色
	cv::namedWindow("Image");
	//4.处理并显示结果
	cv::imshow("Image", cdetect.process(srcImage));
	cv::waitKey();
	return 0;
}
以下分别是原图以及处理后的结果:

【OpenCV学习笔记 006】基于类的图像处理_第1张图片【OpenCV学习笔记 006】基于类的图像处理_第2张图片

二、使用控制器(Controller)实现模块间通信

1.概念及原理

(1)当构建复杂应用程序时,我们需要创建多个算法,组合使用它们可以完成一些高级任务,因此在多个类之间通信将变得越来越复杂。在单个类中集中控制整个应用程序变得很有好处,这便是控制器的目的。

2.实验

创建三个按钮的简单对话框应用,一个用于选择图像,一个用于处理图像,一个用于显示处理后的图像。效果如下所示。

【OpenCV学习笔记 006】基于类的图像处理_第3张图片

在该程序中主要包括两个类,一个ColorDetector用于封装算法,一个ColorDetectController用于控制算法的执行,这两个类的源码如下。

ColorDetector.h

#pragma once

#include     
#include  

class ColorDetector{

private:
	//最小可接受距离
	int minDist;
	//目标色
	cv::Vec3b target;
	//结果图像
	cv::Mat result;

public:
	//空构造函数
	ColorDetector() :minDist(300){
		//初始化默认参数
		target[0] = target[1] = target[2] = 0;
	}

	int getDistance(const cv::Vec3b& color);
	cv::Mat process(const cv::Mat &image);
	void setTargetColor(unsigned char blue, unsigned char green, unsigned char red);
};
ColorDetector.cpp

#include "stdafx.h"
#include "ColorDetector.h"

/*
计算与目标颜色的距离
方法一:计算由RGB三分量的欧拉公式
方法二:为了计算简单高效,我们仅仅是累加了RGB分量插值的绝对值(这也被称为街区距离)
*/
int ColorDetector::getDistance(const cv::Vec3b& color){

	return abs(color[0] - target[0]) + abs(color[1] - target[1]) + abs(color[2] - target[2]);
}

cv::Mat ColorDetector::process(const cv::Mat &image){
	//按需重新分配二值图像
	//与输入图像的尺寸相同,但是只有一个通道
	result.create(image.rows, image.cols, CV_8U);
	//得到迭代器
	cv::Mat_::const_iterator it = image.begin();
	cv::Mat_::const_iterator itend = image.end();
	cv::Mat_::iterator itout = result.begin();

	//对于每个像素
	for (; it != itend; ++it, ++itout){

		//处理每个像素
		//计算离目标颜色的距离
		if (getDistance(*it) < minDist){

			*itout = 255;
		}
		else
		{
			*itout = 0;
		}
	}

	return result;
}

//设置需检测的颜色  也可以使用cv::Vec3b来保存颜色值
void ColorDetector::setTargetColor(unsigned char blue, unsigned char green, unsigned char red){

	//BGR顺序
	target[0] = blue;
	target[1] = green;
	target[2] = red;
}
ColorDetectController.h

#pragma once

#include "ColorDetector.h"
class ColorDetectController{

private:
	//算法类
	ColorDetector *cdetect;
	cv::Mat image;	//被处理的图像
	cv::Mat result;	//结果
public:
	ColorDetectController(){
		cdetect = new ColorDetector();
	}
	~ColorDetectController(){
		delete cdetect;
	}
	void setColorDistanceThreshold(int distance);
	int getColorDistanceThreshold();
	void setTargetColor(unsigned char blue, unsigned char green, unsigned char red);
	void getTargetColor(unsigned char &blue, unsigned char &green, unsigned char &red);
	bool setInputImage(std::string filename);
	const cv::Mat getInputImage();
	void process();
	const cv::Mat getLastResult();
};
ColorDetectController.cpp
#include "stdafx.h"
#include "ColorDetectController.h"

//设置色彩距离阈值
void ColorDetectController::setColorDistanceThreshold(int distance){
	
}

//获取色彩距离阈值
int ColorDetectController::getColorDistanceThreshold(){
	return 0;
}

//设置要检测的色彩
void ColorDetectController::setTargetColor(unsigned char blue, unsigned char green, unsigned char red){

	cdetect->setTargetColor(blue, green, red);
}

//获取需检测的颜色
void ColorDetectController::getTargetColor(unsigned char&blue, unsigned char &green, unsigned char &red){

	cv::Vec3b;
}

//设置输入图像,通过文件读取
bool ColorDetectController::setInputImage(std::string filename){

	image = cv::imread(filename);
	if (!image.data)
	{
		return false;
	}
	else
	{
		return true;
	}
}

//返回当前的输入图像
const cv::Mat ColorDetectController::getInputImage(){

	return image;
}

//开始图像处理
void ColorDetectController::process(){

	result = cdetect->process(image);
}

//获取最近一次处理的结果
const cv::Mat ColorDetectController::getLastResult(){

	return result;
}
该项目的下载地址: http://download.csdn.net/detail/davebobo/9631700

三、使用单件(singleton)设计模式

1.概念及原理

(1)单件是另外一种流行的设计模式,用于简化对一个类实例的访问,同时保证在程序执行期间只有一个实例存在,我们使用单件来访问一个控制对象。

(2)使用上一节中的ColorDetectController类,首先添加一个私有的静态成员变量保存对单个类实例的引用,其次当一个用户的类请求单件类的实例时,它才被创建,这将用一个静态方法实现,如果实例不存在那么创建它,然后返回一个指向该实例的指针。最后因为单件实例是被动态创建的,但不需要时用户必须删除它,这也是通过一个静态方法实现。需要注意1:单件的实现并不是线程安全的,因此在多线程的情况下不应该使用它。注意2:由于单件是一个静态成员变量,它必须在.cpp文件中定义。

(3)由于单件可以通过一个公有的静态方法获取,所有包括单件类的声明类都能访问它,这尤其适用于控制器对象。

修改后的ColorDetectController类。

ColorDetectController.h

#pragma once

#include "ColorDetector.h"
class ColorDetectController{

private:
	cv::Mat image;	//被处理的图像
	cv::Mat result;	//结果
	//单件指针
	static ColorDetectController *singleton;
	//算法类
	ColorDetector *cdetect;
	//私有构造函数
	ColorDetectController(){
		cdetect = new ColorDetector();
	}
public:
	void setColorDistanceThreshold(int distance);
	int getColorDistanceThreshold();
	void setTargetColor(unsigned char blue, unsigned char green, unsigned char red);
	void getTargetColor(unsigned char &blue, unsigned char &green, unsigned char &red);
	bool setInputImage(std::string filename);
	const cv::Mat getInputImage();
	void process();
	const cv::Mat getLastResult();
	static ColorDetectController *getInstance();
	static void destory();
};
ColorDetectController.cpp

#include "stdafx.h"
#include "ColorDetectController.h"

//因为单件是一个静态成员变量,它必须在.cpp文件中定义
ColorDetectController*  ColorDetectController::singleton = 0;

//设置色彩距离阈值
void ColorDetectController::setColorDistanceThreshold(int distance){
	
}

//获取色彩距离阈值
int ColorDetectController::getColorDistanceThreshold(){
	return 0;
}

//设置要检测的色彩
void ColorDetectController::setTargetColor(unsigned char blue, unsigned char green, unsigned char red){

	cdetect->setTargetColor(blue, green, red);
}

//获取需检测的颜色
void ColorDetectController::getTargetColor(unsigned char&blue, unsigned char &green, unsigned char &red){

	cv::Vec3b;
}

//设置输入图像,通过文件读取
bool ColorDetectController::setInputImage(std::string filename){

	image = cv::imread(filename);
	if (!image.data)
	{
		return false;
	}
	else
	{
		return true;
	}
}

//返回当前的输入图像
const cv::Mat ColorDetectController::getInputImage(){

	return image;
}

//开始图像处理
void ColorDetectController::process(){

	result = cdetect->process(image);
}

//获取最近一次处理的结果
const cv::Mat ColorDetectController::getLastResult(){

	return result;
}

//访问单件实例
ColorDetectController* ColorDetectController::getInstance(){
	//在首次调用时创建实例
	if (singleton == 0)
	{
		singleton = new ColorDetectController();
	}
	return singleton;
}

//释放单件的实例
void ColorDetectController::destory(){

	if (singleton != 0)
	{
		delete singleton;
		singleton = 0;
	}
}

该项目的下载地址:http://download.csdn.net/detail/davebobo/9631814

四、使用模型-视图-控制器(MVC)架构设计应用程序

1.概念及原理

(1)联合使用策略、控制器和单件这三种模式以及其他的类,这便是模型-视图-控制器(MVC),它的目的是够清晰地分离程序中的逻辑部分与用户交互部分。

(2)MVC包括三个组件,每个组件的角色为。

模型(Model):包含关于应用程序的信息,它拥有所有应用程序处理的数据。当出现新的数据,它将告知控制器,后者会要求View来显示新的结果。通常,模型将结合多个算法,它们很有可能按照策略模式进行实现。所有这些算法都是模型的一部分。

视图(View):对应于用户界面。它是由不同控件组成的,这些控件向用户显示数据,并允许用户与应用程序交互。它的职责之一是发送用户的命令到控制器。当新数据可用时,它也会刷新自己以显示新的信息。

控制器(Controller):将视图和模型桥接在一起。它接收视图的请求,将其转换为模型中的适合方法。它也会在模型更改状态时得到通知,并因此请求视图刷新以显示新的信息。

五、颜色空间转换

1.原理及概念

(1)之前我们使用RGB颜色空间来计算颜色之间的距离并不是衡量颜色相似度最好的方法。事实上,RGB并不是一个在感知上均匀分布的色彩空间。这意味着在相同的距离下,两个颜色可能看起来非常相似,而另外两个颜色却截然不同。为解决这个问题,人们提出在感知上均匀分布的色彩空间。特别地。CIE L*a*b*便是这样的一个颜色空间。

(2)HSV和HLS颜色空间也很有用,因为它们可以分解成色调(Hue)、饱和度(Saturation)、明度(Value)或亮度(Luminance)分量。对人类而言,用它来描述颜色更加自然。

2.实验

修改之前的代码以工作在CIE L*a*b*空间,实现使用CIE L*a*b*颜色空间检测与目标色相似的像素。

源码

#include    
#include     
#include  
#include 
class ColorDetector{

private:
	//最小可接受距离
	int minDist;
	//包含转换后的图像
	cv::Mat converted;
	//目标色
	cv::Vec3b target;
	//结果图像
	cv::Mat result;

public:
	//空构造函数
	ColorDetector() :minDist(100){
		//初始化默认参数
		target[0] = target[1] = target[2] = 0;
	}

	int getDistance(const cv::Vec3b& color);
	cv::Mat process(const cv::Mat &image);
	void setTargetColor(unsigned char blue, unsigned char green, unsigned char red);
};

/*
计算与目标颜色的距离
方法一:计算由RGB三分量的欧拉公式
方法二:为了计算简单高效,我们仅仅是累加了RGB分量插值的绝对值(这也被称为街区距离)
*/
int ColorDetector::getDistance(const cv::Vec3b& color){

	return abs(color[0] - target[0]) + abs(color[1] - target[1]) + abs(color[2] - target[2]);
}

cv::Mat ColorDetector::process(const cv::Mat &image){
	//按需重新分配二值图像
	//与输入图像的尺寸相同,但是只有一个通道
	result.create(image.rows, image.cols, CV_8U);
	//按需从新分配中间图像
	converted.create(image.rows, image.cols, image.type());
	//转换到Lab颜色空间
	cv::cvtColor(image, converted, CV_BGR2Lab);
	//得到迭代器
	cv::Mat_::const_iterator it = converted.begin();
	cv::Mat_::const_iterator itend = converted.end();
	cv::Mat_::iterator itout = result.begin();

	//对于每个像素
	for (; it != itend; ++it, ++itout){

		//处理每个像素
		//计算离目标颜色的距离
		if (getDistance(*it) < minDist){

			*itout = 255;
		}
		else
		{
			*itout = 0;
		}
	}

	return result;
}

//设置需检测的颜色  也可以使用cv::Vec3b来保存颜色值
void ColorDetector::setTargetColor(unsigned char blue, unsigned char green, unsigned char red){

	//临时的1px图像
	cv::Mat tmp(1, 1, CV_8UC3);
	tmp.at(0, 0)[0] = blue;
	tmp.at(0, 0)[1] = green;
	tmp.at(0, 0)[2] = red;
	//转换目标色到Lab颜色空间
	cv::cvtColor(tmp, tmp, CV_BGR2Lab);
	target = tmp.at(0, 0);
}

int main(){

	//1.创建图像处理的对象
	ColorDetector cdetect;
	//2.装载图像
	cv::Mat srcImage = cv::imread("picture.jpg");
	if (!srcImage.data) return 0;
	//3.设置输入参数
	cdetect.setTargetColor(130, 190, 230);//蓝天的颜色
	cv::namedWindow("Image");
	//4.处理并显示结果
	cv::imshow("Image", cdetect.process(srcImage));
	cv::waitKey();
	return 0;
}

实验后的效果

【OpenCV学习笔记 006】基于类的图像处理_第4张图片

你可能感兴趣的:(OpenCV,opencv编程笔记)