精华内容
下载资源
问答
  • VB图像处理程序源代码.rarVB图像处理程序源代码.rarVB图像处理程序源代码.rarVB图像处理程序源代码.rarVB图像处理程序源代码.rarVB图像处理程序源代码.rar
  • VB图像处理程序案例

    2012-11-20 10:19:25
    VB程序是比较全的图像处理代码,可供学习参考用
  • 强大的VB写的图像处理工具,绿色软件,文件小。操作方便
  • vb图像绘制处理系统程序
  • 用visual studio vb.net编写的数字图像处理程序,含图像处理基础(负片、灰度变换、直方图均衡化、均值中值滤波处理等)、几何变换(对称、旋转、改变图像尺寸)、彩色变换与调色板(调色板处理、指定颜色的调色板、...
  • VB图像处理模板

    2012-03-31 10:41:19
    VB编写的图像处理程序,功能有——反色、去色、灰度。 ——方框模糊、马赛克,GDIP滤镜等
  • VB写高效的图像处理程序,图像腐蚀,膨胀,开,闭,
  • 数字图像处理的作业程序,里面也借鉴了别处的代码,是大作业,下载的别直接用,按需选用。多谢!
  • VB图像处理

    2008-04-24 02:14:38
    用visual basic 写的图像处理程序,能实现抓图,半透明窗体等功能。
  • VB一般不做图像处理方面的东西,我自己用VB做了一个这方面的,对图像进行均值滤波,
  • VB图像处理功能示例源码,里面包括了色彩平衡调整和图像滤镜处理,和PhotoShop中的某些功能很相似,运行用了滑块、色块取色等众多实用的功能,而且通过这个处理程序,你还可以学习到不少的VB图像编程技巧,特此与...
  • 图像处理程序VB6.0)

    热门讨论 2010-03-06 16:59:26
    程序是由VB6.0编写,可以进行一般的图像处理:图像彩色转灰度,图像直方图均衡化增强,灰度线性变换,图像平滑,中值滤波,图像锐化,也可以保存或另存图像
  • 一个简单的图像格式处理程序,希望对需要的人有些帮助
  • 这是一个数字图像处理程序vb编写,实现较为初级的功能,可以用来交作业哦
  • 基于VB的指纹图像识别处理程序,可对指纹图片进行锐化、平滑处理、轮廓显示、特征对比、特殊显示等处理,以方便指纹的识别工作,如果你用过Photoshop图像处理软件,那么你对这些图像处理功能肯定不会陌生。
  • vb图像处理

    2008-05-19 11:23:31
    程序是以图片形式呈现的
  • VB 图像处理带源码

    2012-09-25 11:10:25
    各种图像处理 带全部源码 的可运行程序,对初学者很有用。
  • 一个基于VB图像处理程序:陀螺转动演示,让一个小图片旋转起来,看上去像陀螺在转动一样,转动速度非常快。源代码简单,是一个学习VB的小范例。
  • 基于VB程序图像处理系统的开发源码,自己做的毕业设计,能实现简单的锐化模糊等26个图像处理功能。
  • VB图像调速锐化、对比度、亮度等特效处理程序
  • VB写高效的图像处理程序 (转)[@more@] 一、为什么这么慢? 自盘古开天地以来(好像夸张了点),一直有人抱怨VB程序速度慢。特别是图像处理,被认为是VB的禁区。说起来也是,市面上的关于VB的图像处理的数据都是...
    用VB写高效的图像处理程序 (转)[@more@]

    一、为什么这么慢?

      自盘古开天地以来(好像夸张了点),一直有人抱怨VB程序速度慢。特别是图像处理,被认为是VB的禁区。说起来也是,市面上的关于VB的图像处理的数据都是先讲计算公式,再直接用PSet(或api函数SetPixel)逐点画(至少我见过的书都是这样)。效果是办到了,但速度慢得离谱:对一幅640*480的图像进行半透明合并就需要10秒钟;而在PhotoShop中,只要一设置图层的透明度,半透明效果立即呈现。难怪有人说VB的闲话。

      但这并不表示VB不能写高速的图像处理程序,速度慢是因为没有使用正确的方法。

      从VB5开始,能以本机代码编译成exe文件,所以不存在代码执行速度的问题。那么,是什么拖慢了速度呢?就是PSet和SetPixel!PSet把浮点形式的坐标转为像素单位,再交给SetPixel处理。而SetPixel呢,坐标系转化、剪裁区域判断、将颜色匹配为设备支持的最接近的,最后还要根据不同的颜色格式寻址、为将颜色写入其所在位进行位运算。经过这么多层处理,速度不慢才怪。

      那么,怎样才能提高处理速度呢?使用DIB,直接对位图所在内存进行操作,速度可以大大提高。现在看看.NET/Zyl910/ImgTest.zip">范例程序,这只是一个简单的色彩演示程序。

    cpu:赛扬333;内存:PC100(很老的概念了)的SDRAM,128MB;单位:毫秒 windows 98 Windows XP 说 明 VB_PSet 1,199.4553 786.1885 在VB使用 PSet 画的 VB_SetPixel 872.3621 451.3712 在VB使用 SetPixelV 画的 VB_DIB 8.2218 8.2226 在VB使用 DIB 画的 VB_DIB_Ptr 9.6783 9.4420 在VB使用 DIBSection + 模拟指针 画的 VC(Debug) 6.6896 6.6503 VC写的(Debug版) VC(Release) 3.2736 3.6247 VC写的(Release版)  从这个表中可看出:
      1.VC比VB_DIB、VB_DIB_Ptr快两倍,这是因为SafeArray结构的数组比真正的指针慢,但也不是某些人所说的70~100倍;
      2.VB_DIB_Ptr比VB_DIB慢一点,这是因为模拟指针本来就是靠SafeArray结构的数组,而且模拟指针需要对两个数组进行操作,所以速度慢一点;
      3.真正差了70~100倍是VB_PSet和VB_SetPixel,特别是VB_PSet在Windows98下与VB_DIB差了145倍。

      以上可证,速度慢的原因是SetPixel非常低效,而并不是VB的问题。虽然VC的的确比较快,但是我写这篇文章不是为了讨论速度极限(否则这篇文章会改名为《如何用汇编写高速的图像处理程序》),而是为了告诉大家如何在VB中写能够实时处理的图像处理程序。


    二、DIB的结构

      在 Windows 3.0 以前,Windows系统用的是Ddb(设备有关位图)。DDB没有调色板,显示的颜色依赖硬件,处理色彩很不方便。所以 microsoft 在 Windows 3.0中 重新定义了BMP文件格式(BMP 3.0),使其支持设备无关位图——也就是DIB。

      时至今日,BMP的版本号已升至5.0(windows NT 4.0、Windows95 定义了 BMP 4.0,windows 98Windows 2000 定义了 BMP 5.0),但基本结构没有变——仍是 BMP文件头 和 DIB 组成:

    BMP文件 BITMAPFILEHEADER BMP文件头 DIB BITMAPINFOHEADER 位图信息头 BITMAPINFO RGBQUAD[] 调色板 位图数据
    (#代表可以不填(=0)的项目)
    BMP文件头——BITMAPFILEHEADER 原型定义:

    typedef struct tagBITMAPFILEHEADER { // bmfh word bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;

    VB声明: 
    

    Type BITMAPFILEHEADER bfType(0 to 1) As Byte bfSize As Long bfReserved1 As Integer bfReserved2 As Integer bfOffBits As Long End Type

    说明: bfType 指示文件的类型,必须是“BM” bfSize# 指示文件的大小,包括BITMAPFILEHEADER bfReserved1 保留,=0 bfReserved2 保留,=0 bfOffBits# 从文件头到位图数据的偏移字节数
    文件信息头——BITMAPINFOHEADER 原型定义:

    typedef struct tagBITMAPINFOHEADER{ // bmih DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER;

    VB声明: 
    

    Type BITMAPINFOHEADER biSize As Long biWidth As Long biHeight As Long biPlanes As Integer biBitCount As Integer biCompression As Long biSizeImage As Long biXPelsPerMeter As Long biYPelsPerMeter As Long biClrUsed As Long biClrImportant As Long End Type

    说明: biSize BITMAPINFOHEADER结构的大小。BMP有多个版本,就靠biSize来区别:
      Bmp3.0:BITMAPINFOHEADER(=40)
      Bmp4.0:BITMAPV4HEADER(=108)
      BMP5.0:BITMAPV5HEADER(=124) biWidth 位图的高度,单位是像素 biHeight 位图的宽度,单位是像素 biPlanes 设备的位平面数。现在都是1 biBitCount 图像的颜色位数
       0:当biCompression=BI_JPEG时必须为0(BMP 5.0)
       1:单色位图
       4:16色位图
       8:256色位图
      16:增强色位图,默认为555格式
      24:真彩色位图
      32:32位位图,默认情况下Windows不会处理最高8位,可以将它作为自己的Alpha通道 biCompression 压缩方式
      BI_RGB:无压缩
      BI_RLE8:行程编码压缩,biBitCount必须等于8
      BI_RLE4:行程编码压缩,biBitCount必须等于4
      BI_BITFIELDS:指定RGB掩码,biBitCount必须等于16、32
      BI_JPEG:JPEG压缩(BMP 5.0)
      BI_PNG:PNG压缩(BMP 5.0) biSizeImage# 实际的位图数据所占字节(biCompression=BI_RGB时可以省略) biXPelsPerMeter# 目标设备的水平分辨率,单位是每米的像素个数 biYPelsPerMeter# 目标设备的垂直分辨率,单位是每米的像素个数 biClrUsed# 使用的颜色数(当biBitCount等于1、4、8时才有效)。如果该项为0,表示颜色数为2^biBitCount biClrImportant# 重要的颜色数。如果该项为0,表示所有颜色都是重要的
    调色板
      只有biBitCount等于1、4、8时才有调色板。调色板实际上是一个数组,元素的个数由biBitCount和biClrUsed决定。 原型定义:

    typedef struct tagRGBQUAD { // rgbq BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD;

    VB声明: 
    

    Private Type RGBQUAD rgbBlue As Byte rgbGreen As Byte rgbRed As Byte rgbReserved As Byte End Type

    说明: rgbBlue 蓝色分量 rgbGreen 绿色分量 rgbRed 红色分量 rgbReserved# 保留,=0
    位图数据 ◆扫描行:
      一行的图像数据叫做一个扫描行。一个扫描行的长度必须是4的倍数(字节),如果不是,则需要补齐。计算公式:LineBytes=((biWidth*biBitCount+31)And &HFFFFFFE0)8
      由于BMP设定者认为数学坐标系更总要,所以DIB的扫描行是逆序存储的(相对于屏幕坐标系而言),即屏幕上的第一行是DIB位图数据的最后一行。 ◆1位色:
      用1位表示一个像素,所以一个字节可以表示8个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下: 字节 0 ... 位 7 6 5 4 3 2 1 0 像素 0 1 2 3 4 5 6 7 ◆4位色:
      用4位表示一个像素,所以一个字节可以表示2个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下: 字节 0 ... 位 7 6 5 4 3 2 1 0 像素 0 1 像素位 3 2 1 0 3 2 1 0 ◆8位色:
      用8位表示一个像素,所以一个字节刚好只能表示一个像素。在内存的摆放形式如下: 字节 0 1 ... 像素 0 1 ◆16位色:
      用16位表示一个像素,所以两个字节可以表示1个像素。默认情况下16位DIB是555格式,最高位无效(这对VB是个福音,因为VB没有16位无符号型)。在内存的摆放形式如下(PC机是低字节在前): 字节 0 1 2 3 ... 位 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 像素 0 1 RGB G B x R G G B x R G RGB位 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3 ◆24位色:
      用24位表示一个像素,所以三个字节可以表示1个像素。注意它的顺序是BGR,而不是传统的RGB。在内存的摆放形式如下: 字节 0 1 2 3 4 5 ... 像素 0 1 RGB B G R B G R ◆32位色:
      用32位表示一个像素,所以四个字节可以表示1个像素。注意绝大多数的GDI函数不会处理Alpha通道(只有AlphaBlend支持)。在内存的摆放形式如下: 字节 0 1 2 3 4 5 6 7 ... 像素 0 1 RGB B G R A B G R A

    三、DIB访问函数

    SetDIBitsToDevice 原型定义:

    int SetDIBitsToDevice( HDC hDC, // handle to device context int XDest, // x-coordinate of upper-left corner of dest. rect. int YDest, // y-coordinate of upper-left corner of dest. rect. DWORD dwWidth, // source rectangle width DWORD dwHeight, // source rectangle height int XSrc, // x-coordinate of lower-left corner of source rect. int YSrc, // y-coordinate of lower-left corner of source rect. UINT uStartScan, // first scan line in array UINT cScanLines, // number of scan lines CONST VOID *lpvBits, // address of array with DIB bits CONST BITMAPINFO *lpbmi, // address of structure with bitmap info. UINT fuColorUse // RGB or palette indexes );

    VB声明: Declare Function SetDIBitsToDevice Lib "gdi32.dll" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal dwWidth As Long, ByVal dwHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal fuColorUse As Long) As Long 说明: 将一幅与设备无关位图的全部或部分数据直接复制到一个设备。这个函数在设备中定义了一个目标矩形,以便接收位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据 返回值: 如函数执行成功,返回欲复制的扫描线的数量;如返回常数GDI_ERROR,表示出错 参数: hDC 一个设备场景的句柄。该场景用于接收位图数据 XDest 指定绘制区域的左上角X坐标 YDest 指定绘制区域的左上角Y坐标 dwWidth 指定绘制区域的高度 dwHeight 指定绘制区域的宽度 XSrc 矩形在DIB中的起点X坐标 YSrc 矩形在DIB中的起点Y坐标 uStartScan lpvBits中第一条扫描线的编号。如lpbmi之BITMAPINFOHEADER部分的biHeight字段是正数,那么这条扫描线就会从位图的底部开始计算;如果是负数,就从顶部开始计算 cScanLines 欲复制的扫描线数量 lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构 fuColorUse DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引 DIB_RGB_COLORS 颜色表包含了RGB颜色
    StretchDIBits 原型定义:

    int StretchDIBits( HDC hDC, // handle to device context int XDest, // x-coordinate of upper-left corner of dest. rectangle int YDest, // y-coordinate of upper-left corner of dest. rectangle int nDestWidth, // width of destination rectangle int nDestHeight, // height of destination rectangle int XSrc, // x-coordinate of upper-left corner of source rectangle int YSrc, // y-coordinate of upper-left corner of source rectangle int nSrcWidth, // width of source rectangle int nSrcHeight, // height of source rectangle CONST VOID *lpBits, // address of bitmap bits CONST BITMAPINFO *lpBitsInfo, // address of bitmap data UINT iUsage, // usage flags DWORD dwRop // raster operation code );

    VB声明: Declare Function StretchDIBits Lib "gdi32" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nDestWidth As Long, ByVal nDestHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, lpBits As Any, lpBitsInfo As Any, ByVal wUsage As Long, ByVal dwRop As Long) As Long 说明: 根据一幅与设备无关的位图创建一幅与设备有关的位图 返回值: 执行成功返回位图句柄,零表示失败 参数: hDC 一个设备场景的句柄,该设备场景定义了要创建的与设备有关位图的配置信息 XDest 指定绘制区域的左上角X坐标 YDest 指定绘制区域的左上角Y坐标 nDestWidth 指定绘制区域的高度 nDestHeight 指定绘制区域的宽度 XSrc 矩形在DIB中的起点X坐标 YSrc 矩形在DIB中的起点Y坐标 nSrcWidth 指定原位图绘制区域的左上角X坐标 nSrcHeight 指定原位图绘制区域的左上角Y坐标 lpBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpBitsInfo指定的 lpBitsInfo 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构 iUsage DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引 DIB_RGB_COLORS 颜色表包含了RGB颜色 dwRop 欲进行的光栅运算 
    CreateDIBitmap 原型定义:

    HBITMAP CreateDIBitmap( HDC hDC, // handle to device context CONST BITMAPINFOHEADER *lpbmih, // pointer to bitmap size and format data DWORD fdwInit, // initialization flag CONST VOID *lpbInit, // pointer to initialization data CONST BITMAPINFO *lpbmi, // pointer to bitmap color-format data UINT fuUsage // color-data usage );

    VB声明: Declare Function CreateDIBitmap Lib "gdi32" (ByVal hDC As Long, lpbmih As Any, ByVal fdwInit As Long, lpbInit As Any, lpbmi As Any, ByVal fuUsage As Long) As Long 说明: 将一幅与设备无关位图的全部或部分数据直接复制到一个设备。这个函数在设备中定义了一个目标矩形,以便接收位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据 返回值: 执行成功则返回扫描线的数量,零表示失败。会设置GetLastError 参数: hDC 一个设备场景的句柄。该场景用于接收位图数据 lpbmih BITMAPINFOHEADER(为兼容BMP4/5而声明成Any),对DIB的格式进行描述的一个结构 fdwInit 如不应对位图数据进行初始化,那么设为零。如设为CBM_INIT,表示根据lpbInit和 lpbmi参数对位图进行初始化 lpbInit 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构 fuUsage DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引 DIB_RGB_COLORS 颜色表包含了RGB颜色
    CreateDIBSection 原型定义:

    HBITMAP CreateDIBSection( HDC hDC, // handle to device context CONST BITMAPINFO *lpbmi, // pointer to structure containing bitmap size, format, and color data UINT iUsage, // color data type indicator: RGB values or palette indexes VOID *ppvBits, // pointer to variable to receive a pointer to the bitmap's bit values HANDLE hSection, // optional handle to a file mapping object DWORD dwOffset // offset to the bitmap bit values within the file mapping object );

    VB声明: Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, lpbmi As Any, ByVal iUsage As Long, ByRef ppvBits As Long, ByVal hSection As Long, ByVal dwOffset As Long) As Long 说明: CreateDIBSection能创建一种特殊的DIB,称为DIB项(DIBSection),然后返回一个GDI位图的句柄。它提供了DIB和GDI位图的最好的特性。这样我们可以直接访问DIB的内存,可以利用位图句柄和内存设备环境,我们甚至还可以在DIB中调用GDI函数来绘图 返回值: 执行成功返回DIBSection位图的句柄,零表示失败。会设置GetLastError 参数: hDC 一个设备场景的句柄。如dw设为DIB_PAL_COLORS,那么DIB颜色表就会用来自逻辑调色板的颜色进行初始化 lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),这个结构初始化成欲创建的那幅位图的配置数据 iUsage DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引 DIB_RGB_COLORS 颜色表包含了RGB颜色 ppvBits 用于得到DIBSection数据区的内存地址 hSection 指向一个文件映射对象的可选句柄,位图将在其中创建。如设为零,Windows会自动分配内存 dwOffset 如指定了句柄,就用这个参数指定位图数据在文件映射对象中的偏移量
    GetDIBits 原型定义:

    int GetDIBits( HDC hDC, // handle to device context HBITMAP hbmp, // handle to bitmap UINT uStartScan, // first scan line to set in destination bitmap UINT cScanLines, // number of scan lines to copy LPVOID lpvBits, // address of array for bitmap bits LPBITMAPINFO lpbmi,// address of structure with bitmap data UINT uUsage // RGB or palette index );

    VB声明: Declare Function GetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long 说明: 该函数利用申请到的内存,由GDI位图得到DIB位图数据。通过该函数,可以对DIB的格式进行控制,可以制定颜色的位数,而且可以指定是否进行压缩。如果采用了压缩方式,则必须调用该函数两次,一次为了得到所需内存,另外一次为了得到位图数据 返回值: 执行成功则返回扫描线的数量,零表示失败。会设置GetLastError 参数: hDC 定义了与设备有关位图hBitmap的配置信息的一个设备场景的句柄 hbmp 源位图的句柄 uStartScan 欲复制到DIB中的第一条扫描线的编号 cScanLines 欲复制的扫描线数量 lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any).对DIB的格式及颜色进行说明的一个结构。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的所有字段都必须初始化 uUsage DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引 DIB_RGB_COLORS 颜色表包含了RGB颜色
    SetDIBits 原型定义:

    int SetDIBits( HDC hDC, // handle to device context HBITMAP hbmp, // handle to bitmap UINT uStartScan, // starting scan line UINT cScanLines, // number of scan lines CONST VOID *lpvBits, // array of bitmap bits CONST BITMAPINFO *lpbmi, // address of structure with bitmap data UINT uUsage // type of color indexes to use );

    VB声明: Declare Function SetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long 说明: 将来自与设备无关位图的二进制位复制到一幅与设备有关的位图里 返回值: 执行成功则返回扫描线的数量,零表示失败。会设置GetLastError 参数: hDC 定义了与设备有关位图hBitmap的配置信息的一个设备场景的句柄 hbmp 源位图的句柄 uStartScan 欲复制到DIB中的第一条扫描线的编号 cScanLines 欲复制的扫描线数量 lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的 lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any).对DIB的格式及颜色进行说明的一个结构。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的所有字段都必须初始化 uUsage DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引 DIB_RGB_COLORS 颜色表包含了RGB颜色
    GetDIBColorTable 原型定义:

    UINT GetDIBColorTable( HDC hDC, // handle to device context whose DIB is of interest UINT uStartIndex, // color table index of first entry to retrieve UINT cEntries, // number of color table entries to retrieve RGBQUAD *pColors // pointer to buffer that receives color table entries );

    VB声明: Declare Function GetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long 说明: 从选入设备场景的DIBSection中取得颜色表信息 返回值: 取回的颜色条目数量,零表示失败。会设置GetLastError 参数: hDC 已选入了一个DIBSection对象的设备场景 uStartIndex 颜色表中欲取回的第一个条目的索引 cEntries 欲取回的条目数量 pColors 这个结构数组用于装载颜色表信息的第一个条目
    SetDIBColorTable 原型定义:

    UINT SetDIBColorTable( HDC hDC, // handle to device context whose DIB is of interest UINT uStartIndex, // color table index of first entry to set UINT cEntries, // number of color table entries to set CONST RGBQUAD *pColors // pointer to array of color table entries );

    VB声明: Declare Function SetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long 说明: 从选入设备场景的DIBSection中取得颜色表信息 返回值: 取回的颜色条目数量,零表示失败。会设置GetLastError 参数: hDC 已选入了一个DIBSection对象的设备场景 uStartIndex 颜色表中欲取回的第一个条目的索引 cEntries 欲取回的条目数量 pColors 这个结构数组用于装载颜色表信息的第一个条目

    四、实战练习

      用DIB写图像处理程序的时候,首先要明确一点:DIB并不是图像处理算法,而是一种绘图方法。处理算法比DIB高一级,管理坐标和颜色的运算;而DIB只是为了绘制。所以此时处理算法的效率是速度的关键。

      利用DIB绘制图像并没有比用PSet/SetPixel绘制差多少,它只是把坐标运算改成地址运算而已。很多人知道指针是一个危险的东西,就是因为它能直接访问内存,如果指针不小心指错地方的话,Windows立即报告一般保护性错误。所以,在地址运算的时候一定要小心,同时要注意随时保存,因为此时的非法操作的发生率非常高,否则辛辛苦苦写的代码一瞬间没了可别怪我没提醒啊。


      好了,现在开始!

      由于处理算法起指导作用,所以现在先讲解VB_PSet。所有的代码都在FrmMain.frm中。其他的过程的代码可以不看,现在将注意力集中在“DrawIt”中,它就是管绘制的。

      其实我这个演示程序蛮简单的:R分量延着水平方向增加,G分量延着垂直方向增加,B分量则从右往左滚动。什么?!“And &HFF”是什么意思?!这可是基础啊……(下略&HFFFF...字)。“&H”表示十六进制数,而And表示按位与。&HFF是二进制的“1111 1111”,正好覆盖了低8位,这时用And进行按位与,只会得到低8位,与RGB分量需要的8位正好符合(对于“(J + K) And &HFF”来说,可以实现滚动效果)。

      If Not 看明白了 Then Goto 前两段

      好!现在打开VB_DIB。(由于24位能直接指定RGB分量,所以这里是用的是24位DIB)

      看了前面的“DIB的结构”,是不是有点昏呢?其实DIB也没什么,就是 一个表述位图信息的BITMAPINFO结构 和 一个存储位图数据的数据缓冲区,顶多再用SetDIBitsToDevice绘制,所以VB_DIB与VB_PSet相比只是多了SetDIBitsToDevice、BITMAPINFOHEADER(24位DIB没有调色板,所以用BITMAPINFOHEADER就行)和一些常数的声明而已。由于这个演示程序不需要改变图像大小和色深,所以可以把有关变量作为窗体级变量,再在Form_Load中初始化。由于DIB并没有向系统申请资源(数组的内存是VB分配的,会自动释放),所以不需要写释放代码。

      现在来看DrawIt。
      1.虽然可以逐点把坐标映射成地址再写,但是这样效率太低了,可以利用坐标处理的连续性进行优化
      2.由于我这里用的是数组,所以这里用(数组元素)索引代替地址。
      3.最开始要注意DIB是逆序存储的,要将索引设为最后一行第一个像素的索引。
      4.由于DIB的RGB顺序是B、G、R,所以“MapData(CurIdx + ?)”的顺序是2、1、0。
      5.设置好一个像素的颜色后,要注意把索引改为下个像素的索引。
      6.由于DIB是逆序存储的,移到下一个扫描行是“LineIdx = LineIdx - LineBytes”


      “好了,代码看懂了,按F5运行看看效果。”
      “咦?速度好像没快多少啊?”

      这是由于程序在VB环境下是以解释方式运行的,而解释方式对做图像处理所需要大规模循环和大量的算术运算的执行效率很低,所以要编译成(本机代码)exe再运行。此时还要注意编译优化,可以把“高级优化”的所有勾打上,速度可提升20%左右。


    五、使用DIBSection和模拟指针

      虽然有GetDIBits/SetDIBits函数,但是DIB与GDI位图之间的数据交换还是很不方便,特别处理过程中需要调用GDI函数来处理的时候。而且就算你不怕麻烦,但这样做的处理效率很低。所以Windows为我们提供了DIBSection。DIBSection是一种特殊的DIB,它除了可以像DIB一样直接对位图数据所占内存进行操作,它还可以选入DC、能用GDI函数绘制,非常灵活。但在VB下使用DIBSection还是有困难的,因为用CreateDIBSection创建DIBSection时,得到的是位图数据的地址,而VB没有指针。

      所幸在VB下可以利用SafeArray结构的数组模拟指针。关于模拟指针的原理、方法,网上的资料多的是,比如AdamBear的文章“ASP?id=12902">VB真是想不到系列之四:VB指针葵花宝典之SafeArray”。但这些文章都只是讲一般性的应用,不能像真正的指针一样随意改变地址(他们都是使用CopyMemory改的)。而在图像处理中,由于点运算的频繁,“像真正的指针一样随意改变地址”的功能很重要。

      其实“像真正的指针一样随意改变地址”并没有技术难度(对于已经学会模拟指针的人来说),就看想得到不:再用一个数组(设pDataPtr)控制模拟指针(设pData)的地址,这样可以用pDataPtr控制地址,用pData访问数据。

      由于需要建立两个模拟指针,导致代码写起来比较烦,于是我想写个“模拟指针代码生成器”(在(ImgTest.zip的)MakeSAPtr目录中)。虽然模拟指针建立方法有很多种,但是大多需要临时变量来保存地址,不适合批量生成,所以我只有改成这种方法(指“模拟指针代码生成器”自动生成的那种)。这种方法也许对 通过看AdamBear的文章学模拟指针的人 会造成困惑,这是因为AdamBear在说明VARPtrArray返回的值是SafeArray的地址的地址时没讲全:VarPtrArray返回的值 是 VB存储数组信息的变量的地址,可通过改变该变量的值使数组用另一个SafeArray。

      现在来看VB_DIB_Ptr:
      1.为了演示DIBSection能够像HBITMAP一样,我在Form_Load中创建了DC、将DIBSection选入DC。同时为了释放,Form_UnLoad中写了释放代码。
      2.在DrawIt中,注意处理部分的代码并没有与VB_DIB差多少,只不过把索引计算改为地址运算而已。


    来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-960767/,如需转载,请注明出处,否则将追究法律责任。

    转载于:http://blog.itpub.net/10752043/viewspace-960767/

    展开全文
  • VB写高效的图像处理程序 V2.0

    千次阅读 2008-04-29 22:58:00
    说起来也是,市面上的关于VB图像处理的数据都是先讲计算公式,再直接用PSet(或API函数SetPixel)逐点画(至少我见过的书都是这样)。效果是办到了,但速度慢得离谱:对一幅640*480的图像进行半透明合并就需要10...

    一、为什么这么慢?

      自盘古开天地以来(好像夸张了点),一直有人抱怨VB程序速度慢。特别是图像处理,被认为是VB的禁区。说起来也是,市面上的关于VB的图像处理的数据都是先讲计算公式,再直接用PSet(或API函数SetPixel)逐点画(至少我见过的书都是这样)。效果是办到了,但速度慢得离谱:对一幅640*480的图像进行半透明合并就需要10秒钟;而在PhotoShop中,只要一设置图层的透明度,半透明效果立即呈现。难怪有人说VB的闲话。

     

      但这并不表示VB不能写高速的图像处理程序,速度慢是因为没有使用正确的方法。

     

      从VB5开始,能以本机代码编译成exe文件,所以不存在代码执行速度的问题。那么,是什么拖慢了速度呢?就是PSet和SetPixel!PSet把浮点形式的坐标转为像素单位,再交给SetPixel处理。而SetPixel呢,坐标系转化、剪裁区域判断、将颜色匹配为设备支持的最接近的,最后还要根据不同的颜色格式寻址、为将颜色写入其所在位进行位运算。经过这么多层处理,速度不慢才怪。

     

      那么,怎样才能提高处理速度呢?使用DIB,直接对位图所在内存进行操作,速度可以大大提高。现在看看本文提供的范例程序的执行速度,这只是一个简单的色彩演示程序。

     

    图像尺寸:640*480*24b。单位:毫秒
      Debug Relase 说 明
    1_PSet 1156.7042 936.2807 在VB使用 PSet 画的
    2_SetPixelV 484.7545 460.5382 在VB使用 SetPixelV 画的
    3_DIB 118.6978 3.8317 在VB使用 DIB 画的
    4_DIB_Ptr 107.5791 4.3545 在VB使用 DirectDraw + 模拟指针 画的
    5_DX7Ptr 108.1261 4.5503 在VB使用 DirectDraw + 模拟指针 画的
    6_DX7Arr 131.8148 7.5506 在VB使用 DIBSection + GetLockedArray 画的
    VC 2.8535 1.8994 用Visual C++ 6.0写的

    我的电脑配置
    CPU AMD Athlon XP 1700+(实际频率:1463 MHz (11 x 133))
    内存 Kingston DDR266 256MB *2(两根)
    显卡 nVIDIA GeForce2 MX/MX 400(AGP 4X,显存32MB)
    测试环境 Windows XP sp2


      从这个表中可看出:
      1.VC比3_DIB、4_DIB_Ptr快一些,这是因为SafeArray结构的数组比真正的指针慢,但也不是某些人所说的70~100倍;
      2.4_DIB_Ptr比3_DIB慢一点,这是因为模拟指针本来就是靠SafeArray结构的数组,需要在运行时动态修改数组数据地址,所以速度慢一点;
      3.真正差了70~100倍是1_PSet和2_SetPixelV。
      4.在VB IDE中解释执行程序非常慢,3_DIB就存在30倍的速度差距。所以经常一些无聊的人拿 VC Debug编译的程序 与 VB IDE中解释执行程序 比较速度。
      5.4_DIB_Ptr、5_DX7Ptr速度一样,这是因为都是利用模拟指针技术直接访问内存来做图像处理的。而6_DX7Arr使用的是GetLockedArray返回的二维数组,二维数组比一维数组慢。

     

      以上可证,速度慢的原因是SetPixelV非常低效,而并不是VB的问题。虽然VC的的确比较快,但是我写这篇文章不是为了讨论速度极限(否则这篇文章会改名为《如何用汇编写高速的图像处理程序》),而是为了告诉大家如何在VB中写能够实时处理的图像处理程序。

     

      同时,决定代码速度的不是编程语言或编译器,而是算法。如果算法写得差的话,无论你用什么编程语言或编译器,那程序速度依然很慢(你在VC中用SetPixelV写图像处理程序试试)。而现在的硬件配置已经足够好,用VB完全可以写高速的图像处理程序。

     


    二、DIB的结构

      在 Windows 3.0 以前,Windows系统用的是DDB(设备有关位图)。DDB没有调色板,显示的颜色依赖硬件,处理色彩很不方便。所以 Microsoft 在 Windows 3.0中 重新定义了BMP文件格式(BMP 3.0),使其支持设备无关位图——也就是DIB。

     

      时至今日,BMP的版本号已升至5.0(Windows NT 4.0、Windows95 定义了 BMP 4.0,Windows 98、Windows 2000 定义了 BMP 5.0),但基本结构没有变——仍是 BMP文件头 和 DIB 组成:

     

    BMP文件   BITMAPFILEHEADER BMP文件头
    DIB BITMAPINFOHEADER 位图信息头 BITMAPINFO
    RGBQUAD[] 调色板
      位图数据

    #代表可以不填(=0)的项目)
    BMP文件头——BITMAPFILEHEADER
    原型定义:
    typedef struct tagBITMAPFILEHEADER { // bmfh
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
    } BITMAPFILEHEADER;
    
    VB声明:
    Type BITMAPFILEHEADER
        bfType(0 to 1) As Byte
        bfSize As Long
        bfReserved1 As Integer
        bfReserved2 As Integer
        bfOffBits As Long
    End Type
    
    说明:
    bfType 指示文件的类型,必须是“BM”
    bfSize# 指示文件的大小,包括BITMAPFILEHEADER
    bfReserved1 保留,=0
    bfReserved2 保留,=0
    bfOffBits# 从文件头到位图数据的偏移字节数

    文件信息头——BITMAPINFOHEADER
    原型定义:
    typedef struct tagBITMAPINFOHEADER{ // bmih
        DWORD  biSize;
        LONG   biWidth;
        LONG   biHeight;
        WORD   biPlanes;
        WORD   biBitCount;
        DWORD  biCompression;
        DWORD  biSizeImage;
        LONG   biXPelsPerMeter;
        LONG   biYPelsPerMeter;
        DWORD  biClrUsed;
        DWORD  biClrImportant;
    } BITMAPINFOHEADER;
    
    VB声明:
    Type BITMAPINFOHEADER
        biSize As Long
        biWidth As Long
        biHeight As Long
        biPlanes As Integer
        biBitCount As Integer
        biCompression As Long
        biSizeImage As Long
        biXPelsPerMeter As Long
        biYPelsPerMeter As Long
        biClrUsed As Long
        biClrImportant As Long
    End Type
    
    说明:
    biSize BITMAPINFOHEADER结构的大小。BMP有多个版本,就靠biSize来区别:
      BMP3.0:BITMAPINFOHEADER(=40)
      BMP4.0:BITMAPV4HEADER(=108)
      BMP5.0:BITMAPV5HEADER(=124)
    biWidth 位图的高度,单位是像素
    biHeight 位图的宽度,单位是像素
    biPlanes 设备的位平面数。现在都是1
    biBitCount 图像的颜色位数
       0:当biCompression=BI_JPEG时必须为0(BMP 5.0)
       1:单色位图
       4:16色位图
       8:256色位图
      16:增强色位图,默认为555格式
      24:真彩色位图
      32:32位位图,默认情况下Windows不会处理最高8位,可以将它作为自己的Alpha通道
    biCompression 压缩方式
      BI_RGB:无压缩
      BI_RLE8:行程编码压缩,biBitCount必须等于8
      BI_RLE4:行程编码压缩,biBitCount必须等于4
      BI_BITFIELDS:指定RGB掩码,biBitCount必须等于16、32
      BI_JPEG:JPEG压缩(BMP 5.0)
      BI_PNG:PNG压缩(BMP 5.0)
    biSizeImage# 实际的位图数据所占字节(biCompression=BI_RGB时可以省略)
    biXPelsPerMeter# 目标设备的水平分辨率,单位是每米的像素个数
    biYPelsPerMeter# 目标设备的垂直分辨率,单位是每米的像素个数
    biClrUsed# 使用的颜色数(当biBitCount等于1、4、8时才有效)。如果该项为0,表示颜色数为2^biBitCount
    biClrImportant# 重要的颜色数。如果该项为0,表示所有颜色都是重要的

    调色板
      只有biBitCount等于1、4、8时才有调色板。调色板实际上是一个数组,元素的个数由biBitCount和biClrUsed决定。
    原型定义:
    typedef struct tagRGBQUAD { // rgbq
        BYTE    rgbBlue;
        BYTE    rgbGreen;
        BYTE    rgbRed;
        BYTE    rgbReserved;
    } RGBQUAD;
    
    VB声明:
    Private Type RGBQUAD
        rgbBlue As Byte
        rgbGreen As Byte
        rgbRed As Byte
        rgbReserved As Byte
    End Type
    
    说明:
    rgbBlue 蓝色分量
    rgbGreen 绿色分量
    rgbRed 红色分量
    rgbReserved# 保留,=0

    位图数据
    扫描行:
      一行的图像数据叫做一个扫描行。一个扫描行的长度必须是4的倍数(字节),如果不是,则需要补齐。计算公式:LineBytes=((biWidth*biBitCount+31)And &HFFFFFFE0)/8
      由于BMP设定者认为数学坐标系更总要,所以DIB的扫描行是逆序存储的(相对于屏幕坐标系而言),即屏幕上的第一行是DIB位图数据的最后一行。
    1位色:
      用1位表示一个像素,所以一个字节可以表示8个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:
    字节 0 ...
    7 6 5 4 3 2 1 0
    像素 0 1 2 3 4 5 6 7
    4位色:
      用4位表示一个像素,所以一个字节可以表示2个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:
    字节 0 ...
    7 6 5 4 3 2 1 0
    像素 0 1
    像素位 3 2 1 0 3 2 1 0
    8位色:
      用8位表示一个像素,所以一个字节刚好只能表示一个像素。在内存的摆放形式如下:
    字节 0 1 ...
    像素 0 1
    16位色:
      用16位表示一个像素,所以两个字节可以表示1个像素。默认情况下16位DIB是555格式,最高位无效(这对VB是个福音,因为VB没有16位无符号型)。在内存的摆放形式如下(PC机使用小端规则(little endian),是低字节在前):
    字节 0 1 2 3 ...
    7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
    像素 0 1
    RGB G B x R G G B x R G
    RGB位 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3
    24位色:
      用24位表示一个像素,所以三个字节可以表示1个像素。注意它的顺序是BGR,而不是传统的RGB。在内存的摆放形式如下:
    字节 0 1 2 3 4 5 ...
    像素 0 1
    RGB B G R B G R
    32位色:
      用32位表示一个像素,所以四个字节可以表示1个像素。注意绝大多数的GDI函数不会处理Alpha通道(只有AlphaBlend支持)。在内存的摆放形式如下:
    字节 0 1 2 3 4 5 6 7 ...
    像素 0 1
    RGB B G R A B G R A

    三、DIB访问函数

    SetDIBitsToDevice
    原型定义:
    int SetDIBitsToDevice(
      HDC hDC,              // handle to device context
      int XDest,            // x-coordinate of upper-left corner of dest. rect.
      int YDest,            // y-coordinate of upper-left corner of dest. rect.
      DWORD dwWidth,        // source rectangle width
      DWORD dwHeight,       // source rectangle height
      int XSrc,             // x-coordinate of lower-left corner of source rect.
      int YSrc,             // y-coordinate of lower-left corner of source rect.
      UINT uStartScan,      // first scan line in array
      UINT cScanLines,      // number of scan lines
      CONST VOID *lpvBits,  // address of array with DIB bits
      CONST BITMAPINFO *lpbmi,  // address of structure with bitmap info.
      UINT fuColorUse       // RGB or palette indexes
    );
    
    VB声明:
    Declare Function SetDIBitsToDevice Lib "gdi32.dll" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal dwWidth As Long, ByVal dwHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal fuColorUse As Long) As Long
    说明:
    将一幅与设备无关位图的全部或部分数据直接复制到一个设备。这个函数在设备中定义了一个目标矩形,以便接收位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据
    返回值:
    如函数执行成功,返回欲复制的扫描线的数量;如返回常数GDI_ERROR,表示出错
    参数:
    hDC 一个设备场景的句柄。该场景用于接收位图数据
    XDest 指定绘制区域的左上角X坐标
    YDest 指定绘制区域的左上角Y坐标
    dwWidth 指定绘制区域的高度
    dwHeight 指定绘制区域的宽度
    XSrc 矩形在DIB中的起点X坐标
    YSrc 矩形在DIB中的起点Y坐标
    uStartScan lpvBits中第一条扫描线的编号。如lpbmi之BITMAPINFOHEADER部分的biHeight字段是正数,那么这条扫描线就会从位图的底部开始计算;如果是负数,就从顶部开始计算
    cScanLines 欲复制的扫描线数量
    lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构
    fuColorUse
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    StretchDIBits
    原型定义:
    int StretchDIBits(
      HDC hDC,                // handle to device context
      int XDest,              // x-coordinate of upper-left corner of dest. rectangle
      int YDest,              // y-coordinate of upper-left corner of dest. rectangle
      int nDestWidth,         // width of destination rectangle
      int nDestHeight,        // height of destination rectangle
      int XSrc,               // x-coordinate of upper-left corner of source rectangle
      int YSrc,               // y-coordinate of upper-left corner of source rectangle
      int nSrcWidth,          // width of source rectangle
      int nSrcHeight,         // height of source rectangle
      CONST VOID *lpBits,            // address of bitmap bits
      CONST BITMAPINFO *lpBitsInfo,  // address of bitmap data
      UINT iUsage,                   // usage flags
      DWORD dwRop                    // raster operation code
    );
    
    VB声明:
    Declare Function StretchDIBits Lib "gdi32" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nDestWidth As Long, ByVal nDestHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, lpBits As Any, lpBitsInfo As Any, ByVal wUsage As Long, ByVal dwRop As Long) As Long
    说明:
    根据一幅与设备无关的位图创建一幅与设备有关的位图
    返回值:
    执行成功返回位图句柄,零表示失败
    参数:
    hDC 一个设备场景的句柄,该设备场景定义了要创建的与设备有关位图的配置信息
    XDest 指定绘制区域的左上角X坐标
    YDest 指定绘制区域的左上角Y坐标
    nDestWidth 指定绘制区域的高度
    nDestHeight 指定绘制区域的宽度
    XSrc 矩形在DIB中的起点X坐标
    YSrc 矩形在DIB中的起点Y坐标
    nSrcWidth 指定原位图绘制区域的左上角X坐标
    nSrcHeight 指定原位图绘制区域的左上角Y坐标
    lpBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpBitsInfo指定的
    lpBitsInfo 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构
    iUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色
    dwRop 欲进行的光栅运算

    CreateDIBitmap
    原型定义:
    HBITMAP CreateDIBitmap(
      HDC hDC,                  // handle to device context
      CONST BITMAPINFOHEADER *lpbmih,  // pointer to bitmap size and format data
      DWORD fdwInit,            // initialization flag
      CONST VOID *lpbInit,      // pointer to initialization data
      CONST BITMAPINFO *lpbmi,  // pointer to bitmap color-format data
      UINT fuUsage              // color-data usage
    );
    
    VB声明:
    Declare Function CreateDIBitmap Lib "gdi32" (ByVal hDC As Long, lpbmih As Any, ByVal fdwInit As Long, lpbInit As Any, lpbmi As Any, ByVal fuUsage As Long) As Long
    说明:
    将一幅与设备无关位图的全部或部分数据直接复制到一个设备。这个函数在设备中定义了一个目标矩形,以便接收位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据
    返回值:
    执行成功则返回扫描线的数量,零表示失败。会设置GetLastError
    参数:
    hDC 一个设备场景的句柄。该场景用于接收位图数据
    lpbmih BITMAPINFOHEADER(为兼容BMP4/5而声明成Any),对DIB的格式进行描述的一个结构
    fdwInit 如不应对位图数据进行初始化,那么设为零。如设为CBM_INIT,表示根据lpbInit和 lpbmi参数对位图进行初始化
    lpbInit 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构
    fuUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    CreateDIBSection
    原型定义:
    HBITMAP CreateDIBSection(
      HDC hDC,          // handle to device context
      CONST BITMAPINFO *lpbmi,
                        // pointer to structure containing bitmap size, format, and color data
      UINT iUsage,      // color data type indicator: RGB values or palette indexes
      VOID *ppvBits,    // pointer to variable to receive a pointer to  the bitmap's bit values
      HANDLE hSection,  // optional handle to a file mapping object
      DWORD dwOffset    // offset to the bitmap bit values within the file mapping object
    );
    
    VB声明:
    Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, lpbmi As Any, ByVal iUsage As Long, ByRef ppvBits As Long, ByVal hSection As Long, ByVal dwOffset As Long) As Long
    说明:
    CreateDIBSection能创建一种特殊的DIB,称为DIB项(DIBSection),然后返回一个GDI位图的句柄。它提供了DIB和GDI位图的最好的特性。这样我们可以直接访问DIB的内存,可以利用位图句柄和内存设备环境,我们甚至还可以在DIB中调用GDI函数来绘图
    返回值:
    执行成功返回DIBSection位图的句柄,零表示失败。会设置GetLastError
    参数:
    hDC 一个设备场景的句柄。如dw设为DIB_PAL_COLORS,那么DIB颜色表就会用来自逻辑调色板的颜色进行初始化
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),这个结构初始化成欲创建的那幅位图的配置数据
    iUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色
    ppvBits 用于得到DIBSection数据区的内存地址
    hSection 指向一个文件映射对象的可选句柄,位图将在其中创建。如设为零,Windows会自动分配内存
    dwOffset 如指定了句柄,就用这个参数指定位图数据在文件映射对象中的偏移量

    GetDIBits
    原型定义:
    int GetDIBits(
      HDC hDC,           // handle to device context
      HBITMAP hbmp,      // handle to bitmap
      UINT uStartScan,   // first scan line to set in destination bitmap
      UINT cScanLines,   // number of scan lines to copy
      LPVOID lpvBits,    // address of array for bitmap bits
      LPBITMAPINFO lpbmi,// address of structure with bitmap data
      UINT uUsage        // RGB or palette index
    );
    
    VB声明:
    Declare Function GetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long
    说明:
    该函数利用申请到的内存,由GDI位图得到DIB位图数据。通过该函数,可以对DIB的格式进行控制,可以制定颜色的位数,而且可以指定是否进行压缩。如果采用了压缩方式,则必须调用该函数两次,一次为了得到所需内存,另外一次为了得到位图数据
    返回值:
    执行成功则返回扫描线的数量,零表示失败。会设置GetLastError
    参数:
    hDC 定义了与设备有关位图hBitmap的配置信息的一个设备场景的句柄
    hbmp 源位图的句柄
    uStartScan 欲复制到DIB中的第一条扫描线的编号
    cScanLines 欲复制的扫描线数量
    lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any).对DIB的格式及颜色进行说明的一个结构。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的所有字段都必须初始化
    uUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    SetDIBits
    原型定义:
    int SetDIBits(
      HDC hDC,                  // handle to device context
      HBITMAP hbmp,             // handle to bitmap
      UINT uStartScan,          // starting scan line
      UINT cScanLines,          // number of scan lines
      CONST VOID *lpvBits,      // array of bitmap bits
      CONST BITMAPINFO *lpbmi,  // address of structure with bitmap data
      UINT uUsage               // type of color indexes to use
    );
    
    VB声明:
    Declare Function SetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long
    说明:
    将来自与设备无关位图的二进制位复制到一幅与设备有关的位图里
    返回值:
    执行成功则返回扫描线的数量,零表示失败。会设置GetLastError
    参数:
    hDC 定义了与设备有关位图hBitmap的配置信息的一个设备场景的句柄
    hbmp 源位图的句柄
    uStartScan 欲复制到DIB中的第一条扫描线的编号
    cScanLines 欲复制的扫描线数量
    lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any).对DIB的格式及颜色进行说明的一个结构。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的所有字段都必须初始化
    uUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    GetDIBColorTable
    原型定义:
    UINT GetDIBColorTable(
      HDC hDC,          // handle to device context whose DIB is of interest
      UINT uStartIndex, // color table index of first entry to retrieve
      UINT cEntries,    // number of color table entries to retrieve
      RGBQUAD *pColors  // pointer to buffer that receives color table entries
    );
    
    VB声明:
    Declare Function GetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long
    说明:
    从选入设备场景的DIBSection中取得颜色表信息
    返回值:
    取回的颜色条目数量,零表示失败。会设置GetLastError
    参数:
    hDC 已选入了一个DIBSection对象的设备场景
    uStartIndex 颜色表中欲取回的第一个条目的索引
    cEntries 欲取回的条目数量
    pColors 这个结构数组用于装载颜色表信息的第一个条目

    SetDIBColorTable
    原型定义:
    UINT SetDIBColorTable(
      HDC hDC,                // handle to device context whose DIB is of interest
      UINT uStartIndex,       // color table index of first entry to set
      UINT cEntries,          // number of color table entries to set
      CONST RGBQUAD *pColors  // pointer to array of color table entries
    );
    
    VB声明:
    Declare Function SetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long
    说明:
    从选入设备场景的DIBSection中取得颜色表信息
    返回值:
    取回的颜色条目数量,零表示失败。会设置GetLastError
    参数:
    hDC 已选入了一个DIBSection对象的设备场景
    uStartIndex 颜色表中欲取回的第一个条目的索引
    cEntries 欲取回的条目数量
    pColors 这个结构数组用于装载颜色表信息的第一个条目

    四、实战练习

      用DIB写图像处理程序的时候,首先要明确一点:DIB并不是图像处理算法,而是一种绘图方法。图像处理算法是DIB高级,管理坐标和颜色的运算;而DIB只是为了绘制。所以此时处理算法的效率是速度的关键。

     

      利用DIB绘制图像并没有比用PSet/SetPixel绘制差多少,它只是把坐标运算改成地址运算而已。很多人知道指针是一个危险的东西,就是因为它能直接访问内存,如果指针不小心指错地方的话,Windows立即报告一般保护性错误。所以,在地址运算的时候一定要小心,同时要注意随时保存,因为此时的非法操作的发生率非常高,否则辛辛苦苦写的代码一瞬间没了可别怪我没提醒啊。

     


      好了,现在开始!

     

      由于处理算法起指导作用,所以现在先讲解1_PSet。所有的代码都在FrmMain.frm中。其他的过程的代码可以不看,现在将注意力集中在“DrawIt”中,它就是管绘制的。

     

    vb6/1_PSet/FrmMain.frm中DrawIt
    '绘制
    Private Sub DrawIt()
        Dim I As Long, J As Long
        Static K As Long
        
        For I = 0 To ImgHeight - 1 'Y
            For J = 0 To ImgWidth - 1 'X
                PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
            Next J
        Next I
        
        K = (K + 1) And &HFF
        
    End Sub
    
      其实我这个演示程序蛮简单的:R分量延着水平方向增加,G分量延着垂直方向增加,B分量则从右往左滚动。什么?!“And &HFF”是什么意思?!这可是基础啊……(下略&HFFFF...字)。“&H”表示十六进制数,而And表示按位与。&HFF是二进制的“1111 1111”,正好覆盖了低8位,这时用And进行按位与,只会得到低8位,与RGB分量需要的8位正好符合(对于“(J + K) And &HFF”来说,可以实现滚动效果)。

     

      If Not 看明白了 Then Goto 前两段

     

      好!现在打开3_DIB。(由于24位能直接指定RGB分量,所以这里是用的是24位DIB)

     

      看了前面的“DIB的结构”,是不是有点昏呢?其实DIB也没什么,就是 一个表述位图信息的BITMAPINFO结构 和 一个存储位图数据的数据缓冲区,顶多再用SetDIBitsToDevice绘制,所以3_DIB与1_PSet相比只是多了SetDIBitsToDevice、BITMAPINFOHEADER(24位DIB没有调色板,所以用BITMAPINFOHEADER就行)和一些常数的声明而已。由于这个演示程序不需要改变图像大小和色深,所以可以把有关变量作为窗体级变量,再在Form_Load中初始化。由于DIB并没有向系统申请资源(数组的内存是VB分配的,会自动释放),所以不需要写释放代码。

     

      现在来看DrawIt。
    vb6/3_DIB/FrmMain.frm中DrawIt
    '绘制
    Private Sub DrawIt()
        Dim I As Long, J As Long
        Static K As Long
        Dim iLinePtr As Long, iCurPtr As Long
        
        iLinePtr = (m_BI.biHeight - 1) * m_LineBytes 'DIB是逆序存储的
        For I = 0 To ImgHeight - 1 'Y
            iCurPtr = iLinePtr
            For J = 0 To ImgWidth - 1 'X
                'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
                m_MapData(iCurPtr + 2) = J And &HFF       'Red
                m_MapData(iCurPtr + 1) = I And &HFF       'Green
                m_MapData(iCurPtr + 0) = (J + K) And &HFF 'Blue
                iCurPtr = iCurPtr + 3 '24位
            Next J
            iLinePtr = iLinePtr - m_LineBytes
        Next I
        
        K = (K + 1) And &HFF
        
    End Sub
    
      1.虽然可以逐点把坐标映射成地址再写,但是这样效率太低了,可以利用坐标处理的连续性进行优化。
      2.由于我这里用的是数组,所以这里用(数组元素)索引代替地址。
      3.最开始要注意DIB是逆序存储的,要将索引设为最后一行第一个像素的索引。
      4.由于DIB的RGB顺序是B、G、R,所以“m_MapData(CurIdx + ?)”的顺序是2、1、0。
      5.设置好一个像素的颜色后,要注意把索引改为下个像素的索引。
      6.由于DIB是逆序存储的,移到下一个扫描行是“iLinePtr = iLinePtr - m_LineBytes”

     


      “好了,代码看懂了,按F5运行看看效果。”
      “咦?速度好像没快多少啊?”

      这是由于程序在VB环境下是以解释方式运行的,而解释方式对做图像处理所需要大规模循环和大量的算术运算的执行效率很低,所以要编译成(本机代码)exe再运行。此时还要注意编译优化,可以把“高级优化”的所有勾打上,速度可提升20%左右。

     


    五、使用DIBSection和模拟指针

      虽然有GetDIBits/SetDIBits函数,但是DIB与GDI位图之间的数据交换还是很不方便,特别处理过程中需要调用GDI函数来处理的时候。而且就算你不怕麻烦,但这样做的处理效率很低。所以Windows为我们提供了DIBSection。DIBSection是一种特殊的DIB,它除了可以像DIB一样直接对位图数据所占内存进行操作,它还可以选入DC、能用GDI函数绘制,非常灵活。但在VB下使用DIBSection还是有困难的,因为用CreateDIBSection创建DIBSection时,得到的是位图数据的地址,而VB没有指针。

     

      所幸在VB下可以利用SafeArray结构的数组模拟指针。关于模拟指针的原理、方法,网上的资料多的是,比如AdamBear的文章“VB真是想不到系列之四:VB指针葵花宝典之SafeArray”。但这些文章都只是讲一般性的应用,不能像真正的指针一样随意改变地址(他们都是使用CopyMemory改的)。而在图像处理中,由于点运算的频繁,“像真正的指针一样随意改变地址”的功能很重要。

     

      其实“像真正的指针一样随意改变地址”并没有技术难度(对于已经学会模拟指针的人来说),就看想得到不:直接将一个动态数组(设pByte)指向一个SAFEARRAY结构体变量(设pBytePtr)。模拟指针模块的代码在mPoint.bas

     

      现在来看4_DIB_Ptr:
    vb6/4_DIB_Ptr/FrmMain.frm中DrawIt
    '绘制
    Private Sub DrawIt()
        Dim I As Long, J As Long
        Static K As Long
        Dim pByte() As Byte, pBytePtr As SAFEARRAY1D
        Dim iLinePtr As Long
        
        ' check Image
        Debug.Assert m_pDIB <> 0
        
        '建立模拟指针
        MakePoint VarPtrArray(pByte), pBytePtr, 1
        
        Ptr(pBytePtr) = m_pDIB + (m_BI.biHeight - 1) * m_LineBytes 'DIB是逆序存储的
        iLinePtr = pBytePtr.pvData
        For I = 0 To ImgHeight - 1 'Y
            pBytePtr.pvData = iLinePtr
            For J = 0 To ImgWidth - 1 'X
                'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
                pByte(2) = J And &HFF       'Red
                pByte(1) = I And &HFF       'Green
                pByte(0) = (J + K) And &HFF 'Blue
                pBytePtr.pvData = pBytePtr.pvData + 3 '24位
            Next J
            iLinePtr = iLinePtr - m_LineBytes
        Next I
        
        '释放模拟指针
        FreePoint VarPtrArray(pByte)
        
        K = (K + 1) And &HFF
        
    End Sub
    
      1.为了演示DIBSection能够像HBITMAP一样,我在Form_Load中创建了DC、将DIBSection选入DC。同时为了释放,Form_UnLoad中写了释放代码。
      2.在DrawIt中,注意处理部分的代码并没有与VB_DIB差多少,只不过把索引计算改为地址运算而已。

     


      再来比较vc的代码,感觉跟真正的指针用法差不多嘛:
    vc/ImgTestDlg.cpp中CImgTestDlg::DrawIt
    void CImgTestDlg::DrawIt() 
    {
    	int i=0,j=0;
    	static int k=0;
    	BYTE *pByte, *pLinePtr;
    
    	pLinePtr = (BYTE*)pDIB + (bi.biHeight-1)*LineBytes;
    	for(i=0; i<IMGHEIGHT; i++)
    	{
    		pByte = pLinePtr;
    		for(j=0; j<IMGWIDTH; j++)
    		{
    			pByte[2] = j & 0xff;
    			pByte[1] = i & 0xff;
    			pByte[0] = (j+k) & 0xff;
    			pByte = pByte + 3;
    		}
    		pLinePtr = pLinePtr - LineBytes;
    	}
    
    	k = (k+1) & 0xff;
    
    }
    

    六、结合DirectX

      在GDI中,我们可以用DIB直接操作图像数据,那么号称能直接访问显存的DirectX呢?

     

      一查DirectX SDK,发现IDirectDrawSurface接口的Lock函数可以锁定表面,从而直接访问该位图数据。再在VB的对象浏览器中仔细观察“DirectX 7 for Visual Basic Type Library”,发现DirectDrawSurface7对象也有该方法。

     

      看看5_DX7Ptr:
    vb6/5_DX7Ptr/FrmMain.frm中Render
    '渲染
    Private Sub Render()
        Dim I As Long, J As Long
        Static K As Long
        Dim ddsdInfo As DDSURFACEDESC2
        Dim IsOK As Boolean
        Dim cbPitch As Long
        Dim cbPixel As Long
        Dim iIdxR As Long
        Dim iIdxG As Long
        Dim iIdxB As Long
        Dim iMaxX As Long, iMaxY As Long
        Dim pByte() As Byte, pBytePtr As SAFEARRAY1D
        Dim iLinePtr As Long
        
        ' check Image
        'Debug.Assert m_pDIB <> 0
        Debug.Assert Not m_ddsRender Is Nothing
        
        ' Main
        With m_ddsRender
            'Render
            Call .Lock(m_rctSurf, ddsdInfo, DDLOCK_SURFACEMEMORYPTR Or DDLOCK_WRITEONLY Or DDLOCK_NOSYSLOCK Or DDLOCK_WAIT, 0)
            IsOK = CheckPixelFormat(ddsdInfo.ddpfPixelFormat)
            If IsOK Then
                With ddsdInfo
                    Debug.Assert .lpSurface
                    cbPitch = .lPitch
                    With .ddpfPixelFormat
                        cbPixel = (.lRGBBitCount) / 8
                        iIdxR = MaskToRShift(.lRBitMask) / 8
                        iIdxG = MaskToRShift(.lGBitMask) / 8
                        iIdxB = MaskToRShift(.lBBitMask) / 8
                    End With
                    iMaxX = .lWidth - 1
                    iMaxY = .lHeight - 1
                End With
                
                '建立模拟指针
                MakePoint VarPtrArray(pByte), pBytePtr, 1
                
                Ptr(pBytePtr) = ddsdInfo.lpSurface
                iLinePtr = pBytePtr.pvData
                For I = 0 To iMaxY 'Y
                    pBytePtr.pvData = iLinePtr
                    For J = 0 To iMaxX 'X
                        'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
                        pByte(iIdxR) = J And &HFF       'Red
                        pByte(iIdxG) = I And &HFF       'Green
                        pByte(iIdxB) = (J + K) And &HFF 'Blue
                        pBytePtr.pvData = pBytePtr.pvData + cbPixel '下一个像素
                    Next J
                    iLinePtr = iLinePtr + cbPitch '下一个扫描行
                Next I
                
                '释放模拟指针
                FreePoint VarPtrArray(pByte)
                
            End If
            Call .Unlock(m_rctSurf)
            
            'Error Pixel Format
            If IsOK = False Then
                Call .BltColorFill(m_rctSurf, vbBlack)
                Call .SetForeColor(vbWhite)
                Call .DrawText(0, &H20, "Error Pixel Format!", False)
            End If
            
        End With
        
        K = (K + 1) And &HFF
        
    End Sub
    
      1.由于硬件设备性能不同,所以不要指望像DIB那样可以认定RGB字节顺序。应该根据Lock方法传回的DDSURFACEDESC2结构来确认RGB字节顺序(iIdxR、iIdxG、iIdxB)、像素所占字节(cbPixel)及宽距(cbPitch)。
      2.由于我们有了模拟指针技术,所以这段代码与跟先前4_DIB_Ptr差不多,特别是与在VC中使用IDirectDrawSurface::Lock的处理代码差不多。

      仔细观察对象浏览器的人会发现,DirectDrawSurface7对象GetLockedArray可以取得锁定后的位图数据:
    vb6/6_DX7Arr/FrmMain.frm中Render
    '渲染
    Private Sub Render()
        Dim I As Long, J As Long
        Static K As Long
        Dim ddsdInfo As DDSURFACEDESC2
        Dim IsOK As Boolean
        Dim cbPitch As Long
        Dim cbPixel As Long
        Dim iIdxR As Long
        Dim iIdxG As Long
        Dim iIdxB As Long
        Dim iMaxX As Long, iMaxY As Long
        Dim pByte() As Byte
        Dim iCurPtr As Long
        
        ' check Image
        'Debug.Assert m_pDIB <> 0
        Debug.Assert Not m_ddsRender Is Nothing
        
        ' Main
        With m_ddsRender
            'Render
            Call .Lock(m_rctSurf, ddsdInfo, DDLOCK_SURFACEMEMORYPTR Or DDLOCK_WRITEONLY Or DDLOCK_NOSYSLOCK Or DDLOCK_WAIT, 0)
            IsOK = CheckPixelFormat(ddsdInfo.ddpfPixelFormat)
            If IsOK Then
                With ddsdInfo
                    Debug.Assert .lpSurface
                    cbPitch = .lPitch
                    With .ddpfPixelFormat
                        cbPixel = (.lRGBBitCount) / 8
                        iIdxR = MaskToRShift(.lRBitMask) / 8
                        iIdxG = MaskToRShift(.lGBitMask) / 8
                        iIdxB = MaskToRShift(.lBBitMask) / 8
                    End With
                    iMaxX = .lWidth - 1
                    iMaxY = .lHeight - 1
                End With
                
                Call .GetLockedArray(pByte)
                
                For I = 0 To iMaxY 'Y
                    iCurPtr = 0
                    For J = 0 To iMaxX 'X
                        'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
                        pByte(iCurPtr + iIdxR, I) = J And &HFF      'Red
                        pByte(iCurPtr + iIdxG, I) = I And &HFF      'Green
                        pByte(iCurPtr + iIdxB, I) = (J + K) And &HFF 'Blue
                        iCurPtr = iCurPtr + cbPixel '下一个像素
                    Next J
                Next I
                
            End If
            Call .Unlock(m_rctSurf)
            
            'Error Pixel Format
            If IsOK = False Then
                Call .BltColorFill(m_rctSurf, vbBlack)
                Call .SetForeColor(vbWhite)
                Call .DrawText(0, &H20, "Error Pixel Format!", False)
            End If
            
        End With
        
        K = (K + 1) And &HFF
        
    End Sub
    
      这个就是官方推荐做法,使用一个二维数组来处理位图数据。但是就是因为其使用的是二维数组,所以它比一维数组的模拟指针慢一些。如果你不想使用非常规技术(模拟指针),可以使用GetLockedArray。
      注意到这点没有,GetLockedArray是一个方法而不是一个属性,我们需要将数组变量传递过去,然后就可以利用该数组直接操作位图数据了。啊哈!明白了GetLockedArray也是利用模拟指针技术实现的,只不过它填充的是2维的SAFEARRAY结构而已。

      注意:只操作位于系统内存的表面,千万别操作对显存中的表面。这是因为CPU访问显存比返问内存要慢许多!这个建议并不是绝对的,如果对该表面的Blt的调用次数远超过自己写的图像处理操作的话,可以将该表面放在显存,或者是在内存中计算好后再一次性提交到显存。
      本程序建立图像处理操作的表面的代码:
    vb6/5_DX7Ptr/FrmMain.frm中CreateSurfaces
        ' init Image
        With ddsdInfo
            .lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH Or DDSD_PIXELFORMAT
            With .ddsCaps
                .lCaps = DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_SYSTEMMEMORY 'CPU访问内存比访问显存快
            End With
            .lWidth = ImgWidth
            .lHeight = ImgHeight
            .ddpfPixelFormat = m_ddsdInfo.ddpfPixelFormat
        End With
        Set m_ddsRender = m_dxDraw.CreateSurface(ddsdInfo)
    

    (全文完)

    展开全文
  • VB图像、照片合并合成程序,支持图片的横向、纵向(竖向)合并,合成出来的图片保存成一张图片,只是合并,不包括其它的图像处理功能。
  • VB写高效的图像处理程序 V1.0

    千次阅读 2003-08-18 10:39:00
    说起来也是,市面上的关于VB图像处理的数据都是先讲计算公式,再直接用PSet(或API函数SetPixel)逐点画(至少我见过的书都是这样)。效果是办到了,但速度慢得离谱:对一幅640*480的图像进行半透明合并就需要10...

    一、为什么这么慢?

      自盘古开天地以来(好像夸张了点),一直有人抱怨VB程序速度慢。特别是图像处理,被认为是VB的禁区。说起来也是,市面上的关于VB的图像处理的数据都是先讲计算公式,再直接用PSet(或API函数SetPixel)逐点画(至少我见过的书都是这样)。效果是办到了,但速度慢得离谱:对一幅640*480的图像进行半透明合并就需要10秒钟;而在PhotoShop中,只要一设置图层的透明度,半透明效果立即呈现。难怪有人说VB的闲话。

     

      但这并不表示VB不能写高速的图像处理程序,速度慢是因为没有使用正确的方法。

     

      从VB5开始,能以本机代码编译成exe文件,所以不存在代码执行速度的问题。那么,是什么拖慢了速度呢?就是PSet和SetPixel!PSet把浮点形式的坐标转为像素单位,再交给SetPixel处理。而SetPixel呢,坐标系转化、剪裁区域判断、将颜色匹配为设备支持的最接近的,最后还要根据不同的颜色格式寻址、为将颜色写入其所在位进行位运算。经过这么多层处理,速度不慢才怪。

     

      那么,怎样才能提高处理速度呢?使用DIB,直接对位图所在内存进行操作,速度可以大大提高。现在看看范例程序,这只是一个简单的色彩演示程序。

     

    CPU:赛扬333;内存:PC100(很老的概念了)的SDRAM,128MB;单位:毫秒
      Windows 98 Windows XP 说 明
    VB_PSet 1,199.4553 786.1885 在VB使用 PSet 画的
    VB_SetPixel 872.3621 451.3712 在VB使用 SetPixelV 画的
    VB_DIB 8.2218 8.2226 在VB使用 DIB 画的
    VB_DIB_Ptr 9.6783 9.4420 在VB使用 DIBSection + 模拟指针 画的
    VC(Debug) 6.6896 6.6503 VC写的(Debug版)
    VC(Release) 3.2736 3.6247 VC写的(Release版)
      从这个表中可看出:
      1.VC比VB_DIB、VB_DIB_Ptr快两倍,这是因为SafeArray结构的数组比真正的指针慢,但也不是某些人所说的70~100倍;
      2.VB_DIB_Ptr比VB_DIB慢一点,这是因为模拟指针本来就是靠SafeArray结构的数组,而且模拟指针需要对两个数组进行操作,所以速度慢一点;
      3.真正差了70~100倍是VB_PSet和VB_SetPixel,特别是VB_PSet在Windows98下与VB_DIB差了145倍。

     

      以上可证,速度慢的原因是SetPixel非常低效,而并不是VB的问题。虽然VC的的确比较快,但是我写这篇文章不是为了讨论速度极限(否则这篇文章会改名为《如何用汇编写高速的图像处理程序》),而是为了告诉大家如何在VB中写能够实时处理的图像处理程序。

     


    二、DIB的结构

      在 Windows 3.0 以前,Windows系统用的是DDB(设备有关位图)。DDB没有调色板,显示的颜色依赖硬件,处理色彩很不方便。所以 Microsoft 在 Windows 3.0中 重新定义了BMP文件格式(BMP 3.0),使其支持设备无关位图——也就是DIB。

     

      时至今日,BMP的版本号已升至5.0(Windows NT 4.0、Windows95 定义了 BMP 4.0,Windows 98、Windows 2000 定义了 BMP 5.0),但基本结构没有变——仍是 BMP文件头 和 DIB 组成:

     

    BMP文件   BITMAPFILEHEADER BMP文件头
    DIB BITMAPINFOHEADER 位图信息头 BITMAPINFO
    RGBQUAD[] 调色板
      位图数据

    #代表可以不填(=0)的项目)
    BMP文件头——BITMAPFILEHEADER
    原型定义:
    typedef struct tagBITMAPFILEHEADER { // bmfh
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
    } BITMAPFILEHEADER;
    
    VB声明:
    Type BITMAPFILEHEADER
        bfType(0 to 1) As Byte
        bfSize As Long
        bfReserved1 As Integer
        bfReserved2 As Integer
        bfOffBits As Long
    End Type
    
    说明:
    bfType 指示文件的类型,必须是“BM”
    bfSize# 指示文件的大小,包括BITMAPFILEHEADER
    bfReserved1 保留,=0
    bfReserved2 保留,=0
    bfOffBits# 从文件头到位图数据的偏移字节数

    文件信息头——BITMAPINFOHEADER
    原型定义:
    typedef struct tagBITMAPINFOHEADER{ // bmih
        DWORD  biSize;
        LONG   biWidth;
        LONG   biHeight;
        WORD   biPlanes;
        WORD   biBitCount;
        DWORD  biCompression;
        DWORD  biSizeImage;
        LONG   biXPelsPerMeter;
        LONG   biYPelsPerMeter;
        DWORD  biClrUsed;
        DWORD  biClrImportant;
    } BITMAPINFOHEADER;
    
    VB声明:
    Type BITMAPINFOHEADER
        biSize As Long
        biWidth As Long
        biHeight As Long
        biPlanes As Integer
        biBitCount As Integer
        biCompression As Long
        biSizeImage As Long
        biXPelsPerMeter As Long
        biYPelsPerMeter As Long
        biClrUsed As Long
        biClrImportant As Long
    End Type
    
    说明:
    biSize BITMAPINFOHEADER结构的大小。BMP有多个版本,就靠biSize来区别:
      BMP3.0:BITMAPINFOHEADER(=40)
      BMP4.0:BITMAPV4HEADER(=108)
      BMP5.0:BITMAPV5HEADER(=124)
    biWidth 位图的高度,单位是像素
    biHeight 位图的宽度,单位是像素
    biPlanes 设备的位平面数。现在都是1
    biBitCount 图像的颜色位数
       0:当biCompression=BI_JPEG时必须为0(BMP 5.0)
       1:单色位图
       4:16色位图
       8:256色位图
      16:增强色位图,默认为555格式
      24:真彩色位图
      32:32位位图,默认情况下Windows不会处理最高8位,可以将它作为自己的Alpha通道
    biCompression 压缩方式
      BI_RGB:无压缩
      BI_RLE8:行程编码压缩,biBitCount必须等于8
      BI_RLE4:行程编码压缩,biBitCount必须等于4
      BI_BITFIELDS:指定RGB掩码,biBitCount必须等于16、32
      BI_JPEG:JPEG压缩(BMP 5.0)
      BI_PNG:PNG压缩(BMP 5.0)
    biSizeImage# 实际的位图数据所占字节(biCompression=BI_RGB时可以省略)
    biXPelsPerMeter# 目标设备的水平分辨率,单位是每米的像素个数
    biYPelsPerMeter# 目标设备的垂直分辨率,单位是每米的像素个数
    biClrUsed# 使用的颜色数(当biBitCount等于1、4、8时才有效)。如果该项为0,表示颜色数为2^biBitCount
    biClrImportant# 重要的颜色数。如果该项为0,表示所有颜色都是重要的

    调色板
      只有biBitCount等于1、4、8时才有调色板。调色板实际上是一个数组,元素的个数由biBitCount和biClrUsed决定。
    原型定义:
    typedef struct tagRGBQUAD { // rgbq
        BYTE    rgbBlue;
        BYTE    rgbGreen;
        BYTE    rgbRed;
        BYTE    rgbReserved;
    } RGBQUAD;
    
    VB声明:
    Private Type RGBQUAD
        rgbBlue As Byte
        rgbGreen As Byte
        rgbRed As Byte
        rgbReserved As Byte
    End Type
    
    说明:
    rgbBlue 蓝色分量
    rgbGreen 绿色分量
    rgbRed 红色分量
    rgbReserved# 保留,=0

    位图数据
    扫描行:
      一行的图像数据叫做一个扫描行。一个扫描行的长度必须是4的倍数(字节),如果不是,则需要补齐。计算公式:LineBytes=((biWidth*biBitCount+31)And &HFFFFFFE0)/8
      由于BMP设定者认为数学坐标系更总要,所以DIB的扫描行是逆序存储的(相对于屏幕坐标系而言),即屏幕上的第一行是DIB位图数据的最后一行。
    1位色:
      用1位表示一个像素,所以一个字节可以表示8个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:
    字节 0 ...
    7 6 5 4 3 2 1 0
    像素 0 1 2 3 4 5 6 7
    4位色:
      用4位表示一个像素,所以一个字节可以表示2个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:
    字节 0 ...
    7 6 5 4 3 2 1 0
    像素 0 1
    像素位 3 2 1 0 3 2 1 0
    8位色:
      用8位表示一个像素,所以一个字节刚好只能表示一个像素。在内存的摆放形式如下:
    字节 0 1 ...
    像素 0 1
    16位色:
      用16位表示一个像素,所以两个字节可以表示1个像素。默认情况下16位DIB是555格式,最高位无效(这对VB是个福音,因为VB没有16位无符号型)。在内存的摆放形式如下(PC机是低字节在前):
    字节 0 1 2 3 ...
    7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
    像素 0 1
    RGB G B x R G G B x R G
    RGB位 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3 2 1 0 4 3 2 1 0 0 4 3 2 1 0 4 3
    24位色:
      用24位表示一个像素,所以三个字节可以表示1个像素。注意它的顺序是BGR,而不是传统的RGB。在内存的摆放形式如下:
    字节 0 1 2 3 4 5 ...
    像素 0 1
    RGB B G R B G R
    32位色:
      用32位表示一个像素,所以四个字节可以表示1个像素。注意绝大多数的GDI函数不会处理Alpha通道(只有AlphaBlend支持)。在内存的摆放形式如下:
    字节 0 1 2 3 4 5 6 7 ...
    像素 0 1
    RGB B G R A B G R A

    三、DIB访问函数

    SetDIBitsToDevice
    原型定义:
    int SetDIBitsToDevice(
      HDC hDC,              // handle to device context
      int XDest,            // x-coordinate of upper-left corner of dest. rect.
      int YDest,            // y-coordinate of upper-left corner of dest. rect.
      DWORD dwWidth,        // source rectangle width
      DWORD dwHeight,       // source rectangle height
      int XSrc,             // x-coordinate of lower-left corner of source rect.
      int YSrc,             // y-coordinate of lower-left corner of source rect.
      UINT uStartScan,      // first scan line in array
      UINT cScanLines,      // number of scan lines
      CONST VOID *lpvBits,  // address of array with DIB bits
      CONST BITMAPINFO *lpbmi,  // address of structure with bitmap info.
      UINT fuColorUse       // RGB or palette indexes
    );
    
    VB声明:
    Declare Function SetDIBitsToDevice Lib "gdi32.dll" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal dwWidth As Long, ByVal dwHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal fuColorUse As Long) As Long
    说明:
    将一幅与设备无关位图的全部或部分数据直接复制到一个设备。这个函数在设备中定义了一个目标矩形,以便接收位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据
    返回值:
    如函数执行成功,返回欲复制的扫描线的数量;如返回常数GDI_ERROR,表示出错
    参数:
    hDC 一个设备场景的句柄。该场景用于接收位图数据
    XDest 指定绘制区域的左上角X坐标
    YDest 指定绘制区域的左上角Y坐标
    dwWidth 指定绘制区域的高度
    dwHeight 指定绘制区域的宽度
    XSrc 矩形在DIB中的起点X坐标
    YSrc 矩形在DIB中的起点Y坐标
    uStartScan lpvBits中第一条扫描线的编号。如lpbmi之BITMAPINFOHEADER部分的biHeight字段是正数,那么这条扫描线就会从位图的底部开始计算;如果是负数,就从顶部开始计算
    cScanLines 欲复制的扫描线数量
    lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构
    fuColorUse
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    StretchDIBits
    原型定义:
    int StretchDIBits(
      HDC hDC,                // handle to device context
      int XDest,              // x-coordinate of upper-left corner of dest. rectangle
      int YDest,              // y-coordinate of upper-left corner of dest. rectangle
      int nDestWidth,         // width of destination rectangle
      int nDestHeight,        // height of destination rectangle
      int XSrc,               // x-coordinate of upper-left corner of source rectangle
      int YSrc,               // y-coordinate of upper-left corner of source rectangle
      int nSrcWidth,          // width of source rectangle
      int nSrcHeight,         // height of source rectangle
      CONST VOID *lpBits,            // address of bitmap bits
      CONST BITMAPINFO *lpBitsInfo,  // address of bitmap data
      UINT iUsage,                   // usage flags
      DWORD dwRop                    // raster operation code
    );
    
    VB声明:
    Declare Function StretchDIBits Lib "gdi32" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nDestWidth As Long, ByVal nDestHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal nSrcWidth As Long, ByVal nSrcHeight As Long, lpBits As Any, lpBitsInfo As Any, ByVal wUsage As Long, ByVal dwRop As Long) As Long
    说明:
    根据一幅与设备无关的位图创建一幅与设备有关的位图
    返回值:
    执行成功返回位图句柄,零表示失败
    参数:
    hDC 一个设备场景的句柄,该设备场景定义了要创建的与设备有关位图的配置信息
    XDest 指定绘制区域的左上角X坐标
    YDest 指定绘制区域的左上角Y坐标
    nDestWidth 指定绘制区域的高度
    nDestHeight 指定绘制区域的宽度
    XSrc 矩形在DIB中的起点X坐标
    YSrc 矩形在DIB中的起点Y坐标
    nSrcWidth 指定原位图绘制区域的左上角X坐标
    nSrcHeight 指定原位图绘制区域的左上角Y坐标
    lpBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpBitsInfo指定的
    lpBitsInfo 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构
    iUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色
    dwRop 欲进行的光栅运算

    CreateDIBitmap
    原型定义:
    HBITMAP CreateDIBitmap(
      HDC hDC,                  // handle to device context
      CONST BITMAPINFOHEADER *lpbmih,  // pointer to bitmap size and format data
      DWORD fdwInit,            // initialization flag
      CONST VOID *lpbInit,      // pointer to initialization data
      CONST BITMAPINFO *lpbmi,  // pointer to bitmap color-format data
      UINT fuUsage              // color-data usage
    );
    
    VB声明:
    Declare Function CreateDIBitmap Lib "gdi32" (ByVal hDC As Long, lpbmih As Any, ByVal fdwInit As Long, lpbInit As Any, lpbmi As Any, ByVal fuUsage As Long) As Long
    说明:
    将一幅与设备无关位图的全部或部分数据直接复制到一个设备。这个函数在设备中定义了一个目标矩形,以便接收位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据
    返回值:
    执行成功则返回扫描线的数量,零表示失败。会设置GetLastError
    参数:
    hDC 一个设备场景的句柄。该场景用于接收位图数据
    lpbmih BITMAPINFOHEADER(为兼容BMP4/5而声明成Any),对DIB的格式进行描述的一个结构
    fdwInit 如不应对位图数据进行初始化,那么设为零。如设为CBM_INIT,表示根据lpbInit和 lpbmi参数对位图进行初始化
    lpbInit 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),对DIB的格式和颜色进行描述的一个结构
    fuUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    CreateDIBSection
    原型定义:
    HBITMAP CreateDIBSection(
      HDC hDC,          // handle to device context
      CONST BITMAPINFO *lpbmi,
                        // pointer to structure containing bitmap size, format, and color data
      UINT iUsage,      // color data type indicator: RGB values or palette indexes
      VOID *ppvBits,    // pointer to variable to receive a pointer to  the bitmap's bit values
      HANDLE hSection,  // optional handle to a file mapping object
      DWORD dwOffset    // offset to the bitmap bit values within the file mapping object
    );
    
    VB声明:
    Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, lpbmi As Any, ByVal iUsage As Long, ByRef ppvBits As Long, ByVal hSection As Long, ByVal dwOffset As Long) As Long
    说明:
    CreateDIBSection能创建一种特殊的DIB,称为DIB项(DIBSection),然后返回一个GDI位图的句柄。它提供了DIB和GDI位图的最好的特性。这样我们可以直接访问DIB的内存,可以利用位图句柄和内存设备环境,我们甚至还可以在DIB中调用GDI函数来绘图
    返回值:
    执行成功返回DIBSection位图的句柄,零表示失败。会设置GetLastError
    参数:
    hDC 一个设备场景的句柄。如dw设为DIB_PAL_COLORS,那么DIB颜色表就会用来自逻辑调色板的颜色进行初始化
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any),这个结构初始化成欲创建的那幅位图的配置数据
    iUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色
    ppvBits 用于得到DIBSection数据区的内存地址
    hSection 指向一个文件映射对象的可选句柄,位图将在其中创建。如设为零,Windows会自动分配内存
    dwOffset 如指定了句柄,就用这个参数指定位图数据在文件映射对象中的偏移量

    GetDIBits
    原型定义:
    int GetDIBits(
      HDC hDC,           // handle to device context
      HBITMAP hbmp,      // handle to bitmap
      UINT uStartScan,   // first scan line to set in destination bitmap
      UINT cScanLines,   // number of scan lines to copy
      LPVOID lpvBits,    // address of array for bitmap bits
      LPBITMAPINFO lpbmi,// address of structure with bitmap data
      UINT uUsage        // RGB or palette index
    );
    
    VB声明:
    Declare Function GetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long
    说明:
    该函数利用申请到的内存,由GDI位图得到DIB位图数据。通过该函数,可以对DIB的格式进行控制,可以制定颜色的位数,而且可以指定是否进行压缩。如果采用了压缩方式,则必须调用该函数两次,一次为了得到所需内存,另外一次为了得到位图数据
    返回值:
    执行成功则返回扫描线的数量,零表示失败。会设置GetLastError
    参数:
    hDC 定义了与设备有关位图hBitmap的配置信息的一个设备场景的句柄
    hbmp 源位图的句柄
    uStartScan 欲复制到DIB中的第一条扫描线的编号
    cScanLines 欲复制的扫描线数量
    lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any).对DIB的格式及颜色进行说明的一个结构。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的所有字段都必须初始化
    uUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    SetDIBits
    原型定义:
    int SetDIBits(
      HDC hDC,                  // handle to device context
      HBITMAP hbmp,             // handle to bitmap
      UINT uStartScan,          // starting scan line
      UINT cScanLines,          // number of scan lines
      CONST VOID *lpvBits,      // array of bitmap bits
      CONST BITMAPINFO *lpbmi,  // address of structure with bitmap data
      UINT uUsage               // type of color indexes to use
    );
    
    VB声明:
    Declare Function SetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hbmp As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal uUsage As Long) As Long
    说明:
    将来自与设备无关位图的二进制位复制到一幅与设备有关的位图里
    返回值:
    执行成功则返回扫描线的数量,零表示失败。会设置GetLastError
    参数:
    hDC 定义了与设备有关位图hBitmap的配置信息的一个设备场景的句柄
    hbmp 源位图的句柄
    uStartScan 欲复制到DIB中的第一条扫描线的编号
    cScanLines 欲复制的扫描线数量
    lpvBits 指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
    lpbmi 指向BITMAPINFO(为兼容BMP4/5而声明成Any).对DIB的格式及颜色进行说明的一个结构。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的所有字段都必须初始化
    uUsage
    DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
    DIB_RGB_COLORS 颜色表包含了RGB颜色

    GetDIBColorTable
    原型定义:
    UINT GetDIBColorTable(
      HDC hDC,          // handle to device context whose DIB is of interest
      UINT uStartIndex, // color table index of first entry to retrieve
      UINT cEntries,    // number of color table entries to retrieve
      RGBQUAD *pColors  // pointer to buffer that receives color table entries
    );
    
    VB声明:
    Declare Function GetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long
    说明:
    从选入设备场景的DIBSection中取得颜色表信息
    返回值:
    取回的颜色条目数量,零表示失败。会设置GetLastError
    参数:
    hDC 已选入了一个DIBSection对象的设备场景
    uStartIndex 颜色表中欲取回的第一个条目的索引
    cEntries 欲取回的条目数量
    pColors 这个结构数组用于装载颜色表信息的第一个条目

    SetDIBColorTable
    原型定义:
    UINT SetDIBColorTable(
      HDC hDC,                // handle to device context whose DIB is of interest
      UINT uStartIndex,       // color table index of first entry to set
      UINT cEntries,          // number of color table entries to set
      CONST RGBQUAD *pColors  // pointer to array of color table entries
    );
    
    VB声明:
    Declare Function SetDIBColorTable Lib "gdi32" (ByVal hDC As Long, ByVal uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long
    说明:
    从选入设备场景的DIBSection中取得颜色表信息
    返回值:
    取回的颜色条目数量,零表示失败。会设置GetLastError
    参数:
    hDC 已选入了一个DIBSection对象的设备场景
    uStartIndex 颜色表中欲取回的第一个条目的索引
    cEntries 欲取回的条目数量
    pColors 这个结构数组用于装载颜色表信息的第一个条目

    四、实战练习

      用DIB写图像处理程序的时候,首先要明确一点:DIB并不是图像处理算法,而是一种绘图方法。处理算法比DIB高一级,管理坐标和颜色的运算;而DIB只是为了绘制。所以此时处理算法的效率是速度的关键。

     

      利用DIB绘制图像并没有比用PSet/SetPixel绘制差多少,它只是把坐标运算改成地址运算而已。很多人知道指针是一个危险的东西,就是因为它能直接访问内存,如果指针不小心指错地方的话,Windows立即报告一般保护性错误。所以,在地址运算的时候一定要小心,同时要注意随时保存,因为此时的非法操作的发生率非常高,否则辛辛苦苦写的代码一瞬间没了可别怪我没提醒啊。

     


      好了,现在开始!

     

      由于处理算法起指导作用,所以现在先讲解VB_PSet。所有的代码都在FrmMain.frm中。其他的过程的代码可以不看,现在将注意力集中在“DrawIt”中,它就是管绘制的。

     

      其实我这个演示程序蛮简单的:R分量延着水平方向增加,G分量延着垂直方向增加,B分量则从右往左滚动。什么?!“And &HFF”是什么意思?!这可是基础啊……(下略&HFFFF...字)。“&H”表示十六进制数,而And表示按位与。&HFF是二进制的“1111 1111”,正好覆盖了低8位,这时用And进行按位与,只会得到低8位,与RGB分量需要的8位正好符合(对于“(J + K) And &HFF”来说,可以实现滚动效果)。

     

      If Not 看明白了 Then Goto 前两段

     

      好!现在打开VB_DIB。(由于24位能直接指定RGB分量,所以这里是用的是24位DIB)

     

      看了前面的“DIB的结构”,是不是有点昏呢?其实DIB也没什么,就是 一个表述位图信息的BITMAPINFO结构 和 一个存储位图数据的数据缓冲区,顶多再用SetDIBitsToDevice绘制,所以VB_DIB与VB_PSet相比只是多了SetDIBitsToDevice、BITMAPINFOHEADER(24位DIB没有调色板,所以用BITMAPINFOHEADER就行)和一些常数的声明而已。由于这个演示程序不需要改变图像大小和色深,所以可以把有关变量作为窗体级变量,再在Form_Load中初始化。由于DIB并没有向系统申请资源(数组的内存是VB分配的,会自动释放),所以不需要写释放代码。

     

      现在来看DrawIt。
      1.虽然可以逐点把坐标映射成地址再写,但是这样效率太低了,可以利用坐标处理的连续性进行优化。
      2.由于我这里用的是数组,所以这里用(数组元素)索引代替地址。
      3.最开始要注意DIB是逆序存储的,要将索引设为最后一行第一个像素的索引。
      4.由于DIB的RGB顺序是B、G、R,所以“MapData(CurIdx + ?)”的顺序是2、1、0。
      5.设置好一个像素的颜色后,要注意把索引改为下个像素的索引。
      6.由于DIB是逆序存储的,移到下一个扫描行是“LineIdx = LineIdx - LineBytes”

     


      “好了,代码看懂了,按F5运行看看效果。”
      “咦?速度好像没快多少啊?”

      这是由于程序在VB环境下是以解释方式运行的,而解释方式对做图像处理所需要大规模循环和大量的算术运算的执行效率很低,所以要编译成(本机代码)exe再运行。此时还要注意编译优化,可以把“高级优化”的所有勾打上,速度可提升20%左右。

     


    五、使用DIBSection和模拟指针

      虽然有GetDIBits/SetDIBits函数,但是DIB与GDI位图之间的数据交换还是很不方便,特别处理过程中需要调用GDI函数来处理的时候。而且就算你不怕麻烦,但这样做的处理效率很低。所以Windows为我们提供了DIBSection。DIBSection是一种特殊的DIB,它除了可以像DIB一样直接对位图数据所占内存进行操作,它还可以选入DC、能用GDI函数绘制,非常灵活。但在VB下使用DIBSection还是有困难的,因为用CreateDIBSection创建DIBSection时,得到的是位图数据的地址,而VB没有指针。

     

      所幸在VB下可以利用SafeArray结构的数组模拟指针。关于模拟指针的原理、方法,网上的资料多的是,比如AdamBear的文章“VB真是想不到系列之四:VB指针葵花宝典之SafeArray”。但这些文章都只是讲一般性的应用,不能像真正的指针一样随意改变地址(他们都是使用CopyMemory改的)。而在图像处理中,由于点运算的频繁,“像真正的指针一样随意改变地址”的功能很重要。

     

      其实“像真正的指针一样随意改变地址”并没有技术难度(对于已经学会模拟指针的人来说),就看想得到不:再用一个数组(设pDataPtr)控制模拟指针(设pData)的地址,这样可以用pDataPtr控制地址,用pData访问数据。

     

      由于需要建立两个模拟指针,导致代码写起来比较烦,于是我想写个“模拟指针代码生成器”(在(ImgTest.zip的)MakeSAPtr目录中)。虽然模拟指针建立方法有很多种,但是大多需要临时变量来保存地址,不适合批量生成,所以我只有改成这种方法(指“模拟指针代码生成器”自动生成的那种)。这种方法也许对 通过看AdamBear的文章学模拟指针的人 会造成困惑,这是因为AdamBear在说明VarPtrArray返回的值是SafeArray的地址的地址时没讲全:VarPtrArray返回的值 是 VB存储数组信息的变量的地址,可通过改变该变量的值使数组用另一个SafeArray。

     

      现在来看VB_DIB_Ptr:
      1.为了演示DIBSection能够像HBITMAP一样,我在Form_Load中创建了DC、将DIBSection选入DC。同时为了释放,Form_UnLoad中写了释放代码。
      2.在DrawIt中,注意处理部分的代码并没有与VB_DIB差多少,只不过把索引计算改为地址运算而已。

     

    展开全文
  • 数字图像处理的框架程序,DIP,基于VB

空空如也

空空如也

1 2 3 4 5 ... 17
收藏数 337
精华内容 134
关键字:

vb图像处理程序