A few months ago, one of my friends asked me for a favor that he wanted me to help him out with his digital recognition project. Fortunately, I had decided to go further for a master degree and I had plenty of time, moreover I was totally interested in computer vision, so I was happy to do him a favor.
Soon enough, I found some source code,which implemented two small projects, one of which was a small image processing system and the other was a digital recognition system based on the BP algorithm.The two small projects were implemented based on the MFC. Once I opened thesource code, I found there was a problem in the code organization, because it was kind of messy and totally hard to understand. As I have mentioned above,the two projects were implemented base on MFC which you can totally rely on to construct your own application, however, the implementation of the basic image processing algorithms and the BP algorithm should not have relied onthe MFC if one was considering the portability of some general algorithm’simplementations. But in the two projects’ source code, the implementation of thebasic image processing algorithms and the BP algorithm was somewhat relied onthe MFC, so I decided to extract the basic algorithms out of the original version and implement them using standard C++ language while learning the theoryof these algorithms.
Actually the thinking I was doing a few months ago is a matter of Design Pattern, and in the recent couple of days I was learning to do secondary development using opecv which is a famous opensource computer vision library, the same question came out again, so I decide to give a close thinking about it and write it down in detail.
Now I would like firstly analysis the architecture MVC in two aspects one of which is its advantage and the other is its essenceand then give an example of using MVC based on opencv version 2.
The most important advantage of the MVC architecture is that it has the objective of producing an application that clearly separates the application logic from the user interface. MVC stands for ModelView Controller in short and clearly it consists of three parts which could bedemonstrated as follows:
The Model contains the information concerning the application. It holds all of the data that is processed by the application.When new data is produced, it will inform the Controller, which in turn willask the view to display the new results. Often, the Model will group together several algorithms, possibly implemented following the Strategy pattern. All ofthese algorithms are part of the Model.
The View corresponds to the user interface. It is composed ofthe different widgets that present the data to the user and allow the user to interact with the application. One of its roles is to send the commands issued by the user to the Controller. When new data is available, it refreshes itself in order to display the new information.
The Controller is the module that bridges the View and theModel together. It receives requests from the View and relays them to the appropriate methods in the model. It is also informed when the Model changes its state, and consequently asks the View to refresh in order to display this new information.
The true essence of the MVC architecture is that it uses Strategy Pattern to build bond of the Model with the Controllerand the Controller with the View, meanwhile clearly build the separation between the Model and the View This bond and separation building could be vividly expressed using the following example base on the opencv version 2. Thefollowing content consists of main two parts: using Strategy Pattern to build bond and separation in MVC, using Singleton to design the Controller.
Using Strategy Pattern to build bond and separation in MCV
What is Strategy Pattern?
The Strategy Pattern could be described using the following picture.
The key structure here is to capsule thelogic abstract interface(DoAction()) of algorithms in the class Context, andthen delegate the implementation of algorithms to the class Strategy. Now wecan use it to build bond between classes.
Using Strategy Pattern to build bond between classes
Firstly we construct the Model class ColorDetectorModelbased on opencv version 2. The ColorDetectorModel class is an instance of the right part(Strategy) in the Strategy Pattern describing picture above, but itis simpler, because it does not use the inherit feature of object orientated programming, but it is ok for us to understand. The ColorDetectorModel builds a simple algorithm that will identify all of the pixels in an image which have agiven color. The algorithm has then to accept an image and a color as input and returns a binary image showing the pixels having the specified color.
classColorDetectorModel{
private:
//minimum acceptable distance
intminDist;
//target color
cv::Vec3b target;
//image containing resulting binary map
cv::Mat result;
//Computes the distance from target color.
intgetDistance(const cv::Vec3b&color)const{
returnabs(color[0]-target[0])+
abs(color[1]-target[1])+
abs(color[2]-target[2]);
/*
Euclideannorm of a vector
returnstatic_cast<int>(
cv::norm<int,3>(cv::Vec3i(color[0]-target[0],
color[1]-target[1],
color[2]-target[2])));
*/
/*
returnstatic_cast<int>(
cv::norm<uchar,3>(color-target);
*/
}
public:
//empty constructor
ColorDetectorModel(): minDist(100) {
//default parameter initialization here
target[0]=target[1]= target[2]= 0;
}
//Sets the color distance threshold.
//Threshold must be positive,
//otherwise distance threshold is set to 0.
voidsetColorDistanceThreshold(intdistance){
if(distance<0)
distance=0;
minDist=distance;
}
//Gets the color distance threshold
intgetColorDistanceThreshold() const {
returnminDist;
}
//Sets the color to be detected
voidsetTargetColor(unsignedcharred,
unsignedchargreen,
unsignedcharblue){
//BGR order
target[2]=red;
target[1]=green;
target[0]=blue;
}
//Sets the color to be detected
voidsetTargetColor(cv::Vec3bcolor){
target=color;
}
//Gets the color to be detected
cv::Vec3b getTargetColor() const{
returntarget;
}
cv::Mat process(const cv::Mat &image) {
//re-allocate binary map if necessary
// samesize as input image, but 1-channel
result.create(image.rows,image.cols,CV_8U);
// getthe iterators
cv::Mat_<cv::Vec3b>::const_iteratorit=
image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::const_iteratoritend=
image.end<cv::Vec3b>();
cv::Mat_<uchar>::iterator itout=
result.begin<uchar>();
// foreach pixel
for( ; it!= itend; ++it, ++itout) {
//process each pixel ---------------------
//compute distance from target color
if(getDistance(*it)<minDist) {
*itout= 255;
} else{
*itout= 0;
}
// endof pixel processing ----------------
}
returnresult;
}
};
Secondly, we construct the Controller class ColorDetectorController based on opencv version 2. The ColorDetectorController class is an instance ofthe left part(Context) in the Strategy Pattern describing picture above.
classColorDetectorController{
private:
//the algorithm class
ColorDetectorModel *cdetect;
cv::Mat image; // The image to be processed
cv::Mat result; // The image result
public:
ColorDetectorController(){
//settingup the application
cdetect=newColorDetectorModel();
}
//Sets the color distance threshold
voidsetColorDistanceThreshold(intdistance){
cdetect->setColorDistanceThreshold(distance);
}
//Gets the color distance threshold
intgetColorDistanceThreshold() const {
returncdetect->getColorDistanceThreshold();
}
//Sets the color to be detected
voidsetTargetColor(unsignedcharred,
unsignedchargreen,unsignedcharblue){
cdetect->setTargetColor(red,green,blue);
}
//Gets the color to be detected
voidgetTargetColor(unsignedchar&red,
unsignedchar&green, unsignedchar&blue) const{
cv::Vec3b color= cdetect->getTargetColor();
red=color[2];
green=color[1];
blue=color[0];
}
//Sets the input image. Reads it from file.
boolsetInputImage(std::stringfilename){
image=cv::imread(filename);
if(!image.data)
returnfalse;
else
returntrue;
}
//Returns the current input image.
constcv::Mat getInputImage() const{
returnimage;
}
//Performs image processing.
voidprocess() {
result=cdetect->process(image);
}
//Returns the image result from the latest processing.
constcv::Mat getLastResult() const{
returnresult;
}
//Deletes processor objects created by the controller.
~ColorDetectorController(){
deletecdetect;
}
};
We can easily tell from the implementation of the ColorDetectionController that making an instance of theColorDetectionModel class as a member of the class ColorDetectionController is to build bond between the two classes. When we call the public functions of the ColorDetectionController class, it simply delegates requests to the object of the ColorDetectionModule class.
Using Strategy Pattern two times to build separation betweenclasses
Very much similar to the previous steps of building bond between classes using Strategy Pattern, we construct the Viewclass ColorDetectorView as below. However, this ColorDetectorView class is simply a demo to illustrate the usage of Strategy Pattern and it is not usable,but if you are familiar with MFC based application development then it is easy for you to understand the ColorDetectorView class and adjust it to theProjectNameView.h and ProjectNameView.cpp files of one of your MFC based applications, also you could use it to QT based applications if you have some requests of it. And from the ColorDetectorView class we can clearly see that it is separated from the ColorDetectorModel class, if you make some changes to the implementation ofthe basic algorithms in the ColorDetectorModel class, it will not affect theColorDetectorView class.
class ColorDetectorView {
private:
ColorDetectorControllercolordetect;
public:
//Callback method of "Open" button.
voidOnOpen()
{
//MFC widget to select a file of type bmp or jpg
CFileDialogdlg(TRUE, _T("*.bmp"), NULL,
OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,
_T("imagefiles (*.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
std::stringfilename= dlg.GetPathName();
//set and display the input image
colordetect.setInputImage(filename);
cv::imshow("InputImage",colordetect.getInputImage());
}
}
//Callback method of "Process" button.
voidOnProcess()
{
//target color is hard-coded here
colordetect.setTargetColor(130,190,230);
//process the input image and display result
colordetect.process();
cv::imshow("OutputResult",colordetect.getLastResult());
}
};
You may have noticed that there are some callback functions in the ColorDetectorView class, then, what is a callbackfunction?
Callback function is a function that could only be called bya function pointer which point to a certain function. It could be used when youpass a function pointer as a parameter to another function in the body of which it uses the function pointer parameter to call the function to which the functionpointer pointed.
Callback functions are not called by their implementers, but bysome other sides, the operating system for instance. Applications on the WindowsOperating System, is based on the message mechanism. The operating system Windows captures the users action and capsules the action in the message struct and put it into the message queue which is continuing with the kernel of Windows, then the application gets message, translates message, and dispatches message, after this the operating system will decide which callback function should be called,however the implementation of callback function should be done by the application developer. If you ever wrote an application based on the Win32 API,you should be very much familiar with it.
Using Singleton Pattern to design the Controller
Sometimes we want to control the number of objects in an application, because more objects mean more cost of resource of the system. For example, if only single one object of the class ColorDetectorController meets the need of developing andapplication, then we should forbid making more than two objects of it, for we can carelessly do so. And this problem could be solved by using the SingletonPattern that is used to facilitate access to a class instance and also toguarantee that only one instance of that class will exist during program execution.There is an example bellow, and I have talked about it in another blog context in chinese at 7.3) here http://remyspot.blog.51cto.com/8218746/1555464.
//colorDetectorController.h file
class ColorDetectorController {
private:
//pointer to the singleton
staticColorDetectorController *singleton;
ColorDetector*cdetect;
//private constructor
ColorDetectorController(){
//settingup the application
cdetect=new ColorDetector();
}
//Gets access to Singleton instance
staticColorDetectorController *getInstance() {
//Creates the instance at first call
if(singleton == 0)
singleton=new ColorDetectorController;
returnsingleton;
}
//Releases the singleton instance of this controller.
staticvoid destroy() {
if(singleton != 0) {
deletesingleton;
singleton=0;
}
}
};
//colorDetectorController.cpp file
#include "colorDetectorController.h"
ColorDetectorController *ColorDetectorController::singleton=0;
class ColorDetectorView {
private:
ColorDetectorControllercolordetect;
public:
//Callback method of "Open" button.
voidOnOpen()
{
//MFC widget to select a file of type bmp or jpg
CFileDialogdlg(TRUE, _T("*.bmp"), NULL,
OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,
_T("imagefiles (*.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
std::stringfilename= dlg.GetPathName();
//set and display the input image
ColorDetectorController::
getInstance()->setInputImage(filename);
cv::imshow("InputImage",ColorDetectorController::
getInstance()->getInputImage());
}
}
//Callback method of "Process" button.
voidOnProcess()
{
//target color is hard-coded here
ColorDetectController::
getInstance()->setTargetColor(130,190,230);
//process the input image and display result
ColorDetectorController::getInstance()->process();
cv::imshow("OutputResult",
ColorDetectorController::getInstance()->getLastResult());
}
//Callback method of "Close" button.
voidOnClose()
{
//Releases the Singleton.
ColorDetectorController::getInstance()->destroy();
OnOK();
}
};