2017-05-31 19:31:27 sinat_36246371 阅读数 988
  • 绘制文字、路径、图像、模式

    本阶段主要带领同学们完成以下几个方面的学习:文件、目录、输入输出、典型案例演示,QT自定义外观设计、样式表设计与实现、绘制模式、典型案例演示,XMl原理、接口设计、SAX、DOM等,数据库基本原理、Sqlite、mysql...

    9012课时 0分钟 12人学习 李浩林
    免费试看

数字图像处理是计算机视觉,视频语义分析的基础知识。要对数字图像进行处理,比如调整灰度级,图像增强,图像模糊等等操作,首先要对图像进行读写操作。

用Java对数字图像进行读写比较简单,用ImageIO.read读,用ImageIO.write写。

import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

public class ImageRW {
    public static void main(String args[]) throws IOException {
        int width = 963;
        int height = 640;
        BufferedImage image = null;
        try {
            File input_file = new File("F:\\in.jpg"); //image file path
            image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            image = ImageIO.read(input_file);

            File output_file = new File("F:\\out.jpg");
            ImageIO.write(image, "jpg", output_file);
        } catch (IOException e) {
            System.out.println("Error: " + e);
        }
    }
}

上述代码相当于实现了一个图像复制功能,其中有一行代码是这样写的:

BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)

这一行意思就是说图像在内存中的表现形式,在这里我们用8位的Alpha, Red,Green与Blue表示图像的像素点。

2019-10-17 16:23:15 Vichael_Chan 阅读数 1428
  • 绘制文字、路径、图像、模式

    本阶段主要带领同学们完成以下几个方面的学习:文件、目录、输入输出、典型案例演示,QT自定义外观设计、样式表设计与实现、绘制模式、典型案例演示,XMl原理、接口设计、SAX、DOM等,数据库基本原理、Sqlite、mysql...

    9012课时 0分钟 12人学习 李浩林
    免费试看

文档下载:

1.前言

通过计算机处理的图像一般称为数字图像,它与传统照片不同,它可以通过某种软件被任意修改和编辑。但他又与传统照片有密切联系,因为好多图像信息大都由照片扫描而来,另外,离开了照相机的镜头,数字图像也会濒临枯竭。数字图像根据其不同特性,可分为两类:向量图(Vector)和点阵图(又称光栅图(Raster))。

  • 向量图不是通过扫描而来,是利用诸如CorelDRAW 、Adobe Illustrator、FreeHand、AutoCAD这样的软件绘制而成,它记录的是所绘对象的几何形状、线条粗细和色彩等,所占的存储空间很小。但它的缺点是不易制作色彩丰富的图像,而且绘制出来的图像不是很逼真,同时也不宜在不同的软件间进行交换。
  • 点阵图有许多点组成,这些点称为像素。它在保存时需记录每个像素的色彩,占用的空间较大,其缺点是在缩放或旋转时会出现失真。随着计算机技术的发展,很多软件都可同时处理向量图形与位图
  • 但是当我们处理完后用什么格式存储呢?这就是本文要阐明的主要方面。图像格式是指计算机图像信息的存储格式。同一幅图像可以用不同的格式存储,但不同格式之间所包含的图像信息并不完全相同,其图像质量也不同,文件大小也有很大差别。

2.几种常见图像格式及特点

1.BMP( .bmp)
bmp是Windows及OS/2中的标准图像文件格式,己成为PC机Windows系统中事实上的工业标准.有压缩和不压缩两种形式。它以独立于设备的方法描述位图,可用非压缩格式存储图像数据,解码速度快.支持多种图像的存储,常见的各利,PC图形图像软件都能对其进行处理。该格式支持1—24位颜色深度,使用的颜色模式可为RGB、索引颜色、灰度和位图等,与设备无关。在PhotoShop中,最多可以使用16M 的色彩渲染bmp图像。

2.JPEG( * .jpg、* .jpe)
JPEG(联合图像专家组)标准的产物,该标准由ISO 与CCI TT(国际电报电话咨询委员会)共同制定,是面向连续色调静止图像的一种压缩标准。由于其高效的压缩效率和标准化要求,目前已广泛用于彩色传真、静止图像、电话会议、印刷及新闻图片的传送上。但那些被删除的资料无法在解压时还原,所以ipeg文件并不适合放大观看,存在一定程度失真,输出成印刷品时品质也会受到影响,因此.制作印刷品时最好不要选择此格式。同样一幅画面,用jpeg格式储存的文件是其它类型图形文件的1/10~ 1/20。一般情况下,jpeg文件只有几十KB,而色彩数最高可达到24位,所以它被广泛运用在Internet—k,以节约宝贵的网络传输资源。JPEG格式支持RGB、CMYK 和灰度颜色模式,但不支持Alpha通道。该格式主要用于图像预览和制作HTMI 网页。

3.GIF( *.gif)
该格式由Compuserver公司创建.存储色彩最高只能达到256种,仅支持8位图像文件。在颜色深度和图像大小上,gif类似于pcx:在结构上,gif类似于tif。正因为它是经过压缩的图像文件格式,所以大多用在网络传输上和Internet的HTMI 网页文档中.速度要比传输其它图像文件格式快得多。它的最大缺点是最多只能处理256种色彩,故不能用于存储真彩色的图像文件,但其G1F89a格式能够存储成背景透明的形式,并且可以将数张图存成一个文件,从而形成动画效果。

4.PNG( *.png)
便携式网络图形是一种无损压缩的位图片形格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小。

PNG同时还支持真彩和灰度级图像的Alpha通道透明度。 最高支持24位真彩色图像以及8位灰度图像。支持Alpha通道的透明/半透明特性。支持图像亮度的Gamma校准信息。支持存储附加文本信息,以保留图像名称、作者、版权、创作时间、注释等信息。

5.TIFF( *.tiff)
Tiff格式是由Aldus为Macintosh机开发的一种图像文件格式,最早流行于Macintosh,现在Windows上主流的图像应用程序都支持该格式。目前,它是Macintosh和PC机上使用最广泛的位图格式,在这两种硬件平台上移植tiff图像十分便捷,大多数扫描仪也都可以输出tiff格式的图像文件。该格式支持的色彩数最高可达16M 种。其特点是:存储的图像质量高,但占用的存储空间也非常大,其大小是相应gIf图像的3倍,jpeg图像的10倍:表现图像细微层次的信息较多,有利于原稿阶调与色彩的复制。该格式有压缩和非压缩两种形式,其中压缩形式使用的是LZW (I empel—ziv—Welch)无损压缩方案。在PhotoShop中.tiff格式能够支持24个通道。它是除PhotoShop自身格式(即psd和pdd)外唯一能够存储多个通道的文件格式。唯一的不足之处是由于tiff独特的可变结构,所以对tiff文件解压缩非常困难。tiff文件被用来存储一些色彩绚丽、构思奇妙的贴图文件, 它将3DS、Macintosh、PhotoShop有机地结合在一起。该格式支持RGB、CMYK、I ab、Indexed Color、位图和灰度颜色模式。

6.PSD(* .psd)
psd是PhotoShop中使用的一种标准图形文件格式,可包括层、通道和颜色模式等信息,且该格式是唯一支持全部颜色模式的图像格式。psd文件能够将不同的物件以层(Layer)的方式来分离保存,便于修改和制作各种特殊效果。pdd和psd一样,都是PhotoShop软件中专用的一种图形文件格式,能够保存图像数据的每一个细小部分.包括层、附加的蒙版、通道以及其它内容,而这些内容在转存成其它格式时将会丢失。另外.因为这两种格式是PhotoShop 支持的自身格式文件,所以PhotoShop能以比其它格式更快的速度打开和存储它们。在保存图像时,若图像中含有层信息,则必须以PSD 格式保存。但是由于PSD格式保存的信息较多,因此.其文件非常庞大。

7.CDR(*.cdr)
CDR格式是著名的图形设计软件CoreDRAW的专用格式,属于矢量图,其最大的优点是图像占用内存较小,便于处理。

3.其他图像格式

1.PCX( *.pcx)
Pcx 格式最早是由Zsoft公司的PCPaintbrush图形软件所支持的一种经过压缩的PC位图文件格式。后来,Microsoft将PCPaintbrush移植到windows环境中,pcx图像格式也就得到了更多的图形图像处理软件的支持。该格式支持的颜色数从最早的16色发展到目前的1677万色。它采用行程编码方案进行压缩,带有一个128字节的文件头。该格式比较简单,适合保存索引和线画稿模式图像。其不足之处是它只有一个颜色通道。PCX格式支持I一24位格式颜色深度以及RGB、索引颜色、灰度和位图颜色模式。

2.TGA(Traga Format)
tga是True Vision公司为其显示卡开发的一种图像文件格式,创建时间较早.最高色彩数可达32位,其中包括8位Alpha通道用于显示实况电视。该格式已经被广泛应用于PC机的各个领域,而且该格式文件使得Windows与3DS相互交换图像文件成为可能。该格式支持带一个单独Alpha通道的32位RGB文件.和不带Alpha通道的索引颜色模式、灰度模式、16位和24位RGB文件。以该格式保存文件时,可选择颜色深度。

3.EPS( * .eps)
此格式为压缩的PostScript格式,是为在PostScript打印机上输出图像开发的。在PostScrpt图形打印机上能打印出高品质的图形图像,最高能表示32位图形图像。该格式分为PhotoShop EPS格式(Adobelllustrator Eps)和标准EPS格式。其中标准EPS格式又可分为图形格式和图像格式。值得注意的是,在PhotoShop中只能打开图像格式的EPS文件。EPS格式包含两个部分:第一部分是屏幕显示的低解析度影像,方便影像处理时的预览和定位:第二部分包含各个分色的单独资料。eps文件以DCS/CMYK 形式存储.文件中包含CMYK 四种颜色的单独资料,可以直接输出四色网片。其最大优点是可以在排版软件中以低分辨率预览,而在打印时以高分辨率输出。eps格式还有许多缺陷:

  • 首先,eps格式存储图像效率特别低:
  • 其次,eps格式的压缩方案也较差,一般同样的图像经tif的LZW 压缩后要比eps的图像小3到4倍。

4.RAW(* .raw)
如果图像需要在不同的平台上被不同的应用程序所使用,而对这些平台又不熟悉,那么可以试试RAW 文件格式。该格式支持带Alpha通道的CMYK、ROB 和灰度模式,和不带Alpha通道的CMYK、RGB和灰度模式,和不带Alpha多通道、I ab、索引颜色和双色调模式。

5.Film Strip( *.flm)
filmstrip即幻灯片.它是Prermiere中的一种输出文件格式。这种格式的图像只能在Photoshop中打开、修改和保存,而不能将其它格式的图像以FI M 格式保存。此外.如果在Photoshop中更改了filmstrip文件的大小,则这幅图片就不能再存回filmstrip格式了.也就不能再返回Premiere了。

6.PICT( * .pic、* .pct)
Dic文件格式主要应用的Mac机上,也可在安装了Quick Time的PC机上使用。该格式的文件不适用于打印,经常用于多媒体项目。pic也是Mac应用软件用于图像显示的格式之一, 其特点是能够对具有大块相同颜色的图像进行有效压缩。该格式支持RGB、索引颜色、灰度和位图模式,在RGB模式下还支持Alpha通道。

7.PDF( *.pdf)
该格式是由Adobe公司推出的专为线上出版而制定的,它以PostScript Level2语言为基础,因此,可以覆盖矢量式图象和点阵式图象,并且支持超级连接。该格式可以保存多页信息,其中可以包含图形和文本。此外,由于该格式支持超级链接,因此是网络下载经常使用的文件格式。PDF格式支持RGB、索引颜色、CMYK、灰度、位图和I ab颜色模式,但不支持Alpha通道。

8.PhotoCD( *.pCd)
pcd是一种Photo CD文件格式,由Kodak公司开发,其它软件系统只能对其进行读取。该格式主要用于存储CD- ROM 上的彩色扫描图像,它使用YCC色彩模式定义图像中的色彩。YCC色彩模式是CIE色彩模式的一个变种。CIE色彩空间是定义所有人眼能观察到的颜色的国际标准。YCC和CIE色彩空间包含比显示器和打印设备的RGB色和CMYK色多得多的色彩。Photo CD图像大多具有非常高的质量.将一卷胶卷扫描为Photo CD文件的成本并不高,但扫描的质量还要依赖于所用胶卷的种类和扫描仪使用者的操作水平,该格式只能在Photoshop中打开,而不能保存。

4.几种格式所适用的领域和场所

每种图像格式都有自己的特点,有的图像质量好,包含信息多,但是存储空间大;有的压缩率较高,图像完整,但空间较少。至于在什么场合使用哪种格式的图像应由每种格式的特点来决定。
1.从其用途来说:

  • eps格式的图像质量较高,适合在PostScript打印机上输出图像,而且可以在排版软件中以低分辨率预览,在打印时以高分辨率输出。
  • pdf格式支持超级连接,可用于网络下载。
  • tiff格式的图像信息最紧凑,儿乎所有的扫描仪都支持这一格式。
  • gif格式可以用于通信领域和Internet的HTMI 网页文档中。
  • jPg主要用于图像预览和制作HTMI 网页,但不适合制作印刷品。线上出版可用Adobe公司推出的专门格式。
  • pdf格式支持超级连接。

2.从几种格式适用的软件来说,有的格式适合在不同的软件上运用,有的是某种软件的专用格式:

  • psd格式是Photoshop生成的图像格式,只能在Photoshop中使用。
  • flm格式是Adobe Premiere动画软件使用的格式,而且只能在Photoshop中打开、修改和保存。
  • tif则是多数图像软件都支持的格式,它得到了Macintosh和IBM 等各种平台上软件的广泛支持。
  • bmp则是专门为Windows3.x及后来版本的“画笔”或“画图”建立的。
  • raw适和于在不同的平台上被不同的应用程序所使用。
  • pcd格式只能在Photoshop中打开.而不能保存。
  • 另外,在word中,pcx、tga、raw、pdf、psd等格式的文件都不能打开:在authorware中.除了pct和tiff格式中以jpeg格式存储的文件不能打开以外,其余的都能打开。

3.从几种格式所支持的图像的色彩模式来说:

  • pcX格式支持1— 24位格式颜色深度以及RGB、索引颜色、灰度和位图颜色模式。
  • tif格式支持RGB、CMYK、Lab、IndexedColor、位图和灰度颜色模式。
  • bmp格式支持1— 24颜色深度,适用的颜色模式可为RGB、索引颜色、灰度和位图等。
  • tga格式支持带一个单独Alpha通道的32位RGB文件,和不带Alpha通道的索引颜色模式、灰度模式、16位和24位RGB文件。
  • eps支持Photoshop的所有颜色模式,但不支持Alpha通道。
  • gif格式仅支持8位图像文件。
  • jpeg格式支持RGB、CMYK和灰度颜色模式,但不支持Alpha通道。
  • raw 格式支持带Alpha通道的CMYK、RGB和灰度颜色模式,和不带Alpha通道的多通道、Lab、索引颜色和双色调模式。
  • psd是唯一支持全部颜色模式的图像格式。
  • pic格式支持RGB、索引颜色、灰度和位图模式,在RGB模式下还支持Alpha通道。
  • pdf格式支持RGB索引颜色、CMYK、灰度、位图和I ab颜色模式,但不支持Alpha通道。
2016-03-03 14:39:15 EbowTang 阅读数 11183
  • 绘制文字、路径、图像、模式

    本阶段主要带领同学们完成以下几个方面的学习:文件、目录、输入输出、典型案例演示,QT自定义外观设计、样式表设计与实现、绘制模式、典型案例演示,XMl原理、接口设计、SAX、DOM等,数据库基本原理、Sqlite、mysql...

    9012课时 0分钟 12人学习 李浩林
    免费试看
作者:mimi

图像的变换 

① fft2:fft2函数用于数字图像的二维傅立叶变换,如:i=imread('104_8.tif');

j=fft2(i);

②ifft2::ifft2函数用于数字图像的二维傅立叶反变换,如:

 i=imread('104_8.tif');

 j=fft2(i);

 k=ifft2(j);


噪声生成函数

① imnoise:用于对图像生成模拟噪声,如:

 i=imread('104_8.tif');

 j=imnoise(i,'gaussian',0,0.02);%模拟高斯噪声


定义滤波器

② fspecial:用于产生预定义滤波器,如:

h=fspecial('sobel');%sobel水平边缘增强滤波器

h=fspecial('gaussian');%高斯低通滤波器

h=fspecial('laplacian');%拉普拉斯滤波器

h=fspecial('log');%高斯拉普拉斯(LoG)滤波器

h=fspecial('average');%均值滤波器

 

图像的增强

①直方图:imhist函数用于数字图像的直方图显示,如:

i=imread('104_8.tif');

imhist(i);

②直方图均化:histeq函数用于数字图像的直方图均化,如:

i=imread('104_8.tif');

j=histeq(i);

③对比度调整:imadjust函数用于数字图像的对比度调整,如:i=imread('104_8.tif');

j=imadjust(i,[0.3,0.7],[]);

④对数变换:log函数用于数字图像的对数变换,如:

i=imread('104_8.tif');

j=double(i);

k=log(j);

⑤基于卷积的图像滤波函数:filter2函数用于图像滤波,如:i=imread('104_8.tif');

h=[1,2,1;0,0,0;-1,-2,-1];

j=filter2(h,i);

⑥线性滤波:利用二维卷积conv2滤波, 如:

i=imread('104_8.tif');

h=[1,1,1;1,1,1;1,1,1];

h=h/9;

j=conv2(i,h);

⑦中值滤波:medfilt2函数用于图像的中值滤波,如:

i=imread('104_8.tif');

j=medfilt2(i);

⑧锐化

(1)利用Sobel算子锐化图像, 如:

i=imread('104_8.tif');

h=[1,2,1;0,0,0;-1,-2,-1];%Sobel算子

j=filter2(h,i);

(2)利用拉氏算子锐化图像, 如:

i=imread('104_8.tif');

j=double(i);

h=[0,1,0;1,-4,0;0,1,0];%拉氏算子

k=conv2(j,h,'same');

m=j-k;

 

图像边缘检测

①sobel算子 如:

i=imread('104_8.tif');

j = edge(i,'sobel',thresh)

②prewitt算子 如:

i=imread('104_8.tif');

j = edge(i,'prewitt',thresh)

③roberts算子  如:

i=imread('104_8.tif');

j = edge(i,'roberts',thresh)

④log算子  如:

i=imread('104_8.tif');

j = edge(i,'log',thresh)

⑤canny算子 如:

i=imread('104_8.tif');

j = edge(i,'canny',thresh)

⑥Zero-Cross算子 如:

i=imread('104_8.tif');

j = edge(i,'zerocross',thresh)

 

形态学图像处理

①膨胀:是在二值化图像中“加长”或“变粗”的操作,函数imdilate执行膨胀运算,如:

a=imread('104_7.tif'); %输入二值图像

b=[0 1 0;1 1 1;0 1 0];

c=imdilate(a,b);

②腐蚀:函数imerode执行腐蚀,如:

a=imread('104_7.tif'); %输入二值图像

b=strel('disk',1);

c=imerode(a,b);

③开运算:先腐蚀后膨胀称为开运算,用imopen来实现,如:

 a=imread('104_8.tif');

b=strel('square',2);

c=imopen(a,b);

④闭运算:先膨胀后腐蚀称为闭运算,用imclose来实现,如:

 a=imread('104_8.tif');

b=strel('square',2);

c=imclose(a,b);

 

===========================================================

Matlab 图像处理相关函数命令大全

一、通用函数:

colorbar  显示彩色条

语法:colorbar colorbar('vert') colorbar('horiz') colorbar(h) h=colorbar(...) colorbar(...,'peer',axes_handle)

getimage 从坐标轴取得图像数据

语法:A=getimage(h) [x,y,A]=getimage(h) [...,A,flag]=getimage(h) [...]=getimage


imshow 显示图像

语法:imshow(I,n) imshow(I,[low high]) imshow(BW) imshow(X,map) imshow(RGB) imshow(...,display_option) imshow(x,y,A,...) imshow filename h=imshow(...)


montage 在矩形框中同时显示多幅图像

语法:montage(I) montage(BW) montage(X,map) montage(RGB) h=montage(...)


immovie 创建多帧索引图的电影动画

语法:mov=immovie(X,map) mov=immovie(RGB)


subimage 在一副图中显示多个图像

语法:subimage(X,map) subimage(I) subimage(BW)   subimage(RGB) subimage(x,y,...) subimage(...)


truesize 调整图像显示尺寸

语法:truesize(fig,[mrows mcols]) truesize(fig)


warp 将图像显示到纹理映射表面

语法:warp(X,map) warp(I ,n) warp(z,...) warp(x,y,z,...)   h=warp(...)


zoom 缩放图像

语法:zoom on zoom off zoom out zoom reset zoom zoom xon zoom yon zoom(factor) zoom(fig,option)

 

二、图像文件I/O函数命令

imfinfo  返回图形图像文件信息

语法:info=imfinfo(filename,fmt) info=imfinfo(filename)


imread  从图像文件中读取(载入)图像

语法:A=imread(filename,fmt) [X,map]=imread(filename,fmt) [...]=imread(filename) [...]=imread(URL,...) [...]=imread(...,idx) (CUR,ICO,and TIFF only) [...]=imread(...,'frames',idx) (GIF only) [...]=imread(...,ref) (HDF only) [...]=imread(...,'BackgroundColor',BG) (PNG only) [A,map,alpha] =imread(...) (ICO,CUR,PNG only)


imwrite  把图像写入(保存)图像文件中

语法:imwrite(A,filename,fmt) imwrite(X,map,filename,fmt) imwrite(...,filename) imwite(...,Param1,Val1,Param2,Val2...)


imcrop  剪切图像

语法:I2=imcrop(I) X2=imcrop(X,map) RGB2=imcrop(RGB) I2=imcrop(I,rect) X2=imcrop(RGB,rect) [...]=imcrop(x,y,...) [A,rect]=imcrop(...) [x,y,A,rect]=imcrop(...)


imresize  改变图像大小

语法:B=imresize(A,m,method)


imrotate  旋转图像

语法:B=imrotate(A,angle,method) B=imrotate(A,angle,method,'crop')

 

三、像素和统计处理函数

corr2  计算两个矩形的二维相关系数

语法:r=corr2(A,B)


imcontour 创建图像数据的轮廓图

语法:imcontour(I,n) imcontour(I,v) imcontour(x,y,...) imcontour(...,LineSpec) [C,h] =imcontour(...)


imfeature  计算图像区域的特征尺寸

语法:stats=imfeature(L,measurements) stats=imfeature(L,measurements,n)


imbist  显示图像数据的柱状图


impixel 确定像素颜色值

语法:P=impixel(I) P=impixel(X,map) P=impixel(RGB) P=impixel(I,c,r) P=impixel(X,map,c,r) P=impixel(RGB,c,r) [c,r,P]=impixel(...) P=impixel(x,y,I,xi,yi) P=impixel(x,y,RGB,xi,yi) P=impixel(x,y,X,map,xi,yi)

[xi,yi,P]=impixel(x,y,...)


improfile 沿线段计算剖面图的像素值

语法:c=improfile c=improfile(n) c=improfile(I,xi,yi) c=improfile(I,xi,yi,n)   [cx,cy,c]=improfile(...) [cx,cy,c,xi,yi]=improfile(...) [...]=improfile(x,y,I,xi,yi)   [...]=improfile(x,y,I,xi,yi,n) [...]=improfile(...,method)


mean2 计算矩阵元素的平均值

语法:B=mean2(A)


pixval  显示图像像素信息

语法:pixval on


std2 计算矩阵元素的标准偏移

语法:b=std2(A)

 

四、图像分析函数:

edge 图像边缘检测

语法:BW=edge(I,'sobel') BW=edge(I,'sobel',thresh) BW=edge(I,'sobel',thresh,direction) [BW,thresh]=edge(I,'sobel',...) BW=edge(I,'prewitt') BW=edge(I,'prewitt',thresh) BW=edge(I,'prewitt',thresh,direction)

[BW,thresh]=edge(I,'prewitt',...)   BW=edge(I,'roberts') BW=edge(I,'roberts',thresh) [BW,thresh]=edge(I,'roberts',...) BW=edge(I,'log') BW=edge(I,'log',thresh) BW=edge(I,'log',thresh,sigma) [BW,threshold]=edge(I,'log',...) BW=edge(I,'zerocross',thresh,h) [BW,thresh]=edge(I,'zerocross',...)

BW=edge(I,'canny') BW=edge(I,'canny',thresh) BW=edge(I,'canny',thresh,sigma) [BW,threshold]=edge(I,'canny',...)

qtgetblk  获取四叉树分解的块值

语法:[vals,r,c]=qtgetblk(I,S,dim) [vals,idx]=qtgetblk(I,S,dim)

qtsetblk 设置四叉树分解中的块值

语法:J=qtsetblk(I,S,dim,vals)

 

五、图像增强函数

histeq 用柱状图均等化增强对比

语法:J=histeq(I,hgram) J=histeq(I,n) [J,T]=histeq(I,...) newmap=histeq(X,map,hgram) newmap=histeq(X,map)

imadjust 调整图像灰度值或颜色映像表

语法:J=imadjust(I,[low_in ,high_in]),[low_out ,high_out],gamma) newmap=imadjust(map,[low_in ,high_in]),[low_out ,high_out],gamma) RGB2=imadjust(RGB1,...)

imnoise 增强图像的渲染效果

语法:J=imnoise(I,type) J=imnoise(I,type,parameters)

medfilt2 进行二维中值过滤

语法:B=medfilt2(A,[m n]) B=medfilt2(A) B=medfilt2(A,'indexed',...)

ordfilt2 进行二维统计顺序过滤

语法:B=ordfilt2(A,order,domain) B=ordfilt2(A,order,domain,S) B=ordfilt2(...,padopt)

wiener2 进行二维适应性去噪过滤处理

语法:J=wiener2(I,[m  n],noise) [J,noise]=wiener2(I,[m n])

 

六、线性滤波函数

conv2 进行二维卷积操作

语法:C=conv2(A,B) C=conv2(hcol,hrow,A) C=conv2(...,'shape')

convmtx2 计算二维卷积矩阵

语法:T=convmtx2(H,m,n) T=convmtx2(H,[m n])

convn 计算n维卷积

语法:C=convn(A,B) C=convn(A,B,'shape')

filter2 进行二维线性过滤操作

语法:Y=filter2(h,X) Y=filter2(h,X,shape)

fspecial 创建预定义过滤器

语法:h=fspecial(type) h=fspecial(type,parameters)

 

七、线性二维滤波设计函数

freqspace 确定二维频率响应的频率空间

语法:[f1,f2]=freqspace(n) [f1,f2]=freqspace([m n]) [x1 ,y1]=freqspace(...,'meshgrid') f=freqspace(N) f=freqspace(N,'whole')

freqz2 计算二维频率响应

语法:[H,f1,f2]=freqz2(h,n1,n2) [H,fi,f2]]=freqz2(h,[n2,n1]) [H,fi,f2]]=freqz2(h,f1,f2]) [H,fi,f2]]=freqz2(h) [...]=freqz2(h,...,[dx dy]) [...]=freqz2(h,...,dx) freqz2(...)

fsamp2 用频率采样法设计二维FIR过滤器

语法:h=fsamp2(Hd) h=fsamp2(f1,f2,Hd,[m n])

ftrans2 通过频率转换设计二维FIR过滤器

语法:h=ftrans2(b,t) h=ftrans2(b)

fwind1 用一维窗口方法设计二维FIR过滤器

语法:h=fwind1(Hd,win) h=fwind1(Hd,win1,win2) h=fwind1(f1,f2,Hd,...)

fwind2 用二维窗口方法设计二维FIR过滤器

语法:h=fwind2(Hd,win) h=fwind2(f1,f2,Hd,win)

 

八、图像变换函数

dct2 进行二维离散余弦变换(反余弦变换用idct2)

语法:B=dct2(A) B=dct2(A,m.n) B=dct2(A,[m n])

dctmtx 计算离散余弦傅立叶变换

语法:D=dctmtx(n)

fft2 进行二维快速傅立叶变换(反变换用ifft2)

语法:Y=fft2(X) Y=fft2(X,m,n)

fftn 进行n维快速傅立叶变换(反变换用ifftn)

语法:Y=ffn(X) Y=fftn(X,siz)

fftshift 快速傅立叶变换的DC组件移到光谱中心

语法:Y=fftshift(X) Y=fftshift(X,dim)

iradon 进行反radon变换

语法:I=iradon(P,theta) I=iradon(P,theta,interp,filter,d,n) [I,h]=iradon(...)

phantom 产生一个头部幻影图像

语法:P=phantom(def,n) P=phantom(E,n) [P,E]=phantom(...)

radon 计算radon变换

语法:R=radon(I,theta) [R,xp]=radon(...)

 

九、边沿和块处理函数

bestblk 确定进行块操作的块大小

语法:siz=bestblk([m n],k) [mb,nb]=bestblk([m n],k)

blkproc 实现图像的显示块操作

语法:B=blkproc(A,[m n]),fun) B=blkproc(A,[m n],fun,P1,P2,...) B=blkproc(A,[m n],[mborder nborder],fun,...)

col2im 将矩阵的列重新组织到块中

语法:A=col2im(B,[m n],[mm nn],block_type) A=col2im(B,[m n],[mm nn])

colfilt 利用列相关函数进行边沿操作

语法:B=colfilt(A,[m n],block_type,fun) B=colfilt(A,[m n],block_type,fun,P1,P2,...) B=colfilt(A,[m n],[mblock nblock],...) B=colfilt(A,'indexed',...)

im2col 重调图像块为列

语法:B=im2col(A,[m n],block_type) B=im2col(A,[m n]) B=im2col(A,'indexed',...)

nlfilter 进行边沿操作

语法:B=nlfilter(A,[m n],fun) B=nlfilter(A,[m n],fun,P1,P2,...) B=nlfilter(A,'indexed',...)

 

十、二进制图像操作函数

applylut 在二进制图像中利用lookup表进行行边沿操作

语法:A=applylut(BW,LUT)

bwarea 计算二进制图像对象的面积

语法:total=bwarea(BW)

bweuler 计算二进制图像的欧拉数

语法:eul=bweuler(BW)

bwfill 填充二进制图像的背景色

语法:BW2=bwfill(BW1,c,r,n) BW2=bwfill(BW1,n) [BW2,idx]=bwfill(...) BW2=bwfill(x,y,BW1,xi,yi,n) [x,y,BW2,idx,xi,yi]=bwfill(...)   [BW2,idx]=bwfill(BW1,'holes',n)

bwlabel 标注二进制图像中已连接的部分

语法:L=bwlabel(BW,n) [L,num]=bwlabel(BW,n)

bwmorph 提取二进制图像的轮廓

语法:BW2=bwmorph(BW1,operation) BW2=bwmorph(BW1,operation,n)

bwperim 计算二进制图像中对象的周长

语法:BW2=bwperim(BW1) BW2=bwperim(BW1,CONN)

bwselect 在二进制图像中选择对象

语法:BW2=bwselect(BW1,c,r,n) BW2=bwselect(BW1,n) [BW2,idx]=bwselect(...) BW2=bwselect(x,y,BW1,xi,yi,n) [x,y,BW2,idx,xi,yi]=bwselect(...)

dilate 放大二进制图像

语法:BW2=dilate(BW1,SE) BW2=dilate(BW1,SE,alg) BW2=dilate(BW1,SE,...,n)

erode 弱化二进制图像的边界

语法:BW2=erode(BW1,SE) BW2=erode(BW1,SE,alg) BW2=erode(BW1,SE,...,n)

makelut 创建一个用于applylut函数的lookup表

语法:lut=makelut(fun,n) lut=makelut(fun,n,P1,P2,...)

 

十一、区域处理函数

roicolor 选择感兴趣的颜色区

语法:BW=roicolor(A,low,high) BW=rocicolor(A,v)

roifill 在图像的任意区域中进行平滑插补

语法:J=roifill(I,c,r) J=roifill(I) J=roifill(I,BW) [J,BW]=roifill(...) J=roifill(x,y,I,xi,yi) [x,y,J,BW,xi,yi]=roifill(...)

roifilt2 过滤敏感区域

语法:J=roifilt2(h,I,BW) J=roifilt2(I,BW,fun) J=roifilt2(I,BW,fun,P1,P2,...)

roipoly 选择一个敏感的多边形区域

语法:BW=roipoly(I,c,r) BW=roipoly(I) BW=roipoly(x,y,I,xi,yi) [BW,xi,yi]=roipoly(...) [x,y,BW,xi,yi]=roipoly(...)

 

十二、颜色映像处理函数

brighten 增加或降低颜色映像表的亮度

语法:brighten(beta) brighten(h,beta) newmap=brighten(beta) newmap=brighten(cmap,beta)

cmpermute 调整颜色映像表中的颜色

语法:[Y,newmap]=cmpermute(X,map) [Y,newmap]=cmpermute(X,map,index)

cmunigue 查找颜色映像表中特定的颜色及相应的图像

语法:[Y,newmap]=cmunigue(X,map) [Y,newmap]=cmunigue(RGB) [Y,newmap]=cmunique(I)

imapprox 对索引图像进行近似处理

语法:[Y,newmap]=imapprox(X,map,n)   [Y,newmap]=imapprox(X,map,tol) Y=imapprox(X,map,newmap) [...]=imapprox(...,dither_option)

rgbplot 划分颜色映像表

语法:rgbplot(cmap)

 

十三、颜色空间转换函数

hsv2rgb 转换HSV值为RGB颜色空间:M=hsv2rgb(H)

ntsc2rgb 转换NTSC值为RGB颜色空间:rgbmap=ntsc2rgb(yiqmap) RGB=ntsc2rgb(YIQ)

rgb2hsv 转换RGB值为HSV颜色空间:cmap=rgb2hsv(M)

rgb2ntsc 转换RGB值为NTSC颜色空间:yiqmap=rgb2ntsc(rgbmap) YIQ=rgb2ntsc(RGB)

rgb2ycbcr 转换RGB值为YCbCr颜色空间:ycbcrmap=rgb2ycbcr(rgbmap) YCBCR=rgb2ycbcr(RGB)

ycbcr2rgb 转化YCbCr值为RGB颜色空间:rgbmap=ycbcr2rgb(ycbcrmap) RGB=ycbcr2rgb(YCBCR)

 

十四、图像类型和类型转换函数

dither 通过抖动增加外观颜色分辨率转换图像

语法:X=dither(RGB,map) BW=dither(I)

gray2ind 转换灰度图像为索引图像

语法:[X,map]=gray2ind(I,n) [X,map]=gray2ind(BW,n)

grayslice 从灰度图像为索引图像

语法:X=grayslice(I,n) X=grayslice(I,v)

im2bw 转换图像为二进制图像

语法:BW=im2bw(I,level) BW=im2bw(X,map,level) BW=im2bw(RGB,level)

im2double 转换图像矩阵为双精度型

语法:I2=im2double(I1) RGB2=im2double(RGB1) I=im2double(BW) X2=im2double(X1,'indexed')

double 转换数据为双精度型

语法:double(X)

unit8 、unit16转换数据为8位、16位无符号整型: i=unit8(x) i=unit16(x)

im2unit8 转换图像阵列为8位无符号整型

语法:I2=im2unit8(I1) RGB2=im2unit8(RGB1) I=im2unit8(BW) X2=im2unit8(X1,'indexed')

im2unit16 转换图像阵列为16位无符号整型

语法:I2=im2unit16(I1) RGB2=im2unit16(RGB1) I=im2unit16(BW) X2=im2unit16(X1,'indexed')

ind2gray 把检索图像转化为灰度图像

语法:I=ind2gray(X,map)

ind2rgb  转化索引图像为RGB真彩图像

语法:RGB=ind2rgb(X,map)

isbw 判断是否为二进制图像

语法:flag=isbw(A)

isgray 判断是否为灰度图像

语法:flag=isgray(A)

isind 判断是否为索引图像

语法:flag=isind(A)

isrgb 判断是否为RGB真彩色图像

语法:flag=isrgb(A)

mat2gray 转换矩阵为灰度图像

语法:I=mat2gray(A,[amin amax]) I=mat2gray(A)

rgb2gray 转换RGB图像或颜色映像表为灰度图像

语法:I=rgb2gray(RGB) newmap=rgb2gray(map)

rgb2ind 转换RGB图像为索引图像

语法:[X,map]=rgb2ind(RGB,tol) [X,map]=rgb2ind(RGB,n) X=rgb2ind(RGB,map) [...]=rgb2ind(...,dither_option)

 

十五、新增图像处理工具箱函数

adapthisteq 限制对比度直方图均衡化: J=adapthisteq(I) J=adapthisteq(I,param1,val1,param2,val2...)

applycform 用于颜色空间变换 out=applyform(I,C)

bwboundaries 描绘二进制图像边界

语法: B=bwboundaries(BW) B=bwboundaries(BW,CONN) B=bwboundaries(BW,CONN,options) [BW,CONN,options] [BL]=bwboundaries(...) [BLNA]=bwboundaries()

bwtraceboundary 描述二进制图像中的物体

B=bwtraceboundary(BW,P,fstep) B=bwtraceboundary(BW,P,fstep,CONN) B=bwtraceboundary(...N,dir)

decorrstrech 对多通道图像进行去相关处理

语法:S=decorrstretch(I) S=decorrstretch(I,TOL)

dicomdict 获取或读取DICOM文件

语法:dicomdict('set',dictionary) dictionary=dicomdict('get')

getline 用鼠标选择ployline

语法:[x,y]=getline(fig) [x,y]=getline(ax) [x,y]=getline [x,y]=getline(...,'closed')

getpts 用鼠标选择像素点

语法:[x,y]=getpts(fig) [x,y]=getpts(ax) [x,y]=getpts

getrect 用鼠标选择矩阵

语法:rect=getrect(fig) rect=getrect(ax) rect=getrect(fig)

iccread 读取ICC剖面

语法:P=iccread(filename)

im2java2d 将图像转换为Java缓冲图像

语法:jimage=im2java2d(I) jimage=im2java2d(X,MAP)

imview 在图像与蓝旗中显示图像

语法:imview(I)   imview(RGB) imview(X,map) imview(I,range) imview(filename) imview(....'InitialMagnification',initial_mag) h=imview(...)    imview close all

ippl 检查IPPL的存在

语法:TF=ippl [TF B]=ippl

iptdemos 显示图像处理工具箱中的索引图像

lab2double、lab2unit16、lab2unit8 将L*a*b数据分别转换为双精度、16位数据、8位数据

makecform 创造一个色彩转换结构

poly2mask 把多边形区域转换成mask区域

语法:BW=poly2mask(x,y,m,n)

unitlut 查找表中A像素值

语法:B=unitlut(A,LUT)

xyz2double、xyz2unit16 将颜色数据从XYZ转换到双精度、16进制。

语法:xyzd=xyz2double(XYZ) xyz16=xyz2unit16(xyz)

2019-05-18 19:14:27 JustDoIt_201603 阅读数 846
  • 绘制文字、路径、图像、模式

    本阶段主要带领同学们完成以下几个方面的学习:文件、目录、输入输出、典型案例演示,QT自定义外观设计、样式表设计与实现、绘制模式、典型案例演示,XMl原理、接口设计、SAX、DOM等,数据库基本原理、Sqlite、mysql...

    9012课时 0分钟 12人学习 李浩林
    免费试看

数字图像:每一个数字图像都是一个像素点矩阵,这个矩阵包含所有像素点的强度值

像素点:最小的图像单元,一张图像由好多的像素点组成。像素就是图像的尺寸

位图:也称点阵图,它是由许多点组成的,这些点称为像素。当许多不同颜色的点组合在一起后,便构成了一副完整的图像。 位图可以记录每一个点的数据信息,从而精确地制作色彩和色调变化丰富的图像。但是,由于位图图像与分辨率有关,它所包含的图像像素数目是一定的,若将图像放大到一定程度后,图像就会失真,边缘出现锯齿。

灰度:表示图像像素明暗程度的数值,也就是黑白图像中点的颜色深度。若灰度级为256,灰度值的范围一般从0到255,白色为255 ,黑色为0。灰度值指的是单个像素点的亮度,灰度值越大表示越亮。灰度级表明图像中不同灰度的最大数量灰度级越大,图像的亮度范围越大。在单色图中, intensity 是 强度  gray scale 是灰度。而强度一般由光源的能量和物体反射能量的比例的乘积决定,所以如果能量很低,颜色就很暗,极限就是能量为0,是黑色,而能量很大,就很亮,最大为255白色。

通道:把图像分解成一个或多个颜色成分;①单通道:一个像素点只需一个数值表示,只能表示灰度,0为黑色; ②三通道:RGB模式,把图像分为红绿蓝三个通道,可以表示彩色,全0表示黑色;③四通道:在RGB基础上加上alpha通道,表示透明度,alpha=0表示全透明

深度:深度即位数(比特数)①位深:一个像素点所占的总位数,也叫像素深度、图像深度等,其中位深 = 通道数 × 每个通道所占位数  ②256色图:n位的像素点可以表示2^n种颜色,称2^n色图,n=8时为256色图 ③8位RGB与8位图:前者的位数指每个通道所占的位数,后者指整个像素点共占的位数,其中8位RGB是一个24位图,也称为真彩

对比度:指不同颜色之间的差别。对比度越大,不同颜色之间的反差越大,即所谓黑白分明,对比度过大,图像就会显得很刺眼。对比度越小,不同颜色之间的反差就越小。对比度=最大灰度值/最小灰度值

亮度:指照射在景物或图像上光线的明暗程度。图像亮度增加时,就会显得耀眼或刺眼,亮度越小时,图像就会显得灰暗。

色相:颜色,调整色相就是调整景物的颜色,例如,彩虹由红、橙、黄、绿、青、蓝、紫七色组成,那么它就有七种色相。顾名思义即各类色彩的相貌称谓,如大红、普蓝、柠檬黄等。色相是色彩的首要特征,是区别各种不同色彩的最准确的标准。事实上任何黑白灰以外的颜色都有色相的属性,而色相也就是由原色、间色和复色来构成的

色调:各种图像色彩模式下原色的明暗程度,级别范围从0到255,共256级色调。例如对灰度图像,当色调级别为255时,就是白色,当级别为0时,就是黑色,中间是各种程度不同的灰色。在RGB模式中,色调代表红、绿、蓝三种原色的明暗程度,对绿色就有淡绿、浅绿、深绿等不同的色调。色调是指色彩外观的基本倾向。在明度、纯度、色相这三个要素中,某种因素起主导作有用,可以称之为某种色调

饱和度:指图像颜色的浓度。饱和度越高,颜色越饱满,即所谓的青翠欲滴的感觉。饱和度越低,颜色就会显得越陈旧、惨淡,饱和度为0时,图像就为灰度图像。可以通过调整电视机的饱和度来进一步理解饱和度的概念。

频率:灰度值变化剧烈程度的指标,是灰度在平面空间上的梯度。低频就是颜色缓慢地变化,也就是灰度缓慢地变化,就代表着那是连续渐变的一块区域,这部分就是低频。 高频就是频率变化快,即相邻区域之间灰度相差很大,这就是变化得快。图像中,一个影像与背景的边缘部位的频率高,即高频显示图像边缘。图像的细节处也是属于灰度值急剧变化的区域,正是因为灰度值的急剧变化,才会出现细节。另外噪声(即噪点)也是这样,在一个像素所在的位置,之所以是噪点,就是因为它与正常的点颜色不一样了,灰度有了快速地变化。固有“图像的低频是轮廓,高频是噪声和细节”。

空域:也叫空间域,即所说的像素域,在空域的处理就是在像素级的处理,如在像素级的图像叠加。通过傅立叶变换后,得到的是图像的频谱。表示图像的能量梯度。

频域: 也叫频率域,任何一个波形都可以分解成多个正弦波之和。每个正弦波都有自己的频率和振幅。所以任意一个波形信号有自己的频率和振幅的集合。频率域就是空间域经过傅立叶变换的信号

图像分辨率:每英寸图像内的像素点数。分辨率越高,像素的点密度越高,图像越逼真。

空间分辨率:图像中可辨别的最小细节的度量,如果一幅图像的尺寸为MxN,表明在成像时采集了MxN个样本,空间分辨率是MxN。

灰度分辨率:在灰度级中可分辨的最小变化,在数字图像处理教程中,灰度分辨率指的是色阶,色阶是表示图像亮度强弱的指数标准,也就是我们说的色彩指数。灰度分辨率指亮度,和颜色无关,但最亮的只有白色,最不亮的只有黑色。

颜色空间(也称为“颜色模型”):描述颜色的三维空间坐标系,一个颜色定义为颜色空间的一个点。

1.灰度模式:“灰度”模式可以表现出丰富的色调,但是也只能表现黑白图像。“灰度”模式图像中的像素是由8位的分辨率来记录的,能够表现出256种色调,从而使黑白图像表现的更完美。灰度模式的图像只有明暗值,没有色相和饱和度这两种颜色信息。其中,0%为黑色,100%为白色,K值是用来衡量黑色油墨用量的。使用黑白和灰度扫描仪产生的图像常以灰度模式显示。

2.位图模式:“位图”模式的图像又叫黑白图像,它用黑、白两种颜色值来表示图像中的像素。其中的每个像素都是用1 bit的位分辨率来记录色彩信息的,占用的存储空间较小,因此它要求的磁盘空间最少。位图模式只能制作出黑、白颜色对比强烈的图像。如果需要将一副彩色图像转换成黑白颜色的图像,必须先将其转换成“灰度”模式的图像,然后再转换成黑白模式的图像,即“位图”模式的图像。

转载于“https://blog.csdn.net/u010608296/article/details/84799265

2013-12-04 17:30:59 wzy198852 阅读数 6771
  • 绘制文字、路径、图像、模式

    本阶段主要带领同学们完成以下几个方面的学习:文件、目录、输入输出、典型案例演示,QT自定义外观设计、样式表设计与实现、绘制模式、典型案例演示,XMl原理、接口设计、SAX、DOM等,数据库基本原理、Sqlite、mysql...

    9012课时 0分钟 12人学习 李浩林
    免费试看



  数字图像处理技术与理论是计算机应用的一个重要领域,许多工程应用都涉及到图像处理,一直有一个强烈的愿望,想系统的写一个关于数字图像处理的讲座,由于工作学习很忙,时至今日才得以实现。

  “图”是物体透射光或反射光的分布,“像”是人的视觉系统对图的接收在大脑中形成的印象或认识。图像是两者的结合。人类获取外界信息是靠听觉、视觉、触觉、嗅觉、味觉等,但绝大部分(约80%左右)来自视觉所接收的图像信息。图像处理就是对图像信息进行加工处理,以满足人的视觉心理和实际应用的需要。简单的说,依靠计算机对图像进行各种目的的处理我们就称之为数字图像处理。早期的数字图像处理的目的是以人为对象,为了满足人的视觉效果而改善图像的质量,处理过程中输入的是质量差的图像,输出的是质量好的图像,常用的图像处理方法有图像增强、复原等。随着计算机技术的发展,有一类图像处理是以机器为对象,处理的目的是使机器能够自动识别目标,这称之为图像的识别,因为这其中要牵涉到一些复杂的模式识别的理论,所以我们后续的讲座只讨论其中最基本的内容。由于在许多实际应用的编程中往往都要涉及到数字图像处理,涉及到其中的一些算法,这也是许多编程爱好者感兴趣的一个内容,我们这个讲座就是讨论如何利用微软的Visual C++开发工具来实现一些常用的数字图像处理算法,论述了图像处理的理论,同时给出了VC实现的源代码。本讲座主要的内容分为基础篇、中级篇和高级篇,具体包含的主要内容有:

  1. 图像文件的格式;

  2. 图像编程的基础-操作调色板;

  3. 图像数据的读取、存储和显示、如何获取图像的尺寸等;

  4. 利用图像来美化界面;

  5. 图像的基本操作:图像移动、图像旋转、图像镜像、图像的缩放、图像的剪切板操作;

  6. 图像显示的各种特技效果;

  7. 图像的基本处理:图像的二值化、图像的亮度和对比度的调整、图像的边缘增强、如何得到图像的直方图、图像直方图的修正、图像的平滑、图像的锐化等、图像的伪彩色、彩色图像转换为黑白图像、物体边缘的搜索等等;

  8. 二值图像的处理:腐蚀、膨胀、细化、距离变换等;

  9. 图像分析:直线、圆、特定物体的识别;

  10.JEPG、GIF、PCX等格式文件相关操作;

  11.图像文件格式的转换;

  12.图像的常用变换:付利叶变换、DCT变换、沃尔什变换等;

  13.AVI视频流的操作; 

  图像处理技术博大精深,不仅需要有很强的数学功底,还需要熟练掌握一门计算机语言,在当前流行的语言中,我个人觉的Visual C++这个开发平台是图像开发人员的首选工具。本讲座只是起到抛砖引玉的作用,希望和广大读者共同交流。
VC数字图像处理编程讲座之二
在这一讲中作者详细介绍了BMP、GIF、JPG等图像的文件格式
第一节 图像的文件格式

  要利用计算机对数字化图像进行处理,首先要对图像的文件格式要有清楚的认识,因为我们前面说过,自然界的图像以模拟信号的形式存在,在用计算机进行处理以前,首先要数字化,比如摄像头(CCD)摄取的信号在送往计算机处理前,一般情况下要经过数模转换,这个任务常常由图像采集卡完成,它的输出一般为裸图的形式;如果用户想要生成目标图像文件,必须根据文件的格式做相应的处理。随着科技的发展,数码像机、数码摄像机已经进入寻常百姓家,我们可以利用这些设备作为图像处理系统的输入设备来为后续的图像处理提供信息源。无论是什么设备,它总是提供按一定的图像文件格式来提供信息,比较常用的有BMP格式、JPEG格式、GIF格式等等,所以我们在进行图像处理以前,首先要对图像的格式要有清晰的认识,只有在此基础上才可以进行进一步的开发处理。
在讲述图像文件格式前,先对图像作一个简单的分类。除了最简单的图像外,所有的图像都有颜色,而单色图像则是带有颜色的图像中比较简单的格式,它一般由黑色区域和白色区域组成,可以用一个比特表示一个像素,“1”表示黑色,“0”表示白色,当然也可以倒过来表示,这种图像称之为二值图像。我们也可以用8个比特(一个字节)表示一个像素,相当于把黑和白等分为256个级别,“0”表示为黑,“255”表示为白,该字节的数值表示相应像素值的灰度值或亮度值,数值越接近“0”,对应像素点越黑,相反,则对应像素点越白,此种图像我们一般称之为灰度图像。单色图像和灰度图像又统称为黑白图像,与之对应存在着彩色图像,这种图像要复杂一些,表示图像时,常用的图像彩色模式有RGB模式、CMYK模式和HIS模式,一般情况下我们只使用RGB模式,R对应红色,G对应绿色,B对应蓝色,它们统称为三基色,这三中色彩的不同搭配,就可以搭配成各种现实中的色彩,此时彩色图像的每一个像素都需要3个样本组成的一组数据表示,其中每个样本用于表示该像素的一个基本颜色。

  对于现存的所有的图像文件格式,我们在这里主要介绍BMP图像文件格式,并且文件里的图像数据是未压缩的,因为图像的数字化处理主要是对图像中的各个像素进行相应的处理,而未压缩的BMP图像中的像素数值正好与实际要处理的数字图像相对应,这种格式的文件最合适我们对之进行数字化处理。请读者记住,压缩过的图像是无法直接进行数字化处理的,如JPEG、GIF等格式的文件,此时首先要对图像文件解压缩,这就要涉及到一些比较复杂的压缩算法。后续章节中我们将针对特殊的文件格式如何转换为BMP格式的文件问题作专门的论述,经过转换,我们就可以利用得到的未压缩的BMP文件格式进行后续处理。对于JPEG、GIF等格式,由于涉及到压缩算法,这要求读者掌握一定的信息论方面的知识,如果展开的话,可以写一本书,限于篇幅原因,我们只作一般性的讲解,有兴趣的朋友可以参考相关书籍资料。
一、BMP文件结构 

  1. BMP文件组成 

  BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。文件头主要包含文件的大小、文件类型、图像数据偏离文件头的长度等信息;位图信息头包含图象的尺寸信息、图像用几个比特数值来表示一个像素、图像是否压缩、图像所用的颜色数等信息。颜色信息包含图像所用到的颜色表,显示图像时需用到这个颜色表来生成调色板,但如果图像为真彩色,既图像的每个像素用24个比特来表示,文件中就没有这一块信息,也就不需要操作调色板。文件中的数据块表示图像的相应的像素值,需要注意的是:图像的像素值在文件中的存放顺序为从左到右,从下到上,也就是说,在BMP文件中首先存放的是图像的最后一行像素,最后才存储图像的第一行像素,但对与同一行的像素,则是按照先左边后右边的的顺序存储的;另外一个需要读者朋友关注的细节是:文件存储图像的每一行像素值时,如果存储该行像素值所占的字节数为4的倍数,则正常存储,否则,需要在后端补0,凑足4的倍数。 

  2. BMP文件头 

  BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。其结构定义如下: 
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; // 位图文件的类型,必须为“BM”
DWORD bfSize; // 位图文件的大小,以字节为单位
WORD bfReserved1; // 位图文件保留字,必须为0
WORD bfReserved2; // 位图文件保留字,必须为0
DWORD bfOffBits; // 位图数据的起始位置,以相对于位图文件头的偏移量表示,以字节为单位
} BITMAPFILEHEADER;该结构占据14个字节。

  3. 位图信息头 

  BMP位图信息头数据用于说明位图的尺寸等信息。其结构如下:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; // 本结构所占用字节数
LONG biWidth; // 位图的宽度,以像素为单位
LONG biHeight; // 位图的高度,以像素为单位
WORD biPlanes; // 目标设备的平面数不清,必须为1
WORD biBitCount// 每个像素所需的位数,必须是1(双色), 4(16色),8(256色)或24(真彩色)之一
DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
DWORD biSizeImage; // 位图的大小,以字节为单位
LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数
LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数
DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数
DWORD biClrImportant;// 位图显示过程中重要的颜色数
} BITMAPINFOHEADER;该结构占据40个字节。

  注意:对于BMP文件格式,在处理单色图像和真彩色图像的时候,无论图象数据多么庞大,都不对图象数据进行任何压缩处理,一般情况下,如果位图采用压缩格式,那么16色图像采用RLE4压缩算法,256色图像采用RLE8压缩算法。

  4. 颜色表 

  颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下: 
typedef struct tagRGBQUAD {
BYTErgbBlue;// 蓝色的亮度(值范围为0-255)
BYTErgbGreen; // 绿色的亮度(值范围为0-255)
BYTErgbRed; // 红色的亮度(值范围为0-255)
BYTErgbReserved;// 保留,必须为0
} RGBQUAD;

  颜色表中RGBQUAD结构数据的个数由BITMAPINFOHEADER 中的biBitCount项来确定,当biBitCount=1,4,8时,分别有2,16,256个颜色表项,当biBitCount=24时,图像为真彩色,图像中每个像素的颜色用三个字节表示,分别对应R、G、B值,图像文件没有颜色表项。位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader; // 位图信息头
RGBQUAD bmiColors[1]; // 颜色表
} BITMAPINFO;

  注意:RGBQUAD数据结构中,增加了一个保留字段rgbReserved,它不代表任何颜色,必须取固定的值为“0”,同时,RGBQUAD结构中定义的颜色值中,红色、绿色和蓝色的排列顺序与一般真彩色图像文件的颜色数据排列顺序恰好相反,既:若某个位图中的一个像素点的颜色的描述为“00,00,ff,00”,则表示该点为红色,而不是蓝色。

  5. 位图数据 

  位图数据记录了位图的每一个像素值或该对应像素的颜色表的索引值,图像记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。这种格式我们又称为Bottom_Up位图,当然与之相对的还有Up_Down形式的位图,它的记录顺序是从上到下的,对于这种形式的位图,也不存在压缩形式。位图的一个像素值所占的字节数:当biBitCount=1时,8个像素占1个字节;当biBitCount=4时,2个像素占1个字节;当biBitCount=8时,1个像素占1个字节;当biBitCount=24时,1个像素占3个字节,此时图像为真彩色图像。当图像不是为真彩色时,图像文件中包含颜色表,位图的数据表示对应像素点在颜色表中相应的索引值,当为真彩色时,每一个像素用三个字节表示图像相应像素点彩色值,每个字节分别对应R、G、B分量的值,这时候图像文件中没有颜色表。上面我已经讲过了,Windows规定图像文件中一个扫描行所占的字节数必须是4的倍数(即以字为单位),不足的以0填充,图像文件中一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;// 一个扫描行所占的字节数

  位图数据的大小按下式计算(不压缩情况下):
  DataSize= DataSizePerLine* biHeight。

  上述是BMP文件格式的说明,搞清楚了以上的结构,就可以正确的操作图像文件,对它进行读或写操作了。
二、GIF图像文件格式

  GIF图象格式的全称为Graphics Interchange Format,从这个名字可以看出,这种图像格式主要是为了通过网络传输图像而设计的。GIF文件不支持24位真彩色图像,最多只能存储256色的图像或灰度图像;GIF格式文件也无法存储CMY和HIS模型的图像数据;另外,GIF图像文件的各种数据区域一般没有固定的数据长度和存储顺序,所以为了方便程序寻找数据区,将数据区中的第一个字节作为标志符;最后需要读者注意的是GIF文件存储图像数据是有二种排列顺序:顺序排列或交叉排列。交叉排列的方式适合网络传输,这样一来允许用户在不完全掌握图像数据之前,获取当前图像的轮廓数据。

  GIF文件格式分为87和89两个版本,对于87这个版本,该文件主要是有五个部分组成,它,们是按顺序出现的:文件头块、逻辑屏幕描述块、可选择的调色板块、图像数据块、最后是标志文件结束的尾块,该块总是取固定的值3BH。其中第一和第二两个块用GIF图像文件头结构描述:
GIFHEADER:{
DB Signature; //该字段占六个字节,为了用于指明图像为GIF格式,前三个字符必须为“GIF”,后三字符用于指定是哪个版本,87或89。
DW ScreenWidth;//
DW ScreenDepth;//占两个字节,以像素为单位表示图像的宽、高
DB GlobalFlagByte;//该字节的各个位用于调色版的描述
DB BackGroundColor;//代表图象的背景颜色的索引
DB AspectRatio;图像的长宽比
}

  GIF格式中的调色板有通用调色板和局部调色板之分,因为GIF格式允许一个文件中存储多个图像,因此有这两种调色板,其中通用调色板适于文件中的所有图像,而局部调色板只适用于某一个图像。格式中的数据区域一般分为四个部分,图像数据识别区域,局部调色板数据,采用压缩算法得到的图象数据区域和结束标志区域。

  在GIF89版本中,它包含七个部分,分别是文件头、通用调色板数据、图像数据区和四个补充数据区,它们主要是用于提示程序如何处理图像的。

  三、JEPG图像文件

  JEPG简称为联合摄影专家小组,作为一种技术,主要用于数字化图像的标准编码,JPEG主要采用有损的压缩编码方式,它比GIF、BMP图像文件要复杂的多,这不是短短的几页篇幅可以将清楚的,万幸的是,我们可以通过一些别的方法将该格式转化为BMP格式。读者需要知道的是在对JEPG文件格式编码时,通常需要分为以下四步:颜色转化、DCT变换、量化、编码。

  以上介绍了一些常用的图像文件,对比较复杂的格式,如GIF和JEPG,仅仅作了极其浮浅的介绍,后文我们会和它们作进一步的接触。实际应用中,还有许多图像格式,文章中都没有提到,读者如果需要做进一步的研究,还需要参考一些关于图像格式方面的资料。
VC数字图像处理编程讲座之三
本节主要讲述如何操作BMP文件,如对其读、写和显示等
BMP图像的基本操作

  上一讲我们主要介绍了图像的格式,其中重点说明了BMP文件的存储格式,同时对JEPG和GIF等常用格式作了简单的介绍。本节主要讲述如何操作BMP文件,如对其读、写和显示等。

  在实现数字图象处理的过程中,主要是通过对图像中的每一个像素点运用各种图像处理算法来达到预期的效果,所以进行图像处理的第一步,也是我们最关心的问题,是如何得到图像中每一个像素点的亮度值;为了观察和验证处理的图像效果,另一个需要解决的问题是如何将处理前后的图像正确的显示出来。我们这章内容就是解决这些问题。

  随着科技的发展,图像处理技术已经渗透到人类生活的各个领域并得到越来越多的应用,但是突出的一个矛盾是图像的格式也是越来越多,目前图像处理所涉及的主要的图像格式就有很多种,如TIF、JEMP、BMP等等,一般情况下,为了处理简单方便,进行数字图像处理所采用的都是BMP格式的图像文件(有时也称为DIB格式的图像文件),并且这种格式的文件是没有压缩的。我们通过操作这种格式的文件,可以获取正确显示图像所需的调色板信息,图像的尺寸信息,图像中各个像素点的亮度信息等等,有了这些数据,开发人员就可以对图像施加各种处理算法,进行相应的处理。如果特殊情况下需要处理其它某种格式的图像,如GIF、JEMP等格式的图象文件,可以首先将该格式转换为BMP格式,然后再进行相应的处理。这一点需要读者清楚。

  BMP格式的图像文件又可以分为许多种类,如真彩色位图、256色位图,采用RLE(游程编码)压缩格式的BMP位图等等。由于在实际的工程应用和图像算法效果验证中经常要处理的是256级并且是没有压缩的BMP灰度图像,例如通过黑白采集卡采集得到的图像就是这种格式,所以我们在整个讲座中范例所处理的文件格式都是BMP灰度图像。如果读者对这种格式的位图能够作到熟练的操作,那么对于其余形式的BMP位图的操作也不会很困难。

  BMP灰度图像作为Windows环境下主要的图像格式之一,以其格式简单,适应性强而倍受欢迎。正如我们在上一讲中介绍过的那样,这种文件格式就是每一个像素用8bit表示,显示出来的图像是黑白效果,最黑的像素的灰度(也叫作亮度)值为“0”,最白的像素的灰度值为“255”,整个图像各个像素的灰度值随机的分布在“0”到“255”的区间中,越黑的像素,其灰度值越接近于“0”,越白(既越亮)的像素,其灰度值越接近于“255”;与此对应的是在该文件类型中的颜色表项的各个RGB分量值是相等的,并且颜色表项的数目是256个。

  在进行图像处理时,操作图像中的像素值就要得到图像阵列;经过处理后的图像的像素值需要存储起来;显示图像时要正确实现调色板、得到位图的尺寸信息等。结合这些问题,下面我们针对性的给出了操作灰度BMP图像时的部分函数实现代码及注释。
一、 BMP位图操作

  首先我们回顾一下上讲中的重要信息:BMP位图包括位图文件头结构BITMAPFILEHEADER、位图信息头结构BITMAPINFOHEADER、位图颜色表RGBQUAD和位图像素数据四部分。处理位图时要根据文件的这些结构得到位图文件大小、位图的宽、高、实现调色板、得到位图像素值等等。这里要注意的一点是在BMP位图中,位图的每行像素值要填充到一个四字节边界,即位图每行所占的存储长度为四字节的倍数,不足时将多余位用0填充。

  有了上述知识,可以开始编写图像处理的程序了,关于在VC的开发平台上如何开发程序的问题这里不再赘述,笔者假定读者都具有一定的VC开发经验。在开发该图像处理程序的过程中,笔者没有采用面向对象的方法,虽然面向对象的方法可以将数据封装起来,保护类中的数据不受外界的干扰,提高数据的安全性,但是这种安全性是以降低程序的执行效率为代价的,为此,我们充分利用了程序的文档视图结构,在程序中直接使用了一些API函数来操作图像。在微软的MSDN中有一个名为Diblook的例子,该例子演示了如何操作Dib位图,有兴趣的读者可以参考一下,相信一定会有所收获。

  启动Visual C++,生成一个名为Dib的多文档程序,将CDibView类的基类设为CscrollView类,这样作的目的是为了在显示位图时支持滚动条,另外在处理图像应用程序的文档类(CDibDoc.h)中声明如下宏及公有变量:
#define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4)//计算图像每行象素所占的字节数目;
HANDLE m_hDIB;//存放位图数据的句柄;
CPalette* m_palDIB;//指向调色板Cpalette类的指针;
CSize m_sizeDoc;//初始化视图的尺寸,该尺寸为位图的尺寸;

  最后将程序的字符串表中的字符串资源IDR_DibTYPE修改为:“\nDib\nDib\nDib Files(*.bmp;*.dib)\n.bmp\nDib.Document\nDib Document”。这样作的目的是为了在程序文件对话框中可以选择BMP或DIB格式的位图文件。

  1、 读取灰度BMP位图

  可以根据BMP位图文件的结构,操作BMP位图文件并读入图像数据,为此我们充分利用了VC的文档视图结构,重载了文挡类的OnOpenDocument()函数,这样用户就可以在自动生成程序的打开文件对话框中选择所要打开的位图文件,然后程序将自动调用该函数执行读取数据的操作。该函数的实现代码如下所示:
BOOL CDibDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
LOGPALETTE *pPal;//定义逻辑调色板指针;
pPal=new LOGPALETTE;//初始化该指针;
CFile file;
CFileException fe;
if (!file.Open(lpszPathName, CFile::modeRead | CFile::shareDenyWrite, &fe))
{//以“读”的方式打开文件;
AfxMessageBox("图像文件打不开!");
return FALSE;
}
DeleteContents();//删除文挡;
BeginWaitCursor();
BITMAPFILEHEADER bmfHeader;//定义位图文件头结构;
LPBITMAPINFO lpbmi;
DWORD dwBitsSize;
HANDLE hDIB;
LPSTR pDIB;//指向位图数据的指针;
BITMAPINFOHEADER *bmhdr;//指向位图信息头结构的指针
dwBitsSize = file.GetLength();//得到文件长度
if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) !=sizeof(bmfHeader))
return FALSE;//读取位图文件的文件头结构信息;
if (bmfHeader.bfType != 0x4d42) //检查该文件是否为BMP格式的文件;
return FALSE;
hDIB=(HANDLE) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize);
//为读取图像文件数据申请缓冲区
if (hDIB == 0)
{
return FALSE;
}
pDIB = (LPSTR) ::GlobalLock((HGLOBAL)hDIB); 
//得到申请的缓冲区的指针;
if (file.ReadHuge(pDIB, dwBitsSize - sizeof(BITMAPFILEHEADER)) !=
dwBitsSize - sizeof(BITMAPFILEHEADER) )
{
::GlobalUnlock((HGLOBAL)hDIB);
hDIB=NULL;
return FALSE;
}//此时pDIB数据块中读取的数据包括位图头信息、位图颜色表、图像像素的灰度值;
bmhdr=(BITMAPINFOHEADER*)pDIB;//为指向位图信息头结构的指针赋值;
::GlobalUnlock((HGLOBAL)hDIB);
if ((*bmhdr).biBitCount!=8)//验证是否为8bit位图
{
AfxMessageBox("该文件不是灰度位图格式!");
return FALSE;
}
m_hDIB=hDIB;//将内部变量数据赋于全局变量;
//下面是记录位图的尺寸;
m_sizeDoc.x=bmhdr->biWidth;
m_sizeDoc.y=bmhdr->biHeight;
//下面是根据颜色表生成调色板;
m_palDIB=new Cpalette;
pPal->palVersion=0x300;//填充逻辑颜色表
pPal->palNumEntries=256;
lpbmi=(LPBITMAPINFO)bmhdr;
for(int i=0;i<256;i++)
{//每个颜色表项的R、G、B值相等,并且各个值从“0”到“255”序列展开;
Pal->palPalentry[i].peRed=lpbmi->bmiColors[i].rgbRed;
pPal->palPalentry[i].peGreen=lpbmi->bmiColors[i].rgbGreen;
pPal->palPalentry[i].peBlue= lpbmi->bmiColors[i].rgbBlue;;
pPal->palPalentry[i].peFlags=0;
}
m_palDIB->CreatePalette(pPal);
//根据读入的数据得到位图的宽、高、颜色表;
if(pPal)
delete pPal;
EndWaitCursor();
SetPathName(lpszPathName);//设置存储路径
SetModifiedFlag(FALSE); // 设置文件修改标志为FALSE
return TRUE;
}

  上面的方法是通过CFile类对象的操作来读取位图文件的,它需要分析位图中的文件头信息,从而确定需要读取的图像长度。这种方法相对来说有些繁琐,其实还可以以一种相对简单的方法读取位图数据,首先在程序的资源中定义DIB类型资源,然后添加位图到该类型中,将图像数据以资源的形式读取出来,这时候就可以根据所获取的数据中的位图信息结构来获取、显示图像数据了。下面的函数实现了以资源形式装载图像文件数据,该函数的实现代码如下所示:
/////////////////////////////////////////////////////////////////
HANDLE LoadDIB(UINT uIDS, LPCSTR lpszDibType)
{
LPCSTR lpszDibRes =MAKEINTRESOURCE(uIDS);//根据资源标志符确定资源的名字;
HINSTANCE hInst=AfxGetInstanceHandle();//得到应用程序的句柄;
HRSRC hRes=::FindResource(hInst,lpszDibRes, lpszDibType);//获取资源的句柄,这里lpszDibType为资源的名字“DIB”;
If(hRes==NULL)
return NULL
HGLOBAL hData=::LoadResource(hInst, hRes);//转载资源数据并返回该句柄;
return hData;
}

  2、 灰度位图数据的存储

  为了将图像处理后所得到的像素值保存起来,我们重载了文档类的OnSaveDocument()函数,这样用户在点击Save或SaveAs子菜单后程序自动调用该函数,实现图像数据的存储。该函数的具体实现如下:
///////////////////////////////////////////////////////////////////
BOOL CDibDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
CFile file;
CFileException fe;
BITMAPFILEHEADER bmfHdr; // 位图文件头结构;
LPBITMAPINFOHEADER lpBI;//指向位图头信息结构的指针;
DWORD dwDIBSize;;
if (!file.Open(lpszPathName, CFile::modeCreate |CFile::modeReadWrite | CFile::shareExclusive, &fe))
{
AfxMessageBox("文件打不开");
return FALSE;
}//以读写的方式打开文件;
BOOL bSuccess = FALSE;
BeginWaitCursor();
lpBI = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL) m_hDIB);
if (lpBI == NULL)
return FALSE;
dwDIBSize = *(LPDWORD)lpBI + 256*sizeof(RGBQUAD);
//图像的文件信息所占用的字节数;
DWORD dwBmBitsSize;//BMP文件中位图的像素所占的字节数
dwBmBitsSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount))
*lpBI->biHeight;// 存储时位图所有像素所占的总字节数
dwDIBSize += dwBmBitsSize; //BMP文件除文件信息结构外的所有数据占用的总字节数;
lpBI->biSizeImage = dwBmBitsSize; // 位图所有像素所占的总字节数
//以下五句为文件头结构填充值
bmfHdr.bfType =0x4d42; // 文件为"BMP"类型
bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);//文件总长度
bmfHdr.bfReserved1 = 0;
bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize
+ 256*sizeof(RGBQUAD);
//位图数据距离文件头的偏移量;
file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));//向文件中写文件头信息;
file.WriteHuge(lpBI, dwDIBSize);
//将位图信息(信息头结构、颜色表、像素数据)写入文件;
::GlobalUnlock((HGLOBAL) m_hDIB);
EndWaitCursor();
SetModifiedFlag(FALSE); // 将文档设为“干净”标志,表示此后文档不需要存盘提示;
return TRUE
}
二、 调色板的操作

  通过上面的操作,我们已经可以获取图像中的数据了,现在的又一个问题是如何在窗口中显示出图像数据。灰度图像要正确显示,必须实现逻辑调色板和系统调色板。首先我们介绍一下逻辑调色板结构LOGPALETTE,该结构定义如下:
typedef struct tagLOGPALETTE
{
WORD palVersion;//调色板的板本号,应该指定该值为0x300;
WORD palNumEntries;//调色板中的表项数,对于灰度图像该值为256;
PALETEENTRY palPalEntry[1];//调色板中的颜色表项,由于该表项的数目不一定,所以这里数组长度定义为1,灰度图像对应的该数组的长度为256;
}LOGPALETTE;
颜色表项结构PALETTEENTRY定义了调色板中的每一个颜色表项的颜色和使用方式,定义如下:
typedef struct tagPALETTEENTRY
{
BYTE peRed; //R分量值;
BYTE peGreen; //G分量值;
BYTE peBlue; //B分量值;
BYTE peFlags; // 该颜色被使用的方式,一般情况下设为“0”;
}PALETTEENTRY;

  Windows系统使用调色板管理器来管理与调色板有关的操作,通常活动窗口的调色板即是当前系统调色板,所有的非活动窗口都必须按照此系统调色板来显示自己的颜色,此时调色板管理器将自动的用系统调色板中的最近似颜色来映射相应的显示颜色。如果窗口或应用程序按自己的调色板显示颜色,就必须将自己的调色板载入到系统调色板中,这种操作叫作实现调色板,实现调色板包括两个步骤,既首先将调色板选择到设备上下文中,然后在设备上下文中实现它。可以通过CDC::SelectPalette()、CDC::RealizePalette()或相应的API函数来实现上述的两个步骤。在实现调色板的过程中,通过在主框架类中处理Windows定义的消息WM_QUERYNEWPALETTE 、WM_PALETTECHANGED及视图类中处理自定义消息WM_DOREALIZE(该消息在主框架窗口定义如下:#define WM_REALIZEPAL (WM_USER+101))来实现调色板的操作。当系统需要处理调色板的变化时,将向程序的主窗口发送WM_QUERYNEWPALETTE 、WM_PALETTECHANGED,例如当某一窗口即将激活时,主框架窗口将收到WM_QUERYNEWPALETTE消息,通知该窗口将要收到输入焦点,给它一次机会实现其自身的逻辑调色板;当系统调色板改变后,主框架窗口将收到WM_PALETTECHANGED消息,通知其它窗口系统调色板已经改变,此时每一窗口都应该实现其逻辑调色板,重画客户区。

  由于上述的调色板变更消息是发往主框架窗口的,所以我们只能在主窗口中响应这两个消息,然后由主框架窗口通知各个视窗,使得程序激活时能自动装载自己的调色板。我们定义的用户消息WM_REALIZEPAL用于主框架窗口通知视窗它已经收到调色板变更消息,视窗应该协调其调色板。下面我们给出了各个消息的响应处理函数的具体实现代码和注释:
//////////////////////////////////////////////////////////
void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)
{//总实现活动视的调色板
CMDIFrameWnd::OnPaletteChanged(pFocusWnd);
CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针;
if (pMDIChildWnd == NULL)
return
CView* pView = pMDIChildWnd->GetActiveView();//得到视图的指针;
ASSERT(pView != NULL);
SendMessageToDescendants(WM_DOREALIZE, (WPARAM)pView->m_hWnd);
//通知所有子窗口系统调色板已改变
}
////////////////////////////////////////////////
BOOL CMainFrame::OnQueryNewPalette()//提供实现系统调色板的机会
{
// 实现活动视的调色板
CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针;
if (pMDIChildWnd == NULL)
return FALSE;//no active MDI child frame (no new palette)
CView* pView = pMDIChildWnd->GetActiveView();//得到活动子窗口的视图指针;
ASSERT(pView != NULL);
//通知活动视图实现系统调色板
pView->SendMessage(WM_DOREALIZE, (WPARAM)pView->m_hWnd);
return TRUE;
}
/////////////////////////////////////////////////
BOOL CDibView::OnDoRealize(WPARAM wParam, LPARAM)//实现系统调色板
{
ASSERT(wParam != NULL);
CDibDoc* pDoc = GetDocument();
if (pDoc->m_hDIB == NULL)
return FALSE// must be a new document
CPalette* pPal = pDoc->m_palDIB;
//调色板的颜色表数据在InitDIBData()函数中实现
if (pPal != NULL)
{
CMainFrame* pAppFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;//得到程序的主框架指针;
ASSERT_KINDOF(CMainFrame, pAppFrame);
CClientDC appDC(pAppFrame);//获取主框架的设备上下文;
CPalette* oldPalette = appDC.SelectPalette(pPal, ((HWND)wParam) != m_hWnd);
//只有活动视才可以设为"FALSE",即根据活动视的调色板设为"前景"调色板;
if (oldPalette != NULL)
{
UINT nColorsChanged = appDC.RealizePalette();//实现系统调色板
if (nColorsChanged > 0)
pDoc->UpdateAllViews(NULL);//更新视图
appDC.SelectPalette(oldPalette, TRUE);
//将原系统调色板置为背景调色板
}
else
{
TRACE0(“\tSelectPalette failed in”);
}
return TRUE
}

  注:在调用API函数显示位图时,不要忘记设置逻辑调色板,即"背景"调色板,否则位图将无法正确显示,读者可以从后面的显示部分的实现看出我们在显示时实现了逻辑调色板。上述的处理相对来说比较繁琐复杂,可能对于初学者来说也比较难于理解,所以如果我们的程序仅仅限于处理灰度图象,可以采用另外一种相对简单的办法,即在文档类的初始化阶段定义一个灰度调色板,然后在设备上下文中实现它,这样作的好处是在度取灰度位图时可以不再考虑文件中的颜色表信息,提高了文件读取速度,笔者在开发一个基于机器视觉的项目时采用的就是这种方法,取的了比较满意的效果。首先定义一个指向逻辑颜色表结构LOGPALETTE的指针pPal,填充该指针,然后将该指针与调色板指针联系起来,该方法的具体实现如下:
/////////////////////////////////////////////////////////
CDibDoc::CDibDoc()
{
……………………….
LOGPALETTE *Pal;
Pal=new LOGPALETTE;
m_palDIB=new Cpalette;
pPal->palVersion=0x300;
pPal->palNumEntries=256;
for(int i=0;i<256;i++)
{//每个颜色表项的R、G、B值相等,并且各个值从“0”到“255”序列展开;
Pal->palPalentry[i].peRed=i;
pPal->palPalentry[i].peGreen=i;
pPal->palPalentry[i].peBlue=i;
pPal->palPalentry[i].peFlags=0;
}
m_palDIB->CreatePalette(pPal);
…………………..
}
三、 图像的显示

  显示DIB位图数据可以通过设备上下文CDC对象的成员函数CDC::Bitblt()或CDC::StretchBlt()来实现,也可以通过API函数SetDIBBitsToDevice()或StretchDIBBits()来实现,函数中具体所用到的各个参数的意义可以参考MSDN。其中StretchDIBBits()和CDC::StretchBlt()可以将图像进行放大和缩小显示。当从文档中装入位图文件时,CDIBView类的OnInitialUpdate函数将被调用,因此可以在该函数中实现对视图尺寸的设置,用于正确的显示位图,然后就可以在视图类的OnDraw()函数中正确的显示位图了。这两个函数的具体实现代码分别如下所示:
/////////////////////////////////////////////////////////////
void CDIBView::OnInitialUpdate()
{
CscrollView::OnInitalUpdate();
CDIBDoc *pDoc=GetDocument();
If(pDoc->m_hDIB==NULL)//如果位图数据为空,设置m_sizeDoc的默认尺寸;
pDoc->m_sizeDoc.cx=pDoc->m_sizeDoc.cy=100;
SetScrollSizes(MM_TEXT,pDoc-> m_sizeDoc);
}
/////////////////////////////////////////////////////////////
void CDIBView::OnDraw(CDC *pDC)
{
BITMAPINFOHEADER *lpDIBHdr;//位图信息头结构指针;
BYTE *lpDIBBits;//指向位图像素灰度值的指针;
BOOL bSuccess=FALSE;
CPalette*OldPal=NULL;//调色板指针;
HDC hDC=pDC->GetSafeHdc();//获取当前设备上下文的句柄;
CDIBDoc *pDoc=GetDocument();//获取活动文档的指针;
If(pDoc->m_hDIB ==NULL)
{//判断图像数据是否为空;
AfxMessageBox("图像数据不能为空,请首先读取图像数据!");
return;
}
lpDIBHdr=( BITMAPINFOHEADER *)GlobalLock(pDoc->m_hDIB);//得到图像的位图头信息
lpDIBBits=lpDIBHdr+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);//获取保存图像像素值的缓冲区的指针;
if(pDoc-> m_palDIB)
{//如果存在调色板信息,实现逻辑调色板;
OldPal=pDC-> SelectPalette(pDoc-> m_palDIB,TRUE);
PDC->RealizePalette();
}
else
{
AfxMessageBox("图像的调色板数据不能为空,请首先读取调色板信息!");
return ;
}
SetStretchBltMode(hDC,COLORONCOLOR);
//显示图像
BSuccess=StretchDIBBits(hDC,0,0,pDoc-> m_sizeDoc.cx, pDoc-> m_sizeDoc.cy,
0, pDoc-> m_sizeDoc.cy,0, pDoc-> m_sizeDoc.cy,
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);
GlobalUnlock(pDoc->m_hDIB);
If(OldPal)//恢复调色板;
PDC->SelectPalette(OldPal,FALSE);
retrun;
}

  四、 小结

  在本期讲座里我们主要介绍了如何操作灰度位图,它具有较强的代表性,同时为后续的图像处理编程的学习作了必要的准备工作,经过学习,对于如何操作其它类型的BMP格式的图像文件,可以达到举一反三的作用。 

VC数字图像处理编程讲座之四
在这一讲中作者进一步深入讲解了图像特效的显示技术......
BMP图像显示的特效操作

  上期讲座中我们主要讲述了BMP图像数据的存取、图像的显示和调色板的操作等内容,在上面的学习基础上,我们可以进一步深化,学习并掌握图像特效显示技术。有了这种技术,可以用来在今后的项目开发中美化我们的软件界面,提高软件的视觉效果。在如今的商业软件中,几乎每一幅图像的显示都采用了图像特效显示,例如读者比较熟悉的Windows的屏幕保护程序就采用了各种各样的图像特效显示,使人感到眼花缭乱和耳目一新。专业图像处理软件更是提供了丰富的显示方式供用户使用,可以方便的在程序中实现图像的特效显示,如PhotoShop 、Authorware等。本节主要介绍如何实现图像的浮雕、雕刻、百页窗、旋转、扫描、栅条、马赛克、和渐显渐隐显示等效果。通过这期讲座的学习,读者朋友们也可以自己动手制作拥有特效显示效果的软件了。

  图像的显示我们讲过主要有BitBlt()、SetDIBitsToDevice()和StretchDIBits()等函数。需要读者注意的是,在特效显示时,并不是每个显示函数都适宜,BitBlt()函数主要是用来显示设备无关位图(DDB),后两个函数用来显示设备无关位图(DIB)。由于我们讲座里处理的是设备无关位图,所以我们主要关心的是后两个函数的应用,其中SetDIBitsToDevice()使用起来较死板,远不如StretchDIBits()用的灵活,并且对大多数的特效显示无能为力,所以为了实现图像的特效显示效果,需要使用StretchDIBits()函数来显示图像,具体什么原因,我想可能是微软在实现这些函数时使用的方法不同吧。这些函数如何使用,各个参数的含义,可以参考微软的MSDN。

  实现图像的特殊效果的显示的基本思路是要么是操作图像的像素,要么是对图像分块按一定的方向或次序,分阶段的显示或擦除对应的图像块。对于第二种显示的思路,其中的要点是:1.划分图像块;2.确定图像块的操作次序;3.显示或清除对应的图像块;4.在两个连续显示的图像块之间插入一个固定的延迟。其中图像块的划分决定了图像的显示方式,图像块的显示顺序决定了显示的方向和细分的依据。不同的效果决定了不同的分块方法和显示次序,我们将在后面的各种特效显示中介绍如何分块和决定次序。为了使图像的显示过程明显的表现出来,实现显示的特效,就需要在图像块的依此显示中插入固定的延迟。也许读者朋友会想到利用sleep()函数或用Settime()来实现延迟,由于Windows是个基于消息的多任务操作系统,这些方法所产生的延迟时间对于图像的显示来说是不精确的,为了实现与机器无关的更精确的时间延迟,可以采用timeGetTime()函数来产生微秒级的延迟。使用这个函数时为了编译不产生错误,要在连接设置中引入“Winmm.lib”库,并要包含头文件“Mmsystem.h”。这里我们首先给出一个延迟函数,它用来实现固定时间的延迟:
void DelayTime(DWORD time)
{
DWORD BeginTime ,EndTime;
BeginTime=timeGetTime();//得到当前的系统时间、单位为微秒;
do
{
EndTime=TimeGetTime();//再次得到当前的系统时间;
}
while((EndTime-BeginTime) <time)//判断延迟时间是否已经结束;
}
一、操作位图的像素实现显示的特效

  我们首先介绍直接操作图像中的像素的灰度值来实现图像显示的特效、这里我们主要介绍如何实现图像的浮雕和雕刻效果。经常看电视的朋友们不知注意到没有,有些电视连续剧在每集片头或片尾部分都有显示一些特殊效果的图像,比如前一阵子中央一套放的《长征》和《康熙王朝》,这些特效称为"图像的浮雕效果""图像的雕刻效果",经过这些特效处理后的图像增强了观众们的视觉效果,它们看上去仿佛是使用3D技术作的,这也许就是为什么这种技术那么流行的原因吧。其实,我们完全可以用一些简单的数字图像处理算法来实现这些看似复杂高深的显示效果。下面以一个标准的Lena灰度图像为原图,给出了处理后的效果图,同时给出了VC开发平台上的部分实现源代码。

  1."浮雕"图像

  "浮雕"图象效果是指图像的前景前向凸出背景。所谓的"浮雕"概念是指标绘图像上的一个像素和它左上方的那个像素之间差值的一种处理过程,为了使图像保持一定的亮度并呈现灰色,我在处理过程中为这个差值加了一个数值为128的常量。需要读者注意的是,当设置一个像素值的时候,它和它左上方的像素都要被用到,为了避免用到已经设置过的像素,应该从图像的右下方的像素开始处理,下面是实现的源代码:
void CDibView::OnFDImage() //产生"浮雕"效果图函数
{
 HANDLE data1handle;//用来存放图像数据的句柄;
 LPBITMAPINFOHEADER lpBi;//图像的信息头结构;
 CDibDoc *pDoc=GetDocument();//得到文挡指针;
 HDIB hdib;//用来存放图像数据的句柄;
 unsigned char *pData;//指向原始图像数据的指针;
 unsigned char *data;//指向处理后图像数据的指针;
 hdib=pDoc->m_hDIB;//拷贝存放已经读取的图像文件数据句柄;
 lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib);//获取图像信息头
pData=(unsigned char*)FindDIBBits((LPSTR)lpBi);
//FindDIBBits是我定义的一个函数、根据图像的结构得到位图的灰度值数据、
pDoc->SetModifiedFlag(TRUE);
//设置文档修改标志为“真”、为后续的修改存盘作准备;
data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); //声明一个缓冲区用来暂存处理后的图像数据;
data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);//得到该缓冲区的指针;
AfxGetApp()->BeginWaitCursor();
int i,j,buf;
for( i=lpBi->biHeight; i>=2; i--)//从图像右下角开始对图像的各个像素进行“浮雕”处理;
  for( j=lpBi->biWidth; j>=2; j--)
  {
//浮雕处理
buf=*(pData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)-*(pData+(lpBi->biHeight-i+1)*WIDTHBYTES(lpBi->biWidth*8)+j-1)+128;
if(buf>255) buf=255;
if(buf<0)buf=0; *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf;
}
for( j=0; jbiHeight; j++)
for( i=0; ibiWidth; i++)
//重新写回原始图像的数据缓冲区;
*(pData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j);    AfxGetApp()->EndWaitCursor();
pDoc->m_hDIB =hdib//将处理过的图像数据写回pDoc中的图像缓冲区;
GlobalUnlock((HGLOBAL)hdib);//解锁、释放缓冲区;
GlobalUnlock((HGLOBAL)data1handle);
GlobalFree((HGLOBAL)hdib);
GlobalFree((HGLOBAL)data1handle);
Invalidate(TRUE);//显示图像
}
2."雕刻"图像

  上面讲述了通过求一个像素和它左上方像素之间的差值并加上一个常数的方法生成"浮雕"效果的灰度图像,"雕刻"图像与之相反,它是通过取一个像素和它右下方的像素之间的差值并加上一个常数,这里我也取128,经过这样处理,就可以得到"雕刻"图像,这时候图像的前景凹陷进背景之中。同样需要读者注意的是为了避免重复使用处理过的图像像素,处理图像时要从图像的左上方的像素开始处理。实现代码如下:
void CDibView::OnDKImage() 
{
 // TODO: Add your command handler code here
 HANDLE data1handle;//这里的内部变量与前面的含义一致、这里不再赘述;
 LPBITMAPINFOHEADER lpBi;
 CDibDoc *pDoc=GetDocument();
 HDIB hdib;
 unsigned char *pData;
 unsigned char *data;
 hdib=pDoc->m_hDIB;//拷贝图像数据的句柄;
 lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib);
 pData=(unsigned char*)FindDIBBits((LPSTR)lpBi);
 pDoc->SetModifiedFlag(TRUE);
 data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight);//申请缓冲区;
 data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);//得到新的缓冲去的指针; AfxGetApp()->BeginWaitCursor();
 int i,j,buf;
 for( i=0;i<=lpBi->biHeight-2; i++)//对图像的各个像素循环进行"雕刻"处理;
  for( j=0;j<=lpBi->biWidth-2; j++)
 {
   buf=*(pData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)-*(pData+(lpBi->biHeight-i-1)*WIDTHBYTES(lpBi->biWidth*8)+j+1)+128;//“雕刻”处理;
 if(buf>255) buf=255;
 if(buf<0)buf=0;
 *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf;
 }
 for( j=0; jbiHeight; j++)
  for( i=0; ibiWidth; i++)  //重新将处理后的图像数据写入原始的图像缓冲区内;  *(pData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j);
pDoc->m_hDIB =hdib//将处理过的图像数据写回pDoc中的图像缓冲区;
GlobalUnlock((HGLOBAL)hdib);//解锁、释放缓冲区;
GlobalUnlock((HGLOBAL)data1handle);
GlobalFree((HGLOBAL)hdib);
GlobalFree((HGLOBAL)data1handle);
Invalidate(TRUE);//显示图像
}

  3.图像的旋转

  根据图像像素的位置来调节该位置的灰度可以实现许多显示的特效,例如图像的镜像、翻转等。灰度图像旋转就是根据这一个思想实现的,它是指把定义的图像绕某一点以逆时针或顺时针方向旋转一定的角度,通常是指绕图像的中心以逆时针方向旋转。首先根据旋转的角度、图像对角线的长度计算旋转后的图像的最大宽度、高度,根据旋转后图象最大的宽度、高度生成新的缓冲区,假设图像的左上角为(left, top),右下角为(right, bottom),则图像上任意点(x, y)绕其中心(xcenter, ycenter)逆时针旋转angle角度后,新的坐标位置(x1, y1)的计算公式为:
  xcenter = (width+1)/2+left;
  ycenter = (height+1)/2+top;
  x1 = (x-xcenter) cosθ - (y - ycenter) sinθ+xcenter;
  y1 = (x-xcenter) sinθ+ (y- ycenter) cosθ+ ycenter;

  与图像的镜像变换相类似,下一步就是把原图中的(x,y)处象素的灰度值读入新缓冲区的(x1,y1)点处。注意在新缓冲区中与原图没有对应的象素点的值用白色或指定的灰度代替。
二、图像的分块显示和清除

  1. 图像的扫描显示和清除

  扫描显示图像是最基本的特效显示方法,它表现为图像一行行(或一列列)地显示出来或从屏幕上清除掉,有种大戏院种的拉幕效果。根据扫描的方向的不同,可以分为上、下、左、右、水平平分和垂直平分等六种扫描。这里以向下移动为例,分别介绍显示和清除的实现。其余的扫描效果可以依次类推。向下扫描显示的实现方法是:从图像的底部开始将图像一行一行的复制到目标区域的顶部。每复制一行后,复制的行数便要增加一行,并加上一些延迟;向下移动清除的实现方法是图像向下移动显示,并在显示区域的上部画不断增高的矩形。

  1)扫描显示的代码:
CdibView::OnImageDownScan()
{
CDibDoc *pDoc=GetDocument();
HDIB hdib;
CClientDC pDC(this);
hdib=pDoc->m_hDIB;//获取图像数据句柄;
BITMAPINFOHEADER *lpDIBHdr;//位图信息头结构指针;
BYTE *lpDIBBits;//指向位图像素灰度值的指针;
HDC hDC=pDC.GetSafeHdc();//获取当前设备上下文的句柄;
lpDIBHdr=( BITMAPINFOHEADER *)GlobalLock(hdib);//得到图像的位图头信息;
lpDIBBits=(BYTE*)lpDIBHdr+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);//获取指向图像像素值;
SetStretchBltMode(hDC,COLORONCOLOR);
//显示图像;
for(int i=0;i<lpDIBHdr->biHeight;i++)
//每次循环显示图象的“0”到“i”行数据;
SetDIBitsToDevice (hDC,0,0,lpDIBHdr->biWidth, lpDIBHdr->biHeight,
0, 0,0, i,
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS
);
DelayTime(50);//延迟;
}
GlobalUnlock(hdib);
return;
}

  2)清除代码:
…………………………………//由于篇幅的限制,省略了与上面的相同代码
Cbrush brush(crWhite);//定义一个“白色”的刷子;
Cbrush *oldbrush=pDC->SelectObject(&brush);
for(int i=0;i < lpDIBHdr->biHeight ;i++)
{//每次循环将目标区域中的“0”到“i”行刷成“白色”;
pDC->Rectangle(0,0,lpDIBHdr->biWidth,lpDIBHdr->biHeight);
DelayTime(50);
}
…………………………………

  2. 百页窗效果

  所谓百页窗显示效果,就如同关闭和开启百页窗一样,图像被分为一条条或一列列地分别显示或清除掉,根据显示时以行或列为单位可以将该效果分为垂直或水平两种方式。以垂直百页窗为例来说明如何实现这种特效显示。实现垂直百页窗显示时,需要将图像垂直等分为n部分由上向下扫描显示,其中每一部分包括m个条、这个n可以根据具体应用时的需要来决定、m既为图像的高度除n。扫描显示时,依照差值进行扫描显示,即第k次显示k-1、k*m-1、…k*n-1条扫描线。同样,垂直百页窗清除的实现与垂直百页窗的显示相似,不同的是将绘制位图换成画矩形而已。在下面的例子中,我将图像的分成8份。
…………………………………
int m=8;
int n=lpDIBHdr->biHeight/m;//图像的高度能够整除8;
for(int l=1;l<=m;l++)
for(int k=0;k<n;k++)
//每次循环依次显示图像中的k-1、k*m-1、…k*n-1行; 
StretchDIBits (hDC,0,4*k+l-1,lpDIBHdr->biWidth,1,
0, lpDIBHdr->biHeight-4*k-l+1,lpDIBHdr->biWidth,1,
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);//juanlianxiaoguo

DelayTime(50);
}
…………………………………
3.栅条显示特效

  栅条特效是移动特效的复杂组合,可以分为垂直栅条和水平栅条两类。它的基本思想是将图像分为垂直或水平的的小条,奇数条向上或向左显示/清除,偶数条向下或向右显示/清除。当然也可以规定进行相反的方向显示/清除。下面的代码是实现垂直栅条的例子:
…………………………………
int m=8;
for(int i=0;i<=lpDIBHdr->biHeight;i++)
for(int j=0;j<=lpDIBHdr->biWidth;j+=m)
{//向下显示偶数条;
StretchDIBits (hDC,j,0,m,i,j,lpDIBHdr->biHeight-i,
m,i,
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);//juanlianxiaoguo
j=j+m;
//向上显示奇数条;
StretchDIBits (hDC,j,lpDIBHdr->biHeight-i,m,i,j,0,
m,i,
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);//
DelayTime(20);
}…………………………………

  4.马赛克效果

  马赛克显示是指图像被分成许多的小块,它们以随机的次序显示出来,直到图像显示完毕。实现马赛克的效果主要解决的问题是如何定义显示随机序列的小方块,这个问题的解决可以在定义过小方块的基础上,用一个数组来记录各个方块的左上角的坐标的位置。显示图像过程中,产生一个随机数来挑选即将显示的小方块,显示后将该方块的位置坐标从数组中剔除。清除过程与之相仿。剔除显示过的方块的位置坐标的方法是将该数组中的最后的一个点的坐标拷贝到当前位置,然后删除数组中的最后点的坐标,经过实现发现这样处理有时显示的图像是不完整的,分析其原因是生成随机数的过程有舍入溢出误差。读者可以采用其它的办法解决这个问题,例如可以生成固定的随机数组或采用一个动态的数组来跟踪未显示的图像方块的坐标等方法。
…………………………………
int m,n;
int RectSize=60;//方块的宽、高尺寸为60个像素;
if(lpDIBHdr->biWidth%RectSize!=0)//得到图像水平方块的个数;
m= lpDIBHdr->biWidth/RectSize+1;
else
m= lpDIBHdr->biWidth/RectSize;
if(lpDIBHdr->biHeight%RectSize!=0)//得到图像垂直方块的个数;
n= lpDIBHdr->biHeight/RectSize+1;
else
n=lpDIBHdr->biHeight/RectSize;
POINT *point=new POINT[n*m];//申请一个数组用来记录各个方块的左上角的坐标;
POINT point1;
for(int a=0;a<m;a++)//将各个方块的左上角的坐标记录到数组中;
for(int b=0;b<n;b++)
{
point1.x=a*RectSize;
point1.y=b*RectSize;
*(point+a*b+b)=point1;
}
//开始随机的显示各个小方块;
double fMax=RAND_MAX;//定义Rand()函数的最大值;
for(int k=m*n-1;k>=0;k--)
{
int c=(int)((double)(m*n)*rand()/fMax);
int mx=point[c].x;
int my=point[c].y;
//显示对应的图像的小块;
StretchDIBits (hDC,mx,my,RectSize,RectSize,
mx,lpDIBHdr->biHeight-my,RectSize,RectSize,
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);
point[c].x=point[k].x;
point[c].y=point[k].y;
DelayTime(50);
}
…………………………………

  5.图像的淡入淡出效果

  图像的淡入淡出的显示效果被广泛的应用在多媒体娱乐软件中,是一种特别重要的特效显示方法。淡入就是将显示图像的目标区域由本色逐渐过度的图像中的各个像素点的颜色;淡出就是由显示的图像逐渐过度到目标区域的本色。实现图像的淡入淡出有两种办法:一是均匀的改变图像的调色板中的颜色索引值;另一种方法是改变图像像素的灰度值。第一种方法实现起来比较繁琐,第二种方法就比较简单。下面是我们采用第二种方法实现图像淡入效果的代码:
…………………………………
//申请一个与图像缓冲区相同大小的内存;
hdibcopy=(HDIB)GlobalAlloc(GMEM_SHARE,lpDIBHdr->biWidth*lpDIBHdr->biHeight);
lpbits=(BYTE*)GlobalLock(hdibcopy);
//将缓冲区的数据初始化; 
for(int k=0;k<lpDIBHdr->biWidth*lpDIBHdr->biHeight;k++)
{
*(lpbits+k)=(BYTE)255;
}
//显示最初的图像为“白色”
StretchDIBits (hDC,0,0,lpDIBHdr->biWidth,lpDIBHdr->biHeight,0,0,
lpDIBHdr->biWidth,lpDIBHdr->biHeight,
lpbits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);
//布尔变量end用来标志何时淡入处理结束; 
BOOL end=false;
while(!end)
int a=0;
for(int k=0;k<lpDIBHdr->biWidth*lpDIBHdr->biHeight;k++)
{
//判断是否待显示的像素的灰度值已经小于原始图像对应点的灰度值,如是则计数;
if(*(lpbits+k)<*(lpDIBBits+k))
a++;
else//否则对应点的灰度值继续减少;
*(lpbits+k)-=(BYTE)10;
}
//显示处理后的图像数据lpbits;
StretchDIBits (hDC,0,0,lpDIBHdr->biWidth,lpDIBHdr->biHeight,0,0,
lpDIBHdr->biWidth,lpDIBHdr->biHeight,
lpbits,(LPBITMAPINFO)lpDIBHdr,
DIB_RGB_COLORS,
SRCCOPY);
//如果所有的点的灰度值的都小于或等于原始图像的像素点的灰度值,则认为图像的淡入处理结束。
if(a==lpDIBHdr->biWidth*lpDIBHdr->biHeight) 
end=true; DelayTime(50);
…………………………………

  本文上面的内容介绍了几种图像的特殊显示效果,代码在Windows2000和Visual C++6.0编程环境下编译通过,运行正常,处理达到了预期的效果。读者可以将上面介绍的显示图像的函数和处理思路结合起来,实现更多效果。 
VC数字图像处理编程讲座之五
本期讲座继续介绍BMP图像特效效果
第四节 BMP图像操作的补充篇

  上一讲中我们介绍了图像特效显示操作的实现方法,如随机显示效果、马赛克效果、拉幕显示效果等,由于篇幅的限制,还有许多效果没有介绍;本期讲座将接着上一讲的内容,继续介绍一些图像特效显示效果。
另外,我们前面的学习都是针对现成的BMP图像,在实际工作学习中,绝大部分处理图像过程都是在一个系统环境中,也就是说需要和图像数据的获取设备直接打交道,一般情况下,计算机图像处理系统从系统层次上可分为高、中、低档三个层次,目前比较普及的是低档次的系统,该系统由CCD(摄像头)、图像采集卡、计算机三个部分组成,其结构简单,应用方便,效果也比较不错,得到的图像较清晰,所以目前在工程应用中采用的比较多。这就给开发人员带来一个现实的问题,如何使用图像采集卡呢?目前虽然各种编程资源中基于VC开发经验的文章不少,但是关于如何在VC开发平台上使用图像采集卡的文章的确没发现,笔者借这期讲座的宝贵机会,补充介绍一下如何在程序中编写自己的代码来操作图像采集卡,从而搭建一个完整的图像处理系统。希望通过这部分内容的学习,在读者的脑海里就可以建立一个完整的图像操作系统概念;同时也能够给目前正需要利用图像采集卡开发自己的图像处理系统的朋友有所帮助。

  1. 抖动图像

  在上一节讲座中,我们讲到了如何实现图象的"雕刻""浮雕"效果,它们的实现思想是通过求取"没有处理过的相邻两个像素之间的差值"来实现的。如果没有限制"以前没有处理过的两个像素之间的操作",取而代之的是"处理以前已经操作过的像素",那末就可以将一个像素的灰度值传递到与其相邻的若干像素。事实上,有时后我们必须通过上述的约定才能实现一些效果,如图像的抖动效果。例如,为了使图象看起来好象从左上角向右下角扫过,以产生运动的感觉,必须要反复拷贝左上方的那些像素的灰度值,逐步把它们融合在一起,看起来好象图像后边有一些颜色在逐渐的消失,这就是我们要讲的图象的抖动效果。下面给出了该效果的实现代码:
void CDibView::OnDouDongImage() //产生"抖动"效果图函数
{
 HANDLE data1handle;//用来存放图像数据的句柄;
 LPBITMAPINFOHEADER lpBi;//图像的信息头结构;
 CDibDoc *pDoc=GetDocument();//得到文挡指针;
 HDIB hdib;//用来存放图像数据的句柄;
 unsigned char *pData;//指向原始图像数据的指针;
 unsigned char *data;//指向处理后图像数据的指针;
 hdib=pDoc->m_hDIB;//拷贝存放已经读取的图像文件数据句柄;
 lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib);//获取图像信息头;
pData=(unsigned char*)FindDIBBits((LPSTR)lpBi);
//FindDIBBits是我定义的一个函数,根据图像的结构得到位图的灰度值数据;
pDoc->SetModifiedFlag(TRUE);
//设置文档修改标志为"真",为后续的修改存盘作准备;
data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); //声明一个缓冲区用来暂存处理后的图像数据;
data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);//得到该缓冲区的指针;
AfxGetApp()->BeginWaitCursor();
int i,j,buf;
for( i=lpBi->biHeight; i>=2; i--)//从图像右下角开始对图像的各个像素进行"抖动"处理;
  for( j=lpBi->biWidth; j>=2; j--)
  {
//抖动处理、从图像的右下角开始计算图像斜上方相邻像素的均值;
buf=(*(pData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)+*(pData+(lpBi->biHeight-i+1)*WIDTHBYTES(lpBi->biWidth*8)+j-1))/2;
if(buf>255) buf=255;//限制像素点的灰度范围为0-255;
if(buf<0)buf=0; *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf;
}
for( j=0; j<biHeight; j++)
for( i=0; i<biWidth; i++)
//重新写回原始图像的数据缓冲区;
*(pData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j);     AfxGetApp()->EndWaitCursor();
pDoc->m_hDIB =hdib//将处理过的图像数据写回pDoc中的图像缓冲区;
GlobalUnlock((HGLOBAL)hdib);//解锁、释放缓冲区;
GlobalUnlock((HGLOBAL)data1handle);
GlobalFree((HGLOBAL)hdib);
GlobalFree((HGLOBAL)data1handle);
Invalidate(TRUE);//显示图像


  对于比较复杂的图像,计算当前像素的灰度和斜上方像素的均值产生的抖动效果可能不明显,为了解决这个问题,笔者的解决办法是隔行隔列的计算,比如说计算当前位置(i j)的灰度值,我取(i,j)和(i-2,j-2)两个位置的像素的灰度值的平均。
2.图像合成技术

  图像合成技术很重要,其实质是操作将两幅或两幅以上的图像,将它们的信息融合在一起,产生1+1>2的效果。我们在进行图像合成的时候可以采用Alpha值的方法,下面来看一下如何利用Alpha值来合成两张图片。

  采用Alpha图象合成的方法,就是最终合成的图象的各点像素值是由用来制作合成图的两张图片的相应点的像素值按一定比例混合而成的,这个比例由Alpha值决定,具体算式如下: 
resultPixe= (pixel1*(255-Alpha)+pixel2*Alpha)/255;
// Alpha取值范围从0到255 

  上面的算式中,pixel1代表图像1的当前像素点的灰度值,pixel2代表图像2的当前像素点的灰度值,Alpha可以看作两个像素在最终合成的结果中所占的权重。可以看出,只要修改Alpha的值,就可以改变合成后的图象中用来合成的两张图片各自所占的比值,改变合成后的显示效果。我们可以利用这个方法,按一定的时间间隔修改Alpha的值、这样就可以很轻易的制作出生动的淡入淡出效果、实现两幅图片间的平滑过度效果。下面给出一个制作合成图的具体源码:
BOOL CompoundImage(HANDLE HDib1,HANDLE HDib2,int alpha)
{
BYTE lpData1,lpData2;
// 源图象2的信息 
//由于待合成的两个图象的格式、大小是一样的,所以我只获取一个图像文件的图像信息就可以了。
LPBITMAPINFO lpBi=(LPBITMAPINFO)HDib2; 
// 计算图象数据偏移量
lpData2=(LPVOID)((LPBYTE)lpBi->bmiColors+256*sizeof(RGBQUAD)); 
//获取源图像2的图像数据;
lpBi=(LPBITMAPINFO)HDib1;
lpData1=(LPVOID)((LPBYTE)lpBi->bmiColors+256*sizeof(RGBQUAD));
//通过alpha值合并两张图象的像素值 
for ( int i=0;i< lpBi->biWidth; i++ )
for(int j=0;j< lpBi->biHeight;j++ )
{
//套用alpha图像混合公式;
*(lpData1+i*WIDTHBYTES(lpBi->biWidth*8)+j)=(*(lpData1+i*WIDTHBYTES(lpBi->biWidth*8)+j)*(255-alpha)+ *(lpData2+i*WIDTHBYTES(lpBi->biWidth*8)+j)*alpha)/255;
}
return lpData1;
}

  以上内容我们主要是讲述了alpha图像混合的实现原理和方法,其实读者大可不必自己写这么多代码,微软给我们提供了一个名为AlphaBlend()的函数,它就可以直接实现图像合成的功能,具体怎么使用,还请读者参考MSDN。
3.采集卡的操作

  图像处理所涉及的应用领域有军事应用、医学诊断、工业监控、物体的自动分检识别等等,这些应用系统无不需要计算机提供实时动态,效果逼真的图像。目前获取实时图像一般都需要在计算机内部安装一个图像采集卡,用来实现CCD端获取的模拟图像的数字化转换。笔者结合自己在项目开发中积累的一些经验,谈一下如何操作图像采集卡、然后再此基础基础上再实现一些特殊处理。

  笔者的摄像机采用台湾BENTECH INDUSTRIAL 有限公司生产的CV-155L黑白摄像机。该摄像机分辨率为752x582。图象采集卡采用的是北京中科院科技嘉公司开发的基于PCI 总线的CA-MPE 1000 黑白图象采集卡。一般情况下,使用图像采集卡分三步,首先安装采集卡的驱动程序,并将虚拟驱动文件VxD.vxd拷贝到Windows的SYSTEM目录下;这时候就可以进入开发状态了,进入VC开发平台,生成新的项目,由于生产厂家为图像采集卡提供了以mpew32.dll、mpew32.lib命名的库文件,库中提供了初始硬件、采集图像等函数,为使用这些函数,需要在新项目上连接该动态库;最后一步就是采集图像并显示处理了,这一步要设置系统调色板,因为采集卡提供的是裸图形式,既纯图像数据,没有图像的规格和调色板信息,这些需要开发者自己规定实现,下面是实现的部分代码: 
////////////////////////////////////////
CTestView::CTestView()
{
W32_Init_MPE1000();//初始化采集卡
W32_Modify_Contrast(50);//下面的函数是为了对采集卡进行预设置 
W32_Modify_Brightness(45);//设置亮度 
W32_Set_HP_Value(945);//设置水平采集点数
wCurrent_Frame = 1;//当前帧为1,获取的图像就是从这帧取得的 
// 设置采集信号源,仅对MPE1000有效 
W32_Set_Input_Source(1);//该图像采集卡支持三路视频,目前采集的图像来自第二路输入端;
W32_Set_PAL_Range(1250, 1024);//设置水平采集范围 
W32_Set_VGA_Mode ( 1 ); 采用PAL制式;
wGrabWinX1 = 0; // 采集窗口的左上角的坐标
wGrabWinY1 = 0;
firstTime=TRUE//第一次采集;
bGrabMode = FRAME; //抓图模式为?格式;
bZipMode = ZIPPLE; //压缩模式为ZIPPLE;
lpDib=NULL;//存放获取的图像数据缓冲区为空;

////////////////////////////////////////
CTestView::~CTestView()
{
W32_Close_MPE1000();//关闭采集卡 
}
////////////////////////////////////////////
void CTestView::OnGraboneframe()//显示采集的图象,双击鼠标采集停止

// TODO: Add your command handler code here
wCurrent_Frame = 1; 
// 设置采集目标为内存 
W32_CACardParam (AD_SETGRABDEST, CA_GRABMEM);
// 启动采集
if (lpDib != NULL)//如果图像缓冲区不为空,释放该缓冲区; 
{
GlobalUnlock( hglbDIB );
GlobalFree( hglbDIB ); 
}
//为采集到的图像数据分配内存;
hglbDIB=GlobalAlloc(GHND, (DWORD)wImgWidth*(DWORD)wImgHeight ); 
lpDib = (BYTE *)GlobalLock( hglbDIB ); //得到图像数据的指针;
hdc = GetDC()->GetSafeHdc( ) ; //获取视图的设备上下文句柄;
if(lpDib != NULL) 

cxDib = wImgWidth; 
cyDib = wImgHeight; 
SetLogicPal( hdc, cxDib, cyDib, 8 ); //设置调色板;
SetStretchBltMode (hdc, COLORONCOLOR) ; 
bGrabMark = TRUE
while (bGrabMark == TRUE

if(msg.message==WM_LBUTTONDBLCLK) //分析是否为鼠标双击消息;
bGrabMark = FALSE;//如为鼠标双击消息,停止采集图象;
W32_ReadXMS2Buf (wCurrent_Frame,lpDib) ; //将图象数据读入到图像数据缓冲区;
SetDIBitsToDevice (hdc, 0, 0, cxDib, cyDib, 0, 0, 
0, cyDib, (LPSTR) lpDib, 
bmi, DIB_RGB_COLORS) ; //显示图像;
}
// 停止采集
W32_CAStopCapture();
::ReleaseDC( GetSafeHwnd(), hdc );
return ; 

//将下面这个函数添加在视图类的CTestView::OnSize()函数中,就可以对系统的调色板进行设置。
void WINAPI InitLogicPal( HDC hdc , short width, short height, WORD bitCount )

int j, i; 
short cxDib, cyDib; 
LOGPALETTE * pLogPal; 
j=256 ; 
if((pLogPal=(LOGPALETTE*)malloc(sizeof(LOGPALETTE)+ (j*sizeof(PALETTEENTRY)))) == NULL) 
return ;
pLogPal->palVersion=0x300; //设置调色版的颜色信息;
pLogPal->palNumEntries=j; 
for (i=0;i pLogPal->palPalEntry[i].peRed = i ; 
{
pLogPal->palPalEntry[i].peGreen = i ; 
pLogPal->palPalEntry[i].peBlue = i ; 
pLogPal->palPalEntry[i].peFlags = 0; 

hPal = ::CreatePalette(pLogPal); //创建调色板;
delete pLogPal; 
::SelectPalette(hdc,hPal,0);//系统实现调色板;
::RealizePalette(hdc); 
cxDib = width; cyDib = height; 
if ( (bmi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + j*sizeof(RGBQUAD))) == NULL ) 
return ; 
//定义图