应该说,OpenCV 和设计模式并没有什么必然的联系,在 OpenCV 程序中实现 MVC 也没有什么特别的魔法。但在这之前,我已经读完了被称为 Gang of Four 的《设计模式:可复用面向对象软件的基础》,感觉内容有点偏学术,不太好读。而通过 OpenCV 的实际案例来掌握几种设计模式,比起直接去阅读各种设计模式的定义要容易让人接受得多。
/** * detectColor - detect color with target * * @param image - the color to be detected * @param target - the target color * @param minDist - minimum distance * * @return the detected result */ cv::Mat detectColor(const cv::Mat &image, const cv::Vec3b &target, constint minDist = 100){ // create a Mat container for storing result cv::Mat result; // same size as input image, but 1-channel result.create(image.rows, image.cols, CV_8U); // get the iterators 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>();
// for each pixel for ( ; it!=itend; ++it, ++itout) { // process each pixel // compute distance from target color if (getDistance(*it, target) < minDist) { *itout = 255; } else { *itout = 0; } } // end of pixel precessing return result; }
编写模型(Model)
对于图像处理程序,模型是若干个算法模块的组合,所有这些算法模块都是模型的一部分。这些模块往往采取 策略(Strategy)模式 来实现。附图 2 Design Patterns: Elements of Reusable Object-Oriented Software GoF 将策略模式定义为:
Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
// empty constructor ColorDetector() : minDist(100) { // default parameter initialization here target[0] = target[1] = target[2] = 0; } // Set the color distance threshold. voidsetColorDistanceThreshold(int distance); // Get the color distance threshold. intgetColorDistanceThreshold()const; // Set the target color. voidsetTargetColor(unsignedchar red, unsignedchar green, unsignedchar blue); // Get the target color. const cv::Vec3b getTargetColor(); // Core process of color detect algorithm. cv::Mat process(const cv::Mat &image); private: // minimum acceptable distance int minDist; // 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;
};
/** * getDistance - Computes the distance from target color. * * @param color - the color to be compared to * @param target - target color * * @return the Euclidean distance */ intColorDetector::getDistance(const cv::Vec3b &color)const { returnabs(color[0] - target[0]) + abs(color[1] - target[1]) + abs(color[2] - target[2]); }
/** * setColorDistanceThreshold - Set the color distance threshold. * * Threshold must be positive. * otherwise distance threshold is set to 0. * * @param distance - threshold. */ voidColorDetector::setColorDistanceThreshold(int distance) { if (distance < 0) distance = 0; minDist = distance; }
/** * getColorDistanceThreshold - Get the color distance threshold. * * @return the color distance */ intColorDetector::getColorDistanceThreshold()const { return minDist; }
/** * setTargetColor - Set the target color. * * @param red - value of red channel * @param green - value of green channel * @param blue - value of blue channel */ voidColorDetector::setTargetColor(unsignedchar red, unsignedchar green, unsignedchar blue) { // BGR order target[2] = red; target[1] = green; target[0] = blue; }
/** * getTargetColor - Get the target color. * * @return the target color */ const cv::Vec3b ColorDetector::getTargetColor() { return target; }
/** * process - Core process of color detect algorithm. * * @param image - image for detecting color * * @return - detect result */ cv::Mat ColorDetector::process(const cv::Mat &image){ // re-allocate binary map if neccessary // same size as input image, but 1-channel result.create(image.rows, image.cols, CV_8U); // get the iterators 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>();
// for each pixel for ( ; it!=itend; ++it, ++itout) { // process each pixel // compute distance from target color if (getDistance(*it) < minDist) { *itout = 255; } else { *itout = 0; } } // end of pixel precessing return result; }
classColorDetectController { private: // pointer to the singleton static ColorDetectController *singleton; // the algorithm class ColorDetector *cdetect; // private constructor ColorDetectController() { // setting up the application cdetect = newColorDetector(); } cv::Mat image; // The image to be processed cv::Mat result; // The image result public: // Deletes processor objects created by the controller. ~ColorDetectController() { delete cdetect; } // Gets access to Singleton instance static ColorDetectController *getInstance(){ // Creates the instance at first call if (singleton == 0) singleton = new ColorDetectController; return singleton; } // Release the singleton instance of this controller. staticvoiddestroy(){ if (singleton != 0) { delete singleton; singleton = 0; } } // Sets the color distance threshold voidsetColorDistanceThreshold(int distance){ cdetect->setColorDistanceThreshold(distance); } // Gets the color distance threshold intgetColorDistanceThreshold()const{ return cdetect->getColorDistanceThreshold(); } // Sets the color to be detected voidsetTargetColor(unsignedchar red, unsignedchar green, unsignedchar blue){ 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::string filename){ image = cv::imread(filename); if (!image.data) returnfalse; else returntrue; } // Returns the current input image. const cv::Mat getInputImage()const{ return image; } // Performs image processing. voidprocess(){ result = cdetect->process(image); } // Returns the image result from the latest processing const cv::Mat getLastResult()const{ return result; } };
/** * saveImage - Save the file to a specified location * * @param fileName - the target location * * @return true if the file is successfully saved */ boolMainWindow::saveImage(const QString &fileName) { QFile file(fileName);
if (!file.open(QFile::WriteOnly)) { QMessageBox::warning(this, tr("ImageViewer"), tr("Enable to save %1: \n %2") .arg(fileName).arg(file.errorString())); returnfalse; }
// change the cursor QApplication::setOverrideCursor(Qt::WaitCursor);
// save all the contents to file cv::imwrite(fileName.toStdString(), ColorDetectController::getInstance()-> getLastResult());
// restore the cursor QApplication::restoreOverrideCursor();
returntrue; }
/** * onProcess - process image * */ voidMainWindow::onProcess() { ColorDetectController::getInstance()->process(); cv::Mat resulting = ColorDetectController::getInstance()->getLastResult(); if (!resulting.empty()) showImage(resulting); }
/** * onSave - save the image * */ voidMainWindow::onSave() { QString fileName = QFileDialog::getSaveFileName(this, tr("Save as"), curFile); if (!fileName.isEmpty()) saveImage(fileName); }