您的位置:首页 > 理论基础

OpenCV2计算机编程手册(二)基于类的图像处理

2017-01-11 15:15 561 查看

1. 在算法设计中使用策略(Strategy)模式

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

准备工作

比方说,我们需要构建一个简单的算法,它可以鉴别出图像中含有给定颜色的所有像素。该算法输入的是图像以及颜色,并返回表示含有指定颜色的像素的二值图像,该算法还需要指定另外一个参数,即对颜色偏差的容忍度。

实现方法

让我们写一个主函数,然后看看我们颜色检测算法的运行结果是什么样的:

int main()
{
//1. 创建图像处理对象
ColorDector cdetect;
//2. 读取输入图像
cv::Mat image = cv::imread("boldt.jpg");
cv::namedWindow("original", 0);
cv::imshow("original", image);
//3. 设置输入参数
cv::Mat result;
cdetect.setTargetColor(130, 190, 230);//蓝天的颜色
//处理并显示结果
cv::namedWindow("result", 0);
cv::imshow("result", cdetect.process(image, result));
cv::waitKey(0);

return 0;
}


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

using namespace std;

class ColorDetector{
private:
//最小可接受距离
int minDist;
//目标色
cv::Vec3b target;
//结果图像
cv::Mat result;
public:
//构造函数
ColorDetector() : minDist(100)
{
//初始化默认参数
target[0] = target[1] = target[2] = 0;
}
//设置彩色距离阈值,阈值须为非负数
void setColorDistabceThreshold(int distance)
{
if (distance < 0)
distance = 0;
minDist = distance;
}
//获取彩色距离阈值
int getColorDistanceThreshold() const
{
return minDist;
}
//设置需检测的颜色
void setTargetColor(unsigned char red, unsigned char green, unsigned char blue)
{
target[2] = red;
target[1] = green;
target[0] = blue;
}
//设置需检测的颜色
void setTargetColor(cv::Vec3b color)
{
target = color;
}
//获取需检测的颜色
cv::Vec3b getTargetColor()    const
{
return target;
}
//二值化处理函数
cv::Mat_<uchar> process(cv::Mat &image)
{
result.create(image.rows, image.cols, CV_8U);
//得到迭代器
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>();

while (it != itend)
{
if (getDistance(*it) < minDist)
{
*itout = 255;
}
else
{
*itout = 0;
}
it++;//更新输入迭代器
itout++;;//更新输出迭代器
}
return result;
}
//计算颜色距离
int getDistance(const cv::Vec3b &color)    const
{
cv::Vec3b dist;
cv::absdiff(color, target, dist);
return cv::sum(dist)[0];
}
};

class ColorDetectController
{
private:
ColorDetector* cdetect;//算法类
cv::Mat image;//待处理的图像
cv::Mat result;//结果
public:
ColorDetectController()
{
cdetect = new ColorDetector();
}
//设置色彩距离阈值
void setColorDistanceThreshold(int distance)
{
cdetect->setColorDistabceThreshold(distance);
}
//获取色彩距离阈值
int getColorDistancethreshold()    const
{
return cdetect->getColorDistanceThreshold();
}
//设置要检测的颜色
void setTargetColor(unsigned char red,
unsigned char green, unsigned char blue)
{
cdetect->setTargetColor(red, green, blue);
}
//获取要检测的颜色
void getTargetColor(unsigned char& red, unsigned char& green, unsigned char& blue)    const
{
cv::Vec3b color = cdetect->getTargetColor();
red = color[2];
green = color[1];
blue = color[0];
}
//设置输入图像,通过文件读取
bool setInputImage(string filename)
{
image = cv::imread(filename);
if (!image.data)
return false;
else
return true;
}
//返回当前的输入图像
const cv::Mat getInputImage()    const
{
return image;
}
//开始处理图像
void process()
{
result = cdetect->process(image);
}
//获取最近一次处理的结果
const cv::Mat getLastResult()    const
{
return result;
}
//删除当前控制器创建的处理对象
~ColorDetectController()
{
delete cdetect;
}
};


View Code
MFC中两个按键触发函数

  CFileDialog dlg(TRUE, _T("*.bmp"), NULL,
OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
_T("image file(*.bmp;*,jpg)|*.bmp;*.jpg|ALL Files(*.*)|*.*||"), NULL);

dlg.m_ofn.lpstrTitle = _T("Open Image");
// if a filename has been selected
if (dlg.DoModal() == IDOK) {
// get the path of the selected filename
CString strMfc = dlg.GetPathName();
std::string filename = CT2CA(strMfc.GetBuffer(0));
// set and display the input image
controller.setInputImage(filename);
cv::imshow("Input Image", controller.getInputImage());
}


  // target color is hard-coded here
controller.setTargetColor(130, 190, 230);
// process the input image and display result
controller.process();
cv::imshow("Output Result", controller.getLastResult());


3. 使用单件(Singleton)设计模式

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

准备工作

我们使用上面的 ColorDetectController 类。该类将被修改,以包含一个单件类。

实现方法

要做的第一件事是添加一个私有静态成员变量,他将保存对单个类实例的引用。同时,为了禁止创建额外的类实例,构造函数也是私有的:

class ColorDetectController{
private:
cv::Mat image;//待处理的图像
cv::Mat result;//结果
//单件指针
static ColorDetectController *singleton;
ColorDetector *cdetect;
//私有构造函数
ColorDetectController()
{
//初始化工作
cdetect = new ColorDetector();
}
...
}


此外,你还可以使复制构造函数和操作符 = 私有化,以确保无法创建独一无二的单件实例的拷贝。当一个用户的类要求单件类的一个实例时,它才被创建。这通过使用一个公有静态方法实现,如果实例不存在那么创建它,然后返回一个指向该实例的指针:

static ColorDetectController * getInstance()
{
if (singleton == 0)
singleton = new ColorDetectController();
return singleton;
}


需要注意的是,单件的实现并不是线程安全的。因此,在多线程情况下不应该使用它。

最后,因为单件实例是被动态创建,当不需要时用户必须删除它。这也是通过一个静态方法实现的:

static void destroy()
{
if (singleton != 0)
delete singleton;
singleton = 0;
}


由于单件是一个静态成员变量,它必须在 .cpp 文件中定义,如下:

ColorDetectController *ColorDetectController::singleton = 0;


作用原理

因为单件可以通过一个公共的静态方法获取,所有包括单件类声明的类都能访问它。这尤其适用于控制器对象,它被多个拥有复杂GUI的窗口控件类访问。其中的任何一个GUI类都不需要声明一个成员变量,这和前一节不同。对话框类的两个回调方法编写如下:

void CMy3_3单件设计模式Dlg::OnBnClickedOpen()
{
// TODO:  在此添加控件通知处理程序代码
CFileDialog dlg(TRUE, _T("*.bmp"), NULL,
OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
_T("image file(*.bmp;*,jpg)|*.bmp;*.jpg|ALL Files(*.*)|*.*||"), NULL);

dlg.m_ofn.lpstrTitle = _T("Open Image");
if (dlg.DoModal() == IDOK)
{
// get the path of the selected filename
CString strMfc = dlg.GetPathName();
std::string filename = CT2CA(strMfc.GetBuffer(0));
// set and display the input image
ColorDetectController::getInstance()->setInputImage(filename);
cv::imshow("Input Image", ColorDetectController::getInstance()->getInputImage());
}
}

void CMy3_3单件设计模式Dlg::OnBnClickedProcess()
{
// TODO:  在此添加控件通知处理程序代码
ColorDetectController::getInstance()->setTargetColor(130, 190, 230);
ColorDetectController::getInstance()->process();
cv::imshow("Output result", ColorDetectController::getInstance()->getLastResult());
}


当应用程序关闭时,单件的实例必须被释放:

void CMy3_3单件设计模式Dlg::OnBnClickedOk()
{
// TODO:  在此添加控件通知处理程序代码
ColorDetectController::getInstance()->destroy();
CDialog::OnOK();
}


如上所示,当一个控制器被封装在一个单件中,它变得更容易访问。然而,一个更好的实现需要一个更复杂的GUI。这将在下一节中实现。

4. 使用模型-试图-控制器(Model-View-Controller)架构设计应用程序

5. 颜色空间转换

下面先介绍几个不同的颜色空间:
RGB: 基于红、绿、蓝三原色的使用。

Lab: 在感知上均匀分布的色彩空间。

HSV: 代表色调,饱和度和值(Value)。

HLS: 代表色调(Hue),饱和度(Saturation)和亮度(Lightness)。

调用方式为:

cvtColor(image_, gray, COLOR_BGR2GRAY);
cvtColor(image_, hls, COLOR_BGR2HLS);
cvtColor(image_, hsv, COLOR_BGR2HSV);
cvtColor(image_, hsv, COLOR_BGR2Lab);


作用原理

当图像从一个颜色空间转换到另一个,线性或非线性变换将作用于每个输入像素,以产生输出像素。

注意,三原色红、绿、蓝在RGB次序或BGR次序转换到相同颜色空间是不一样的(灰度一样),比如转到HSV空间,H会相反。

#include <iostream>
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

using namespace std;
using namespace cv;

void Show_Image(Mat & input, char* name)
{
namedWindow(name, 0);
imshow(name, input);

}

int main()
{
Mat image = imread("waves.jpg");
Mat hsv;
cvtColor(image, hsv, COLOR_BGR2HSV);
Mat channel[3];
split(hsv, channel);
for (int index = 0; index < 3; index++)
{
char* buff[3] = { "H", "S", "V" };

Show_Image(channel[index], buff[index]);
}

waitKey(0);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: