本文档翻译自:http://www.site.uottawa.ca/~laganier/tutorial/opencv+directshow/ 目录 一步一步教你用OpenCV和DirectShow技术 本页的目的是教你使用OpenCV库进行图像或图像序列处理。此外还介绍了DirectShow技术,该技术对处理图像序列或用摄像机捕获的序列尤其有用。 由于这是一份初学指南,我们尽力提供为达成目的的每一步的细节。此外,源代码也是可以获得的。注意,为了使程序尽可能简单短小,并没有总是采用一个好的编程风格。 本教程例子均采用OpenCV beta 3.1 + DirectX 8.1 + Visual C++ 6.0 service pack 5 在win2000下完成。 创建基于对话框的应用程序 第一个任务是打开并显示一幅图像。首先我们加入一个选择图像文件的按钮,将其Caption属性改为“Open Image”。双击该按钮,修改相应的成员函数名为:OnOpen。对话框看起来将如下: CFileDialog这个类是用来创建一个打开文件对话框。在OnOpen里面加入如下代码: void CCvisionDlg::OnOpen()
{ CFileDialog dlg(TRUE, _T("*.bmp"), "", OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY, "image files (*.bmp; *.jpg) |*.bmp;*.jpg|\\ AVI files (*.avi) |*.avi|\\ All Files (*.*)|*.*|",NULL); char title[]= {"Open Image"}; dlg.m_ofn.lpstrTitle= title; if (dlg.DoModal() == IDOK) { CString path= dlg.GetPathName(); // contain the // selected filename } } 注意1.CFileDialog的构造函数的第四个参数要打开的文件扩展名。注意2. "image files (*.bmp; *.jpg) |*.bmp;*.jpg| AVI files (*.avi) |*.avi|All Files (*.*)|*.*|",NULL);是在同一行,中间勿断行。若断行请用\\连接。 单击Open Image按钮,显示如下对话框: 图 装载并显示一幅图像 首先我们看如何进行完全的环境配置,选择Project|Settings,C/C++ tab ,Preprocessor。添加一下路径: C:\\Program Files\\Intel\\plsuite\\include C:\\Program Files\\Intel\\plsuite\\lib\\msvc ipl.lib cv.lib highgui.lib 最好把这个设置通过Tools|Options...菜单设为全局选项。
图
#if !defined IMAGEPROCESSOR
#define IMAGEPROCESSOR #include <stdio.h> #include <math.h> #include <string.h> #include "cv.h" // include core library interface #include "highgui.h" // include GUI library interface class ImageProcessor { IplImage* img; // Declare IPL/OpenCV image pointer public: ImageProcessor(CString filename, bool display=true) { img = cvvLoadImage( filename ); // load image if (display) { // create a window cvvNamedWindow( "Original Image", 1 ); // display the image on window cvvShowImage( "Original Image", img ); } } ~ImageProcessor() { cvReleaseImage( &img ); } }; #endif void CCvisionDlg::OnOpen()
{ CFileDialog dlg(TRUE, _T("*.bmp"), "", OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY, "BMP files (*.bmp) |*.bmp|AVI files (*.avi) |*.avi| All Files (*.*)|*.*||",NULL); char title[]= {"Open Image"}; dlg.m_ofn.lpstrTitle= title; if (dlg.DoModal() == IDOK) { CString path= dlg.GetPathName(); ImageProcessor ip(path); // load, create and display } }
图 处理一幅图像 #if !defined IMAGEPROCESSOR
#define IMAGEPROCESSOR #include <stdio.h> #include <math.h> #include <string.h> #include "cv.h" // include core library interface #include "highgui.h" // include GUI library interface class ImageProcessor { IplImage* img; // Declare IPL/OpenCV image pointer public: ImageProcessor(CString filename, bool display=true) { img = cvvLoadImage( filename ); // load image if (display) { cvvNamedWindow( "Original Image", 1 ); cvvShowImage( "Original Image", img ); } } void display() { cvvNamedWindow( "Resulting Image", 1 ); cvvShowImage( "Resulting Image", img ); } void execute(); ~ImageProcessor() { cvReleaseImage( &img ); } }; extern ImageProcessor *proc; #endif 添加c++源文件,名字cvapp.cpp,包含着执行处理的函数体: #include "stdafx.h"
#include "cvapp.h" // A global variable ImageProcessor *proc = 0; // the function that processes the image void process(void* img) { IplImage* image = reinterpret_cast<IplImage*>(img); cvErode( image, image, 0, 2 ); } void ImageProcessor::execute() { process(img); } 函数process调用OpenCV的函数完成。本例中,处理仅包括简单的形态学腐蚀(cvErode)。显然所有的处理都直接在execute成员函数里完成。同时没有调整,这里使用void指针作为process的参数。这是为了保持随后的例子的一致性(作为一个回调函数处理一个序列),为了简单,我们添加了 一个只想ImageProcessor的实例的全局变量。让我们修改我们的对话框,增加另一个按钮: 《图》
void CCvisionDlg::OnOpen()
{ CFileDialog dlg(TRUE, _T("*.bmp"), "", OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST| OFN_HIDEREADONLY, "image files (*.bmp; *.jpg) |*.bmp;*.jpg| AVI files (*.avi) |*.avi|All Files (*.*)|*.*||",NULL); char title[]= {"Open Image"}; dlg.m_ofn.lpstrTitle= title; if (dlg.DoModal() == IDOK) { CString path= dlg.GetPathName(); if (proc != 0) delete proc; proc= new ImageProcessor(path); } } void CCvisionDlg::OnProcess() { if (proc != 0) { // process and display proc->execute(); proc->display(); } } 如果打开图像并单击process按钮,结果如下: 《图》 创建图像并访问象素 // Creating a gray level image
IplImage* gray= iplCreateImageHeader(1,0,IPL_DEPTH_8U, "GRAY","G", IPL_DATA_ORDER_PIXEL,IPL_ORIGIN_TL,IPL_ALIGN_QWORD, width,height, NULL,NULL,NULL,NULL); iplAllocateImage(gray, 1, 0); // Creating a color image IplImage* color = iplCreateImageHeader(3,0, IPL_DEPTH_8U, "RGB", "BGR", IPL_DATA_ORDER_PIXEL, IPL_ORIGIN_TL, IPL_ALIGN_QWORD, width, height NULL,NULL,NULL,NULL); iplAllocateImage(color, 1, 0); 第一个参数表示通道个数;如果图像中没有a通道,则第二个参数为0(在机器视觉中大部分情况都是这样)。第三个参数定义了像素类型。通常选择一个无符号8位像素(IPL_DEPTH_8U ),但是2字节有符号整型 (IPL_DEPTH_16S)4字节浮点型(IPL_DEPTH_32F ) 也很有用。下一个参数指定颜色模式(主要是"GRAY" 或"RGB") 和信道序列(在彩色图象的情况下) 。数据序列参量指定不同的颜色信道如何排列。 ...
... ... ... 完整类如下: class SequenceProcessor {
IplImage* img; // Declare IPL/OpenCV image pointer IGraphBuilder *pGraph; IMediaControl *pMediaControl; IMediaEvent *pEvent; public: SequenceProcessor(CString filename, bool display=true) { CoInitialize(NULL); pGraph= 0; // Create the filter graph if (!FAILED( CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **)&pGraph))) { // The two control interfaces pGraph->QueryInterface(IID_IMediaControl, (void **)&pMediaControl); pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent); // Convert Cstring into WCHAR* WCHAR *MediaFile= new WCHAR[filename.GetLength()+1]; MultiByteToWideChar(CP_ACP, 0, filename, -1, MediaFile, filename.GetLength()+1); // Create the filters pGraph->RenderFile(MediaFile, NULL); if (display) { // Execute the filter pMediaControl->Run(); // Wait for completion. long evCode; pEvent->WaitForCompletion(INFINITE, &evCode); } } } ~SequenceProcessor() { // Do not forget to release after use SAFE_RELEASE(pMediaControl); SAFE_RELEASE(pEvent); SAFE_RELEASE(pGraph); CoUninitialize(); } }; 当选择了一个AVI文件,一个渲染滤波器被创建,序列被显示。为了知道什么滤波器被创建,我们增加如下成员函数,对他们进行例举: std::vector<CString> enumFilters() {
IEnumFilters *pEnum = NULL; IBaseFilter *pFilter; ULONG cFetched; std::vector<CString> names; pGraph->EnumFilters(&pEnum); while(pEnum->Next(1, &pFilter, &cFetched) == S_OK) { FILTER_INFO FilterInfo; char szName[256]; CString fname; pFilter->QueryFilterInfo(&FilterInfo); WideCharToMultiByte(CP_ACP, 0, FilterInfo.achName, -1, szName, 256, 0, 0); fname= szName; names.push_back(fname); SAFE_RELEASE(FilterInfo.pGraph); SAFE_RELEASE(pFilter); } SAFE_RELEASE(pEnum); return names; } 创建一个滤波器的图
|