图片类型转换

(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图要保存埋原调色板),不重复了。好吧,我好累,好累,真的累死了.....

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值