本篇博文将展示一些面向对象编程的原理,充分利用他们有助于构建高质量的代码,我们介绍一些重要的设计模式,能够帮助大家构建易测试、易维护、可重用的代码库。
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;
}
以下分别是原图以及处理后的结果:
1.概念及原理
(1)当构建复杂应用程序时,我们需要创建多个算法,组合使用它们可以完成一些高级任务,因此在多个类之间通信将变得越来越复杂。在单个类中集中控制整个应用程序变得很有好处,这便是控制器的目的。
2.实验
创建三个按钮的简单对话框应用,一个用于选择图像,一个用于处理图像,一个用于显示处理后的图像。效果如下所示。
在该程序中主要包括两个类,一个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
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
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;
}
实验后的效果