IplImage于CDib的相互转换
关于MFC中使用OPENCV的论述在这里就不重复了,很好的OPENCV入门文章,读者请参考
http://wiki.opencv.org.cn/index.php/%e5%9c%a8MFC%e4%b8%ad%e4%bd%bf%e7%94%a8OpenCV。
http://wiki.opencv.org.cn/index.php/MFC%E4%B8%AD%E5%BF%AB%E9%80%9F%E5%BA%94%E7%94%A8OpenCV
朋友们如果配置好了OPENCV,这里下载本篇文章的示例代码http://download.csdn.net/detail/li171049/6754749。(vs2008)
本文探讨OPENCV中表示图像的结构体变量IplImage与MFC中的CDib类的相互转换,以方便我们在选择使用算法时可以切换OPENCV算法或者CDib类的算法。这里我们仅讨论灰度图,即单通道8 BIT之间的转换。
让我们先讲结果再说基础的数据结构。
OpenCV程序在MFC中实现的方法通常是采用CvvImage类。但是OpenCV2.2里面,把原来的CvvImage整个类给删除掉了,因此在MFC下使用带来诸多不方便,大家可以通过提前opencv2.1中的代码的方法来解决(弄一个h文件和一个cpp文件,然后放到你的项目里面一起编译就可以了)。具体的方法和文件内容,请看:Re: OpenCV2.2里CImage(CvvImage)取消,MFC下显示图片少了个好方法。
这里我们使用OPNECV2.1, CvvImage类定义在highgui.h文件中,读者可以自己看一下这个类的定义,该类中函数大多定义为虚函数,以方便我们子类派生。我们就做个该类的派生类就可以使用该类的函数了,具体定义openCVImage.h头文件如下:
//openCVImage.h
#include "highgui.h"//openCV接口头文件
#include "cv.h"//包含openCV中的图像处理算法
#include "Dib.h"
class OpenCVImage : public CvvImage
{
public:
OpenCVImage();
~OpenCVImage();
bool Ipl2Dib(IplImage *ImageScr ,CDib *m_PhotoImageCDib);//ImageScr8位通道,Ipl->Dib
bool Dib2Ipl(CDib *CDibScr,IplImage *IplDst);//ImageScr8位通道,Dib->Ipl
};
上述类中的两个函数完成两种数据类型的转换。其函数openCVImage.cpp如下:
#include "stdafx.h"//MFC新类必须包含的头文件
#include "OpenCVImage.h"
OpenCVImage::OpenCVImage()
{
}
OpenCVImage::~OpenCVImage()
{
}
bool OpenCVImage::Ipl2Dib(IplImage *ImageScr ,CDib *m_PhotoImageCDib)
{
//CDib m_PhotoImageCDib=*DibDesImage;//
int nPicWidth = ImageScr->width;
int nPicHeight = ImageScr->height;
int nByteCnt = ImageScr->nChannels;
BYTE* buf =(BYTE*)ImageScr->imageData;
// 清理空间
m_PhotoImageCDib->Empty(TRUE);
//m_BufloadSet=TRUE;
// 生成位图文件头
m_PhotoImageCDib->m_lpBmpFileHeader = (LPBITMAPFILEHEADER)new BYTE[sizeof(BITMAPFILEHEADER)];
m_PhotoImageCDib->m_lpBmpFileHeader->bfType= (WORD)0x4D42; //;((WORD)('M'<<8) | 'B')
if(nByteCnt == 1)
{
UINT uGradeBmpLineByte = (nPicWidth + 3) / 4 * 4;
DWORD dwGradeBmpDataSize = uGradeBmpLineByte * nPicHeight;
DWORD dwGradeBmpSize = sizeof(RGBQUAD) * 256 + dwGradeBmpDataSize;
m_PhotoImageCDib->m_lpBmpFileHeader->bfSize=(DWORD)(sizeof(BITMAPFILEHEADER) +sizeof(BITMAPINFOHEADER) + dwGradeBmpSize);//指定文件大小
m_PhotoImageCDib->m_lpBmpFileHeader->bfOffBits=(DWORD)(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) +sizeof(RGBQUAD) * 256);//从文件头到实际的位图数据的偏移字节数
m_PhotoImageCDib->m_lpDib = new BYTE[nPicWidth*nPicHeight*nByteCnt+sizeof(BITMAPINFOHEADER) +sizeof(RGBQUAD) * 256];
}
else
{
m_PhotoImageCDib->m_lpBmpFileHeader->bfSize=(DWORD)(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER) + nPicWidth*nPicHeight*nByteCnt);//指定文件大小
m_PhotoImageCDib->m_lpBmpFileHeader->bfOffBits=(DWORD)(sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER));//从文件头到实际的位图数据的偏移字节数
m_PhotoImageCDib->m_lpDib = new BYTE[nPicWidth*nPicHeight*nByteCnt+sizeof(BITMAPINFOHEADER)];
}
m_PhotoImageCDib->m_lpBmpFileHeader->bfReserved1 = 0;
m_PhotoImageCDib->m_lpBmpFileHeader->bfReserved2 = 0;
// 生成位图信息头指针
m_PhotoImageCDib->m_lpBmpInfoHeader = (LPBITMAPINFOHEADER)new BYTE[sizeof(BITMAPINFOHEADER)];
m_PhotoImageCDib->m_lpBmpInfoHeader->biSize = sizeof(BITMAPINFOHEADER); //指定这个结构的长度
m_PhotoImageCDib->m_lpBmpInfoHeader->biWidth = nPicWidth; //指定图像的宽度
m_PhotoImageCDib->m_lpBmpInfoHeader->biHeight =nPicHeight; //指定图像的高度
m_PhotoImageCDib->m_lpBmpInfoHeader->biPlanes = 1; // 指定图像的位数,必须是
m_PhotoImageCDib->m_lpBmpInfoHeader->biBitCount = nByteCnt*8; //指定表示颜色时用到的位数
m_PhotoImageCDib->m_lpBmpInfoHeader->biCompression = BI_RGB;//指定位图是否压缩
m_PhotoImageCDib->m_lpBmpInfoHeader->biSizeImage = nPicWidth*nPicHeight*nByteCnt;//指定实际的位图数据占用的字节数
m_PhotoImageCDib->m_lpBmpInfoHeader->biXPelsPerMeter = 0;
m_PhotoImageCDib->m_lpBmpInfoHeader->biYPelsPerMeter = 0;
m_PhotoImageCDib->m_lpBmpInfoHeader->biClrUsed = 0;
m_PhotoImageCDib->m_lpBmpInfoHeader->biClrImportant = 0;
m_PhotoImageCDib->m_lpBmpInfoHeader->biClrUsed=0;
// 读取除位图文件头的所有数据
memcpy(m_PhotoImageCDib->m_lpDib,m_PhotoImageCDib->m_lpBmpInfoHeader,sizeof(BITMAPINFOHEADER));
if(nByteCnt == 1)
{
LPRGBQUAD lpRgbQuad;
LPRGBQUAD lpGradeBmpRgbQuad = (LPRGBQUAD)(m_PhotoImageCDib->m_lpDib +sizeof(BITMAPINFOHEADER));
memcpy( m_PhotoImageCDib->m_lpDib + m_PhotoImageCDib->m_lpBmpInfoHeader->biSize +sizeof(RGBQUAD) * 256,buf,nPicWidth*nPicHeight*nByteCnt);
for(int k = 0; k < 256; k++)
{
lpRgbQuad = (LPRGBQUAD)(lpGradeBmpRgbQuad + k);
lpRgbQuad->rgbBlue = k;
lpRgbQuad->rgbGreen = k;
lpRgbQuad->rgbRed = k;
lpRgbQuad->rgbReserved = 0;
}
}
else
{
memcpy( m_PhotoImageCDib->m_lpDib + m_PhotoImageCDib->m_lpBmpInfoHeader->biSize,buf,nPicWidth*nPicHeight*nByteCnt);
}
delete [] m_PhotoImageCDib->m_lpBmpInfoHeader;
m_PhotoImageCDib->m_lpBmpInfoHeader = (LPBITMAPINFOHEADER)m_PhotoImageCDib->m_lpDib;
// 设置位图信息指针
m_PhotoImageCDib->m_lpBmpInfo = (LPBITMAPINFO)m_PhotoImageCDib->m_lpDib;
// 设置每像素占的位数
m_PhotoImageCDib->m_uBitCount = m_PhotoImageCDib->m_lpBmpInfoHeader->biBitCount;
// 计算每行像素所占位数
m_PhotoImageCDib->m_uLineByte = (m_PhotoImageCDib->GetWidth() * m_PhotoImageCDib->m_uBitCount / 8 + 3) / 4 * 4;
// 设置位图颜色表指针
m_PhotoImageCDib->m_lpRgbQuad = (LPRGBQUAD)(m_PhotoImageCDib->m_lpDib + m_PhotoImageCDib->m_lpBmpInfoHeader->biSize);
// 计算颜色表长度
DWORD dwRgbQuadLength = m_PhotoImageCDib->CalcRgbQuadLength();
// 设置位图数据指针
m_PhotoImageCDib->m_lpData = m_PhotoImageCDib->m_lpDib + m_PhotoImageCDib->m_lpBmpInfoHeader->biSize + dwRgbQuadLength;
// 判断是否有颜色表
if(m_PhotoImageCDib->m_lpRgbQuad == (LPRGBQUAD)m_PhotoImageCDib->m_lpData)
{
m_PhotoImageCDib->m_lpRgbQuad = NULL; // 将位图颜色表指针置空
m_PhotoImageCDib->m_bHasRgbQuad = FALSE; // 无颜色表
}
else
{
m_PhotoImageCDib->m_bHasRgbQuad = TRUE; // 有颜色表
m_PhotoImageCDib->MakePalette(); // 根据颜色表生成调色板
}
// 设置位图大小
m_PhotoImageCDib->m_lpBmpInfoHeader->biSizeImage = m_PhotoImageCDib->GetSize();
// 位图有效
m_PhotoImageCDib->m_bValid = TRUE;
return true;
}
bool OpenCVImage::Dib2Ipl(CDib *CDibScr, IplImage *IplDst)//单通道u8
{
if(CDibScr->m_lpBmpFileHeader->bfType == (WORD)0x4D42) //;((WORD)('M'<<8) | 'B') //bmp文件
{
IplDst->nChannels = (int)((CDibScr->m_lpBmpInfoHeader->biBitCount)/8);//单通道u8
if(1 == IplDst->nChannels)
{
IplDst->nSize = sizeof(IplImage);
IplDst->ID = 0;
IplDst->alphaChannel = 0;
IplDst->depth = CDibScr->m_lpBmpInfoHeader->biBitCount;
IplDst->colorModel[0] = 'G';
IplDst->colorModel[1] = 'R';
IplDst->colorModel[2] = 'A';
IplDst->colorModel[3] = 'Y';
IplDst->channelSeq[0] = 'G';
IplDst->channelSeq[1] = 'R';
IplDst->channelSeq[2] = 'A';
IplDst->channelSeq[3] = 'Y';
// strcpy(IplDst->colorModel,"GRAY");
// strcpy(IplDst->channelSeq,"GRAY");
IplDst->dataOrder = 0;
IplDst->origin = 0;
IplDst->align = 4;
IplDst->width = CDibScr->m_lpBmpInfoHeader->biWidth;
IplDst->height = CDibScr->m_lpBmpInfoHeader->biHeight;
IplDst->roi = NULL;
IplDst->maskROI = NULL;
IplDst->imageId = NULL;
IplDst->tileInfo = NULL;
IplDst->imageSize = CDibScr->m_lpBmpInfoHeader->biWidth * CDibScr->m_lpBmpInfoHeader->biHeight;
IplDst->imageData = newchar[CDibScr->m_lpBmpInfoHeader->biWidth * CDibScr->m_lpBmpInfoHeader->biHeight];
memcpy(IplDst->imageData,CDibScr->m_lpData,CDibScr->m_lpBmpInfoHeader->biHeight * CDibScr->m_lpBmpInfoHeader->biWidth);
IplDst->widthStep = CDibScr->m_lpBmpInfoHeader->biWidth;
IplDst->BorderMode[0] = 0;
IplDst->BorderMode[1] = 0;
IplDst->BorderMode[2] = 0;
IplDst->BorderMode[3] = 0;
IplDst->BorderConst[0] = 0;
IplDst->BorderConst[1] = 0;
IplDst->BorderConst[2] = 0;
IplDst->BorderConst[3] = 0;
IplDst->imageDataOrigin = IplDst->imageData;
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
至此我们完成了Ipl到CDib的数据转换,enjoy!!!!,于是我们就可以随便切换选择算法了。下面是一些基础数据类型的介绍。
使用过MFC做图像的应该都知道CDib类,读者可以参考《VC++图像处理程序设计(杨淑莹)》,该书简单易懂,适合入门。一般的CDib类完成如下功能:
//======================================================================
// 文件:Dib.h
// 内容:设备无关位图类-头文件
// 功能:()位图的加载与保存;
// ()位图信息的获取;
// ()位图数据的获取;
// ()位图的显示;
// ()位图的转换;
// ()位图相关判断;
// 作者:李平科
// 联系:[email protected]
// 日期:2009-7-26
//======================================================================
#pragma once
#include "stdafx.h"
class CDib : public CObject
{
public:
// 构造函数,初始化数据成员
CDib(void);
// 析构函数,释放内存空间
~CDib(void);
// 从文件加载位图
BOOL LoadFromFile(LPCTSTR lpszPath);
//从内存中加载图像
BOOL LoadFromBuf(BYTE* buf,int nPicWidth,int nPicHeight,int nByteCnt);
// 将位图保存到文件
BOOL SaveToFile(LPCTSTR lpszPath);
// 获取位图文件名
LPCTSTR GetFileName();
// 获取位图宽度
LONG GetWidth();
// 获取位图高度
LONG GetHeight();
void SetWidth(LONG nWidth);
void SetHeight(LONG nHeight);
// 获取位图的宽度和高度
CSize GetDimension();
// 获取位图大小
DWORD GetSize();
// 获取单个像素所占位数
WORD GetBitCount();
// 获取每行像素所占字节数
UINT GetLineByte();
// 获取位图颜色数
DWORD GetNumOfColor();
// 获取位图颜色表
LPRGBQUAD GetRgbQuad();
// 获取位图数据
LPBYTE GetData();
// 显示位图
BOOL Draw(CDC *pDC, CPoint origin, CSize size);
// 24位彩色位图转位灰度位图
BOOL RgbToGrade();
// 32位彩色位图转位彩色位图
BOOL Rgb32To24();
// 8位灰度位图转位彩色位图
BOOL GradeToRgb();
// 判断是否含有颜色表
BOOL HasRgbQuad();
// 判断是否是灰度图
BOOL IsGrade();
// 判断位图是否有效
BOOL IsValid();
// 根据颜色表生成调色板
BOOL MakePalette();
// 计算位图颜色表长度
DWORD CalcRgbQuadLength();
protected:
// 计算位图颜色表长度
// DWORD CalcRgbQuadLength();
// 根据颜色表生成调色板
// BOOL MakePalette();
public:
// 清理空间
void Empty(BOOL bFlag = TRUE);
public:
// 位图文件名
CString m_fileName;
// 位图文件头指针
LPBITMAPFILEHEADER m_lpBmpFileHeader; // 需要动态分配和释放
// 位图指针(包含除位图文件头的所有内容)
LPBYTE m_lpDib; // 需要动态分配和释放
// 位图信息指针
LPBITMAPINFO m_lpBmpInfo;
// 位图信息头指针
LPBITMAPINFOHEADER m_lpBmpInfoHeader;
// 位图颜色表指针
LPRGBQUAD m_lpRgbQuad;
// 位图数据指针
LPBYTE m_lpData;
// 调色板句柄
HPALETTE m_hPalette;
// 是否有颜色表
BOOL m_bHasRgbQuad;
// 位图是否有效
BOOL m_bValid;
// 每像素占的位数
UINT m_uBitCount;
//每行像素所占字节数,为的倍数
UINT m_uLineByte;
};
而在OpenCV中使用IplImage结构体来表示一张图片,该结构体很容以理解,具体的参数含义请参考《OpenCV教程-基础篇》,该书很详细的介绍了每个参数的具体作用。在OPENCV中是这样定义IplImage结构体的:
typedef struct _IplImage
{
int nSize; /* sizeof(IplImage) */
int ID; /* version (=0)*/
int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */
int alphaChannel; /* Ignored by OpenCV */
int depth; /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,
IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */
char colorModel[4]; /* Ignored by OpenCV */
char channelSeq[4]; /* ditto */
int dataOrder; /* 0 - interleaved color channels, 1 - separate color channels.
cvCreateImage can only create interleaved images */
int origin; /* 0 - top-left origin,
1 - bottom-left origin (Windows bitmaps style). */
int align; /* Alignment of image rows (4 or 8).
OpenCV ignores it and uses widthStep instead. */
int width; /* Image width in pixels. */
int height; /* Image height in pixels. */
struct _IplROI *roi; /* Image ROI. If NULL, the whole image is selected. */
struct _IplImage *maskROI; /* Must be NULL. */
void *imageId; /* " " */
struct _IplTileInfo *tileInfo; /* " " */
int imageSize; /* Image data size in bytes
(==image->height*image->widthStep
in case of interleaved data)*/
char *imageData; /* Pointer to aligned image data. */
int widthStep; /* Size of aligned image row in bytes. */
int BorderMode[4]; /* Ignored by OpenCV. */
int BorderConst[4]; /* Ditto. */
char *imageDataOrigin; /* Pointer to very origin of image data
(not necessarily aligned) -
needed for correct deallocation */
}
IplImage;
感谢您的耐心!