(1):原文链接:http://blog.csdn.net/jtujtujtu/article/details/3734722
Gdiplus::Bitmap转 IplImage
// pIplImage 需要外部释放 Mosesyuan
void CGeneral::BitmapToIplImage(Bitmap* pBitmap, IplImage* &pIplImg)
{
if (!pBitmap)
{
return;
}
if(pIplImg)
{
cvReleaseImage(&pIplImg);
pIplImg = NULL;
}
BitmapData bmpData;
Rect rect(0,0,pBitmap->GetWidth(),pBitmap->GetHeight());
pBitmap->LockBits(&rect, ImageLockModeRead, PixelFormat24bppRGB, &bmpData);
IplImage* tempImg = cvCreateImage(cvSize(pBitmap->GetWidth(), pBitmap->GetHeight()), IPL_DEPTH_8U, 3);
BYTE* temp = (bmpData.Stride>0)?((BYTE*)bmpData.Scan0):((BYTE*)bmpData.Scan0+bmpData.Stride*(bmpData.Height-1));
memcpy(tempImg->imageData, temp, abs(bmpData.Stride)*bmpData.Height);
pBitmap->UnlockBits(&bmpData);
pIplImg = tempImg;
//判断Top-Down or Bottom-Up
if (bmpData.Stride<0)
cvFlip(pIplImg, pIplImg);
}
OpenCV中 IplImage 转 Gdiplus::Bitmap
// pBitmap 同样需要外部释放!!
void CGeneral::IplImageToBitmap(IplImage* pIplImg, Bitmap* &pBitmap)
{
if(!pIplImg)
return;
BITMAPINFOHEADER bmih;
memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
bmih.biSize = sizeof(BITMAPINFOHEADER);
bmih.biWidth = pIplImg->width;
bmih.biHeight = pIplImg->height;
bmih.biPlanes = 1;
bmih.biBitCount = pIplImg->depth*pIplImg->nChannels;
bmih.biSizeImage = pIplImg->imageSize;
BYTE* pData=new BYTE[bmih.biSizeImage];
memcpy(pData, pIplImg->imageDataOrigin, pIplImg->imageSize);
if (pBitmap)
{
delete pBitmap;
pBitmap = NULL;
}
pBitmap = Gdiplus::Bitmap::FromBITMAPINFO((BITMAPINFO*)&bmih, pData);
}
经测试可用,如果有问题,欢迎讨论!
(2):VC Picture控件的加载图像的使用总结
文章来源:http://wenku.baidu.com/view/57fe877ea26925c52cc5bf41.html
(一) 非动态显示图片(即图片先通过资源管理器载入,有一个固定ID)
(二) 动态载入图片(即只需要在程序中指定图片的路径即可载入)
为方便说明,我们已经建好一个基于对话框的工程,名为Ttest,对话框类为CTestDlg
(一)vc picture控件非动态载入图片.
方法1.先从最简单的开始,用picture 控件来实现.
步骤:
先在资源里Import一张图片,ID为IDB_BITMAP2,然后在对话框上添加一个picture控件,右键点击打开属性,将type下拉框选择BITMAP,紧跟着下面就出现一个Image下拉框,拉开就会看到所有已经载入好的图片,选择你要的图片.运行程序即可看到.
方法2.vc picture控件.通过背景图同样如上,先载入一张图片,ID为IDB_BITMAP2
TestDlg.h中
CBrush m_brBk;//在public中定义
TestDlg.cpp中
在初始化函数OnInitDialog()中加入:
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
CBitmap bmp;
bmp.LoadBitmap(IDB_BITMAP2);
m_brBk.CreatePatternBrush(&bmp);
bmp.DeleteObject();
return TRUE; // return TRUE unless you set the focus to a control
}
在打开类向导,找到WM_CTLCOLOR消息,重载得对应函数OnCtlColor(),添加如下:
HBRUSH CTestDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
if (pWnd == this)
{
return m_brBk;
}
return hbr;
}
(二)vc picture控件动态载入图片.
方法3. 图像控件(本例用KoDak 图像编辑控件)
1.首先应该保证系统中有这个控件。注意,它不能单独使用,必须和其他几个控件(特别是Imgcmn.dll)一同使用。如果没有,从别的机器上copy过来即可。这几个文件是Imgadmin.ocx,Imgcmn.dll,Imgedit.ocx,Imgscan.ocx,Imgshl.dll,Imgthumb.ocx,Imgutil.dll,把它们copy到windows\system目录下,然后用regsvr32.exe将它们分别注册。
2.打开工程,进入资源管理器,在对话框上单击右键,单击Insert Activex control… 选择Kodak图象编辑控件,大小任意。
3.在对话框上选中该控件,为其添加变量:m_ctrlPicture。。
4.在BOOL CTestDlg::OnInitDialog()添加如下:
BOOL CTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_ctrlPicture.SetImage("aa.jpg"); //保证图像在工程目录下,也可以写绝对路径
m_ctrlPicture.Display();
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
编译运行就OK了,此种方法的好处就是可能针对多种图像格式.
方法4. vc picture控件通过CBitmap,HBITMAP,直接用OnPaint()绘制
首先在CTestDlg类中声明一个变量:
CBitmap m_bmp;
然后我们在对话框中加入一个picture 标签,名为IDC_STATIC1,然后:
BOOL CDisplayPic::OnInitDialog()
{
CDialog::OnInitDialog();
if( m_bmp.m_hObject != NULL )//判断
m_bmp.DeleteObject();
//载入图片
HBITMAP hbmp = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
"c:\\aaa.bmp", IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION|LR_LOADFROMFILE);
if( hbmp == NULL )
return FALSE;
//该断程序用来取得加载的BMP的信息//
m_bmp.Attach( hbmp );
DIBSECTION ds;
BITMAPINFOHEADER &bminfo = ds.dsBmih;
m_bmp.GetObject( sizeof(ds), &ds );
int cx=bminfo.biWidth; //得到图像宽度
int cy=bminfo.biHeight; //得到图像高度
///
//得到了图像的宽度和高度后,我们就可以对图像大小进行适应,
//即调整控件的大小,让它正好显示一张图片
CRect rect;
GetDlgItem(IDC_STATIC1)->GetWindowRect(&rect);
ScreenToClient(&rect);
GetDlgItem(IDC_STATIC1)->MoveWindow(rect.left,rect.top,cx,cy,true);//调整大小
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
图片加载成功了,标签大小也适应了,下面就是绘制绘制图像了,打开类向导,重载WM_PAINT消息
void CDisplayPic::OnPaint()
{
//以下三种情况任选一种会是不同效果(只能一种存在)///
//CPaintDC dc(this); //若用此句,得到的是对话框的DC,图片将被绘制在对话框上.
CPaintDC dc(GetDlgItem(IDC_STATIC1)); //用此句,得到picture控件的DC,图像将被绘制在控件上
//CDC dc;
//dc.m_hDC=::GetDC(NULL); //若用此两句,得到的是屏幕的DC,图片将被绘制在屏幕上
///
CRect rcclient;
GetDlgItem(IDC_STATIC1)->GetClientRect(&rcclient);
CDC memdc;
memdc.CreateCompatibleDC(&dc);
CBitmap bitmap;
bitmap.CreateCompatibleBitmap(&dc, rcclient.Width(), rcclient.Height());
memdc.SelectObject( &bitmap );
CWnd::DefWindowProc(WM_PAINT, (WPARAM)memdc.m_hDC , 0);
CDC maskdc;
maskdc.CreateCompatibleDC(&dc);
CBitmap maskbitmap;
maskbitmap.CreateBitmap(rcclient.Width(), rcclient.Height(), 1, 1, NULL);
maskdc.SelectObject( &maskbitmap );
maskdc.BitBlt( 0, 0, rcclient.Width(), rcclient.Height(), &memdc,
rcclient.left, rcclient.top, SRCCOPY);
CBrush brush;
brush.CreatePatternBrush(&m_bmp);
dc.FillRect(rcclient, &brush);
dc.BitBlt(rcclient.left, rcclient.top, rcclient.Width(), rcclient.Height(),
&memdc, rcclient.left, rcclient.top,SRCPAINT);
brush.DeleteObject();
// Do not call CDialog::OnPaint() for painting messages
}
以上四种方法唯有KoDak可以支持多种图像,其它的只支持BMP
(3):
认识HBITMAP与Bmp操作(整内存拷贝版)
原文链接:http://www.zwqxin.com/archives/image-processing/bmp-operate-copy-memory.html
本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
原文地址:http://www.zwqxin.com/archives/image-processing/bmp-operate-copy-memory.html
要知道,窗口的那个循环每次都要把像素一个一个打印到屏幕窗口里(通过简单地SetPixel),你移动一下窗口都会感受到那彻骨的延迟:怎么可以这么慢!要是我去计算FPS,那可真可能是只有几帧/秒。但是我的WIN32得依附于那个基于帧的循环啊!惟有用别的方法去处理BMP数据了。
Blit这个词给我的印象很深刻。首先是glBlitFramebufferEXT(见:OpenGL怎样近似进行同时到FBO和屏幕的渲染)见识这种技术的厉害,其次是WINDOWS游戏编程大师技巧第6章里提到DIRECTDRAW的blit技术。恩,其实都是一样的东西(这个......或许是吧)。预先把希望屏幕窗口显示的内容放在一块跟屏幕等大内存里,需要显示的时候直接把这块内存的数据一次过映射到屏幕......在读入BMP时,BMP数据的保存位置跟上篇不一样,不再是一个COLORREF的数组了,而是一个专门被指名要做Blit的内存,这都在BMP的载入过程完成——对一次BMP载入只执行一次。在WIN32程序循环里要显示图片的时候,直接从该内存BLIT出像素数据到屏幕上就好了。
恩,说起来简单,做起来不那么容易。指名?我能办到?不太可能吧。所谓的指名,还是得交给WINDOWS来做。的确,又要沉浸在WIN32的API里了……HBITMAP,跟什么HPEN啊HBRUSH啊的一样,是一种Object(WINDOWS里的对象)的句柄,干脆叫位图句柄。如果我们把我们的BITMAP_FILE转变为一个能用HBITMAP描述的(WINDOWS认得的)对象,我们就有方法用WIN32的BitBlt或StretchBlt函数API,来把HBITMAP描述的对象所在内存区域拷贝到屏幕。
首先,跳过前戏:
bool OperateBMP::LoadBmp(const char *filename)
{
ifstream fileBmp;
fileBmp.open(filename,ios::binary);
if (!fileBmp)
{
MessageBox(NULL, "无法打开喔", "XinXin's WinMain", MB_OK | MB_ICONINFORMATION);
fileBmp.close();
return false;
}
fileBmp.read( (char*)&bitmap.bitmapheader, sizeof(BITMAPFILEHEADER) );
if(bitmap.bitmapheader.bfType != 0x4D42)
{
fileBmp.close();
MessageBox(NULL, "不是0x4D42-BMP", "XinXin's WinMain", MB_OK | MB_ICONINFORMATION);
return false;
}
fileBmp.read( (char*)&bitmap.bitmapinfoheader, sizeof(BITMAPINFOHEADER) );
BmpBit = bitmap.bitmapinfoheader.biBitCount;
if(bitmap.bitmapinfoheader.biSizeImage > 0)
BmpDataSize = bitmap.bitmapinfoheader.biSizeImage;
else
BmpDataSize = bitmap.bitmapinfoheader.biWidth *
bitmap.bitmapinfoheader.biBitCount * bitmap.bitmapinfoheader.biHeight;
//(TO BE CONTINUED)
因为基于与设备有关(<=8BIT时需要)或无关(>8BIT时需要),建立对应HBITMAP的方法不同,我先说后一种情况:
(接TO BE CONTINUED处)
fileBmp.seekg((BmpDataSize)*(-1), ios::end);
bitmap.buffer = new UCHAR[BmpDataSize];
memset(bitmap.buffer,0,BmpDataSize);
BmpDC = CreateCompatibleDC(DrawDeviceHandler);
//if BmpBit > 8
if(bitmap.buffer != NULL)bitmap.buffer = NULL;
mBMP = CreateDIBSection(BmpDC, (BITMAPINFO *)&bitmap.bitmapinfoheader,
DIB_RGB_COLORS, (void **)&bitmap.buffer, NULL, 0);
fileBmp.read( (char*)bitmap.buffer, bitmap.bitmapinfoheader.biSizeImage );
fileBmp.close();
....//主要的载入操作完毕
}
//上面用到主要的的成员变量:
BITMAP_FILE bitmap;
HDC BmpDC;
HBITMAP mBMP;
HDC DrawDeviceHandler;
看见了吗?在读入BMP数据区的数据之前,建立一个基于内存的DC:BmpDC,它兼容于我们要显示BMP图像的屏幕窗口(DrawDeviceHandler是窗口客户区DC哦)。然后在我们自己的BITMAP_FILE 的数据区被填充数据前,用CreateDIBSection建立一个HBITMAP。由它创建的HBITMAP描述的是一个DIB(与设备无关,独立的一个BITMAP),所需要的参数不言自明,其中倒数第3个就是数据区的“起始地址的地址”了。记住,只有先建立了这个DIB的Section,之后填充的数据才有效,所以对设备无关之DIB要在CreateDIBSection后才能加载真正的数据!(我可是绕了弯,现在你看到这篇拙作希望你能少绕弯~)之后,在显示部分SelectObject(BmpDC, mBMP)后用BitBlt这一个函数就能搞掂传图的工作。
DIB与DDB,可以参考这里。我的理解就是,前者独立于你的系统设备,就如同一张BMP格式的图片一样,你传送到谁的电脑上,他/她的电脑设备如何不要紧,只要他有打开BMP的软件(例如XP自带的画图程序)就能查看这张BMP,而且不会看到有什么不同,因为数据是一样的。而DDB(设备相关图)则依赖于系统设备,换言之,它不能离开设备而独立存在,是一种WINDOWS内部对一堆像素颜色信息的抽象表示。对于DIB(WINDOWS可直接操作的位图结构),我们用CreateDIBSection建立而返回一个HBITMAP描述的OBJECT;对于DDB,我们用CreateDIBitmap来创建(事实上我也觉得它这名字不好,既然创建DDB为什么还标个DIB上去捏~)。
8位位图有一个调色板,调色板这东西其实很复杂。我的理解就是,大体来说,除了图片内置的调色板外,还有每个绘图环境(DC)都有属于它的一个逻辑调色板,然后系统自己也有一个。上篇说过,非灰度图的8BIT图片数据区里那个“像素对应之BYTE”对应着调色板数组,数组每个元素标识一种颜色。问题在于“颜色”两字,这里正确的说法是“颜色值”,譬如(25,255,0)。但是,系统怎么会认得调色板里的(25,255,0)就是黄色?之前上篇里做法是把颜色转移到一个COLORREF数组里,然后当你通过窗口DC调用RGB(*,*,*)的时候,系统会认得RGB宏,认得COLORREF(它自己的数据结构嘛);在之前的8位以上BMP里,DIB与系统无关,什么调色板它不认识,直接跟系统对话了:我要XXX颜色(我要说的是,这只是表面看上去如此而已,背地里干了什么勾当呢?阴笑)。但是8BIT图(或以下)的调色板没有那个能耐,它需要在映射到屏幕窗口DC后,由该DC的逻辑调色板去跟系统调色板通话,请求系统调色板“放权”,以获得系统规定的正确颜色。(具体请参考:http://cs.ccnu.edu.cn/datastruct/download/waiweikecheng/VC/chap11_1.htm)
在ZwqXin大费周折后,8BIT图象的正确载入与显示弄出来了:
//(接TO BE CONTINUED处)
//if BmpBit <= 8
fileBmp.read( (char*)bitmap.palette, 256*sizeof(PALETTEENTRY) );
LOGPALETTE *logPal;
logPal=(LOGPALETTE*)new BYTE[sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*256];
logPal->palVersion = 0x300;
logPal->palNumEntries = 256;
for (int i = 0; i < logPal->palNumEntries; i++)
logPal->palPalEntry[i] = bitmap.palette[i];
mPalette = CreatePalette(logPal);
HPALETTE oldpal = NULL;
if(mPalette)
{
oldpal = SelectPalette(DrawDeviceHandler, mPalette, FALSE);
RealizePalette(DrawDeviceHandler);
}
fileBmp.seekg((BmpDataSize)*(-1), ios::end);
bitmap.buffer = new UCHAR[BmpDataSize];
memset(bitmap.buffer,0,BmpDataSize);
BmpDC = CreateCompatibleDC(DrawDeviceHandler);
fileBmp.read( (char*)bitmap.buffer, bitmap.bitmapinfoheader.biSizeImage );
mBMP = CreateDIBitmap(DrawDeviceHandler,&bitmap.bitmapinfoheader,CBM_INIT,bitmap.buffer,(BITMAPINFO*)&bitmap.bitmapinfoheader, DIB_RGB_COLORS);
fileBmp.close();
....//主要的载入操作完毕
}
//上面用到主要的的成员变量:
BITMAP_FILE bitmap;
HDC BmpDC;
HBITMAP mBMP;
HPALETTE mPalette;
HDC DrawDeviceHandler;
步骤是这样的:我们读入BMP文件的调色板,然后根据它建立一个逻辑调色板结构(LOGPALETTE),也就是要给窗口DC看的那样东西(创建过程就是给LOGPALETTE结构填充信息),HPALETTE mPalette同样是个描述调色板OBJECT的句柄(调色板句柄)——我们通过CreatePalette(LOGPALETTE logpal)函数建立一个实际的WINDOWS认识的逻辑调色板OBJECT。SelectPalette把逻辑调色板选入到要使用它的DC中(这里就是我们的当前的屏幕窗口DrawDeviceHandler啦),然后RealizePalette把该逻辑调色板实现到系统调色板中(跟系统交涉)。
之后的步骤就跟建立DIB差不多了。不过用的是建立设备无关图DDB的CreateDIBitmap,参数也是不言自明的,但注意这次要把数据读入放在建立DDB之前哦,不然它不知道具体的要用到调色板的哪些颜色的。对了,建立的是DDB口牙,这BitBlt呀StretchBlt的能读它咩?其实在读之前把BMP位图的内存DC和窗口屏幕DC都匹配这个逻辑调色板mPalette就万无一失了。诶?不是之前已经匹配过了吗?是的,所以我去掉这步也还能正确读8BIT图。但是对我这种小菜鸟来说,万一嘛万一....
void OperateBMP::ShowBmp(int oriXpos, int OriYpos, int width, int height)
{
if(BmpBit <= 8)
{
HPALETTE OldPal1 = NULL;
HPALETTE OldPal2 = NULL;
if (mPalette)
{
OldPal1 = SelectPalette(BmpDC, mPalette, FALSE);
OldPal2 = SelectPalette (DrawDeviceHandler, mPalette, FALSE);
}
}
SelectObject(BmpDC, mBMP);
BitBlt(DrawDeviceHandler, oriXpos, OriYpos, IMAGE_Width, IMAGE_Height, BmpDC, 0,0, SRCCOPY);
}
这里的ShowBmp在客户区指定起点,尺寸(最好是原图尺寸,原图过大客考虑换用StretchBlt传图,能调节BLIT的目标大小哦)显示图片——把它放WIN32循环里吧!
保存步骤跟上篇差不多的(注意8BIT图要保存埋原调色板),不重复了。好吧,我好累,好累,真的累死了.....