如何分块导入大位图,图像大小为1.2G左右,有什么类可以用吗?还是用API函数?该怎么做呢?对其进行处理还得新建一个位图以存放处理完后的数据吧?谁有现成的代码吗?谢谢

解决方案 »

  1.   

    经过试验,读取大文件时,分配内存失败,我查了一些资料,大文件操作时的内存分配都是分块进行,看来你需要自己进行内存分配管理了,大概思路就是读一块数据,分配一段内存,进行处理,然后显示到屏幕上,然后释放内存,继续读取下一块,再分配、再释放。至于分块读取位图的问题,这个就要看你想怎么分了,是按照行来分,还是按照块来分。大概思路就是分配一段内存lpDIB,再定义一个临时内存块lpTDIB,用来寻找目的块,用readhuge一段一段的读给lpTDIB,到了想要的段,就用memcpy拷贝给lpDIB,最后你想要的lpDIB就出来了。至于位图文件数据的存储格式和DIB图像数据存储格式是一样的,都是按照从左到右,从下到上的顺序存储像素数据的,注意黑白图像一个像素是1个机器位,也就是1个字节代表8个像素。另外需要注意的是,略过位图头文件后,不是读取到DIB头信息吗?这里的DIB头信息中的图像宽度、高度需要根据你所分的块进行修改,要是想把黑白图像转成8位图,那么DIB头信息还需要更改biBitCount、biClrUsed、biSizeImage的信息,调色板不能用原来的,需要自己加,就是从0,0,0,0 1,1,1,0 一直添加到255,255,255,0,最后像素数据也要从1位表示,改成8位表示,我在百度上刚发完。
      

  2.   

    我尝试了分块导入图像,其中图像是灰度的,大小为1.2G,以下是我分块读入的部分代码,为什么第一个块分配内存可以成功,第二个开始就出错了呢?
    //获取像素数据的大小(文件总长度-位图文件头的长度-位图信息的长度)
     DWORD dwSize=dwFileLength-sizeof(BITMAPFILEHEADER)-sizeof(BITMAPINFO); //Read the image by stocks
      lWidth=bitmapinfo.bmiHeader.biWidth;
      nBitCount=bitmapinfo.bmiHeader.biBitCount;
     LONG lLineByte=WIDTHBYTE(lWidth*nBitCount);
     DWORD dwSize1=lLineByte*10000;
     m_hDIB1=(HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE,dwSize1);
      BYTE* m_pdib1=(BYTE*)::GlobalLock((HGLOBAL)m_hDIB1);
     DWORD dwReadSize1=dibFile.Read((void*)m_pdib1,dwSize1);  DWORD dwSize2=lLineByte*10000;
     m_hDIB2=(HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE,dwSize2);
      BYTE* m_pdib2=(BYTE*)::GlobalLock((HGLOBAL)m_hDIB2);
     DWORD dwReadSize2=dibFile.Read((void*)m_pdib2,dwSize2);  DWORD dwSize3=lLineByte*10000;
     m_hDIB3=(HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE,dwSize3);
      BYTE* m_pdib3=(BYTE*)::GlobalLock((HGLOBAL)m_hDIB3);
     DWORD dwReadSize3=dibFile.Read((void*)m_pdib3,dwSize3);  DWORD dwSize4=dwSize-lLineByte*30000;
     m_hDIB4=(HGLOBAL)::GlobalAlloc(GMEM_MOVEABLE,dwSize4);
      BYTE* m_pdib4=(BYTE*)::GlobalLock((HGLOBAL)m_hDIB4);
     DWORD dwReadSize4=dibFile.Read((void*)m_pdib4,dwSize4);
      

  3.   

    我一猜你就会这样顺序分块读取文件,这样肯定会出问题的。
    首先,我需要知道你用什么方法实现图像在DC上的显示,反正不管什么方法,最后肯定用到windows API位图显示函数,以及位图句柄CBitmap m_hDIB1 ,以及位图指针CBitmap *m_pdib1,按照你的理解,把位图分块就是把文件按照大小分割,才得到m_pdib1,m_pdib2,m_pdib3,m_pdib4。但其实呢?除了第一个m_pdib1,其他的你得到的仅仅是一堆像素数据,并没有包含bitmapinfo信息,windows API位图显示函数不可能识别出来的。
    因此,图像分割不是说像你想象的那样把图像数据分成几份就可以的,还需要分别给他们加上bitmapinfo,使他们分别成为一个独立的完整位图。另外单单把原有的bitmapinfo头拿来直接用,也是不行的,一看你就没好好看我的回答。我再次强调下
    这里的DIB头信息中的图像宽度、高度需要根据你所分的块进行修改,
    再次强调:黑白位图(就是单色图)和灰度图(大部分教材提到的都是指256色,8位图)是两回事,存储方式都不一样,别搞混了。
    要是想把黑白图像转成8位图,那么DIB头信息还需要更改biBitCount、biClrUsed、biSizeImage的信息,调色板不能用原来的,需要自己加,就是从0,0,0,0 1,1,1,0 一直添加到255,255,255,0,最后像素数据也要从1位表示,改成8位表示
      

  4.   

    对于大文件的处理,不要全部读入内存,直接用内存映射文件就可以了
    CreateFileMapping
    MapViewOfFile
      

  5.   

    额 你这个问题我刚处理过,其实你可以打开文件,用文件指针操作文件里面的数据,1.2G就算文件映射完了,你也得读到缓存,但是没那么大的缓存,就算分段申请,也相对麻烦些,不如打开文件,分开读文件头信息头,然后接着移动文件指针,对数据内容进行一步步处理。我当初是这么实现的,也是处理1.2G的位图。不过有一缺陷,就是1.2G的文件在你打开瞬间还不能立即读,因为机子稍微不好的话,会导致程序崩溃,因为文件在打开瞬间,太大了,,没反应过来你就去读了肯定报错,因此我在这稍微Sleep了几秒钟,不过机子好的话几乎不存在这样的问题。希望可以帮到你。
      

  6.   

    我前几天发过这个问题的帖子,你可以看一下http://topic.csdn.net/u/20110905/10/d1de46d7-67ab-4fda-9abd-b0e76971394d.html?31861。
      

  7.   

    根据5楼的提示,采用CreateFileMapping、MapViewOfFile文件映射的方式做,还是这个办法好,直观、省事,不过比较考验机器的性能,我用了一下午时间,研究了一下,下面给出代码,你自己参考。
    // 创建文件内核对象,其句柄保存于hFile 
    HANDLE hFile = CreateFile(
    lpszPathName,
    GENERIC_WRITE|GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING, //注意这里是打开现有的CREATE_ALWAYS新建
    FILE_FLAG_SEQUENTIAL_SCAN, 
    NULL); 
    DWORD dwFileSize;
            // 得到文件的大小
    dwFileSize = GetFileSize(hFile,NULL);
    // 创建文件映射内核对象,句柄保存于hFileMapping
    HANDLE hFileMapping = CreateFileMapping(hFile,
    NULL,
    PAGE_READWRITE,
    0,//(DWORD)(dwBitsSize >> 32), //因为我的文件不可能超过4G,所以我就没有采用__int64类型,
              //而是DWORD类,如果超过4G,请把dwBitsSize定义为__int64,然后采用注释掉的语句就可以了
    dwFileSize,//(DWORD)(dwBitsSize & 0xFFFFFFFF),
    NULL); 
    // 释放文件内核对象
    CloseHandle(hFile); 
    // 设定大小、偏移量等参数 __int64是long long类型64位整数
    __int64 qwFileOffset = 0;
    // 将文件数据映射到进程的地址空间 
    PBYTE pbFile = (PBYTE)MapViewOfFile(
    hFileMapping,FILE_MAP_ALL_ACCESS,
    (DWORD)(qwFileOffset>>32), 
    (DWORD)(qwFileOffset&0xFFFFFFFF), 
    dwFileSize);
    BITMAPFILEHEADER bmfHeader; //bmp文件头结构
    BITMAPINFO lpDIBHdr; //DIB头结构
    PBYTE    lpDIBBits;  //DIB像素数据首地址
    //获取bmp文件头数据
    memcpy(&bmfHeader,pbFile,sizeof(bmfHeader));
    //获取DIB头信息
    memcpy(&lpDIBHdr,pbFile+sizeof(bmfHeader), *(pbFile+sizeof(bmfHeader)));
    //获得DIB像素首地址
    lpDIBBits = pbFile + sizeof(bmfHeader) + *(pbFile+sizeof(bmfHeader));
    // 从进程的地址空间撤消文件数据映像
    UnmapViewOfFile(pbFile);
    // 关闭文件映射对象
    CloseHandle(hFileMapping); 
    经过验证完全没有问题,你要知道,大部分高手都比较懒,因为他们已经回答过好多遍了,所以都懒得回答太详细的内容,许多时候往往只是提点一句两句,给你提供一个方向,然后就需要你自己去研究,只有这样得来的东西才是自己的,否则拿来就用,往往学的似是而非,学习程序一定要有研究精神,我喜欢回答详细一点是因为我也是初学者,我学C++还不到3个月,学习图像处理也才2个月,只有在回答问题的过程中,才能发现自己没有理解到的地方。
      

  8.   

    内存映射的话,多大的文件都没问题。当然每次读入多少到内存,取决于你的内存大小。还有就是,你准备如何处理数据,内存映射读入局部到内存,只是文本搜索,很好办,但要是排序,就无能为力了。所以取决于你图像处理如何进行。如果真要一次性读入1.2G到内存,你就用window sever 2003 64bit或者Win 7做操作系统。
      

  9.   

    如果1.2g只是图像数据,不含文件头信息,可以使用createdibsection,其中hSection参数为数据文件的映射句柄,用CreateFileMapping创建的,还有个参数是设置数据文件的偏移量,以字节为单位,这个好处是可以将图片分块映射成为dib,不占用大量虚拟内存,我试用这种方法显示1.6g图像的,dib是1024*1024像素块.
      

  10.   

    又学习了,我还在想呢,能计算出图像数据的地址,怎么得到DIB图像的句柄呢,15楼就给出了函数,知识还在于积累和交流。
    createdibsection()函数
    HBITMAP CreateDIBSection(HDC hdc,CONST BITMAPINFO *pbmi,UINT iUsage,VOID** ppvBits,HANDLE hSection,DWORD dwOffset); 
    如果函数执行成功,那么返回值是一个指向刚刚创建的与设备无关位图的句柄,并且*ppvBits指向该位图的位数据值;如果函数执行失败,那么返回值为NULL,并且*ppvBit也为NULL
      

  11.   

    我尝试了这段代码,在运行时会在这一步出错://获取bmp文件头数据
    memcpy(&bmfHeader,pbFile,sizeof(bmfHeader));
    提示读取位置时发生访问冲突。这是为什么?
      

  12.   

    是否是因为&bmfHeader尚未创建?
      

  13.   

    经测试,用这方法可以顺利读取560MB的图片,但读入1.2G图像时会出错,即在memcpy处出错,这是为什么?是因为文件太大,空间不够?
      

  14.   

    你倒是把你用的memcpy的代码贴出来啊
    你不贴怎么知道哪里错了,再说根据我的用法的话
    //获取bmp文件头数据
    memcpy(&bmfHeader,pbFile,sizeof(bmfHeader));
    //获取DIB头信息
    memcpy(&lpDIBHdr,pbFile+sizeof(bmfHeader), *(pbFile+sizeof(bmfHeader)));
    //获得DIB像素首地址
    lpDIBBits = pbFile + sizeof(bmfHeader) + *(pbFile+sizeof(bmfHeader));
    sizeof(bmfHeader)和*(pbFile+sizeof(bmfHeader))应该都不大,也就几十字节
    lpDIBBits只是一个地址,不涉及大小问题。
    要出错也可能是*(pbFile+sizeof(bmfHeader))这个地址的值有问题。
    有问题自己追踪把,很可能机器运行的过程和你想象的不一样,调试程序也是非常重要的一步。
      

  15.   

    //获取bmp文件头数据
    memcpy(&bmfHeader,pbFile,sizeof(bmfHeader));
    追踪到memcpy.asm中,调试指针指向:UnwindUp3:
            mov     eax,[esi+ecx*4-12] ;U(entry)/V(not) - get dword from source
                                       ;V(entry) - spare
            mov     [edi+ecx*4-12],eax ;U - put dword into destination
    我在导入560MB图像都没问题,但到了1G就出错了,我真的搞不懂呢。
      

  16.   

    追踪到memcpy.asm中,调试指针指向:UnwindUp3:
    你追踪这个毫无意义,你只需要查看出错时,sizeof(bmfHeader),pbFile,&bmfHeader这些数据的值就好了
    你可以依次察看*(pbFile),*(pbFile+1),*(pbFile+2),。,*(pbFile+sizeof(bmfHeader)-1)的值啊
    看看是不是符合你预期的数值,sizeof(bmfHeader)不大,而且是个固定植。
    memcpy抱错只可能是你的pbfile当前指针指向有问题,另外如果pbFile是空也会出问题的,加条语句判断一下吧
      

  17.   

    我刚断点追踪了,发现是pbFile是错误的指针,这是什么原因呢?是在将文件数据映射到进程的地址空间时出错?是否跟文件的大小和计算机内存有关吗?急求