Instances Based on Opencv2: A Simple Analysis of MVC Architecture

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 on which you can totally rely to construct your own application, however, the implementation of the basic image processing algorithms and the BP algorithm should not have relied on the MFC if one was considering the portability of some general algorithm’s implementations. But in the two projects’ source code, the implementation of the basic 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 theory of 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 essence and 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 be demonstrated 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 of the 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 Controller and 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 MVC

 

What is Strategy Pattern?

       The Strategy Pattern could be described using the following picture.

wKioL1ReNayCFaD7AAClrHmiLSc658.jpg

       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 two classes

       Firstly we construct the Model class ColorDetectorModel based on opencv version 2. The ColorDetectorModel class is an instance of the right part(Strategy) in the Strategy Pattern describing picture above, but it is 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 a given 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 the ColorDetectionModel 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 the second time to build separation between two classes

       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 the ProjectNameView.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 bonded with the ColorDetectorController meanwhile 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 an application, 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 Singleton Pattern 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();

         }

         ~ColorDetectorController() {

                    delete cdetect;

          }

          ColorDetectorController(const ColorDetectorController &);

public:

         //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();

         }

};


reference:

OpenCV2 Computer Vision Application Programming Cookbook

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章