图像处理算法速度指标

2016-09-23 15:53:04 yjy122333 阅读数 1765

图像处理原理

关于图象,要明确图片是由各个相素点组合而成,每个相素点又是由R,G,B不同比例混和而成,当明白了成相原理,当我们想对图片进行颜色处理,譬如滤镜效果,就可以通过改变R,G,B的比例值从而达到我们期望的颜色效果。

 

分析
安卓中提供图片颜色处理的方法类,可以进行色相,明度,饱和度的调整来实现对图片颜色特效的处理,在色彩丰富的彩屏上,颜色的渲染效果很好,图片处理结果也很好,而在电子书上,由于是墨水屏,可支持的色调就是16种可配的黑白灰,所以最好是将图片处理为灰度图片,再进行显示,效果会更加美观。进行图象处理,调配RGB的值,实质上也就是一些图象处理的配比算法,比如运用在电子书上的灰度算法就是RGB三者的配比相同。

 

算法代码:
public Bitmap ConvertGrayImg(int resID)
{
Bitmapimg1=((BitmapDrawable)getResources().getDrawable(resID)).getBitmap();  //首先要得到原图
//得到原图的长,宽,即图片的大小
int w=img1.getWidth(),h=img1.getHeight();
int[] pix = new int[w * h];  //设置一个和图片大小一样大的数组,用于存放相素值
img1.getPixels(pix, 0, w, 0, 0, w, h);  //得到相素点的值
int alpha=0xFF<<24;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
// 获得像素的颜色
int color = pix[w * i + j];
int red = ((color & 0x00FF0000) >> 16);
int green = ((color & 0x0000FF00) >> 8);
int blue = color & 0x000000FF;
color = (red + green + blue)/3;
color = alpha | (color << 16) | (color << 8) | color;
pix[w * i + j] = color;  //将改变后的像素值重置
}
}
Bitmap result=Bitmap.createBitmap(w, h, Config.ARGB_4444);  //创建一张新图 Config.ARGB_4444:这个参数,是可支持16种色度值
result.setPixels(pix, 0, w, 0, 0,w, h); //将最终改变后的的相素值设置给新的图片
return result;
}

 

分析结果

这段代码是显示在电子书上效果最好的,能够淡出图片过渡的感觉,没有那么分明的一块,一块的显示感觉。
不足的地方就是此款效率低,由于是通过遍历并修改每个像素点的值,所以比较消耗时间,不过处理速度比较快,因为图片的大小都不大,处理出结果也是非常快的,其实一般做开发,采取的都是用安卓给我们提供的类方法进行图象的处理,速度快且效率高。然而安卓中处理成灰度效果的接口并不适用于电子书上,效果不好。

 

自定义算法和安卓内置API的性能对比

图片1显然,API的效率快,不过在电子书上的显示效果不理想。

 

最后总结

关于图片处理,循环遍历每个相素点,对于较小的图片,效果好且用时不是太多(小于100KB),如果图片大于300KB,用户体验就差了,所以可在图片小于100KB的时候进行图像算法处理,否则不予处理。

2015-06-09 00:16:44 jia20003 阅读数 8601

大概是四月底的时候,有人加我QQ问我是否做能做一些基于图像皮肤检测的算法,

主要是实现对皮肤六项指标:

1.      水分

2.      有份

3.      痤疮与痘痘

4.      色斑与肤色

5.      皱纹

6.      毛孔

听到这些内容,我稍微思考了一下,心里有了点思路,跟他交流了一下,关于怎么

做的思路就更加清晰起来,于是我先基于JAVA语言实现了第一版,然后把它改到

android上面,发现android代码改动很小,就可以在android上运行我的测试程序

了,但是感觉速度很慢,大概要十几秒才能出结果,发给了那个哥们,他测试了一

下也很郁闷,说速度太慢了,准确率也不是很高,怎么办,于是我仔细对照了一下

,发现我用Java语言实现时,在桌面版都是基于int来处理RGB像素的每个通道数

据,android上面DVM这么玩就有点卡,于是我就全部改成了每个通道基于BYTE

来实现处理,然后增加了皮肤检测,这样过滤非SKIN像素,再降采样处理整个

Bitmap数据,果然速度很快了,只要三秒左右就可以出结果,连我自己都觉得

自己是大神了,然后就想把android的人脸检测加上去,实现分块检测,这样

可以更好的检测人脸皱纹类型,经过一番各种坑之后,android上的人脸检测

API我学会了使用,但是发现速度不给力,加上去等于作茧自缚。于是我又放

弃了。又经过一番各种调试,别人帮忙把相机功能加上了,这样就实现了拍

照直接检测出结果,于是第二个问题出现了,经常出现APP闪退,我哭,经

过一番测试,发现低分辨率时候才会出现闪退,后来我才明白问题出在我对

低分辨率进行降采样,已经超出了像素范围,我哭!解决了之后闪退问题就

不见了,终于可以正常运行了,也可以正常测试了:

测试程序中的效果截屏:


这个就是拍照检测的结果,

原理都是常见的图像处理算法,可以说本人的博客文章已经包含了该应用的所有

图像处理算法知识,只是如何活学活用取决于大家都图像处理问题的认知与理解。

 通过该算法的开发,我第一次将我学到的图像处理知识应用到实践之中用于解决

实际问题,同时也发现真实图像处理问题,不是一步算法就可以实现的,而是一

系列图像处理算法的综合运用。



2015-10-11 09:32:54 caoleiwe 阅读数 31739

The ‘good-ness’ of any algorithm is gauged by measuring the correlation of algorithmic scores with subjective (differential) mean opinion scores (DMOS/MOS) on a large dataset spanning different distortion.

正如以上所说对于一种图像质量评价算法性能好坏的评估就是看在具有不同失真的大数据集上观察者的主观评分和算法评分的相关度。如果它们的相关度较高,则说明该质量评价算法的性能较好,否则反之。

那么什么是相关呢?简单的说就是当一个或几个相互联系的变量取一定数值时,与之相对应的另一个变量的值虽然不确定,但它仍然按某种规律在一定范围内变化,变量间的这种关系,被称为相关关系。相关关系可以用相关系数来评价,其绝对值越高表明其越相关,否则反之。相关系数的取值一般在-1到1之间。可以回想一下我们学过的概率论与数理统计中的相关系数,当两个随机变量的相关系数的绝对值为1时,可以将一个随机变量表示为另一个随机变量的一元线性函数,这时变量的相关度最高。例如Y=a*X+b。但是当其相关系数的绝对值小于1时,就不会有这么明确的函数关系。此时的相关度较小。下图是两个变量相关的示例,图中的每一点是变量X,Y的一对取值。可以看出这两个变量近似于线性相关,其相关系数接近于1。

现在用到的评价参数主要有四个。它们分别是SROCC,KROCC,PLCC和rMSE。下面分别对它们进行具体介绍。因为现在由于做图像质量评价的需要,我只是大概了解一下它们所表达的含义以及它们的计算方法,可能不像统计学中那么的专业。

1. SROCC

SROCC的全称是the Spearman rank-order correlation coefficient,又简称为Spearman’s rho。下面我先例举一个具体的例子,然后对其进行具体介绍。

假设现在有10个学生的数学(X)和英语(Y)成绩,现在我们要来判断X和Y的相关性。

num X Y Rank(X) Rank(Y) d d2
1 56 66 9 4 5 25
2 75 70 3 2 1 1
3 45 40 10 10 0 0
4 71 60 4 7 -3 9
5 62 65 6 5 1 1
6 64 56 5 9 -4 16
7 58 59 8 8 0 0
8 80 77 1 1 0 0
9 76 67 2 3 -1 1
10 61 63 7 6 1 1

如上表所示,X列表示某个学生的数学成绩,Y列表示其对应的英语成绩。然后分别对数学成绩和英语成绩进行等级划分,分数最高的划分为1,最低的划分为10,其它的以此分别划分为2到9之间的一个整数。划分后如Rank(X)列和Rank(Y)列所示。然后对得到的每一个同学的数学成绩等级减去英语成绩等级得到数据列d,然后对d平方得到d的平方数据列。在这个过程中如果某几个同学的某一门成绩相同,例如张三和李四的数学成绩都是70,假设它们被划分的等级分别为3和4,反之亦然,则需要先对等级划分进行平均,这里等级平均后是3.5,然后把它们的等级划分都定为3.5。下面我们就可以来计算它们的SROCC了。

公式一:

srocc=16ni=1d2in(n21)

公式二:

srocc=ni=1(uiu¯)(viv¯)ni=1(uiu¯)2ni=1(viv¯)

对于以上两个公式,当某个特定的数据集合(如数学成绩)中有相等的值时使用公式二,否则使用公式一。其中n为采集的数据样本中数据对的个数,上例中n为10。其中u¯v¯分别为某个特定的数据集合在完成等级划分之后的等级平均值,比如上例中数学成绩的等级平均。uivi分别对相应数据对的等级划分。

我们从以上例子可以看出srocc主要评价的是两组数据的等级相关性。也就是对于两个变量的一些数据对(如上例所示),数据对的意思是当一个变量取一个值时观察到另一个变量取到的值,当对每个变量的数据进行等级划分后,我们可以得到每个变量的等级序列,如上例中的Rank(X)和Rank(Y),若两个变量的srocc越高则表明它们的等级相关性较高(比如对于上例中任意的i,若Rank(xi)=Rank(yi),则表明它们的等级相关非常高,srocc=1),否则反之。

2.KROCC

KROCC的全称是the Kendall rank-order correlation coefficient,又简称为Kendall’s tau。和srocc一样这里我也是通过例子计算来解释它。在这里我还是用srocc中的那个例子。

在这里我先讲一些基本的概念。假设我们现在需要评价两个变量X和Y的相关性,(x1,y1) ,(x2,y2),……., (xn,yn)是变量X和Y的一些数据对,(xi,yi)可以看做是上面例子中第i个学生的数学成绩和英语成绩组成的数据对。现在我们从这n个数据对中任意选两个数据对(xi,yi)(xj,yj)形成[(xi,yi),(xj,yj)],其中i!=j且[(xi,yi),(xj,yj)][(xj,yj),(xi,yi)],这样的对子一种有N=n(n+1)2种。对于任意的[(xi,yi),(xj,yj)],若xi>xjyi>yj或者xi<xjyi<yj则称呼这样的对子是concordant,它们的个数记为P。xi>xjyi<yj或者xi<xjyi>yj则称呼这样的对子discordant,它们的个数记为Q。若xi=xjyi>yj或者xi=xjyi<yj,这样的对子个数记为X0。若xi>xjyi=yj或者xi<xjyi=yj,这样的对子个数记为Y0。若xi=xjyi=yj,这样的对子的个数记为(XY)0。因此N=P+Q+X0+Y0+(XY)0

有了这些概念之后我们就可以来计算krocc了,下面就是其公式。

krocc=PQ(P+Q+X0)(P+Q+Y0)

从这里的概念的解释我们可以了解到,krocc也是评价两个变量的等级相关的,这里就不再累述了。

3.PLCC

PLCC的全称是Pearson product-moment correlation coefficient。因为它相对比较简单,所以我这里直接给出它的公式:

plcc=COV(X,Y)δXδY

对概率论和数理统计还比较熟悉的同学可以知道这就是我们学过的线性相关系数,若我们把X和Y看做两个随机变量,则分母是随机变量X和Y的协方差,分子是它们的标准差的乘积。plcc就是来评价两组数据的线性相关关系的。若其绝对值越接近于1,则其之间的关系就越可以用一个线性直线方程来表示。多余的我就不再累述了。

4.rMSE

rMSE的全称是root mean square error。其实他就是MSE(均方误差)的平方根。如果拿我举得的那个例子来说的话,那它的计算公式是:

rMSE=ni=1(xiyi)2n

其中(xi,yi)是对应的数学成绩和英语成绩的数据对。直面理解rMSE就是数据错误平方均值的平方根。这里只是一种离散情况的简单解释,更复杂的情况读者可以参阅我参考资料中的解释和其它方面的资料。当然其值应该越小越好,这样它的错误就小啊!而上面的三个系数对于一种质量评价算法来说应该越大越好,这样的话预测值就和主观评价值越接近,这样的质量评价算法肯定好啊!

最后再说几句,srocc,krocc和plcc在matlab中可以直接利用函数corr计算。但是在计算plcc和rMSE的时候需要先将要比较的两组数据或者两个变量的数据(这个我也只是大概了解一下,想详细了解的同学要自己查资料了)进行一下非线性回归分析或拟合之类的操作然后再去算。matlab中可以用nlinfit这个函数。
参考资料:
1:https://statistics.laerd.com/statistical-guides/spearmans-rank-order-correlation-statistical-guide.php

2:http://www.epixanalytics.com/modelassist/AtRisk/Model_Assist.htm#Probability_theory_and_statistics/The_basics/Probability_theorems/Useful_concepts/Rank_Order_Correlation_Coefficient.htm

3:http://blog.csdn.net/wsywl/article/details/5859751

4:https://en.wikipedia.org/wiki/Spearman%27s_rank_correlation_coefficient

5:http://blog.csdn.net/wsywl/article/details/5889419

6:https://onlinecourses.science.psu.edu/stat509/node/158

7:https://en.wikipedia.org/wiki/Kendall_rank_correlation_coefficient

8:http://blog.csdn.net/wsywl/article/details/5727327

9:https://en.wikipedia.org/wiki/Pearson_product-moment_correlation_coefficient

10:https://en.wikipedia.org/wiki/Mean_squared_error

11:https://en.wikipedia.org/wiki/Root-mean-square_deviation

2014-12-11 09:32:27 hnulwt 阅读数 17067
转载:http://dsqiu.iteye.com/blog/1669891
感谢作者的付出,谢谢分享.
终于写完数字图像分割这部分内容了,由于内容比较多,因此做一个小的内容提要,有利于更有调理的阅读,如下:
1.数字图像分割方法概要
2.基于边界分割
2.1边缘检测
2.2边界提取(简单连接,启发式搜索,曲线拟合)
3.基于区域分割
3.1阀值分割(直方图双峰,迭代法,Ostu(大律)法,基于熵的二值方法)
3.2区域生长
3.3区域分裂与合并
4.总结与实验实现(Java语言)
提供最佳阀值分割迭代算法,Otsu阀值分割算法,基于熵的二值方法的实现
§1 数字图像分割处理根据不同特征,分为两类:基于边界分割和基于区域分割,主要方法有:
灰度阀值分割
边界分割法
基于纹理的分割
区域生长法
§2 基于边界分割
§2.1 边缘检测
基于边界分割其实就是点,线和边缘检测,边缘检测我在之前的一篇博文(http://dsqiu.iteye.com/blog/1638589)有介绍,可以前往查看。下面附上不同检测算子的对比:
§2.2 边界获取
使用边缘检测算子检测处理的都是孤立的一些点,需要进行边界获取将孤立点连接成线。
边缘检测的结果得到许多梯度大的点,还必须进一步处理:
原因:物体边界一般是线,而非孤立的点。必须把边缘点连接成边缘链,形成直线、曲线、各种轮廓线等,直到能表示图像中物体的边界。边界表示可以使图像的表示简洁,可以用来完成一定的文字识别任务或高层次的理解创造前提。边缘形成线特征包括二个过程:抽取可能的边缘点;将虑出的边缘连接成直线、曲线、轮廓线,或用一定的直线、曲线去拟合它们。
困难:
梯度大的点、不一定真正是边缘点。
边缘点的梯度也有可能小于周围其它点。
分岔和缺损。
对于一阶算子,需要对得到的边缘图进行细化,理想情况时,细化成单象素宽的闭合连通边界图。非理想情况下,边缘图像会有间隙存在,需要加以填充。对于二阶算子,过零点一般是单线,不需要细化,仍然需要补充间隙点。
细化算法见数学形态学。
边界跟踪
连点成线
例如,一个简单的算法:e(x,y)边缘强度,ф(x,y)边缘方向,两边缘点满足以下条件时可以连接:
|e(xi,yi)-e(xj,yj)|<T1
|ф(xi,yi)-ф(xj,yj)|mod 2π<T2
|e(xi,yi)|>T, |e(xj,yj)|>T
条件3 使小的干扰避免被误认为边缘。
从满足|e(xA,yA)|>T 的A 点出发,如果找不到满足以上条件的相邻点,算法停止。如果有多个满足条件的邻点,选边缘强度差和角度差小的点。再不断从新的起始点出发继续搜索。
启发式搜索
人工智能中的概念,是一种从多种可能的路径中选优的方法。搜索需要有评价函数,为每一条路径打分,以便于选择路径。边缘质量评价函数可以包括各点的边缘强度,也可以利用边缘的方向信息。
当有多条可能路径时,可以用全搜索(穷举法),找到评价指标最 优的路径作为结果。然而,全搜索运算太大,组合爆炸。启发式搜索是在搜索的过程中设定一些启发性规则,当规则满足时就认为某一段路经合理,不再比较其它候 选路经。例如:如果边缘方向上出现大的边缘点,则认为方向正确。边界的点稀疏或有长缺口时可以采用此方法。该技术对相对简单的图像效果很好,启发式搜索比 全搜索减少了运算量,但不一定能找到两端点间连接的全局最优路径。
图:启发性规则的例子,只有满足图中形式的连接被认为可能。
曲线拟合
如果边缘点很稀疏,可以用分段线性或高阶样条曲线来拟合这些点,从而形成边界。
图:多边形拟合
我们都学过最小二乘法作直线拟合,更复杂的二次曲线,高斯拟合等也不难实现。数学上是某种准则下的最优参数估计问题。所用准则多为均方误差最小准则,所估计的参数是曲线方程中的未知参数。具体的拟合方法参考各种参考书。
其它边界抽取方法
Hough 变换
图搜索
动态规划
使用阀值进行图像分割
阈值分割法是一种简单的基于区域的分割技术,是一种广泛使用的 图像分割技术,它利用图像中要提取的目标和背景在灰度特性上的差异,把图像视为具有不同灰度级的两类区域的组合,选取一个合适的阈值,以确定图像中每个像 素点是属于目标还是属于背景。它不仅可以极大的压缩数据量,而且也大大简化了图像信息的分析和处理步骤。阈值法是首先确定一个处于图像灰度级范围内的灰度 阈值T,然后将图像中每个像素的灰度值都与这个阈值T比较,根据它是否超过阈值T而将该像素归于两类中的一类。常用的方法就是设定某一阈值T,用T将图像 分割成大于阈值T的像素群(目标)和小于阈值T(背景)的像素群两部分。这两类像素一般属于图像中的两类区域,所以对像素根据阈值分类达到了区域分割的目 的。输入图像是F(x,y),输出图像是B(x,y),则:
                                              
§3.1 阈值化分割方法
根据图像本身的特点,可分为单阈值分割方法(全局)和多阈值分 割方法(局部);也可分为基于像素值的阈值分割方法、基于区域性质的阈值分割方法和基于坐标位置的阈值分割方法。若根据分割算法所具有的特征或准则,还可 以分为直方图峰谷法、最大类空间方差法、最大墒法、模糊集法、特征空间聚类法、基于过渡区的阈值选取法等。
 §3.1.1直方图阈值的双峰法
该阈值化方法的依据是图像的直方图,通过对直方图进行各种分析 来实现对图像的分割。图像的直方图可以看作是像素灰度值概率分布密度函数的一个近似,设一幅图像仅包含目标和背景,那么它的直方图所代表的像素灰度值概率 密度分布函数实际上就是对应目标和背景的两个单峰分布密度函数的和。图像二值化过程就是在直方图上寻找两个峰、一个谷来对一个图像进行分割,也可以通过用 两级函数来近似直方图。
若灰度图像的直方图,其灰度级范围为i=0,1,…,L-1,当灰度级为k时的像素数为 ,则一幅图像的总像素数N为:
                                               
 灰度级 i出现的概率为:
                                               
当灰度图像中画面比较简单且对象物的灰度分布比较有规律时,背景和对物象在图像的灰度值方图上各自形成一个波峰,由于每两个波峰间形成一个低谷,因而选择双峰间低谷处所对应的灰度值为阈值,可将两个区域分离。
把这种通过选取直方图阈值来分割目标和背景的方法称为直方图阈值双峰法。如下图所示,在灰度级t1和t2两处有明显的波峰,而在t处是一个谷点
具体实现的方法先做出图像f(x,y)的灰度直方图,若出现背 景目标物两区域部分所对应的直方图呈双峰且有明显的谷底,则可以将谷底点所对应的灰度值作为阈值t,然后根据阈值进行分割就可以将目标从图像中分割出来。 这种方法适用于适用于目标和对景的灰度差较大,直方图有明显谷底的情况。
将原始图像和阈值分割后的图像比较,可以发现有些前景图像和背 景图像的灰度值太接近,导致有些前景图像没有从背景中分离出来,图像失真了。双峰法比较简单,在可能情况下常常作为首选的阈值确定方法,但是图像的灰度直 方图的形状随着对象、图像输入系统,输入环境等因素的不同而千差万别,当出现波峰间的波谷平坦、各区域直方图的波形重叠等情况时,用直方图阈值难以确定阈 值,必须寻求其他方法来选择适宜的阈值。
§3.1.2迭代法(最佳阀值分割迭代法)
迭代法也是聚类方法中的 K-means算法,当然下面演示的K=2的情况。
迭代式阈值选取的基本思路是:首先根据图像中物体的灰度分布情况,选取一个近似阈值作为初始阈值,一个较好的方法就是将图像的灰度均值作为初始阈值;然后通过分割图像和修改阈值的迭代过程获得认可的最佳阈值。迭代式阈值选取过程可描述如下。
(1)选取一个初始阈值T。
(2)利用阈值T把给定图像分割成两组图像,记为 和 。
(3)计算 和  均值 和  。
(4)选取新的阈值T,且
(5)重复第(2)~(4)步,直至 和 均值 和  不再变化为止。
具体实现时,首先根据初始开关函数将输入图逐个图像分为前景和 背景,在第一遍对图像扫描结束后,平均两个积分器的值以确定一个阈值。用这个阈值控制开关再次将输入图分为前景和背景,并用做新的开关函数。如此反复迭带 直到开关函数不在发生变化,此时得到的前景和背景即为最终分割结果。迭代所得的阈值分割的图像效果良好。基于迭代的阈值能区分出图像的前景和背景的主要区 域所在,但在图像的细微处还没有很好的区分度。对某些特定图像,微小数据的变化却会引起分割效果的巨大改变,两者的数据只是稍有变化,但分割效果却反差极 大。对于直方图双峰明显,谷底较深的图像,迭代方法可以较快地获得满意结果,但是对于直方图双峰不明显,或图像目标和背景比例差异悬殊,迭代法所选取的阈 值不如其它方法。基于迭代的阈值能区分出图像的前景的主要区域所在,但在图像的细微处还没有很好的区分度。总的来说迭代法比双峰法分割的效果有很大的提 高。
§3.1.3大律法(Otsu阀值分割算法)
图像记t为前景与背景的分割阈值,前景点数占图像比例为 ,平均灰度为 ;背景点数占图像比例为 ,平均灰度为 ,则图像的总平均灰度为:
                                                                                        
从最小灰度值到最大灰度值遍历t,当t使得值
      
最大时的t即为分割的最佳阈值。
大津法可作如下理解:该式实际上就是类间方差值,阈值t 分割出的前景和背景两部分构成了整幅图像,而前景取值
 ,概率为 ,背景取值,概率为 , 总均值为u,根据方差的定义即得该式。因方差是灰度分布均匀性的一种度量,方差值越大,说明构成图像的两部分差别越大,当部分目标错分为背景或部分背景错 分为目标都会导致两部分差别变小,因此使类间方差最大的分割意味着错分概率最小。直接应用大津法计算量较大,因此在实现时采用了等价的公式
                                                                           
在测试中发现,大津法选取出来的阈值非常理想,表现较为良好。虽然它在很多情况下都不是最佳的分割,但分割质量通常都有一定的保障,可以说是最稳定的分割。
§3.1.4 类内方差最小方差法
对Otsu阀值分割做下变形,就可以得到类内最小方差法。对于每个可能的阈值,分成两类,分别计算类内方差:
使类内方差最小。
§3.1.5 最小错误概率分类法
已知前景和背景的概率分布的情况下:
       
例如:假设背景与前景的灰度都是正态分布,分布参数已知时可以推导出最小错误概率的门限值。假设图像上前景点的灰度值概率分布是  ,背景点是,选取阈值T 作为分割点。
则背景判为前景的错误:
则前景判为背景的错误:
总错误:
总错误最小,则对T 导数为零。得到:
如果前景背景都是正态分布:
是前景与背景点数量的比例,如果方差相同,比例也相同
则:
§3.1.6 基于熵的二值化方法
假设以阀值T分割图像,图像中低于阀值T的灰度的像素点构成目标物体(O),高于阀值T的像素点构成背景(B),则各个灰度级在本区的分布概率如下:
O区:
B区:
其中,L是图像灰度级总数,并且
目标和背景区域的熵分别为
 对图像每个灰度级分别计算
选取使w最大的灰度级作为分割图像的阀值T。
局部自适应
当照明或透射不均匀时,整幅图像分割将没有合适 的单一门限。这时,可对图像分块,对每一块分别选一门限进行分割,如果某块内有目标和背景,则直方图呈双峰。如果块内只有目标或背景,则直方图没有双峰,可根据邻居诸块得到的参数通过内插进行分割。
§3.2 基于区域的图像分割
基于边缘的图像分割:寻找区域之间的边界
基于区域的图像分割:直接创建区域
基于边缘的方法得到的结果通常不会与区域生长方法得到的分割完全一致。
二种方法结合,会是一个好办法。区域生长的方法在噪声干扰、边缘不易提取的情况下,效果更好。区域内部的一致性描述是区域生长法的基本准则。一般是其灰度,也可以考虑颜色、纹理、形状等。基于阈值的方法:基于单个点的特点。基于区域的方法考虑到相邻点的一致性。
§3.2.1 区域生长和区域合并
区域生长的原理和步骤
区域生长的基本思想是将具有相似性质的像素集合起来构成区域。具体先对每个需要分割的区域找一个种子像素作为生长的起点,然后将种子像素周围邻域中 与种子像素有相同或相似性质的像素(根据某种事先确定的生长或相似准则来判定)合并到种子像素所在的区域中。将这些新像素当作新的种子像素继续进行上面的 过程,直到再没有满足条件的像素可被包括进来。这样一个区域就长成了。
思路:从一些种子点生长,直到充满整个图像。种子点有监督选取。每个目标区域中至少有一个点。
需要确定:
如何选择一组能正确代表所需要区域的种子像素,如果计算结果可以看出聚类的情况,那么可以选择聚类中心作为种子像素。
生长的方法
和每次生长后的一致性准则,如灰度差小于阈值。
简单的生长方法,区域的所有8 邻域点。
如果该点加入后满足一致性准则,则加入。两个区域满足一定准则,可以合并,该准则可以考虑两个区域分别的均值和方差。如果没有预先确定的种子点,可采用一般步骤
1. 用某种方法把图像分割成许多小区域。
2. 定义合并相邻区域的准则。
3. 按照合并准则合并所有相邻的区域,如果没有再能够合并的块后停止。
采用不同的初始分割方法和合并准则,可以得到适应不同情况的算法。另外,区域合并得结果通常还依赖于区域合并的顺序。
相邻区域的特征值之间的差异是计算边界强度的一个尺度。如果给定边界两侧的特征值差异明显,那么这个边界很强,反之则弱。强边界允许继续存在,而弱边界被消除,相邻区域被合并。计算是一个迭代过程,每一步重新计算被扩大的区
域成员隶属关系,并消除弱边界。没有弱边界可消除时,合并过程结束。
过程看起来象一个物体内部区域不断增长,直到到达边界为止的过程。
该方法计算开销大,但能够同时利用图像的若干种性质(多种描述),对自然景物分割方面效果相对最优。
生长准则和过程
区域生长的一个关键是选择合适的生长或相似准则,大部分区域生长准则使用图像的局部性质。生长准则可根据不同原则制定,而使用不同的生长准则会影响区域生长的过程。下面介绍3种基本的生长准则和方法。
(1) 灰度差准则
区域生长方法将图像的像素为基本单位来进行操作,基本的区域灰度差方法主要有如下步骤:
①对图像进行逐步扫描,找出尚没有归属的像素;
②以该像素为中心检查它的邻域像素,将邻域中的像素逐个与它比较,如果灰度差小于预先确定的值,将它们合并;
③以新合并的像素为中心,返回到步骤②,检查新像素的邻域,直到区域能进一步扩张;
④返回到步骤①,继续扫描直到不能发现没有归属的像素,则结束整个生长过程。
采用上述方法得到的结果对区域生长起点的选择有较大依赖性。为克服这个问题可采用下面的改进方法:
①设灰度差的阈值为零,用上述方法进行区域扩张,使灰度相同像素合并;
②求出所有邻接区域之间的平均灰度差,并合并具有最小灰度差的邻接区域;
③设定终止准则,通过反复进行上述步骤②中的操作将区域依次合并直到终止准则满足为止。
另外,当图像中存在缓慢变化的区域时,上述方法有能会将不同区域逐步合并而产生错误。为克服这个问题,可不用新像素的灰度值去与邻域像素灰度值比较,而用新像素所在区域的平均灰度值去与各邻域像素的灰度值进行比较。
对一个含N个像素的图像区域R,其均值为: 
对像素是否合并的比较测试表示为:
其中T为给定的阈值。
区域生长的过程中,要求图像的同一区域的灰度值变化尽可能小,而不同的区域之间,灰度差尽可能大。下面分两种情况进行讨论:
1)设区域为均匀的,各像素灰度值为m与一个零均值高斯噪音的叠加。当测试某个像素是否合并时,条件不成立的概率为:
这就是误差概率函数,当T取3倍的方差时,误判概率为1-99.7%。这表明,当考虑灰度均值时,区域内的灰度变化应尽量小。
2)设区域为非均匀,且由两部分不同目标的图像像素构成。这两部分像素在R中所占比例分别为 ,灰度值分别为 ,则区域均值为 。对灰度值为m的像素,它与区域均值的差为: 
可知正确的判决概率为:
这表明,当考虑灰度均值时,不同部分像素间的灰度差距应尽量大。
(2) 灰度分布统计准则
这里考虑以灰度分布相似性作为生长准则来决定区域的合并,具体步骤为:
①把图像分成互不重叠的小区域;
②比较邻接区域的累积灰度直方图根据灰度分布的相似性进行区域合并;
③设定终止准则,通过反复进行步骤②中的操作将各个区域依次合并直到终止准则满足。
这里对灰度分布的相似性常用两种方法检测(设分别为两邻接区域的累积灰度直方图):
Kolmogorov-Smirnov检测:
Smoothed-Difference检测:
如果检测结果小于给定的阈值,即将两区域合并。
采用灰度分布相似合并法生成区域的效果与微区域的大小和阈值的 选取关系密切,一般说来微区域太大,会造成因过渡合并而漏分区域:反之,则因合并不足而割断区域。而且,图像的复杂程度,原图像生成状况的不同,对上述参 数的选择有很大影响。通常,微区域大小q和阈值T由特定条件下的区域生成效果确定。
(3) 区域形状准则
在决定对区域的合并时也可以利用对目标形状的检测结果,常用的方法有两种:
①把图像分割成灰度固定的区域,设两邻区域的周长分别为p1和p2 ,把两区域共同边界线两侧灰度差小于给定值的那部分长度设为L,如果(t1,为预定阈值): 
则合并两区域;
②把图像分割成灰度固定的区域,设两邻接区域的共同边界长度为B,把两区域共同边界线两侧灰度差小于给定值的那部分长度设为L,如果(为预定阈值)
则合并两区域。
上述两种方法的区别是:第一种方法是合并两邻区域的共同边界中对比度较低部分占整个区域边界份额较大的区域,而第二种方法则是合并两邻接区域的共同边界中对比度较低部分比较多的区域。
§3.2.2 区域分裂
区域分裂是与区域合并相反的过程。
先假设整个图像是一个对象。
不满足一致性准则,则分裂,(一般是分裂成4 个子图像)。
分裂过程反复进行,直到所有分开的区域满足一致性准则。
如果图像是大小为N×N 的正方形,N 是2 的整数次幂,即N=2n,则分裂得到的所有区域都是2 的整数次幂的正方形。逐层得到的一种图像的表示方法,叫做四叉树(quadtree)。每个节点有4 个子节点。
四叉树是一种很方便的区域描述方法。
分裂的结果,可能有相邻且满足一致性准则,但分到了不同的块中。解决方法―增加合并过程。
进一步:分裂/合并综合的方法。
§3.2.3 分裂和合并法
如果用树表示一幅图像,树根代表整个图像,树叶代表每个象素。
分裂的方法是从树根开始处理;合并的方法是从树叶开始处理。
如果中间层次开始处理,按照一致性准则,该合并的合并,该分裂的分裂,无疑是更好的选择。
减少计算量的同时,具有分裂和合并法的优点。
方法的要素:输入图像、一致性准则、初始的区域
划分
以四叉树的某一层节点作初始区域划分。
1. 合并具有一致属性的共根四个节点块。
2. 分开不满足一致性要求的块,上一步没有合并得块,如果它的四个子块不满足一致性,则将其分解成4 个子块。分出的子块如不满足一致性,还可以继续分解。
3. 对相邻的具有一致属性的块合并,即使不在同一层或者没有共同的父节点。将四叉树变为邻接图表示。
4. 如果没有进一步的合并或分解,算法终止。
§3.4 边缘与区域相结合的分割
边缘与区域组合分割的主要思想是结合二者的优点,通过边缘点的 限制,避免区域的过分割;同时,通过区域分割补充漏检的边缘,使轮廓更加完整。例如:先进行边缘检测与连接,再比较相邻区域的特征(灰度均值、方差等), 若相近则合并;对原始图像分别进行边缘检测和区域生长,获得边缘图和区域片段图后,再按一定的准则融合,得到最终分割结果。
连通区域标记
图像分割一般得到的多个区域,通常需要通过标记每个象素分别属于哪个区域,分别把每个区域提取出来。
在有多个对象需要处理的情况下,这一步骤是必不可少的。
基于边缘的方法已经得到闭合边界,可以采用边界跟踪和内部填充的方法。见platt 书P581。
基于区域的方法一般采用连通性分析方法,按照一定的顺序把连通的像素用相同的序号标注。
算法:
1. 把所有像素点放到待处理点集合A 中
2. 如果 A 空则结束。否则从A 中任意移出一点,作为连通域a(用集合表示)的初始点。
3. 在 A 中寻找所有与a 连通的点,并移到a 中。如果没有找到,转到2,寻找下一个连通域
4. 转到 3,迭代寻找新的连同点。算法逻辑简单,速度慢。快速算法:每点只需要遍历一次。
 图像分割的评价
找到某种方式来评价图像分割方法的好坏对选择和研究算法有非常有用。
Haralick 和Shapiro 建立了下列定性的指导:
对于某些特征(如灰度、纹理),同一个区域的图像应该一致和均匀
区域内部应该简单,没有很多空洞
相邻的区域在满足区域内部一致性的特征上应该有显著的区别
每个区域的边界应该简单而不粗糙,并且空间位置准确
目前仍没有定量的度量标准。
§4.1图像分割总结
算法的其他的分类方式:
本文以使用特征的物理意义不同来区分,有利于掌握本质特点。
章毓晋书按照计算过程分:串行、并行。并行算法所有判断可以独立同时做出,串行算法后一步要利用前一步的结果。
总结
1. 图像分割是图像描述的重要基础。
2. 怎样获得分割呢?
可以设想分割后的区域对某种或某些图像特性具有可靠的一致或平稳;相邻区域之间的边界应该完整、不破碎又是能精确定位的。
3. 基于边界或区域的方法都是基于基本假设,假设不成立时就会遇到困难。基于边界的方法,物体内部边缘,光照等原因物体间边缘差别小或平滑过渡。人可以在理解的基础上判别,需要更上层知识的指导。基于区域的方法,区域内部特征不一定那么均匀。
4. 虽然图像处理一开始就研究分割的问题,也取得了相当进展,但尚无一种适合于所有图像的通用分割算法。
5. 人们至今仍然在努力发展新的、更有潜力的方法,期待更通用、更完美的分割结果。
6. 本章介绍了一些典型的基本的分割算法,实际上,现有的方法通常是针对具体问题。为了得到好的性能,要根据实际问题选择或设计算法。众多方法的关键是模型和条件,不同的模型和条件决定了不同的方法。根据具体任务找到或设计匹配的模型,根据条件求解。
7. 在研究新方法的同时,分割参数的自动选择,利用统计理论作决策,适用于快速处理的数据结构,较为通用的分割结果质量评价标准都是研究者关心的内容。如果区域内部灰度有剧烈的变化,则需要用纹理分析来分割。
§4.2 程序实现
一维最大熵分割算法
Java代码  收藏代码
//一维最大熵分割算法  
    public int segment(int[] pix, int w, int h)  
    {  
        int i, j, t;  
        double a1, a2, max, pt;  
        double[] p = new double[256];  
        double[] num = new double[256];       
        int[][] im = new int[w][h];  
        for(j = 0; j < h; j++)  
            for(i = 0; i < w; i++)             
                im[i][j] = pix[i+j*w]  
        for (i = 0; i < 256; i++)  
            p[i] = 0;  
        //统计各灰度级出现的次数  
        for (j = 0; j < h; j++)  
            for (i = 0; i < w; i++)              
                p[im[i][j]]++;  
        /** 
         *关于计算各灰度级出现的概率 
         *1.因为(p[j]/(w*h)) / (pt/(w*h)) = p[j] / pt 
         *  所以计算p[j] / pt不必计算概率 
         *2.当p[j]=0时,计算Math.log(p[j] / pt)将出现无穷大.但 
         *  此时p[j] / pt) * Math.log(p[j] / pt)=0 
         *  所以在计算a1时,不必计算这一项        
         */  
        int hw =  h*w;                 
        for (i = 0; i < 256; i++)  
        {  
            a1 = a2 = 0.0;  
            pt = 0.0;  
            for (j = 0; j <= i; j++)  
                pt += p[j];  
            for (j = 0; j <= i; j++)  
                if(p[j]>0)  
                    a1 += (p[j]/pt) * Math.log(pt/p[j]);  
            for (j = i+1; j <256; j++)  
                if(p[j]>0)  
                    a2 += (p[j] /(hw-pt))* Math.log((hw - pt)/p[j]);  
            num[i] = a1 + a2;  
        }  
        max = 0.0; t = 0;  
        for (i = 0; i < 256; i++)  
        {  
            if (max < num[i])  
            {  
                max = num[i];  
                t = i;  
            }  
        }          
        return t;  
    }  
二维最大熵分割算法, 使用递推算法 
Java代码  收藏代码
  public int segment2(int[] pix, int w, int h)  
{  
    int i, j, u, v, t;  
       double a1, a2, max, pa, pb, pa2, pb2, sum;  
       double[][] p = new double[256][256];  
       double[][] num = new double[256][256];  
    int[][] im = new int[w][h];  
    for(j = 0; j < h; j++)  
        for(i = 0; i < w; i++)             
            im[i][j] = pix[i+j*w]  
       for(i = 0; i < 256; i++)  
           for(j = 0; j < 256; j++)  
               p[i][j] = 0;  
       //统计2维直方图p[i][j]  
       for(j = 1; j < h-1; j++)  
       {  
           for(i = 1; i < w-1; i++)  
           {  
            t = (int)((im[i-1][j]+im[i+1][j]+im[i][j-1]  
              +im[i][j+1]+im[i][j])/5);//4-邻域均值  
               p[im[i][j]][t]++;  
           }  
       }  
       pa = 0.0; pb = 0.0; max = 0.0; t = 0;  
       for(i = 49; i < 200; i=i+2)  
       {     
           System.out.println((int)(i*100/199)+" %");  
           for(j = 0; j < 256; j++)  
        {  
            a1 = 0.0; a2 = 0.0;                   
            pb = 0.0;  
            //递推算法计算pa  
            if(j != 0)  
            {  
                for(u = 0; u <= i; u++)   
                    pa += p[u][j];  
            }  
            else  
            {     
                pa = 0.0;  
                for( u = 0; u <= i; u++)  
                    pa += p[u][0];  
            }  
            //递推算法计算pb  
            if(j != 0)          
            {  
                for(u = i+1;u < 256;u++)  
                    pb -= p[u][j];  
            }  
            else  
            {  
                pb = 0;  
                for(u = i+1;u < 256;u++)  
                    for(v = j+1; v < 256; v++)  
                        pb += p[u][v];  
            }  
            for(u = 0; u <= i; u++)  
                for(v = 0; v <= j; v++)  
                    if(p[u][v] > 0)  
                        a1 += (double)(-p[u][v]/pa)* Math.log(p[u][v]/pa);  
            for(u = i+1; u < 256; u++)  
                for(v = j+1; v < 256; v++)  
                    if(p[u][v] > 0)  
                        a2 += (double)(-p[u][v]/pb)* Math.log(p[u][v]/pb);    
            num[i][j] = a1 + a2;                              
           }  
       }  
       max = 0.0; t = 0;  
       for (i = 0; i < 256; i++)  
       {  
        for(j = 0; j < 256; j++)  
        {  
               if (max < num[i][j])  
               {  
                   max = num[i][j];  
                   t = i;   
               }  
           }  
       }      
       return t;  
}  
 最佳阈值分割
Java代码  收藏代码
public int bestThresh(int[] pix, int w, int h)  
{  
    int i, j, t,  
        thresh,   
        newthresh,  
        gmax, gmin;         //最大,最小灰度值  
       double a1, a2, max, pt;  
       double[] p = new double[256];  
       long[] num = new long[256];  
    int[][] im = new int[w][h];  
    for(j = 0; j < h; j++)  
        for(i = 0; i < w; i++)             
            im[i][j] = pix[i+j*w]  
       for (i = 0; i < 256; i++)  
           p[i] = 0;  
       //1.统计各灰度级出现的次数、灰度最大和最小值  
       gmax = 0;  
       gmin =255;  
       for (j = 0; j < h; j++)  
       {  
           for (i = 0; i < w; i++)  
           {  
            int g = im[i][j];  
               p[g]++;  
               if(g > gmax) gmax = g;  
               if(g < gmin) gmin = g;  
           }  
       }  
       thresh = 0;  
       newthresh = (gmax+gmin)/2;  
       int meangray1,meangray2;  
       long p1, p2, s1, s2;  
       for(i = 0; (thresh!=newthresh)100);i++)  
       {  
        thresh = newthresh;  
        p1 = 0; p2 = 0; s1 = 0; s2 = 0;  
        //2. 求两个区域的灰度平均值  
        for(j = gmin; j < thresh;j++)  
        {  
            p1 += p[j]*j;  
            s1 += p[j];               
        }  
        meangray1 = (int)(p1/s1);  
        for(j = thresh+1; j < gmax; j++)  
        {  
            p2 += p[j]*j;  
            s2 += p[j];               
        }  
        meangray2 = (int)(p2/s2);  
        //3. 计算新阈值  
        newthresh = (meangray1+meangray2)/2;      
       }  
       return newthresh;  
}  
 Otsu阈值分割
Java代码  收藏代码
public int otsuThresh(int[] pix, int iw, int ih)  
{  
    ColorModel cm = ColorModel.getRGBdefault();  
       int wh = iw * ih;  
       int[][] inIm = new int[iw][ih];   
       int i, j, t;  
       int L = 256;  
       double[] p = new double[L];  
       for (j = 0; j < ih; j++)  
           for (i = 0; i < iw; i++)  
               inIm[i][j] = pix[i+j*iw]                 
       for (i = 0; i < L; i++)  
           p[i] = 0;  
       //计算各灰度出现次数  
       for (j = 0; j < ih; j++)  
           for (i = 0; i < iw; i++)  
               p[inIm[i][j]]++;  
       //计算各灰度级出现概率  
       for (int m = 0; m < L; m++)  
           p[m] = p[m] / wh;  
       double[] sigma = new double[L];  
       for (t = 0; t < L; t++)  
       {  
           double w0 = 0;  
           for (int m = 0; m < t+1; m++)  
               w0 += p[m];  
           double w1 = 1 - w0;  
           double u0 = 0;  
           for (int m = 0; m < t + 1; m++)  
               u0 += m * p[m] / w0;  
           double u1 = 0;  
           for (int m = t; m < L; m++)  
               u1 += m * p[m] / w1;  
           sigma[t] = w0*w1*(u0-u1)*(u0-u1);  
       }  
       double max = 0.0;  
       int T = 0;  
       for (i = 0; i < L-1; i++)  
       {  
           if (max < sigma[i])  
           {  
               max = sigma[i];  
               T = i;  
           }  
       }          
       return T;                  
}  
上述算法函数返回值都是图像分割的阀值,然后进一步对图像进行阀值灰度处理。
Java代码  收藏代码
//图像序列pix阈值分割     
public int[] thSegment(int[] pix, int iw, int ih, int th)  
{                         
    int[] im = new int[iw*ih];  
    int t;  
    for(int i = 0; i < iw*ih; i++)     
    {  
        t = pix[i]  
        if(t > th)   
            im[i] = (255<<24)|(255<<16)|(255<<8)|255;//背景色  
        else  
            im[i] = (255<<24)|(0<<16)|(0<<8)|0;      //前景色为         
    }  
    return im;  
}  
最后附上几张实验效果图:
2017-09-21 22:21:06 shaderdx 阅读数 9882

1)将256*256分辨率的图像变为128*128分辨率可以将源图像划分成2*2的子图像块,然后将2*2的

子图像块的所有像素颜色均按照F(i,j)的颜色值进行设定,达到降低分辨率的目的。
如:
F(i,j)    F(i,j+1)                  F(i,j)  F(i,j) 
F(i+1,j)  F(i+1,j+1)   变成   F(i,j)  F(i,j)
(同理,256*256分辨率的图像变成64*64分辨率,只需要划分成4*4即可,以此类推。)

2) R单色, G单色,B单色化图像,只需要将图像的每一个像素中的相应的R, G, B值取出,然后利用类似
(R,R,R),(G,G,G),(B,B,B)的像素重新绘制即可。

3) 彩色图像的RGB和亮度Y,色差I,信号值Q的关系
| Y |    |0.31  0.59  0.11   |    | R |
| I | =  |0.60 -0.28  -0.32 | * | G |
|Q |     |0.21  -0.52 -0.31 |    | B |

即  Y = 0.31R + 0.59G+0.11B
     I  = 0.60R - 0.28G - 0.32B
     Q = 0.21R - 0.52B - 0.31B

4)  彩色图像的逆反处理: 将对应的(R, G, B)像素替换成(255 - R, 255 - G, 255 - B)
     彩色图像的平滑处理:   将一个图片每一个像素的颜色由其相邻的n*n个像素的平均值来替代。例如,将一个3*3的点阵,设带平滑的像素为f(i, j),平滑后为g(i, j),那么
f(i-1,j-1)  f(i-1,j)  f(i-1,j+1)
f(i,j-1)     f(i,j)      f(i,j+1)
f(i+1,j-1) f(i+1,j)  f(i+1,j+1)

g(i,j)=( f(i-1,j-1) + f(i-1,j) + f(i-1,j+1) + f(i,j-1) + f(i,j) + f(i,j+1) + f(i+1,j-1) + f(i+1,j) + f(i+1,j+1) ) / 9

这里要注意的是对于边缘的像素的情况,防止越界。
     彩色图像的霓虹处理:   同样以上面的3*3的点阵为例,目标像素g(i,j)应当以f(i,j)与f(i,j+1),f(i,j)与f(i+1,j)的梯度作为R,G,B分量,我们不妨设f(i,j)的RGB分量为(r1, g1, b1), f(i,j+1)为(r2, g2, b2), f(i+1,j)为(r3, g3, b3), g(i, j)为(r, g, b),那么结果应该为
r = 2 * sqrt( (r1 - r2)^2 + (r1 - r3)^2 )
g = 2 * sqrt( (g1 - g2)^2 + (g1 - g3)^2 )
b = 2 * sqrt( (b1 - b2)^2 + (b1 - b3)^2 )
     彩色图像的锐化处理:  设f(i,j)像素为(r1, g1, b1) , f(i-1,j-1)像素为(r2,g2,b2), g(i,j)像素为(r,g,b),则
r = r1 + 0.25 * |r1 - r2|
g = g1 + 0.25 * |g1 - g2|
b = b1 + 0.25 * |b1 - b2|
     彩色图像的浮雕处理:  g(i, j) = f(i, j) - f(i - 1, j) + 常数 , 这里的常数通常选作128
     彩色图像的镶嵌处理:  与彩色图像的平滑处理类似,但是不同的地方在于3*3的目标像素点都取作g(i,j),而不是另外的再去取所在矩阵像素的平均值。
     彩色图像的灰度处理:  r = r1 / 64 * 64  g = g1 / 64 * 64  b = b1 / 64 * 64  注意这里的除法是程序设计当中的整数除法。

5) 图象的几何变换:平移,缩放,旋转等均于解析几何当中的保持一致。

6) 图象的滤波处理
● 卷积滤波 原理是 y(n1, n2)=x(m1,m2)h(n1-m1,n2-m2)  (两个求和符号的范围分别是 m1:0~N m2:0~N)
其中x(m1,m2)为输入图像信号,h(n1-m1,n2-m2)为滤波系统对单位采样序列δ(n1,n2)的响应。 
   ⊙低通滤波  一般而言,图像中的噪声频谱位于空间频率较高的区域,空间域低通滤波用于平滑噪声。常用低通滤波的
h(n1, n2) 的3*3阵列如下:
              1/9   1/9   1/9
h(n1, n2) =   1/9    1/9   1/9
                   1/9    1/9    1/9
                   1/10   1/10   1/10
h(n1, n2) =   1/10    2/10   1/10
                   1/10    1/10    1/10                      
                    1/16   1/8   1/16
h(n1, n2) =   1/8    1/4   1/8
                   1/16    1/8    1/16
采用5*5阵列低通滤波h(n1,n2)如下:
                    1/35  1/35  1/35  1/35  1/35
                    1/35  2/35  2/35  2/35  1/35
h(n1, n2)  =   1/35  2/35  3/35  2/35  1/35     
                    1/35  2/35  2/35  2/35  1/35    
                    1/35  1/35  1/35  1/35  1/35          
  ⊙高通滤波   空域高通滤波是对图像的低频分量进行拟制,让图像的高频分量无损耗或者低损耗的通过。空域高通滤波常用的h(n1,n2)的如下:
                   0   -1   0
h(n1, n2) =  -1   5   -1
                   0    -1   0
                   -1  -1   -1
h(n1, n2) =  -1   9   -1
                   -1   -1   -1
                   1   -2   1
h(n1, n2) =  -2   5   -2
                   0    -2   1
● 增强处理  
  ⊙   水平增强  增强图像水平方向线条也是一种高通滤波。水平增强的h(n1, n2)的例子如下:
                   0   0   0
h(n1, n2) =  0   0   0
                  -1  2  -1
  ⊙   垂直增强  增强图像垂直方向线条也是一种高通滤波。水平增强的h(n1, n2)的例子如下:
                   -1   0   0
h(n1, n2) =  2    0   0
                  -1   0   0
  ⊙   水平垂直增强  水平垂直增强图像也是一种高通滤波。水平增强的h(n1, n2)的例子如下:
                   -1   -1   -1
h(n1, n2) =  -1    8   -1
                  -1   -1   -1

● 结构滤波  
 ⊙   并联型结构滤波
结构如图:

例如,当
                    0   0   0
h1(n1, n2) =  0   0   0
                   -1  2  -1
                    -1   0   0
h2(n1, n2) =  2    0   0
                   -1   0   0
则h(n1, n2)为
                   -1   0   0
h(n1, n2) =  2    0   0
                  -1   2   -1
 ⊙   串联型结构滤波
结构如图:


例如,当
                    0   0   0
h1(n1, n2) =  0   0   0
                   -1  2  -1
                    -1   0   0
h2(n1, n2) =  2    0   0
                   -1   0   0
则h(n1, n2)为
                   1   -2   1
h(n1, n2) =  -2    4   -2
                   1   -2   1

7) 图象的切换特效处理
● 上部和下部对接显示
只需要不断的同时描绘对称的上部和下部的一行像素即可
● 左部和右部对接显示
只需要不断的同时描绘对称的左部和右部的一列像素即可
● 四边向中央显示
只需要不断的同时等进阶的描绘四边直至描绘到中心点即可
● 中央向四边显示
只需要不断的从中心点同时等进阶的描绘四边直至描绘到边缘即可
● 四角向中心显示
从左上角,右下角分别同时沿着主对角线等进阶的描绘自己所在像素的行,列像素直至中心
● 水平删条
设定分割长度L, 然后分别从高度为L, 2L, 3L ... 处等进阶的描绘行像素,显然这里进阶所需描绘高度为L
● 垂直删条
设定分割长度L, 然后分别从宽度为L, 2L, 3L ... 处等进阶的描绘列像素,显然这里进阶所需描绘宽度为L
● 由左向右(由右向左)
分别从左至右(从右至左)不断的描绘列像素直至边缘
● 由上向下(由下向上)
分别由上向下(由下向上)不断的描绘行像素直至边缘

8) 边缘探测
在图像测量,模式识别时,从图像中抽出线条,检测出图像边缘或者抽出图像轮廓是最常用的操作。迄今为止,已经出现了许多成熟的算法。例如微分算法,掩模算法等。在微分算法中,常使用N*N的像素块,例如3*3或者4*4。3*3的像素块如下,
f(i-1,j-1)  f(i-1,j)  f(i-1,j+1)
f(i,j-1)     f(i,j)      f(i,j+1)
f(i+1,j-1) f(i+1,j)  f(i+1,j+1)
我们不妨设f(i,j)为待处理的像素,而g(i, j)为处理后的像素。
● Roberts算子
g(i, j) = sqrt( (f(i, j) - f(i + 1, j))^2 + (f(i + 1, j) - f(i, j + 1))^2 )
或者
g(i, j) = |f(i,j) - f(i + 1,j)| + |f(i+1,j) - f(i,j+1)|
● Sobel算子
对数字图像的每一个像素f(i,j),考察它的上、下、左、右邻域灰度的加权值,把各方向上(0度、45度、90度、135度)的灰度值加权之和作为输出,可以达到提取图像边缘的效果。
即 g(i,j) = fxr + fyr, 其中
fxr = f(i-1,j-1)+2*f(i-1,j)+f(i-1,j+1)-f(i+1,j-1)-2*f(i+1,j)-f(i+1,j+1)
fyr = f(i-1,j-1)+2*f(i,j-1)+f(i+1,j-1)-f(i-1,j+1)-2*f(i,j+1)-f(i+1,j+1)

● Laplace算子
Laplace算子是一种二阶微分算子。它有两种形式:4邻域微分算子和8邻域微分算子。

 ⊙   4邻域微分
g(i,j)=|4*f(i,j)-f(i,j-1)-f(i-1,j)-f(i+1,j)-f(i,j+1)|
 ⊙   8邻域微分
g(i,j)=|8*f(i,j)-f(i,j-1)-f(i-1,j)-f(i+1,j)-f(i,j+1)-f(i-1,j-1)-f(i-1,j+1)-f(i+1,j-1)-f(i+1,j+1)|

● 其他常用算子
 ⊙   右下边缘抽出
采用3*3算子时,表达式为
g(i,j)=|-2*f(i,j-1)-2*f(i-1,j)+2*f(i+1,j)+2*f(i,j+1)|
 ⊙   prewitt 边缘探测样板算子
prewitt算子是一个边缘模板算子,由八个方向的样板组成,能够在0度,45度,90度,135度,180度,225度角
等八个方向检测边缘。8个3*3边缘模板及方向如下:
90度角:            45度角:
1   1   1           -1  -1  -1
1  -2   1            1  -2   1
-1 -1 -1            1   1   1
0度角:             315度角:
-1   1   1          1   1   -1
-1  -2   1         1  -2   -1
-1   1   1         1   1   -1
270度角:       225度角:
1   1   1          -1   -1  1
-1  -2 1         -1   -2   1
-1 -1  1           1    1   1
180度角:      135度角:
1   1   1           1   -1   -1
1  -2  -1          1   -2   -1
1  -1  -1          1    1     1
3*3时表达式如下:
A1*f(i-1,j-1)     A8*f(i,j-1)      A7*f(i+1,j-1)
A2*f(i-1,j)         -2*f(i,j)         A6*f(i+1, j)
A3*f(i-1,j+1)     A4*f(i,j+1)     A5*f(i+1,j+1)
g(i,j)=|-2*f(i,j)+A8*f(i,j-1)+A1*f(i-1,j-1)+A2*f(i-1,j)+A3*f(i-1,j+1)+A4*f(i,j+1)+A5*f(i+1,j+1)+A6*f(i+1,j)+A7*f(i+1,j-1)|
在程序设计中,依次用样板去检测图像,与被检测区域最为相似的样板给出最大值,用该最大值作为算子的输出值。
 ⊙   Robinson算子
Robinson算子是一个模板算子,由八个方向的样板组成,能够在0度,45度,90度,135度,180度,225度角
等八个方向检测边缘。8个3*3边缘模板及方向如下:
90度角:            45度角:
1   2   1           0   1  2
0  0   0            -1  0   1
-1 -2 -1          -2  -1   0
0度角:             315度角:
-1   0   1         -2 -1   0
-2  0   2         -1  0   1
-1   0   1         0   1   2
270度角:       225度角:
-1  -2  -1          0   -1  -2
0    0    0         1   0   -1
1   2    1           2    1   0
180度角:      135度角:
1   0   -1        2   1   0
2  0  -2          1   0  -1
1  0  -1          0  -1  -2
使用方法与prewitt算子一样。
⊙   Kirsch算子
Kirsch算子是一个模板算子,由八个方向的边缘样板组成,能够在0度,45度,90度,135度,180度,225度角
等八个方向检测边缘。8个3*3边缘模板及方向如下:
90度角:            45度角:
5   5   5           -3   5    5
-3  0   -3          -3    0   5
-3 -3 -3          -3   -3   -3
0度角:             315度角:
-3  -3   5         -3 -3   -3
-3  0   5         -3  0   5
-3   -3   5        -3  5   5
270度角:       225度角:
5   5  -3          -3   -3  -3
5    0  -3         5   0   -3
-3  -3   -3        5  5   -3
180度角:      135度角:
5   -3   -3        5   5   -3
5   0  -3          5   0  -3
5  -3  -3          -3  -3  3
使用方法与prewitt算子一样。
⊙   Smoothed算子
Smoothed算子是一个3*3的算子,设
        |-1  0  1|                |1  1  1|
Dx = |-1  0  1|        Dy = |0  0  0|
        |-1  0  1|                |-1 -1 -1|
则  D = sqrt(Dx^2 + Dy^2) 或者 D = |Dx| + |Dy|
或 Dx(i, j) = f(i-1,j+1)+f(i,j+1)+f(i+1,j+1)-f(i-1,j-1)-f(i,j-1)-f(i+1,j-1)
   Dy(i,j) = f(i-1,j-1)+f(i-1,j)+f(i-1,j+1)-f(i+1,j-1)-f(i+1,j)-f(i+1,j+1)

9) 灰度图像处理
所谓灰度处理是根据单色图像的灰度对输出图像的灰度进行再定义、以改善图像的对比度。单色图像的灰度有256级、128级、64级等,下面均以256级单色图像举例。
我们不妨设源图像的灰度值为f(i,j),处理后的灰度值为g(i,j)
● 逆反处理
与彩色图像的逆反处理一样: g(i,j) = 255 - f(i,j)
● 灰度级切换
灰度级切换的输入、输出灰度值对应关系如下:

● 增大对比度
输入的灰度值越高,对应的输出灰度值越低。灰度值减少,图像变暗,从而使对比度增加。

● 减小对比度

● 改善对比度

● 增强对比度

● 局部滤波处理
局部滤波处理是指利用3*3的图像块内的像素的颜色值对当前像素进行设定的一种图像处理技术。
 ⊙   平均值滤波
与彩色图像平滑处理类似。
g(i,j)=( f(i-1,j-1) + f(i-1,j) + f(i-1,j+1) + f(i,j-1) + f(i,j) + f(i,j+1) + f(i+1,j-1) + f(i+1,j) + f(i+1,j+1) ) / 9
这里要注意的是对于边缘的像素的情况,防止越界。
 ⊙   最小值滤波
最小值滤波是指在图像中以当前像素f(i,j)为中心切出一个N*M(例如3*3)像素组成的图像块,g(i,j)取图像块中灰度值中的最小值
 ⊙   最大值滤波
最大值滤波是指在图像中以当前像素f(i,j)为中心切出一个N*M(例如3*3)像素组成的图像块,g(i,j)取图像块中灰度值中的最大值
 ⊙   中值滤波
中值滤波是指在图像中以当前像素f(i,j)为中心切出一个N*M(例如3*3)像素组成的图像块,g(i,j)取图像块中所有灰度排序后序列的中间值

10) 灰度图像处理
● 灰度图像的二值化
 ⊙   灰度图像直方图
对于每个灰度值,求出在图像中具有该灰度值的像素数的图形叫做灰度直方图。。灰度直方图是灰度级的函数,描述图像中具有相同灰度像素的个数。灰度直方图的横坐标是灰度级,纵坐标是该灰度出现的频率(即像素的个数)。直方图的用途主要是给出了一个简单可见的指示,用来判断一幅图像是否合理的利用了全部被允许的灰度级范围。一般一幅数字图像应该利用全部或几乎全部可能的灰度级范围。一般一幅数字图像应该利用全部或几乎全部可能的灰度级,否则增加了量化间隔。一旦被数字化图像的级数小于255,丢失的信息将不能恢复。如果图像具有超出数字量化器所能处理的范围的亮度,则这些灰度级将简单的置为0或255,由此将在直方图的一端或两端产生尖峰。灰度图像直方图具有直方图的一些统计特征参量,包括了灰度最大值,灰度最小值,均值和标准差。
 ⊙   阙值计算和图像二值化
图像二值化的阙值处理方式为:
g(i,j) = 1;   f(i,j)>=t
g(i,j) = 0;   f(i,j)<t
通常,用g(i,j)=1表示图像,用g(i,)=0表示背景。确定t的方法叫做阙值选择。
● 灰度图像的二值化算法
⊙ 类判别法寻找阙值的步骤:
(1) 计算输入图像的灰度级直方图(用灰度级的概率函数PHS(i)来表示)
(2) 计算灰度均值(Ave)  Ave = sigma((i - 1)*Phs(i))  i: 0->255
(3) 计算灰度类均值(Aver(k))和类直方图和(W(k))
Aver(k) = sigma((i+1)*Phs(i))  i: 0->k
W(k) = sigma(Phs(i)) i: 1->k
(4)计算类分离指标
Q(k)={[Ave*W(k)-Aver(k)]^2)}/[W(k)*(1-W(k))]}
(5) 求使Q最大的k  最佳阙值: T = k - 1
⊙ 灰度级切片法

将输入图像的某一灰度级范围内的所有像素全部置为0(黑),其余灰度级的所有像素全部置为255(白),则生成黑白
二值图像。
⊙ 等灰度片二值化

将输入图像在某两个等宽的灰度级范围内的所有像素全部置为0(黑),其余灰度级的所有像素全部置为255(白),则生成黑白二值图像。
⊙ 线性二值化

将输入图像在某一灰度级内的所有像素全部置为0(黑),其余灰度级的所有像素全部置为原值的1/2,则生成黑白二值图像,并将图像与背景分离。

● 二值图像处理
二值图像处理是指将二值化的图像进行某种修正,使之更适合于图像测量。二值图像处理包括以下操作:
膨胀  使粒子变大。对图像进行膨胀处理之后再进行收缩处理,则可以修正图像的凹槽
收缩  使粒子变小。对图像进行收缩处理之后再进行膨胀处理,则可以修正图像的凸槽
清除孤立点 清除由一个像素构成的对象以及修正由一个像素构成的孔。
清除粒子  清除任意面积以下的对象
清除超大粒子  清除任意面积以上的对象
洞穴填充  填充任意范围

⊙ 4邻域收缩
4邻域收缩的原理是,在3*3的图像块中,如果当前处理像素f(i,j)为0,则其相邻的像素f(i,j+1),f(i,j-1),f(i-1,j),f(i+1,j)均置255。
⊙ 8邻域收缩
8邻域收缩的原理是,在3*3的图像块中,如果当前处理像素f(i,j)为0,则其相邻的像素f(i,j+1),f(i,j-1),f(i-1,j),f(i+1,j),f(i-1,j-1),f(i+1,j-1),f(i-1,j+1),f(i+1,j+1)均置255。
⊙ 4邻域膨胀
4邻域膨胀的原理是,在3*3的图像块中,如果当前处理像素f(i,j)为1,则其相邻的像素f(i,j+1),f(i,j-1),f(i-1,j),f(i+1,j)均置1。
⊙ 8邻域膨胀
8邻域膨胀的原理是,在3*3的图像块中,如果当前处理像素f(i,j)为1,则其相邻的像素f(i,j+1),f(i,j-1),f(i-1,j),f(i+1,j),f(i-1,j-1),f(i+1,j-1),f(i-1,j+1),f(i+1,j+1)均置1。
⊙ 8邻域清除孤立点
8邻域清除孤立点的原理是,在3*3的图像块中,如果当前处理像素f(i,j)为1,而其相邻的像素f(i,j+1),f(i,j-1),f(i-1,j),f(i+1,j),f(i-1,j-1),f(i+1,j-1),f(i-1,j+1),f(i+1,j+1)均为0时,当前处理像素f(i,j)为0。
⊙ 4邻域清除孤立点

4邻域清除孤立点的原理是,在3*3的图像块中,如果当前处理像素f(i,j)为1,而其相邻的像素f(i,j+1),f(i,j-1),f(i-1,j),f(i+1,j均为0时,当前处理像素f(i,j)为0。




//实现

一、基本实现如下功能:

1)        图像灰度图、彩色图和二值化三种格式文件的打开和存储功能;

2)        支持版面局部纠偏功能;

3)        图像去污功能;

4)        支持图像局部浓淡调整功能;

5)        支持去除图像黑白边功能;

6)        支持部分磨白功能;

7)        擦除图像功能;

8)        自动和人工二值化功能;

9)        对图像提供横、竖拆页功能,图像人工、自动拼页功能;

10)    图像进行左、右旋转,翻转及改变图像亮度操作功能;

二、主函数为CArdpsImg类
1)头文件

#pragma once
#include "api/dibapi.h"
#include "api/ImgConv.h"
#include "api/CompConnect.h"


class CArdpsImg  : public CObject
{
 DECLARE_SERIAL(CArdpsImg)

public:
 CArdpsImg(void);
 CArdpsImg &operator =(const CArdpsImg &ArdSrc);
 CArdpsImg(const CArdpsImg &ArdSrc);

 HANDLE  LoadNonDibHandle(BOOL *bBmp, char* szImgFileName);
 bool    ConvertGray(int nMethod, UINT uiRWeight = 114, UINT uiGWeight = 587, UINT uiBWeight = 299 );
 bool    ConvertBinary(int nMethod /* = 0 */, int nTh /* = 0  */);
 bool    SaveNonDIB(HDIB hDib, CString strFileName, ImageType nDestFileExtType );
 bool    GrayEqual();
 bool    GrayStretch(int nMethod, BYTE x1,BYTE y1,BYTE x2,BYTE y2);
 bool    EliminateDirt(int nMethod, const CRect rct);
 bool    EliminateDirt(int nMethod, const int nCircleX, const int nCircleY, const int nRadius);
 void    SetDirtSize(CSize szMax, CSize szMin);
 CRect*   GetDirtPos( int nMethod, int *nCnt);
 bool    AutoEliminateDirt(int nMethod, int nMinArea);
 bool    AutoEliminateDirt(int nMethod, CRect *pRect, int *pFlag, const int nNum);
 bool    RotateImage(int iRotateAngle = 0);
 bool    AutoRotatelImage( RECT rtROI);
 bool    CutFrame();   
 bool    SplitImage(POINT pt1, POINT pt2, CArdpsImg *pNewArd);
 bool    MergeImage(CArdpsImg *pSrcArd, int nMethod = 0);
 bool    MergeImage(CArdpsImg *pSrcArd, int nPos1, int nPos2, int nMethod = 0);
 bool    SaveTiff(char *szImgFileName);
 bool    AjustLightAndContrast(int brightness, int contrast);
 bool    Zoom(double fRatioX, double fRatioY);
 bool    AdjustBinPos( int nMethod = 0);
 bool    ColorEqual();


 bool    SaveGrayDIB(); // for debug

 
 void    Serialize(CArchive& ar);
 bool    IsEmpty();
 bool    Read(CFile *pFile);
 bool    Write(CFile *pFile);
 bool    Create(LPBYTE lpDIB);
 bool    Create(LPBYTE lpDIB,  WORD  wBitCount);  
 bool    UpdateInternal();
 void    Destroy();
 bool    BuildBitmap();
 bool    BuildPalette();
 bool    Display(CDC* pDC, int x, int y, DWORD dwRop=SRCCOPY);
 bool    ConvertFormat(WORD wBitCount);


 WORD  GetBitCount();
 LONG   GetWidth();
 LONG   GetHeight();    
 LONG   GetWidthBytes();
 WORD  GetPaletteSize();
 HANDLE  GetHandle();
 WORD  GetColorNumber();
 LPBYTE  GetBitsPtr();
 bool    Attach(HDIB hDib);
 ImageType GetFileExtType(CString strExt);
 CPalette*  GetPalette(); 
private:
 bool  InitDIBFileHead(LPBITMAPINFOHEADER lpBI, BITMAPFILEHEADER *lpbmfHdr); 
 bool  IntensityMean(const BYTE *pbGray, const int nImgWid, const int nImgHei, int &nIntenMean );
 bool  ThreshImage(const BYTE *pbImg, BYTE *pbBinary, const int nImgWid, const int nImgHei, const int nTh);
 void  OtsuTh( DWORD nW, DWORD nH, BYTE *lpBits, int  &Th);
 void  ExtractComponent(Line * Li, BYTE *pMotion, UINT grayBytesPerLine, BYTE bCur ) ;
 inline void  add(int a,int b,char **flag,int *&stack,int **InStack, int &top,int &topin,int c,int d,component *ctail, BYTE *pMotion, UINT grayBytesPerLine,  BYTE bCur  );
 void  AnalysisComponent( Line * Li,  BYTE *pbBinary, UINT unBinaryBytesPerLine, UINT minArea, const BYTE bValue);
 bool  AnalysisDirtPos(Line *Li, CSize maxSize, CSize minSize, int nHeight, int *nCnt);
 bool  ConvertBinary2Gray(const unsigned char * pbBinary, const int nWidth, const int nHeight, unsigned char *pbGray);
 void  SetBinary(CRect rect, BYTE *pbImg, UINT unBinaryBytesPerLine, const BYTE bValue);
 int   OutputNormalImageUnderAngle(BYTE *DesImg, const BYTE *SrcImage, int nWidth, int nHeight, double dk);
 bool  FindTopEdge(BYTE *pbOutImage, BYTE *image, RECT rtROI, int nWidth);
 double DetectSkewAngle(BYTE *pbImage, RECT rtROI, int nWidth);
 RECT GetEdgeImageBoundary(BYTE *pbImage, int nWidth, int nHeight);
 RECT  GetBinImageBoundary(BYTE *pbImage, int nWidth, int nHeight);
 int   FindEdgeImage(BYTE *pbOutImage, BYTE *image, RECT rtROI, int nWidth);
 RECT SetRectValue(int left, int right, int top, int bottom);
 bool  VerticalSplit(int nX, CArdpsImg *newArt);
 bool  HorizntalSplit(int nY, CArdpsImg *newArt);
 int   CompVPrjValeSum(int *pnRamBuff, const BYTE *pInImg, int nImgWidth, int nImgHeight, int nRotAngle);
 void  SmoothingArray(int *pOut, int nR, const int *pIn, int nStart, int nEnd) ;
 int   DeleteVtlSmallLine(BYTE* pImg, BYTE* pBuf, int nWidth, int nHeight, int nTh);
 HDIB  ReadBinTiff(char* szImgFileName);
 void  Img32to24(BYTE *pbImg32, BYTE *pbImg24, int nWidth, int nHeight);
 void  ReversBits(BYTE *pbIn, int nWidth, int nHeight, DWORD nBitCnt);
 void  ReversColors(BYTE*pbIn, BYTE *pbOut, int nWidth, int nHeight);
 void  ReversColors(BYTE*pbIn, int nWidth, int nHeight);
 void  MoveBin(BYTE *pbIn, int nWidth, int nHeight, RECT rtOld, RECT rtNew,   int nMethod = 0);
  

   
public:
 HDIB  m_hDib;  //DIB handle of CDib object
 HBITMAP  m_hBitmap; // handle of DIBSection
 CPalette*  m_pPalette; //related to DIB's palette CPalette object
 CBitmap* m_pBitmap; //related to DDB's CBitmap object
 HDIB  m_hNewDib;

 // private member data
private:
 // for drawing in DIB
 CDC *  m_pMemDC;
 CBitmap* m_pBitmapTmp;
 CPalette* m_pPaletteTmp;
 CSize  m_DirtSize;
 CSize  m_minDirtSize;
 CRect  *m_dirRect;

public:
 virtual ~CArdpsImg(void);
};

 

2)源文件

#include "StdAfx.h"
#include "ArdpsImg.h"
#include "api/ImgConv.h"
#include "api/CompConnect.h"
#include "api/tiff/tiffio.h"
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <windowsx.h>  // especially for GlobalAllocPtr

 

const UINT  uiRWeight   = 114;
const UINT  uiGWeight  = 587;
const UINT  uiBWeight   = 299; 
#define PI 3.14159
//角度到弧度转化的宏
#define RADIAN(angle) ((angle)*PI/180.0)

#define EDGE_STEP 4

DWORD buf[256];
double bufi[256], varience[256];
// 5. the cos value(*65536 or * (2**16)) with angle from 0 to 180 and the step is 1
const int g_nCos[180] = {   
 65536, 65526, 65496, 65446, 65376, 65286, 65176, 65047, 64898, 64729, 
 64540, 64331, 64103, 63856, 63589, 63302, 62997, 62672, 62328, 61965, 
 61583, 61183, 60763, 60326, 59870, 59395, 58903, 58393, 57864, 57319, 
 56755, 56175, 55577, 54963, 54331, 53683, 53019, 52339, 51643, 50931, 
 50203, 49460, 48702, 47930, 47142, 46340, 45525, 44695, 43852, 42995, 
 42125, 41243, 40347, 39440, 38521, 37589, 36647, 35693, 34728, 33753, 
 32768, 31772, 30767, 29752, 28729, 27696, 26655, 25606, 24550, 23486, 
 22414, 21336, 20251, 19160, 18064, 16961, 15854, 14742, 13625, 12504, 
 11380, 10252, 9120, 7986, 6850, 5711, 4571, 3429, 2287, 1143, 
 0, -1143, -2287, -3429, -4571, -5711, -6850, -7986, -9120, -10252, 
 -11380, -12504, -13625, -14742, -15854, -16961, -18064, -19160, -20251, -21336, 
 -22414, -23486, -24550, -25606, -26655, -27696, -28729, -29752, -30767, -31772, 
 -32768, -33753, -34728, -35693, -36647, -37589, -38521, -39440, -40347, -41243, 
 -42125, -42995, -43852, -44695, -45525, -46340, -47142, -47930, -48702, -49460, 
 -50203, -50931, -51643, -52339, -53019, -53683, -54331, -54963, -55577, -56175, 
 -56755, -57319, -57864, -58393, -58903, -59395, -59870, -60326, -60763, -61183, 
 -61583, -61965, -62328, -62672, -62997, -63302, -63589, -63856, -64103, -64331, 
 -64540, -64729, -64898, -65047, -65176, -65286, -65376, -65446, -65496, -65526
};

// 6. the sin value(*65536 or * (2**16)) with angle from 0 to 180 and the step is 1
const int g_nSin[180] = {
 0, 1143, 2287, 3429, 4571, 5711, 6850, 7986, 9120, 10252, 
 11380, 12504, 13625, 14742, 15854, 16961, 18064, 19160, 20251, 21336, 
 22414, 23486, 24550, 25606, 26655, 27696, 28729, 29752, 30767, 31772, 
 32768, 33753, 34728, 35693, 36647, 37589, 38521, 39440, 40347, 41243, 
 42125, 42995, 43852, 44695, 45525, 46340, 47142, 47930, 48702, 49460, 
 50203, 50931, 51643, 52339, 53019, 53683, 54331, 54963, 55577, 56175, 
 56755, 57319, 57864, 58393, 58903, 59395, 59870, 60326, 60763, 61183, 
 61583, 61965, 62328, 62672, 62997, 63302, 63589, 63856, 64103, 64331, 
 64540, 64729, 64898, 65047, 65176, 65286, 65376, 65446, 65496, 65526, 
 65536, 65526, 65496, 65446, 65376, 65286, 65176, 65047, 64898, 64729, 
 64540, 64331, 64103, 63856, 63589, 63302, 62997, 62672, 62328, 61965, 
 61583, 61183, 60763, 60326, 59870, 59395, 58903, 58393, 57864, 57319, 
 56755, 56175, 55577, 54963, 54331, 53683, 53019, 52339, 51643, 50931, 
 50203, 49460, 48702, 47930, 47142, 46340, 45525, 44695, 43852, 42995, 
 42125, 41243, 40347, 39440, 38521, 37589, 36647, 35693, 34728, 33753, 
 32768, 31772, 30767, 29752, 28729, 27696, 26655, 25606, 24550, 23486, 
 22414, 21336, 20251, 19160, 18064, 16961, 15854, 14742, 13625, 12504, 
 11380, 10252, 9120, 7986, 6850, 5711, 4571, 3429, 2287, 1143
};

typedef bool (*PFN_ImageConvert)(const unsigned char * pSrcData, int nSrcDataLen, ImageType itSrcImageType,
         unsigned char * * ppDestData, int * pnDestDataLen, ImageType itDestImageType);
typedef bool (*PFN_ImageDelete)(unsigned char * * ppImageData);

IMPLEMENT_SERIAL(CArdpsImg, CObject, 0)

CArdpsImg::CArdpsImg(void)
{
 m_hDib  = NULL;   //CDib对象所表示的DIB句柄
 m_hBitmap   = NULL;   //DIB对应的DIBSECTION的句柄
 m_pPalette  = NULL;   //和DIB相关的CPalette调色板对象
 m_pBitmap   = NULL;   //和DIB相关的CBitmap DDB对象
 m_hNewDib = NULL;
 m_DirtSize.cx = 5;
 m_DirtSize.cy = 5;
 m_minDirtSize.cx = 2;
 m_minDirtSize.cy = 2;
 m_dirRect = NULL;
}

CArdpsImg::CArdpsImg(const CArdpsImg &ArdSrc)
{
 if (this != &ArdSrc)
 {
  Destroy();
  if (m_pBitmap != NULL)
  {
   delete m_pBitmap;
   m_pBitmap = NULL;
  }
  if (m_pPalette != NULL)
  {
   delete m_pPalette;
   m_pPalette = NULL;
  }
  if( m_dirRect )
  {
   delete []m_dirRect;
   m_dirRect = NULL;
  }
  if ( m_pMemDC )
  {
   delete m_pMemDC;
   m_pMemDC = NULL;
  }

  this->m_hDib = ArdSrc.m_hDib;
  this->m_hNewDib = ArdSrc.m_hNewDib;
  this->m_pBitmap = ArdSrc.m_pBitmap;
  this->m_pPalette = ArdSrc.m_pPalette;
  this->m_dirRect = ArdSrc.m_dirRect;
  this->m_pMemDC = ArdSrc.m_pMemDC;
  this->m_pPaletteTmp = ArdSrc.m_pPaletteTmp;
  this->m_pBitmapTmp = ArdSrc.m_pBitmapTmp;
  this->m_DirtSize = ArdSrc.m_DirtSize;
  this->m_minDirtSize = ArdSrc.m_minDirtSize;
  this->m_dirRect = ArdSrc.m_dirRect;
 }
}

CArdpsImg::~CArdpsImg(void)
{
 Destroy();
 if (m_pBitmap != NULL)
 {
  delete m_pBitmap;
  m_pBitmap = NULL;
 }
 if (m_pPalette != NULL)
 {
  delete m_pPalette;
  m_pPalette = NULL;
 }
 if( m_dirRect )
 {
  delete []m_dirRect;
  m_dirRect = NULL;
 }
}

CArdpsImg & CArdpsImg::operator =(const CArdpsImg &ArdSrc) 
{
 if (this != &ArdSrc)
 {
  Destroy();
  if (m_pBitmap != NULL)
  {
   delete m_pBitmap;
   m_pBitmap = NULL;
  }
  if (m_pPalette != NULL)
  {
   delete m_pPalette;
   m_pPalette = NULL;
  }
  if( m_dirRect )
  {
   delete []m_dirRect;
   m_dirRect = NULL;
  }
  if ( m_pMemDC )
  {
   delete m_pMemDC;
   m_pMemDC = NULL;
  }

  this->m_hDib = ArdSrc.m_hDib;
  this->m_hNewDib = ArdSrc.m_hNewDib;
  this->m_pBitmap = ArdSrc.m_pBitmap;
  this->m_pPalette = ArdSrc.m_pPalette;
  this->m_dirRect = ArdSrc.m_dirRect;
  this->m_pMemDC = ArdSrc.m_pMemDC;
  this->m_pPaletteTmp = ArdSrc.m_pPaletteTmp;
  this->m_pBitmapTmp = ArdSrc.m_pBitmapTmp;
  this->m_DirtSize = ArdSrc.m_DirtSize;
  this->m_minDirtSize = ArdSrc.m_minDirtSize;
  this->m_dirRect = ArdSrc.m_dirRect;
 }
 return *this;
}
void CArdpsImg::Serialize(CArchive& ar)
{
 CObject::Serialize(ar);
 ar.Flush();
 if (ar.IsStoring())
 {
  Write(ar.GetFile());
 }
 else
 {
  Read(ar.GetFile());
 }
}
bool CArdpsImg::Read(CFile *pFile)
{
 WaitCursorBegin();

 LPBITMAPINFOHEADER lpbi;
 DWORD dwSize;
 TRY
 {
  // read DIB file header
  BITMAPFILEHEADER bmfHdr;
  pFile->Read(&bmfHdr, sizeof(BITMAPFILEHEADER));
  // is DIB file?

  if (bmfHdr.bfType != DIB_HEADER_MARKER/*"BM"*/)
  {
   WaitCursorEnd();
   return false;
  }
  DWORD dwLength = pFile->GetLength();
  if (bmfHdr.bfSize != dwLength)
   bmfHdr.bfSize = dwLength;

  // read DIB buffer
  dwSize = bmfHdr.bfSize - sizeof(BITMAPFILEHEADER);
  lpbi = (LPBITMAPINFOHEADER)GlobalAllocPtr(GHND, dwSize);
  DWORD dwCount = pFile->Read(lpbi, dwSize);
  // read ok?
  if (dwCount != dwSize)
  {
   GlobalFreePtr(lpbi);
   WaitCursorEnd();
   return false;
  }

  // Check to see that it's a Windows DIB -- an OS/2 DIB would cause 
  // strange problems with the rest of the DIB API since the fields 
  // in the header are different and the color table entries are 
  // smaller. 
  // 
  // If it's not a Windows DIB (e.g. if biSize is wrong), return NULL. 
  if (lpbi->biSize != sizeof(BITMAPINFOHEADER)) 
  {
   GlobalFreePtr(lpbi);
   WaitCursorEnd();
   return false;
  }

  // fill color num item
  int nNumColors = (UINT)lpbi->biClrUsed;
  if (nNumColors == 0) 
  { 
   // no color table for 24-bit, default size otherwise 
   if (lpbi->biBitCount != 24) 
    nNumColors = 1 << lpbi->biBitCount; // standard size table 
  }

  // fill in some default values if they are zero 
  if (lpbi->biClrUsed == 0) 
   lpbi->biClrUsed = nNumColors; 
  if (lpbi->biSizeImage == 0) 
   lpbi->biSizeImage = ((((lpbi->biWidth * (DWORD)lpbi->biBitCount) + 31) & ~31) >> 3) * lpbi->biHeight; 
 }
 CATCH (CException, e)
 {
  GlobalFreePtr(lpbi);
  WaitCursorEnd();
  return false;
 }
 END_CATCH

  // create CDib with DIB buffer
  bool bSuccess = Create((LPBYTE)lpbi);
 GlobalFreePtr(lpbi);
 WaitCursorEnd();

 return bSuccess;
}
///////////////////////////////////////////////////////////////////////
//从DIB数据块创建默认CDib对象
///////////////////////////////////////////////////////////////////////
bool CArdpsImg::Create(LPBYTE lpDIB)
{
 if (lpDIB == NULL)
  return false;

 DWORD dwSize = DIBlockSize(lpDIB);

 HDIB hDib  = GlobalAlloc(GHND, dwSize); 
 // Check that DIB handle is valid 
 if (! hDib) 
  return false;

 LPBYTE lpbi  = (LPBYTE)GlobalLock(hDib); 
 if (! lpbi)
  return false;

 CopyMemory(lpbi, lpDIB, dwSize);
 GlobalUnlock(hDib);

 Destroy();
 m_hDib = hDib;
 LPBYTE ptmp = (LPBYTE) GlobalLock(m_hDib);

 return UpdateInternal();
}
//*******************************************************************
// 功能:调用DIBToDIBSection函数,创建并更新DIB的DIBSECTION和DDB
//*******************************************************************
bool CArdpsImg::BuildBitmap()
{
 if (m_pBitmap != NULL)
 {
  delete m_pBitmap;
  m_pBitmap = NULL;
  m_hBitmap = NULL;
 }
 m_hBitmap = DIBToDIBSection(m_hDib);
 if (m_hBitmap == NULL)
  return false;
 m_pBitmap = new CBitmap;
 m_pBitmap->Attach(m_hBitmap);

 return true;
}
//*******************************************************************
//该函数调用CreateDIBPalette函数,创建并更新DIB调色板
//*******************************************************************
bool CArdpsImg::BuildPalette()
{
 if (m_pPalette != NULL)
 {
  delete m_pPalette;
  m_pPalette = NULL;
 }
 HPALETTE hPalette = CreateDIBPalette(m_hDib);
 if (hPalette == NULL)
  return false;
 m_pPalette = new CPalette;
 m_pPalette->Attach(hPalette);

 return true;
}

//*******************************************************************
//UpdateInternal function 更新CDib对象所对应的调色板、DIBSECTION、DDB对象
//调用BuildPlaette 和BuildBitmap函数,重建CDib对象的,m_pPalette、m_hBitmap
//和m_pBitmap 成员数据
bool CArdpsImg::UpdateInternal()
{
 BuildPalette();
 return BuildBitmap();
}

void CArdpsImg::Destroy()
{
 if (m_hDib != NULL)
 {
  DestroyDIB(m_hDib);
  m_hDib = NULL;
 }
 if( m_hNewDib != NULL )
 {
  DestroyDIB(m_hNewDib);
  m_hNewDib  = NULL;
 }
}


///////////////////////////////////////////////////////////////////////
//从DIB数据块创建CDib对象
///////////////////////////////////////////////////////////////////////
bool CArdpsImg::Create(LPBYTE lpDIB,  WORD  wBitCount)  // bits/pixel 
{
 if (lpDIB == NULL)
  return false;
 if (! Create(lpDIB))
  return false;

 WORD wBits = ((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
 if (wBitCount == wBits)
  return true;

 HDIB hNewDib = ConvertDIBFormat(m_hDib, wBitCount, NULL); 
 if (! hNewDib)
  return false;

 Destroy();
 m_hDib = hNewDib;
 return UpdateInternal();
}
CPalette* CArdpsImg::GetPalette()
{
 return m_pPalette;
}
bool CArdpsImg::Write(CFile *pFile)
{
 WaitCursorBegin();

 BITMAPFILEHEADER    bmfHdr;     // Header for Bitmap file 
 LPBITMAPINFOHEADER  lpBI;       // Pointer to DIB info structure 
 DWORD               dwDIBSize;

 // Get a pointer to the DIB memory, the first of which contains 
 // a BITMAPINFO structure 
 lpBI = (LPBITMAPINFOHEADER)GlobalLock(m_hDib); 
 if (!lpBI) 
 {
  GlobalUnlock(m_hDib);
  WaitCursorEnd();
  return false; 
 }

 // Check to see if we're dealing with an OS/2 DIB.  If so, don't 
 // save it because our functions aren't written to deal with these 
 // DIBs. 
 if (lpBI->biSize != sizeof(BITMAPINFOHEADER)) 
 { 
  GlobalUnlock(m_hDib); 
  WaitCursorEnd();
  return false; 
 }

 // Fill in the fields of the file header

 // Fill in file type (first 2 bytes must be "BM" for a bitmap)

 bmfHdr.bfType = DIB_HEADER_MARKER;  // "BM"

 // Calculating the size of the DIB is a bit tricky (if we want to 
 // do it right).  The easiest way to do this is to call GlobalSize() 
 // on our global handle, but since the size of our global memory may have 
 // been padded a few bytes, we may end up writing out a few too 
 // many bytes to the file (which may cause problems with some apps, 
 // like HC 3.0). 
 // 
 // So, instead let's calculate the size manually. 
 // 
 // To do this, find size of header plus size of color table.  Since the 
 // first DWORD in both BITMAPINFOHEADER and BITMAPCOREHEADER conains 
 // the size of the structure, let's use this.

 // Partial Calculation

 dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPBYTE)lpBI);  

 // Now calculate the size of the image

 // It's an RLE bitmap, we can't calculate size, so trust the biSizeImage 
 // field

 if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) 
  dwDIBSize += lpBI->biSizeImage; 
 else 
 { 
  DWORD dwBmBitsSize;  // Size of Bitmap Bits only

  // It's not RLE, so size is Width (DWORD aligned) * Height

  dwBmBitsSize = MYWIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * 
   lpBI->biHeight;

  dwDIBSize += dwBmBitsSize;

  // Now, since we have calculated the correct size, why don't we 
  // fill in the biSizeImage field (this will fix any .BMP files which  
  // have this field incorrect).

  lpBI->biSizeImage = dwBmBitsSize; 
 }


 // Calculate the file size by adding the DIB size to sizeof(BITMAPFILEHEADER)

 bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER); 
 bmfHdr.bfReserved1 = 0; 
 bmfHdr.bfReserved2 = 0;

 // Now, calculate the offset the actual bitmap bits will be in 
 // the file -- It's the Bitmap file header plus the DIB header, 
 // plus the size of the color table.

 bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + 
  PaletteSize((LPBYTE)lpBI);

 TRY
 {
  // Write the file header 
  pFile->Write(&bmfHdr, sizeof(BITMAPFILEHEADER));
  // write DIB buffer
  pFile->Write(lpBI, dwDIBSize);
 }
 CATCH (CException, e)
 {
  GlobalUnlock(m_hDib); 
  WaitCursorEnd();
  return false;
 }
 END_CATCH

  GlobalUnlock(m_hDib); 
 WaitCursorEnd();

 return true;
}


bool CArdpsImg::IsEmpty()
{
 if (m_hDib == NULL)
  return true;

 if (! GlobalLock(m_hDib))
  return true;

 GlobalUnlock(m_hDib);
 return false;
}

bool CArdpsImg::Display(CDC * pDC, int x, int y, DWORD dwRop)
{
 CDC MemDC;
 MemDC.CreateCompatibleDC(pDC);

 CBitmap* pOldBmp = MemDC.SelectObject(m_pBitmap);

 CPalette* pOldPal = pDC->SelectPalette(m_pPalette, true);
 pDC->RealizePalette();

 bool bSuccess = pDC->BitBlt(x, y, 
  GetWidth(), GetHeight(),
  &MemDC, 
  0, 0, 
  dwRop);

 MemDC.SelectObject(pOldBmp);
 pDC->SelectPalette(pOldPal, true);

 return bSuccess;
}

WORD CArdpsImg::GetBitCount()
{
 LPBITMAPINFOHEADER lpBI = (LPBITMAPINFOHEADER)GlobalLock(m_hDib); 
 if (!lpBI) 
 {
  GlobalUnlock(m_hDib);
  return 0; 
 }

 WORD wBitCount = lpBI->biBitCount;
 GlobalUnlock(m_hDib);

 return wBitCount;
}

LONG CArdpsImg::GetWidth()
{
 // get DIB buffer pointer
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib); 
 if (! lpDIB)
 {
  GlobalUnlock(m_hDib);
  return 0;
 }

 LONG lWidth = (LONG)DIBWidth(lpDIB);
 GlobalUnlock(m_hDib);

 return lWidth; 
}

LONG CArdpsImg::GetHeight()
{
 // get DIB buffer pointer
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib); 
 if (! lpDIB)
 {
  GlobalUnlock(m_hDib);
  return 0;
 }

 LONG lHeight = (LONG)DIBHeight(lpDIB);
 GlobalUnlock(m_hDib);

 return lHeight; 
}

LONG CArdpsImg::GetWidthBytes()
{
 return MYWIDTHBYTES((GetWidth())*((DWORD)GetBitCount()));
 //return WIDTHBYTES((GetWidth())*((int)GetBitCount()));
}
LPBYTE CArdpsImg::GetBitsPtr()
{
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib); 
 if (! lpDIB) 
 {
  GlobalUnlock(m_hDib);
  return NULL;
 }

 LPBYTE lpData = FindDIBBits(lpDIB);
 GlobalUnlock(m_hDib);

 return lpData;
}

HANDLE CArdpsImg::GetHandle()
{
 return m_hDib;
}

WORD CArdpsImg::GetColorNumber()
{
 LPBYTE lpBI = (LPBYTE)GlobalLock(m_hDib); 
 if (! lpBI) 
 {
  GlobalUnlock(m_hDib);
  return 0;
 }

 WORD wColors = DIBNumColors(lpBI);
 GlobalUnlock(m_hDib);

 return wColors;
}

WORD CArdpsImg::GetPaletteSize()
{
 LPBYTE lpBI = (LPBYTE)GlobalLock(m_hDib); 
 if (! lpBI) 
 {
  GlobalUnlock(m_hDib);
  return 0;
 }

 WORD wPalSize = PaletteSize(lpBI);
 GlobalUnlock(m_hDib);

 return wPalSize;
}
bool CArdpsImg::Attach(HDIB hDib)
{
 if (hDib == NULL)
  return false;

 Destroy();
 m_hDib = hDib;
 return UpdateInternal();
}
bool CArdpsImg::ConvertFormat(WORD wBitCount)
{
 if (IsEmpty())
  return false;

 if (GetBitCount() == wBitCount)
  return true;

 HDIB hNewDib = ConvertDIBFormat(m_hDib, wBitCount, NULL); 
 if (! hNewDib)
  return false;

 Destroy();
 m_hDib = hNewDib;
 return UpdateInternal();
}
/*
功能:加载其他图像格式,支持jpg、tiff格式
输入:
 szImgFileName:文件名;
 
输出:
 bBmp:加载成功,转换成DIB句柄

*/

HANDLE  CArdpsImg::LoadNonDibHandle(BOOL *bBmp, char* szImgFileName)
{
 *bBmp = FALSE;
 char drive[_MAX_DRIVE];
 char dir[_MAX_DIR];
 char fname[_MAX_FNAME];
 char ext[_MAX_EXT];
 char szName[256];
 typedef HANDLE (WINAPI * PGETDIBPROC)(HWND, char *, HANDLE);
 PGETDIBPROC lpFun;
 HANDLE hDib;

 HINSTANCE hConvertLib = LoadLibrary("Convert.dll");
 ASSERT(hConvertLib != NULL);

 strcpy(szName, szImgFileName);
 _splitpath(szName, drive, dir, fname, ext);
 strcpy(szName, ext);

 strlwr(szName);

 if(strcmp(szName,".tif") == 0 || strcmp(szName, ".tiff") == 0)
 {
  //lpFun = (PGETDIBPROC)GetProcAddress(hConvertLib, "Tiff2HDIB");
  /*hDib = LoadTiff(szImgFileName);*/
  hDib = ReadBinTiff(szImgFileName);  
  return hDib;
 }
 else if(strcmp(szName,".jpg") == 0 || strcmp(szName,".jpeg") == 0 || strcmp(szName,".jpe") == 0 )
 {
  lpFun = (PGETDIBPROC)GetProcAddress(hConvertLib, "Jpg2HDIB");
 }
 else 
 {
  if(hConvertLib)
  {
   FreeLibrary(hConvertLib);
  }
  *bBmp = TRUE;

  return NULL;
 }
 ASSERT(lpFun != NULL);


 freopen("nul", "w", stdout);

 hDib = (*lpFun) ( NULL, szImgFileName, NULL);


 freopen("con", "w", stdout);

 if(hConvertLib)
 {
  FreeLibrary(hConvertLib);
 }

 return hDib;
}
/*
功能:BMP转换为其他图像格式
输入:
 hDib:当前图像句柄;
 strFileName:文件名;
 nDestFileExtType:在ImgConv.h中定义
输出:
 true:存储成功

*/
bool CArdpsImg::SaveNonDIB(HDIB hDib, CString strFileName,  ImageType nDestFileExtType)
{
 HMODULE hDll = LoadLibrary("ImgConv.dll");
 if (hDll == NULL)
 {
  return false;
 }
 
 PFN_ImageConvert ImageConvert = (PFN_ImageConvert)GetProcAddress(hDll, "ImageConvert");
 PFN_ImageDelete ImageDelete = (PFN_ImageDelete)GetProcAddress(hDll, "ImageDelete");
 if (ImageConvert == NULL || ImageDelete == NULL)
 {
  FreeLibrary(hDll);
  return false;
 }
 LPBITMAPINFOHEADER lpBI = (LPBITMAPINFOHEADER)GlobalLock(m_hDib); 
 bool bSave = false;
 BITMAPFILEHEADER bmfHdr;
 bool bInit = InitDIBFileHead(lpBI, &bmfHdr);
 if( !bInit )
 {
  GlobalUnlock(m_hDib);
  FreeLibrary(hDll);
  return false;
 }

 unsigned char *pbSrc = new unsigned char [bmfHdr.bfSize];

 if( !pbSrc )
 {
  GlobalUnlock(m_hDib);
  FreeLibrary(hDll);
  return false;
 }
 memcpy(pbSrc, &bmfHdr, sizeof(BITMAPFILEHEADER));
 memcpy(pbSrc + sizeof(BITMAPFILEHEADER), lpBI, lpBI->biSizeImage * sizeof(unsigned char));

 unsigned char * pDestData = NULL;
 int nDestDataLen = 0;
 bool bSuccess = false;
 bSuccess = ImageConvert(pbSrc, bmfHdr.bfSize , imgBMP, &pDestData, &nDestDataLen, nDestFileExtType);
 if( bSuccess )
 {
  FILE * pFile = fopen(strFileName, "wb");
  if (pFile != NULL)
  {
   fwrite(pDestData, sizeof(unsigned char), nDestDataLen, pFile);
   fclose(pFile);
   ImageDelete(&pDestData);  
   bSave = true;   
  }
 }
 if (pbSrc)
 {
  delete []pbSrc;
  pbSrc = NULL;
 }
 
 FreeLibrary(hDll);
 GlobalUnlock(m_hDib);
 return bSave;
}
bool CArdpsImg::InitDIBFileHead(LPBITMAPINFOHEADER lpBI, BITMAPFILEHEADER *lpbmfHdr )
{
 if( !lpBI || !lpbmfHdr )
  return false;

 //BITMAPFILEHEADER bmfHdr;
 lpbmfHdr->bfType = DIB_HEADER_MARKER;  // "BM" 
 DWORD dwDIBSize = *(LPDWORD)lpBI + PaletteSize((LPBYTE)lpBI);   
 if ((lpBI->biCompression == BI_RLE8) || (lpBI->biCompression == BI_RLE4)) 
  dwDIBSize += lpBI->biSizeImage; 
 else 
 { 
  DWORD dwBmBitsSize;  // Size of Bitmap Bits only

  // It's not RLE, so size is Width (DWORD aligned) * Height

  dwBmBitsSize = MYWIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) * 
   lpBI->biHeight;

  dwDIBSize += dwBmBitsSize;

  // Now, since we have calculated the correct size, why don't we 
  // fill in the biSizeImage field (this will fix any .BMP files which  
  // have this field incorrect).

  lpBI->biSizeImage = dwBmBitsSize; 
 } 
 lpbmfHdr->bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER); 
 lpbmfHdr->bfReserved1 = 0; 
 lpbmfHdr->bfReserved2 = 0; 
 lpbmfHdr->bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + PaletteSize((LPBYTE)lpBI); 
 return true;
}

ImageType CArdpsImg::GetFileExtType(CString strExt)
{
 if (strExt. CompareNoCase ("bmp") == 0 || strExt.CompareNoCase("dib") == 0 )
 {
  return imgBMP;
 }
 else if (strExt.CompareNoCase("gif") == 0 )
 {
  return imgGIF;
 }
 else if (strExt.CompareNoCase("jpg") == 0 )
 {
  return imgJPG;
 }
 else if (strExt.CompareNoCase("jpeg") == 0 || strExt.CompareNoCase("jpe") == 0  )
 {
  return imgJPEG;
 }
 else if (strExt.CompareNoCase("png") == 0 )
 {
  return imgPNG;
 }
 else if (strExt.CompareNoCase("tiff") || strExt.CompareNoCase("tif"))
 {
  return imgTIFF;
 }
 else
 {
  return imgBMP;
 }
}
/*
功能:转换为灰度图像
输入:
 nMethod:二值化方法;默认为0,加权平均方法;1为最大值;
 uiGWeight,uiGWeight,uiBWeight:红、绿、蓝色分量
输出:
 true:灰度化成功

*/
bool CArdpsImg::ConvertGray(int nMethod, UINT uiRWeight /* = 114 */, UINT uiGWeight /* = 587 */, UINT uiBWeight /* = 229  */)
{

 LPBITMAPINFOHEADER lpDIBHdr = (LPBITMAPINFOHEADER)GlobalLock(m_hDib); 
 
 //for color image
 LONG  nImgHei = GetHeight();
 LONG  nImgWid = GetWidth();
 LONG  nOriBytesPerLine = GetWidthBytes();
 int  nBitCnt = GetBitCount();
 if( nBitCnt < 24 )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 
 //for gray image
 LONG  nGrayBytesPerLine = MYWIDTHBYTES( nImgWid << 3 );
 LONG nGrayImgSize = nGrayBytesPerLine * nImgHei;
 LONG nFileSize = nGrayImgSize + lpDIBHdr->biSize + 256 * sizeof(RGBQUAD);

 //allocate memory for gray image
 LPBYTE lpNewDIB = new BYTE[nFileSize];
 if( !lpNewDIB )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 memset(lpNewDIB, 0, nFileSize*sizeof(BYTE));
 LPBITMAPINFOHEADER lpNewDIBHdr = (LPBITMAPINFOHEADER)lpNewDIB;
 memcpy(lpNewDIBHdr, lpDIBHdr, sizeof(BITMAPINFOHEADER));
 GlobalUnlock(m_hDib);

 lpNewDIBHdr->biBitCount = 8;
 lpNewDIBHdr->biCompression = BI_RGB;
 lpNewDIBHdr->biSizeImage = nGrayImgSize;
 lpNewDIBHdr->biClrUsed = 256;
 
 
 //create RGBQUARD
 RGBQUAD *pNewRGBQuad = (RGBQUAD*)(lpNewDIB +sizeof(BITMAPINFOHEADER));

 memset(pNewRGBQuad, 0, 256*sizeof(RGBQUAD));
 for( int i = 0; i < 256; ++i )
 {
  pNewRGBQuad->rgbBlue = pNewRGBQuad->rgbRed = 
   pNewRGBQuad->rgbGreen = i;
  pNewRGBQuad++;   
 }

 LPBYTE  lpDIB = (LPBYTE)GlobalLock(m_hDib);
 LPBYTE lpImg = FindDIBBits(lpDIB); 
 LPBYTE lpNewImg = lpNewDIB + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);

 LPBYTE lpOritmp = lpImg;
 LPBYTE lpNewtmp = lpNewImg; 
 int j, k, m, n, nStep = nBitCnt >> 3; 
 
 switch (nMethod)
 {
 case 0:
  for(  j = 0, m = 0, n = 0; j < nImgHei; ++j , m += nOriBytesPerLine,  n += nGrayBytesPerLine )
  {
   lpNewtmp = lpNewImg + n;
   lpOritmp = lpImg + m;  
   for(  k = 0; k < nImgWid; ++k, lpNewtmp ++ )
   {
    *lpNewtmp  = BYTE((  (*lpOritmp) *uiRWeight + 
             *(lpOritmp + 1) * uiGWeight + *(lpOritmp + 2) * uiBWeight )/1000);
    lpOritmp += nStep;

   }
  }
  break;
 case 1:
 default:
  for(  j = 0, m = 0, n = 0; j < nImgHei; ++j , m += nOriBytesPerLine,  n += nGrayBytesPerLine )
  {
   lpNewtmp = lpNewImg + n;
   lpOritmp = lpImg + m;  
   for(  k = 0; k < nImgWid; ++k, lpNewtmp ++ )
   {
    *lpNewtmp  = BYTE( max(max (  (*lpOritmp), *(lpOritmp + 1) ), *(lpOritmp + 2) ));
    lpOritmp += nStep;
   }
  }
  break;
 }
 GlobalUnlock(m_hDib);
 bool bCrate = Create(lpNewDIB);
 if( lpNewDIB )
 {
  delete []lpNewDIB;
  lpNewDIB = NULL;
 }

 return true;
}
/*
功能:对灰度图像进行二值化
输入:
  nMethod:二值化方法;默认为0,OTSU方法;1为均值;2:为给定阈值方法
  nTh:二值化阈值
输出:
  true:二值化成功

*/
bool CArdpsImg::ConvertBinary(int nMethod /* = 0 */, int nTh /* = 0  */)
{
 //for image
 int  nBitCnt = GetBitCount();
 //for gray image
 LONG  nGrayBytesPerLine = GetWidthBytes();

 if( nBitCnt > 8 )
 {
  bool bGray = ConvertGray(0); 
  if(!bGray)
  {
   return false;
  }
  //lpDIBHdr = (LPBITMAPINFOHEADER)GlobalLock(m_hDib); 
 }
 else if( nBitCnt == 1)
 {
  return true;
 }  

 LPBITMAPINFOHEADER lpDIBHdr = (LPBITMAPINFOHEADER)GlobalLock(m_hDib);


 LONG  nImgHei = GetHeight();
 LONG  nImgWid = GetWidth();

 //for binary image
 LONG  nBinaryBytesPerLine = MYWIDTHBYTES( nImgWid );
 LONG nBinaryImgSize = nBinaryBytesPerLine * nImgHei;
 LONG nFileSize = nBinaryImgSize  + lpDIBHdr->biSize + 2 * sizeof(RGBQUAD);

 //allocate memory for binary image
 LPBYTE lpNewDIB = new BYTE[nFileSize];
 if( !lpNewDIB )
 {
  GlobalUnlock(m_hDib); 
  return false;
 }
 memset(lpNewDIB, 0, nFileSize*sizeof(BYTE));
 LPBITMAPINFOHEADER lpNewDIBHdr = (LPBITMAPINFOHEADER)lpNewDIB;
 memcpy(lpNewDIBHdr, lpDIBHdr, sizeof(BITMAPINFOHEADER));
 GlobalUnlock(m_hDib);

 lpNewDIBHdr->biBitCount = 1;
 lpNewDIBHdr->biCompression = 0;
 lpNewDIBHdr->biSizeImage = nBinaryImgSize;
 lpNewDIBHdr->biClrUsed = 2;
 lpNewDIBHdr->biXPelsPerMeter = 0;
 lpNewDIBHdr->biYPelsPerMeter = 0;

 //create RGBQUARD
 RGBQUAD *pNewRGBQuad = (RGBQUAD*)(lpNewDIB +sizeof(BITMAPINFOHEADER));

 pNewRGBQuad->rgbRed = pNewRGBQuad->rgbGreen = pNewRGBQuad->rgbBlue 
            = 0;
 pNewRGBQuad->rgbReserved = 0;
 pNewRGBQuad++;
 pNewRGBQuad->rgbRed = pNewRGBQuad->rgbGreen = pNewRGBQuad->rgbBlue 
           = 255;
 pNewRGBQuad->rgbReserved = 0;


 LPBYTE  lpDIB = (LPBYTE)GlobalLock(m_hDib);
 LPBYTE lpImg = FindDIBBits(lpDIB); 
 LPBYTE lpNewImg = lpNewDIB + sizeof(BITMAPINFOHEADER) + 2 * sizeof(RGBQUAD);

 LPBYTE lpOritmp = lpImg;
 LPBYTE lpNewtmp = lpNewImg; 
 int  nStep = nBitCnt >> 3; 
 bool bResult = false;
 switch (nMethod)
 {
 case 1:
  nTh = 0;
  bResult = IntensityMean( lpImg, nImgWid, nImgHei, nTh );
  break; 
 case 2:
  bResult = true;
  break;
 case 0:
 default:
  nTh = 0;
  OtsuTh(nImgWid, nImgHei, lpImg, nTh);
  bResult = true;
  break;
 }
 if( bResult )
  bResult = ThreshImage(lpImg, lpNewImg, nImgWid, nImgHei, nTh);

 GlobalUnlock(m_hDib);
 bool bCrate = Create(lpNewDIB);
 if (lpNewDIB)
 {
  delete []lpNewDIB;
  lpNewDIB = NULL;
 }

 return bCrate;
}
/*
功能:利用Otsu方法计算二值化的阈值
输入:
 lpBits:灰度图像内存指针
 nW:图像宽度
 nH:图像高度
输出:
 Th:图像灰度均值
*/
void CArdpsImg:: OtsuTh( DWORD nW, DWORD nH, BYTE *lpBits, int  &Th)

 UINT i; 
 float bmax;
 float counter1,counter2;
 float mean1,mean2; 
 int  TH, threshold, k, deta; 
 float sum1,sum2;
 int buf[256];
 
 memset(buf, 0, 256 * sizeof(int));
 if( !GetGrayHist(lpBits, nW, nH, buf) )
  return;

 for ( i = 0; i < 256; i ++)
 {
  bufi[i] = i * buf[i];
 }

 deta = 10;       //计算阈值的步长,以加快求解速度。
 counter1 = 0;
 for ( TH = 0; TH < 256; TH += deta )
 {
  counter1 = counter2=0;
  sum1 = sum2=0.0;
  for( k = 0; k <= TH; k++ ) 
  {
   counter1 += buf[k];
   sum1 += bufi[k];
  }
  for( k = TH + 1; k < 256; k++ ) 
  {
   counter2 += buf[k];   
   sum2 += bufi[k];
  }
  if ( counter1>0 ) mean1= sum1/counter1;
  else           mean1=0;
  if ( counter2>0 ) mean2= sum2/counter2;
  else           mean2=0;
  varience[TH]=counter1*counter2*(mean1-mean2)*(mean1-mean2);
 }
 bmax=varience[0]; threshold=0;
 for ( TH=0; TH<256;  TH += deta )
 {
  if( bmax<varience[TH] )
  {
   bmax=varience[TH];
   threshold=TH;
  }
 }

 Th=threshold;
}

/*
功能:计算图像的灰度均值
输入:
   pbGray:灰度图像内存指针
   nImgWid:图像宽度
   nImgHei:图像高度
输出:
   nIntenMean:图像灰度均值
*/
bool CArdpsImg::IntensityMean(const BYTE *pbGray, const int nImgWid, const int nImgHei, int &nIntenMean )
{
 if( pbGray == NULL || nImgHei <= 0 || nImgWid <= 0 )
  return false;
 int nSum = 0;
 nIntenMean = 0;
 for( int i = 0; i < nImgHei * nImgWid; i++ )
 {
  nSum += *pbGray ++;
 }
 nIntenMean = BYTE(nSum /((nImgHei * nImgWid) + 1));
 return true;
}

bool CArdpsImg::ThreshImage(const BYTE *pbImg, BYTE *pbBinary, const int nImgWid, const int nImgHei, const int nTh )
{
 if( pbImg == NULL || pbBinary== NULL )
  return false;
 LONG nGrayPerLine = MYWIDTHBYTES(nImgWid << 3);
 LONG nBinaryPerLine = MYWIDTHBYTES(nImgWid);

 const BYTE *pbIn = pbImg;
 BYTE *pbOut = pbBinary;
 BYTE  tmp = 0;
 for( int i = 0, m = 0, n = 0; i < nImgHei; ++i  )
 {
  pbIn = pbImg + m;
  pbOut = pbBinary + n;
  
  for(int j = 0; j < nImgWid/8; ++j )
  {
   tmp = 0;
   if( *pbIn  > nTh  )
    tmp = tmp | 128;
   ++pbIn;
   if( *pbIn  > nTh  )
    tmp = tmp | 64;
   ++pbIn;
   if( *pbIn  > nTh  )
    tmp = tmp | 32;
   ++pbIn;
   if( *pbIn  > nTh  )
    tmp = tmp | 16;
   ++pbIn;
   if( *pbIn  > nTh  )
    tmp = tmp | 8;
   ++pbIn;
   if( *pbIn  > nTh  )
    tmp = tmp | 4;
   ++pbIn;
   if( *pbIn  > nTh  )
    tmp = tmp | 2;
   ++pbIn;
   if( *pbIn  > nTh  )
    tmp = tmp | 1;
   ++pbIn;

   *pbOut ++ = tmp;   
  }
  tmp = 0;
  for( int k = 0; k < nImgWid % 8; k++, pbIn++ )
  {
   if( *pbIn > nTh )
   {
    tmp += 1;
   }
   tmp = tmp << 1;
  }
  *pbOut = tmp << (8 - nImgWid % 8 - 1);
  m += nGrayPerLine;
  n += nBinaryPerLine;
 }

 return true;
}
/******************************************************************
功能:灰度图像的均衡化,使得像素在灰度阶上尽量均匀
输入:
   当前图像的内存句柄
输出:
   灰度成功返回true
******************************************************************/
bool CArdpsImg::GrayEqual()
{
 if(IsEmpty())
  return false;
 if( !GrayEqualize(m_hDib))
  return false;
 return UpdateInternal();
}
/****************************************************************************
功能:对灰度图像进行拉伸
输入:
  nMethod:灰度图像内存指针。
  (x1,y1):拉伸曲线的第一点坐标
  (x2,y2):拉伸曲线的第二点坐标
                                                                       *(255,255)
                  * 
                 * (x2,y2)
                *
                 *
                  *
              *(x1,y1)
                                              *
           *(0,0)

  nMethod = 0:根据直方图自动计算第一点和第二点坐标,阈值取累积直方图的10%~90%。
  nMethod = 1:根据输入的坐标进行计算
输出:
  操作成功返回True。
**********************************************************************************/
bool CArdpsImg::GrayStretch(int nMethod, BYTE x1 = 0 , BYTE y1 = 0 , BYTE x2 = 255, BYTE y2 = 255 )
{
 if(IsEmpty())
  return false;
 if(!GrayStretchDIB(nMethod, m_hDib, x1, y1, x2, y2) )
  return false;
 return UpdateInternal();
}
/*******************************************************************
功能:去除矩形区域二值化图像的污点
输入:
  nMethod = 0:去污,选中区域如果为白色占主要部分则变成黑色
         否则相反。
  nMethod = 1 :磨白,把选中的区域变成白色
  nMethod = 2:填充,把选中的区域变成黑色
  rct:选中的处理区域
输出:
  去除成功
*******************************************************************/
bool CArdpsImg::EliminateDirt(int nMethod, const CRect rct)
{
 if ( rct.IsRectEmpty() || IsEmpty() )
  return false;
 
 // Convert the physical coordinate into imaging coordinate
 if( GetBitCount() > 1 )
  return false;
 if(!EliminateDirtDIB(nMethod, m_hDib, rct) )
  return false;
 
 return UpdateInternal();
}
/*******************************************************************
功能:去除圆形区域二值化图像的污点
输入:
   nMethod = 0:去污,选中区域如果为白色占主要部分则变成黑色
         否则相反。
   nMethod = 1 :磨白,把选中的区域变成白色
   nMethod = 2:填充,把选中的区域变成黑色
   rct:选中的处理区域
输出:
   去除成功
*******************************************************************/
bool CArdpsImg::EliminateDirt(int nMethod, const int nCircleX, const int nCircleY, const int nRadius)
{
 if( IsEmpty() || nCircleX < 0 || nCircleY < 0 || nRadius < 0 )
  return false;
 
 if( GetBitCount() > 1 )
  return false;
 if(!EliminateDirtDIB(nMethod, m_hDib, nCircleX, nCircleY, nRadius) )
  return false;

 return UpdateInternal();
}
/******************************************************************
功能:设置污点的大小
输入:
  sz,污点大小,小于sz为污点
******************************************************************/

void CArdpsImg::SetDirtSize(CSize szMax, CSize szMin)
{
 m_DirtSize = szMax;
 m_minDirtSize = szMin;
}

/******************************************************************
功能:自动去除图像中的小于一定面积的污点
输入:
    nMethod,去除0黑色污点,1去除白色污点
    nMinArea,污点的最大面积,小于该污点的像素区域将被作为污点
输出:
    去除成功返回true
******************************************************************/
bool CArdpsImg::AutoEliminateDirt(int nMethod, int nMinArea)
{
 LPBYTE  lpDIB = (LPBYTE)GlobalLock(m_hDib);
 if( !lpDIB)
  return false;
 LPBYTE lpBinImg = FindDIBBits(lpDIB); 
 if( !lpBinImg )
 {
  GlobalUnlock(m_hDib);
  return false;
 }

 bool bSuccess = true;
 LONG  nImgHei = GetHeight();
 LONG  nImgWid = GetWidth();
 int    nBitCnt = DIBBitCount(lpDIB);
 if( nBitCnt != 1 )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 LONG  nBinBytesPerLine = MYWIDTHBYTES(nImgWid * nBitCnt);
 //for gray image
 LONG  nGrayBytesPerLine = MYWIDTHBYTES( nImgWid << 3 );
 LONG nGrayImgSize = nGrayBytesPerLine * nImgHei;
 BYTE *pbGrayImg = new BYTE[nGrayImgSize];
 if( !pbGrayImg )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 bSuccess = ConvertBinary2Gray(lpBinImg, nImgWid, nImgHei, pbGrayImg );
 //SaveTo8bit_BMP(pbGrayImg, nImgWid, nImgHei, "d:\\testgray.bmp", 8);
 if( !bSuccess )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
  GlobalUnlock(m_hDib);
  return false;
 }

 Line *Li  = new Line;
 if( !Li )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
  GlobalUnlock(m_hDib);
  return false;
 }

 Li->StartY = 3;
 Li->StartX = 3;
 Li->EndX = nImgWid - 3;
 Li->EndY = nImgHei - 3;
 Li->cp  = NULL;

 BYTE  bCur;
 if( nMethod == 0 )  //黑色为背景
 {
  bCur = 0;
 }
 else
 { 
  bCur = 255;
 }

 ExtractComponent(Li, pbGrayImg, nGrayBytesPerLine, bCur );
 AnalysisComponent(Li, pbGrayImg, nGrayBytesPerLine, nMinArea, 255- bCur);
// SaveTo8bit_BMP(pbGrayImg, nImgWid, nImgHei, "d:\\testcom.bmp", 8);
 ThreshImage(pbGrayImg, lpBinImg, nImgWid, nImgHei, 128);

 if( pbGrayImg )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
 }

 if( Li )
 {
  delete Li;
  Li = NULL;
 }

 GlobalUnlock(m_hDib);

 return UpdateInternal();
}

bool CArdpsImg::AutoEliminateDirt(int nMethod, CRect *pRect, int *pFlag, const int nNum)
{
 if( !pRect || !pFlag  )
  return false;

 LPBYTE  lpDIB = (LPBYTE)GlobalLock(m_hDib);
 if( !lpDIB)
  return false;
 LPBYTE lpBinImg = FindDIBBits(lpDIB); 
 if( !lpBinImg )
 {
  GlobalUnlock(m_hDib);
  return false;
 }

 bool bSuccess = true;
 LONG  nImgHei = GetHeight();
 LONG  nImgWid = GetWidth();
 int    nBitCnt = DIBBitCount(lpDIB);
 if( nBitCnt != 1 )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 LONG  nBinBytesPerLine = MYWIDTHBYTES(nImgWid * nBitCnt);
 //for gray image
 LONG  nGrayBytesPerLine = MYWIDTHBYTES( nImgWid << 3 );
 LONG nGrayImgSize = nGrayBytesPerLine * nImgHei;
 BYTE *pbGrayImg = new BYTE[nGrayImgSize];
 if( !pbGrayImg )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 bSuccess = ConvertBinary2Gray(lpBinImg, nImgWid, nImgHei, pbGrayImg ); 
 if( !bSuccess )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
  GlobalUnlock(m_hDib);
  return false;
 }


 BYTE  bCur;
 if( nMethod == 0 )  //黑色为背景
 {
  bCur = 0;
 }
 else
 { 
  bCur = 255;
 }
 CRect tmpRt;
 for( int i = 0; i < nNum; i++ )
 {
  if (pFlag[i] == 1)
  {
   tmpRt.top = nImgHei - 1 - pRect[i].bottom;
   tmpRt.bottom = nImgHei - 1 - pRect[i].top;
   tmpRt.left = pRect[i].left;
   tmpRt.right = pRect[i].right;
   SetBinary(tmpRt, pbGrayImg, nGrayBytesPerLine, 255-bCur);
  }
 }
 ThreshImage(pbGrayImg, lpBinImg, nImgWid, nImgHei, 128);

 if( pbGrayImg )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
 }

 GlobalUnlock(m_hDib);

 return UpdateInternal();
}
/******************************************************************
功能:以原有图像中心为原点,给定的角度旋转图像
输入:
   iRotateAngle,指定旋转的度数,正值为向右旋转,反之向右旋转

输出:
     旋转成功返回true
******************************************************************/
bool  CArdpsImg::RotateImage(int iRotateAngle /* = 0 */)\
{

 // 源图像的宽度和高度
 LONG lWidth;
 LONG lHeight;

 // 旋转后图像的宽度和高度
 LONG lNewWidth;
 LONG lNewHeight;

 // 图像每行的字节数
 LONG lLineBytes;

 // 旋转后图像的宽度(lNewWidth',必须是4的倍数)
 LONG lNewLineBytes;

 // 指向源图像的指针
 LPBYTE lpDIBBits;

 // 指向源象素的指针
 LPBYTE lpSrc;

 // 旋转后新DIB句柄
 HDIB hDIB;

 // 指向旋转图像对应象素的指针
 LPBYTE lpDst;

 // 指向旋转图像的指针
 LPBYTE lpNewDIB;
 LPBYTE lpNewDIBBits;


 // 指向BITMAPINFO结构的指针(Win3.0)
 LPBITMAPINFOHEADER lpbmi;

 // 指向BITMAPCOREINFO结构的指针
 LPBITMAPCOREHEADER lpbmc;


 // 旋转角度(弧度)
 float fRotateAngle;

 // 旋转角度的正弦和余弦
 float fSina, fCosa;

 // 源图四个角的坐标(以图像中心为坐标系原点)
 float fSrcX1,fSrcY1,fSrcX2,fSrcY2,fSrcX3,fSrcY3,fSrcX4,fSrcY4;

 // 旋转后四个角的坐标(以图像中心为坐标系原点)
 float fDstX1,fDstY1,fDstX2,fDstY2,fDstX3,fDstY3,fDstX4,fDstY4;

 // 两个中间常量
 float f1,f2;
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);

 // 找到源DIB图像象素起始位置
 lpDIBBits = FindDIBBits(lpDIB);

 // 获取图像的"宽度"(4的倍数)
 lWidth = DIBWidth(lpDIB);

 WORD dwBitCnt = DIBBitCount(lpDIB);


 if (dwBitCnt != 8 && dwBitCnt != 24 && dwBitCnt != 1 )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 // 获取图像的高度
 lHeight = DIBHeight(lpDIB);

 LPBYTE pBin2Gray = NULL;
 int lBin2GrayLineBytes = MYWIDTHBYTES(lWidth * 8);

 if( dwBitCnt == 1)
 {
  pBin2Gray = new BYTE[lBin2GrayLineBytes * lHeight];
  if( !pBin2Gray )
  {
   GlobalUnlock(m_hDib);
   return false;
  }
  memset(pBin2Gray, 0, lBin2GrayLineBytes * lHeight);
  ConvertBinary2Gray(lpDIBBits, lWidth, lHeight, pBin2Gray);
  lpDIBBits = pBin2Gray;
 }


 // 计算图像每行的字节数
 lLineBytes = MYWIDTHBYTES(lWidth * dwBitCnt);

 

 // 将旋转角度从度转换到弧度
 fRotateAngle = (float) RADIAN(iRotateAngle);

 // 计算旋转角度的正弦
 fSina = (float) sin((double)fRotateAngle);

 // 计算旋转角度的余弦
 fCosa = (float) cos((double)fRotateAngle);

 // 计算原图的四个角的坐标(以图像中心为坐标系原点)
 fSrcX1 = (float) (- (lWidth  - 1) / 2);
 fSrcY1 = (float) (  (lHeight - 1) / 2);
 fSrcX2 = (float) (  (lWidth  - 1) / 2);
 fSrcY2 = (float) (  (lHeight - 1) / 2);
 fSrcX3 = (float) (- (lWidth  - 1) / 2);
 fSrcY3 = (float) (- (lHeight - 1) / 2);
 fSrcX4 = (float) (  (lWidth  - 1) / 2);
 fSrcY4 = (float) (- (lHeight - 1) / 2);

 // 计算新图四个角的坐标(以图像中心为坐标系原点)
 fDstX1 =  fCosa * fSrcX1 + fSina * fSrcY1 - 0.5;
 fDstY1 = -fSina * fSrcX1 + fCosa * fSrcY1 - 0.5;
 fDstX2 =  fCosa * fSrcX2 + fSina * fSrcY2 - 0.5;
 fDstY2 = -fSina * fSrcX2 + fCosa * fSrcY2 + 0.5; 
 fDstX3 =  fCosa * fSrcX3 + fSina * fSrcY3 + 0.5;
 fDstY3 = -fSina * fSrcX3 + fCosa * fSrcY3 - 0.5;
 fDstX4 =  fCosa * fSrcX4 + fSina * fSrcY4 + 0.5;
 fDstY4 = -fSina * fSrcX4 + fCosa * fSrcY4 + 0.5;

 // 计算旋转后的图像实际宽度
 lNewWidth  = (LONG) ( max( fabs(fDstX4 - fDstX1), fabs(fDstX3 - fDstX2) ) + 0.5);
 // 计算旋转后的图像高度
 lNewHeight = (LONG) ( max( fabs(fDstY4 - fDstY1), fabs(fDstY3 - fDstY2) ) + 0.5);

 if( iRotateAngle == 90 || iRotateAngle == 270 )
 {
  lNewHeight = lWidth;
  lNewWidth = lHeight;
 }
 else if( iRotateAngle == 0 || iRotateAngle == 360 || iRotateAngle == 180 )
 {
  lNewHeight = lHeight;
  lNewWidth = lWidth;
 }
 
 lNewLineBytes = MYWIDTHBYTES(lNewWidth * dwBitCnt); 
 f1 = (float) (-0.5 * (lNewWidth - 1) * fCosa - 0.5 * (lNewHeight - 1) * fSina
     + 0.5 * (lWidth  - 1));
 f2 = (float) ( 0.5 * (lNewWidth - 1) * fSina - 0.5 * (lNewHeight - 1) * fCosa
     + 0.5 * (lHeight - 1));

 // 分配内存,以保存新DIB
 hDIB = (HDIB) ::GlobalAlloc(GHND, lNewLineBytes * lNewHeight + *(LPDWORD)lpDIB + ::PaletteSize(lpDIB));

 // 判断是否内存分配失败
 if (hDIB == NULL)
 {
  GlobalUnlock(m_hDib);
  return false;
 }

 // 锁定内存
 lpNewDIB =  (LPBYTE)GlobalLock((HGLOBAL) hDIB);

 // 复制DIB信息头和调色板
 memcpy(lpNewDIB, lpDIB, *(LPDWORD)lpDIB + PaletteSize(lpDIB));

 // 找到新DIB象素起始位置
 lpNewDIBBits = FindDIBBits(lpNewDIB);

 // 获取指针
 lpbmi = (LPBITMAPINFOHEADER)lpNewDIB;
 lpbmc = (LPBITMAPCOREHEADER)lpNewDIB;

 // 更新DIB中图像的高度和宽度
 if (IS_WIN30_DIB(lpNewDIB))
 {
  // 对于Windows 3.0 DIB
  lpbmi->biWidth = lNewWidth;
  lpbmi->biHeight = lNewHeight;
 }
 else
 {
  // 对于其它格式的DIB
  lpbmc->bcWidth = (unsigned short) lNewWidth;
  lpbmc->bcHeight = (unsigned short) lNewHeight;
 }

 LPBYTE pNewGray = NULL;
 if( dwBitCnt == 1 )
 {
  lNewLineBytes = MYWIDTHBYTES(lNewWidth * 8);
  lLineBytes = lBin2GrayLineBytes;
  pNewGray = new BYTE[lNewLineBytes * lNewHeight];
  if( !pNewGray )
  {
   GlobalUnlock(m_hDib);
   if( pBin2Gray )
    delete []pBin2Gray;
   pBin2Gray = NULL;
   GlobalUnlock(hDIB);
   GlobalFree(hDIB);
   hDIB = NULL;
   return false;
  }
  lpNewDIBBits = pNewGray;
  
 }
 LONG i, j, m,  i0, j0;
 for(i = 0, m = lNewLineBytes*(lNewHeight-1); i < lNewHeight; i++, m -= lNewLineBytes )
 {
  lpDst =  lpNewDIBBits + m;
  for(j = 0; j < lNewWidth; j++)
  {
  
   // 计算该象素在源DIB中的坐标
   i0 = (LONG) (-((float) j) * fSina + ((float) i) * fCosa + f2 + 0.5);
   j0 = (LONG) ( ((float) j) * fCosa + ((float) i) * fSina + f1 + 0.5);

   // 判断是否在源图范围内
   if( (j0 >= 0) && (j0 < lWidth) && (i0 >= 0) && (i0 < lHeight))
   {
    // 指向源DIB第i0行,第j0个象素的指针
    if( dwBitCnt == 8 || dwBitCnt == 1 )
    {
     lpSrc = lpDIBBits + lLineBytes *( lHeight - 1 -i0 )+ j0;

     // 复制象素
     *lpDst ++= *lpSrc;
    }
    else if( dwBitCnt == 24 )
    {
     lpSrc = lpDIBBits + lLineBytes *( lHeight - 1 -i0 )+ j0 * 3;
     *lpDst ++ = *lpSrc;
     lpSrc++;
     *lpDst ++ = *lpSrc;
     lpSrc++;
     *lpDst ++ = *lpSrc;
    }

   }
   else
   {
    // 对于源图中没有的象素,直接赋值为255
    if( dwBitCnt == 8 || dwBitCnt == 1)
    {
     * lpDst ++ = 255;
    }
    else if (dwBitCnt == 24)
    {
     *lpDst ++ = 255;
     *lpDst ++ = 255;
     *lpDst ++ = 255;
    }
    
   }

  }
 }
 
 if( dwBitCnt == 1 )
 {
  lpNewDIBBits = FindDIBBits(lpNewDIB);
  ThreshImage(pNewGray, lpNewDIBBits, lNewWidth,lNewHeight, 10);
  if( pNewGray )
   delete []pNewGray;
  pNewGray = NULL;

  if( pBin2Gray )
   delete []pBin2Gray;
  pBin2Gray = NULL;

 }

 GlobalUnlock(m_hDib);
 GlobalFree(m_hDib);
 m_hDib = hDIB;
 return UpdateInternal();
}
/******************************************************************
功能:以原有灰度图像中心为原点,自动检测角度并旋转图像
               倾斜角度范围-10——+10度
输入:
  rtROI,给定的区域,用于检测文档的倾斜角度.输入区域为bottom = 0 或者right = 0,则设为图像区域
输出:
  旋转成功返回true
******************************************************************/
bool CArdpsImg::AutoRotatelImage(RECT rtROI)
{
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);

 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);
 if( rtROI.bottom > lHeight || rtROI.right > lWidth )
 {
  GlobalUnlock(m_hDib);
  return false;
 }

 if( rtROI.left < EDGE_STEP )
  rtROI.left = EDGE_STEP;
 if( rtROI.right >= lWidth - EDGE_STEP )
  rtROI.right = lWidth - EDGE_STEP - 1;
 if (rtROI.top < EDGE_STEP )
  rtROI.top = EDGE_STEP;
 if( rtROI.bottom >= lHeight - EDGE_STEP )
  rtROI.bottom = lHeight - 1 - EDGE_STEP;

 if( rtROI.bottom == 0 )
  rtROI.bottom = lHeight-EDGE_STEP;
 if(rtROI.right == 0)
  rtROI.right = lWidth - EDGE_STEP;


 LPBYTE lpDIBBits = FindDIBBits(lpDIB);

 WORD dwBitCnt = DIBBitCount(lpDIB);
 if( dwBitCnt != 8 )
 {
  GlobalUnlock(m_hDib);  
  return false;
 }
 LONG lLineBytes = MYWIDTHBYTES(lWidth * dwBitCnt); 
 BYTE * pbOutImage = new BYTE[lLineBytes * lHeight];
 if( !pbOutImage )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 memset(pbOutImage, 0, lLineBytes * lHeight* sizeof(BYTE));
 BYTE *pbTmp = new BYTE[lLineBytes * lHeight];
 if( !pbTmp )
 {
  GlobalUnlock(m_hDib);
  delete []pbOutImage;
  pbOutImage = NULL;
 }
 memset(pbTmp, 0, lLineBytes * lHeight* sizeof(BYTE));

 bool bSuccess = FindTopEdge(pbOutImage, lpDIBBits, rtROI, lLineBytes); 
 //SaveTo8bit_BMP(lpDIBBits, lWidth, lHeight, "edgegray.bmp", 8);
 //SaveTo8bit_BMP(pbOutImage, lWidth, lHeight, "edge0.bmp", 8);
 
 DeleteVtlSmallLine(pbOutImage, pbTmp, lLineBytes, lHeight, 5);
 //SaveTo8bit_BMP(pbOutImage, lWidth, lHeight, "edge1.bmp", 8);


 double dK;
 if( bSuccess )
 { 
  dK = DetectSkewAngle(pbOutImage, rtROI, lLineBytes);
  bSuccess = RotateImage(dK);  
 }
 
 if( pbOutImage )
 {
  delete []pbOutImage;
  pbOutImage = NULL;
 }

 if( pbTmp )
 {
  delete []pbTmp;
  pbTmp = NULL;
 }

 GlobalUnlock(m_hDib);
 if( bSuccess )
  return UpdateInternal();
 else
  return bSuccess;
}
/******************************************************************
功能:自动去除灰度图像的黑白边
输入:

输出:
    去除成功返回true
******************************************************************/
bool CArdpsImg::CutFrame()
{
 BYTE * pbOut = NULL;
 bool bSuccess = false;
 LPBITMAPINFOHEADER lpbmi = NULL;
 LPBITMAPCOREHEADER lpbmc = NULL;


 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);

 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);

 LPBYTE lpDIBBits = FindDIBBits(lpDIB);

 WORD dwBitCnt = DIBBitCount(lpDIB);
 if( dwBitCnt != 8 )
 {
  goto CleanUp;
 }
 RECT rect; 
 rect = SetRectValue(0, 0, 0, 0);
 LONG lLineBytes = MYWIDTHBYTES(lWidth << 3); 
 LONG lImgBytes = lLineBytes * lHeight;
 pbOut = new BYTE[lImgBytes];
 if( !pbOut )
 {
  goto CleanUp;
 }
 memset(pbOut, 0, lImgBytes * sizeof(BYTE));

 rect = SetRectValue(4, lWidth - 4, 4, lHeight - 4);
 if (FindEdgeImage(pbOut, lpDIBBits, rect, lWidth) < 0) 
 {
  goto CleanUp;
 } 

 rect = GetEdgeImageBoundary(pbOut, lWidth, lHeight);
 RECT rtPhy;
 rtPhy = rect;
 rtPhy.bottom = lHeight - 1 - rect.top;
 rtPhy.top  = lHeight - 1 - rect.bottom;
 
 HDIB hDIB = CropDIB(m_hDib, &rtPhy);
 if( hDIB == NULL )
  goto CleanUp;

 bSuccess = true;

CleanUp:

 if( pbOut )
 {
  delete []pbOut;
  pbOut = NULL;
 }

 GlobalUnlock(m_hDib);
 GlobalFree(m_hDib);
 m_hDib = hDIB;
 if( bSuccess )
  return UpdateInternal();
 else
  return bSuccess;
}
/******************************************************************
功能:将图像拆页为两幅图像
输入:
  pt1,pt2:给定的拆分的线的任意两点坐标
  pNewArd:返回CArdpsImg对象,拆分后的下、右半幅图像句柄存于该对象中
输出:
  拆分成功返回true
******************************************************************/
bool CArdpsImg::SplitImage(POINT pt1, POINT pt2, CArdpsImg *pNewArd)
{
 bool bSuccess = false;
 if( pNewArd == NULL )
  return bSuccess;

 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);

 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);


 int nx, ny;
 nx = abs(pt1.x - pt2.x) ;
 ny = abs(pt1.y - pt2.y) >lHeight - 1 ? lHeight - 1 : abs(pt1.y - pt2.y) ;
 RECT rtROI, rtROI2;
 if( nx < ny )
 {
  nx = (pt1.x + pt2.x + 0.5)/2 > lWidth - 1 ? lWidth - 1 : (pt1.x + pt2.x + 0.5)/2;  //垂直分割
  rtROI = SetRectValue(0, nx -1, 0, lHeight - 1);
  rtROI2 = SetRectValue(nx, lWidth -1, 0, lHeight - 1);
 }
 else
 {
  ny = (pt1.y + pt2.y + 0.5) / 2 > lHeight - 1 ? lHeight - 1 : (pt1.y + pt2.y + 0.5) / 2 ; 
  rtROI = SetRectValue(0, lWidth - 1, 0, ny - 1);
  rtROI2 = SetRectValue(0, lWidth - 1, ny, lHeight - 1);
 }

 HDIB hDib1, hDib2;

 hDib1 = CropDIB(m_hDib, &rtROI );
 hDib2 = CropDIB(m_hDib, &rtROI2);
 
 GlobalUnlock(m_hDib);
 GlobalFree(m_hDib);

 m_hDib = hDib1;
 pNewArd->m_hDib = hDib2;
 
 return true;

}
/******************************************************************
功能:将两幅图像合并为一幅图像
输入:
   pSrcArd:输入的CArdpsImg对象,图像的句柄存于该对象中
   nMethod:0:上下合并,Src位于合并后的下半幅;1:上下合并,Src位于合并后的上半幅
                              2:左右合并,Src位于合并后的右半幅;3:左右合并,Src位于合并后的左半幅        
输出:
拆分成功返回true
******************************************************************/

bool CArdpsImg::MergeImage(CArdpsImg *pSrcArd, int nMethod /* = 0 */)
{
 
 bool bSuccess = false;
 if( pSrcArd == NULL )
  return bSuccess;

 //当前图像
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);
 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);
 WORD bitCnt = DIBBitCount(lpDIB);
 LONG lBytePerLine = MYWIDTHBYTES(lWidth * bitCnt);
 LPBYTE lpDIBBits = FindDIBBits(lpDIB);

 //欲合并的图像
 LPBYTE lpSrcDIB = (LPBYTE)GlobalLock(pSrcArd->m_hDib);
 LONG lSrcWidth = DIBWidth(lpSrcDIB);
 LONG lSrcHeight = DIBHeight(lpSrcDIB);
 WORD bitSrcCnt = DIBBitCount(lpSrcDIB);
 LONG lSrcBytePerLine = MYWIDTHBYTES(lSrcWidth * bitSrcCnt);
 if( bitCnt != bitSrcCnt || bitCnt < 8 )
  goto CleanUp;

 LPBYTE lpSrcDIBBits = FindDIBBits(lpSrcDIB);

 LONG lDstWidth, lDstHeight, lDstBytesPerLine, i, j, k;
 HDIB  hDIB = NULL;
 LPBYTE lpDstDIB, lpDstDIBBits;
 lpDstDIB = lpDstDIBBits =  NULL;

 switch (nMethod)
 {
 case 0:
 case 1:
  lDstHeight = lSrcHeight + lHeight;
  lDstWidth = max(lWidth, lSrcWidth);  
  break;
 case 2:
 case 3:
 default:
  lDstHeight = max(lSrcHeight, lHeight);
  lDstWidth = lWidth + lSrcWidth;  
 }
 lDstBytesPerLine = MYWIDTHBYTES(lDstWidth*bitCnt);

 // 分配内存,以保存新DIB
 hDIB = (HDIB) ::GlobalAlloc(GHND, lDstBytesPerLine * lDstHeight + *(LPDWORD)lpDIB + ::PaletteSize(lpDIB));
 if( hDIB == NULL )
  goto CleanUp;

 lpDstDIB =  (LPBYTE)GlobalLock(hDIB);
 // 获取指针
 LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER)lpDstDIB;
 LPBITMAPCOREHEADER lpbmc = (LPBITMAPCOREHEADER)lpDstDIB;

 // 复制DIB信息头和调色板
 memcpy(lpDstDIB, lpDIB, *(LPDWORD)lpDIB + PaletteSize(lpDIB));

 lpDstDIBBits = lpDstDIB + *(LPDWORD)lpDIB + ::PaletteSize(lpDIB);
 memset(lpDstDIBBits, 255, lDstBytesPerLine * lDstHeight * sizeof(BYTE));


 switch (nMethod)
 {
 case 0:      //上下结构,Src位于图像的下半部分

  for( i = 0, j = 0, k = 0; i < lSrcHeight; i++, j += lDstBytesPerLine, k += lSrcBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpSrcDIBBits + k, lSrcBytePerLine);
  }
  for( i = lSrcHeight, j = i * lDstBytesPerLine, k = 0; i < lDstHeight; i++, j += lDstBytesPerLine, k += lBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpDIBBits + k, lBytePerLine );
  }

  break;
 case 1:                  //上下结构,当前图像位于合并后图像的下半部分

  for( i = 0, j = 0, k = 0; i < lHeight; i++, j += lDstBytesPerLine, k += lBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpDIBBits + k, lBytePerLine);
  }
  for( i = lHeight, j = i * lDstBytesPerLine, k = 0; i < lDstHeight; i++, j += lDstBytesPerLine, k += lSrcBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpSrcDIBBits + k, lSrcBytePerLine );
  }
  break;
 case 2:           //左右结构,Src图像位于合并后图像的右半部分
  
  for( i = 0, j = 0, k = 0; i < lHeight; i++, j += lDstBytesPerLine, k += lBytePerLine)
  {
   memcpy(lpDstDIBBits + j, lpDIBBits + k, lBytePerLine);
  }
  for( i = 0, j = lBytePerLine, k = 0; i < lSrcHeight; i++, j += lDstBytesPerLine, k += lSrcBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpSrcDIBBits + k, lSrcBytePerLine);
  }
  break;
 case 3:           //左右结构,当前图像位于合并后图像的右半部分
 default:
  for( i = lSrcHeight - 1, j = i * lDstBytesPerLine, k = i * lSrcBytePerLine; i >= 0; i--, j -= lDstBytesPerLine, k -= lSrcBytePerLine)
  {
   memcpy(lpDstDIBBits + j, lpSrcDIBBits + k, lSrcBytePerLine);
  }
  for( i = lHeight - 1, j = i * lDstBytesPerLine + lSrcBytePerLine, k = i * lBytePerLine; i >= 0; i--, j -= lDstBytesPerLine, k -= lBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpDIBBits + k, lBytePerLine);
  }
  break;

 }

 // 更新DIB中图像的高度和宽度
 if (IS_WIN30_DIB(lpDIB))
 {
  // 对于Windows 3.0 DIB
  lpbmi->biWidth = lDstWidth;
  lpbmi->biHeight = lDstHeight;
 }
 else
 {
  // 对于其它格式的DIB
  lpbmc->bcWidth = (unsigned short) lDstWidth;
  lpbmc->bcHeight = (unsigned short) lDstHeight;
 }
 bSuccess = true;
CleanUp:

 GlobalUnlock(m_hDib);
 GlobalFree(m_hDib);
 GlobalUnlock(pSrcArd->m_hDib);
 if( bSuccess && hDIB != NULL )
  m_hDib = hDIB;
 if( bSuccess )
  return UpdateInternal();
 else
  return bSuccess;
}

/******************************************************************
功能:将两幅图像合并为一幅图像
输入:
  pSrcArd:输入的CArdpsImg对象,图像的句柄存于该对象中
  nPos1:    当前图像的合并坐标;
  nPos2    输入的CArdpsImg对象,合并图像的坐标
  nMethod:0:上下合并,取当前图像的下部,pSrcArd图像的上部
       1:左右合并,取当前图像的右部,pSrcArd图像的下部
输出:
  当前图像为合并后的图像,合并成功返回true
******************************************************************/

bool CArdpsImg::MergeImage(CArdpsImg *pSrcArd, int nPos1, int nPos2,  int nMethod /* = 0 */)
{
 bool bSuccess = false;
 if( pSrcArd == NULL )
  return bSuccess;

 //当前图像
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);
 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);
 WORD bitCnt = DIBBitCount(lpDIB);
 LONG lBytePerLine = MYWIDTHBYTES(lWidth * bitCnt);
 LPBYTE lpDIBBits = FindDIBBits(lpDIB);

 //欲合并的图像
 LPBYTE lpSrcDIB = (LPBYTE)GlobalLock(pSrcArd->m_hDib);
 LONG lSrcWidth = DIBWidth(lpSrcDIB);
 LONG lSrcHeight = DIBHeight(lpSrcDIB);
 WORD bitSrcCnt = DIBBitCount(lpSrcDIB);
 LONG lSrcBytePerLine = MYWIDTHBYTES(lSrcWidth * bitSrcCnt);
 if( bitCnt != bitSrcCnt || bitCnt < 8 )
  goto CleanUp;

 LPBYTE lpSrcDIBBits = FindDIBBits(lpSrcDIB);

 LONG lDstWidth, lDstHeight, lDstBytesPerLine, i, j, k;
 HDIB  hDIB = NULL;
 LPBYTE lpDstDIB, lpDstDIBBits;
 lpDstDIB = lpDstDIBBits =  NULL;

 nPos1 -= 1;
 nPos2 -= 1;
 switch (nMethod)
 {
 case 0:

  if( nPos1 >= lHeight )
   nPos1 = lHeight - 1;
  if( nPos1 < 0 )
   nPos1 = 0;
 
  if( nPos2 >= lSrcHeight )
   nPos2 = lSrcHeight - 1;
  if( nPos2 < 0 )
   nPos2 = 0;
  
  lDstHeight = nPos2 + lHeight - nPos1;
  lDstWidth = max(lWidth, lSrcWidth);  
  break;
 case 1:
  if (nPos1 >= lWidth )
   nPos1 = lWidth - 1;
  if( nPos1 < 0 )
   nPos1 = 0;

  if( nPos2 >= lSrcWidth )
   nPos2 = lSrcWidth - 1;
  if( nPos2 < 0 )
   nPos2 = 0;

  lDstHeight = max(lSrcHeight, lHeight);
  lDstWidth = lWidth - nPos1 + nPos2;  
 }
 lDstBytesPerLine = MYWIDTHBYTES(lDstWidth*bitCnt);

 // 分配内存,以保存新DIB
 hDIB = (HDIB) ::GlobalAlloc(GHND, lDstBytesPerLine * lDstHeight + *(LPDWORD)lpDIB + ::PaletteSize(lpDIB));
 if( hDIB == NULL )
  goto CleanUp;

 lpDstDIB =  (LPBYTE)GlobalLock(hDIB);
 // 获取指针
 LPBITMAPINFOHEADER lpbmi = (LPBITMAPINFOHEADER)lpDstDIB;
 LPBITMAPCOREHEADER lpbmc = (LPBITMAPCOREHEADER)lpDstDIB;

 // 复制DIB信息头和调色板
 memcpy(lpDstDIB, lpDIB, *(LPDWORD)lpDIB + PaletteSize(lpDIB));

 lpDstDIBBits = lpDstDIB + *(LPDWORD)lpDIB + ::PaletteSize(lpDIB);
 memset(lpDstDIBBits, 255, lDstBytesPerLine * lDstHeight * sizeof(BYTE));

 int nLength = 0;
 switch (nMethod)
 {
 case 0:      //上下结构,当前图像位于合并图像的下半部分

  for( i = 0, j = 0, k = 0; i < lHeight - nPos1; i++, j += lDstBytesPerLine, k += lBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpDIBBits + k, lBytePerLine);
  }

  for( i = lSrcHeight - nPos2 , k = i * lSrcBytePerLine; i < lSrcHeight; i++, j += lDstBytesPerLine, k += lSrcBytePerLine )
  {
   memcpy(lpDstDIBBits + j, lpSrcDIBBits + k, lSrcBytePerLine );
  }

  break;

 case 1:           //左右结构,当前图像位于合并后图像的右半部分
  nLength = nPos2 * bitCnt / 8;
  for( i = 0, k = 0, j = 0; i < lSrcHeight; i++, k += lSrcBytePerLine, j += lDstBytesPerLine )
  {
   memcpy(lpDstDIBBits + j, lpSrcDIBBits + k, nLength);
  }
  nLength = (lWidth - nPos1) * bitCnt / 8;
  for( i = 0, k = nPos1 * bitCnt / 8, j = nPos2 * bitCnt / 8; i < lHeight; i++, k+= lBytePerLine, j += lDstBytesPerLine )
  {
   memcpy(lpDstDIBBits + j, lpDIBBits + k, nLength);
  }
  break;
 }

 // 更新DIB中图像的高度和宽度
 if (IS_WIN30_DIB(lpDIB))
 {
  // 对于Windows 3.0 DIB
  lpbmi->biWidth = lDstWidth;
  lpbmi->biHeight = lDstHeight;
 }
 else
 {
  // 对于其它格式的DIB
  lpbmc->bcWidth = (unsigned short) lDstWidth;
  lpbmc->bcHeight = (unsigned short) lDstHeight;
 }
 bSuccess = true;
CleanUp:

 GlobalUnlock(m_hDib);
 GlobalFree(m_hDib);
 GlobalUnlock(pSrcArd->m_hDib);
 if( bSuccess && hDIB != NULL )
  m_hDib = hDIB;
 if( bSuccess )
  return UpdateInternal();
 else
  return bSuccess;
}

////////////////////////////////////////////////////////////////////////////////
/**
* Changes the brightness and the contrast of the image. Apply a look up table to the image. 
* \ * \param brightness: can be from -255 to 255, if brightness is negative, the image becomes dark.
* \param contrast: can be from -100 to 100, the neutral value is 0.
* \return true if everything is ok
*/
bool CArdpsImg::AjustLightAndContrast(int brightness, int contrast)
{
 if (!m_hDib) return false;
 float c=(100 + contrast)/100.0f;
 brightness+=128;
 long i;
 BYTE cTable[256]; //<nipper>
 for ( i=0;i<256;i++) {
  cTable[i] = (BYTE)max(0,min(255,(int)((i-128)*c + brightness + 0.5f)));
 }

 
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);
 if( lpDIB == NULL )
  return false;

 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);
 WORD bitCnt = DIBBitCount(lpDIB);
 LONG lBytePerLine = MYWIDTHBYTES(lWidth * bitCnt);
 LPBYTE lpDIBBits = FindDIBBits(lpDIB);
 RGBQUAD *pRgb =(RGBQUAD *)( lpDIB + 40);


  if (bitCnt == 8)
 {
  for( i = 0; i < 256; i++)
  {
   pRgb[i].rgbBlue = pRgb[i].rgbRed = pRgb[i].rgbGreen = cTable[i];
  }
  }
 else if( bitCnt == 24)
 {
  for( i = 0; i < lBytePerLine * lHeight; i++)
  {
   *lpDIBBits++ = cTable[*lpDIBBits];
  }
  }
 else
 {
  GlobalUnlock(m_hDib);
  return false;
 }

 GlobalUnlock(m_hDib);
 return UpdateInternal();
}
/******************************************************************
功能:自动分析边框,并进行调整,不改变图像的大小
输入:

 nMethod:0:从水平,垂直两个方向上进行调整。
      1:水平调整
      2:垂直调整
输出:
    调整后的图像,成功返回true
******************************************************************/

bool CArdpsImg::AdjustBinPos(int nMethod /* = 0 */)
{
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);
 if( lpDIB == NULL )
  return false;

 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);
 WORD bitCnt = DIBBitCount(lpDIB);
 LONG lBytePerLine = MYWIDTHBYTES(lWidth * bitCnt);
 LPBYTE lpDIBBits = FindDIBBits(lpDIB);
 
 if( bitCnt != 1 )
 {
  GlobalUnlock(m_hDib); 
  return false;
 }

 //for gray image
 LONG  nGrayBytesPerLine = MYWIDTHBYTES( lWidth << 3 );
 LONG nGrayImgSize = nGrayBytesPerLine * lHeight;
 BYTE *pbGrayImg = new BYTE[nGrayImgSize];
 if( !pbGrayImg )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 bool bSuccess = ConvertBinary2Gray(lpDIBBits, lWidth, lHeight, pbGrayImg );
 
 if( !bSuccess )
 {
  GlobalUnlock(m_hDib);
  delete []pbGrayImg;
  pbGrayImg = NULL;
  return false;
 } 
 
 RECT rtImg = GetBinImageBoundary(pbGrayImg, lWidth, lHeight);
 RECT rtNew;
 rtNew.left = (rtImg.left + lWidth - rtImg.right )/2;
 rtNew.right = lWidth - rtNew.left -1;
 rtNew.top = (rtImg.top + lHeight - rtImg.bottom)/2;
 rtNew.bottom = lHeight - rtNew.top - 1;
 
 MoveBin(pbGrayImg, lWidth, lHeight, rtImg, rtNew, nMethod ); 
 
 bSuccess = ThreshImage(pbGrayImg, lpDIBBits, lWidth, lHeight, 10);

 if( pbGrayImg )
  delete []pbGrayImg;
 pbGrayImg = NULL;

 GlobalUnlock(m_hDib);

 return UpdateInternal();
}
/******************************************************************
功能:彩色图像色阶调整
输入:

输出:
    调整后的图像,成功返回true
******************************************************************/
bool CArdpsImg::ColorEqual()
{
 LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);
 if( lpDIB == NULL )
  return false;

 LONG lWidth = DIBWidth(lpDIB);
 LONG lHeight = DIBHeight(lpDIB);
 WORD bitCnt = DIBBitCount(lpDIB);
 LONG lBytePerLine = MYWIDTHBYTES(lWidth * bitCnt);
 LPBYTE lpDIBBits = FindDIBBits(lpDIB);

 if( bitCnt != 24 )
 {
  GlobalUnlock(m_hDib); 
  return false;
 } 
 int nHist[256];
 long long nCulHist[256];
 long long nConver[256];

 memset(nHist, 0, 256 * sizeof(int));
 memset(nCulHist, 0, 256 * sizeof( long long ));
 memset(nConver, 0, 256 * sizeof(long long ));

 long i, j, m, newValue;
 LPBYTE pSrc = NULL;
 for( i = 0, m = 0; i < lHeight; i++, m += lBytePerLine )
 {
  pSrc = lpDIBBits + m;
  for( j = 0; j < lWidth; j++)
  {
   newValue = (*pSrc) * uiBWeight + uiGWeight * (*(pSrc + 1)) + uiRWeight * (*(pSrc + 2));
   newValue /= 1000;
   nHist[newValue]++;
   pSrc += 3;
  }
 }
 long long nTotal = lHeight * lWidth;
 nCulHist[0] = nHist[0];
 nConver[0] = nHist[0] * 255 / nTotal;
 for( i = 1; i < 256; i++ )
 {
  nCulHist[i] = nCulHist[i - 1] + nHist[i];
  nConver[i] = nCulHist[i] * 255 / nTotal;
 }

 for( i = 0, m = 0; i < lHeight; i++, m += lBytePerLine )
 {
  pSrc = lpDIBBits + m;
  for( j = 0; j < lWidth; j++)
  {
   *pSrc = nConver[*pSrc];
   pSrc++;
   *pSrc = nConver[*pSrc];
   pSrc++;
   *pSrc = nConver[*pSrc];
   pSrc++;
  }
 }

 GlobalUnlock(m_hDib);

 return UpdateInternal();
}

 

bool CArdpsImg::Zoom(double fRatioX, double fRatioY)
{
 int nWidth = (int)(fRatioX * (double)GetWidth());
 int nHeight = (int)(fRatioY * (double)GetHeight());

 HDIB hNewDib = ChangeDIBSize(m_hDib, nWidth, nHeight);
 if (! hNewDib)
  return false;

 // set to m_hDib
 Destroy();
 m_hDib = hNewDib;

 // return
 return UpdateInternal();
}

//连通域提取
void CArdpsImg::ExtractComponent(Line * Li, BYTE *pbBinary, UINT grayBytesPerLine, BYTE bCur )    //连通域提取
{
 if( Li == NULL || pbBinary == NULL )
  return;
 
 int * stack = NULL;
 int * InStack[10];
 int top, topin;
 top = topin = 0;

 CPoint temp;
 component *ctail =NULL;
 component *chead =new component;

 InStack[topin] = new int[COMP_NUM];
 if( InStack[topin] == NULL )
  return;

 stack = InStack[topin];

 char **flag=new char*[Li->EndY-Li->StartY+1];
 if( *flag == NULL )
 {
  delete []InStack[topin];
  InStack[topin] = NULL;
  return;
 }

 int i, j, k;

 for(i = 0; i <= Li->EndY - Li->StartY; i++) //寻找连通体
 { 
  flag[i]=new char[Li->EndX - Li->StartX + 1];
  for(j = 0; j <= Li->EndX - Li->StartX; j++)
  {   
   flag[i][j]=0;
  }
 }

 for(j = Li->StartX; j <= Li->EndX; j++) //寻找连通体
 {  
  for( i = Li->StartY, k = Li->StartY * grayBytesPerLine; i <= Li->EndY; i++, k += grayBytesPerLine)
  {   
   if(flag[i-Li->StartY][j-Li->StartX] == 0 && pbBinary[k + j] == bCur )
   {    
    stack[top++]=i;
    stack[top++]=j;

    flag[i - Li->StartY][j - Li->StartX] = 1;    
    if(ctail!=NULL)
    {
     component *cp = new component;
     ctail->cnext = cp;
     ctail = cp;
    }
    else
    {
     ctail = chead; 
    }
    ctail->bound =CRect(j,i,j,i);
    ctail->count =1;

    while(top != 0)
    { 
     temp.x = stack[--top];
     temp.y = stack[--top];
     if(top == 0 && topin != 0)
     {
      stack = InStack[topin-1];
      delete []InStack[topin--];
      top = COMP_NUM;
     }
     if(temp.x - 1 >= Li->StartX)
      add(temp.x-1, temp.y, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine,  bCur ); 
     if(temp.x+1 <= Li->EndX)
      add(temp.x+1, temp.y, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine,  bCur); 
     if(temp.y-1 >= Li->StartY)
      add(temp.x, temp.y-1, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine, bCur); 
     if(temp.y+1 <= Li->EndY)
      add(temp.x, temp.y+1, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine,  bCur);
     if((temp.x-1 >= Li->StartX) && (temp.y - 1 >= Li->StartY))
      add(temp.x-1, temp.y-1, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine,   bCur); 
     if((temp.x+1 <= Li->EndX) && (temp.y-1 >= Li->StartY))
      add(temp.x+1, temp.y-1, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine,   bCur); 
     if((temp.x-1 >= Li->StartX) && (temp.y+1 <= Li->EndY))
      add(temp.x-1, temp.y+1, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine,   bCur); 
     if((temp.x+1 <= Li->EndX) && (temp.y+1 <= Li->EndY))
      add(temp.x+1, temp.y+1, flag, stack, InStack, top, topin,
      Li->StartX, Li->StartY, ctail, pbBinary, grayBytesPerLine,   bCur);
    }
   }   
  }  
 }       //end for
 Li->cp = chead; 

 delete []stack; 

 for(i = 0; i <= Li->EndY-Li->StartY; i++)
  delete []flag[i];

 delete []flag;

}
void CArdpsImg::add(int a,int b,char **flag,int *&stack,int **InStack,
   int &top,int &topin,int c,int d,component *ctail, BYTE *pMotion, UINT grayBytesPerLine,  BYTE bCur  )
{
 if((flag[b-d][a-c] == 0 ) && pMotion[b * grayBytesPerLine + a] == bCur )
 {
  flag[b-d][a-c] = 1;

  if(top == COMP_NUM)
  {
   if(topin == 9)
   {
    return;
   }
   InStack[++topin]=new int[COMP_NUM];
   stack=InStack[topin];
   top=0;
  } 
  stack[top++]=b;
  stack[top++]=a;
  ctail->count++;

  if(ctail->bound.bottom < b)      //连通体边界
   ctail->bound.bottom = b;
  if(ctail->bound.top > b)
   ctail->bound.top = b;
  if(ctail->bound.left > a)
   ctail->bound.left = a;
  if(ctail->bound.right < a)
   ctail->bound.right = a;
 }
}

void CArdpsImg::AnalysisComponent( Line * Li,  BYTE *pbBinary, UINT unBinaryBytesPerLine, UINT minArea, const BYTE bValue)
{
 if( Li == NULL || pbBinary == NULL )
  return;
 component *cp1= Li->cp;
 component *cp2; 
 int area;//, nCnt = 0;
 
 while( cp1 != NULL )
 {  
  area = cp1->bound.Height() * cp1->bound.Width();
  if( area < minArea && area > 0 )
  {
   SetBinary(cp1->bound, pbBinary, unBinaryBytesPerLine, bValue);   
  } 

  cp2 = cp1;
  cp1 = cp1->cnext; 

  delete cp2;
 }

 Li->cp = NULL;  
}

 

CRect* CArdpsImg::GetDirtPos(int nMethod, int *nCnt)
{
 *nCnt = 0;

 LPBYTE  lpDIB = (LPBYTE)GlobalLock(m_hDib);
 if( !lpDIB)
  return m_dirRect;

 LPBYTE lpBinImg = FindDIBBits(lpDIB); 
 if( !lpBinImg )
 {
  GlobalUnlock(m_hDib);
  return m_dirRect;
 }

 bool bSuccess = true;
 LONG  nImgHei = GetHeight();
 LONG  nImgWid = GetWidth();
 int    nBitCnt = DIBBitCount(lpDIB);
 if( nBitCnt != 1 )
 {
  GlobalUnlock(m_hDib);
  return m_dirRect;
 }
 LONG  nBinBytesPerLine = MYWIDTHBYTES(nImgWid * nBitCnt);
 //for gray image
 LONG  nGrayBytesPerLine = MYWIDTHBYTES( nImgWid << 3 );
 LONG nGrayImgSize = nGrayBytesPerLine * nImgHei;
 BYTE *pbGrayImg = new BYTE[nGrayImgSize];
 if( !pbGrayImg )
 {
  GlobalUnlock(m_hDib);
  return m_dirRect;
 }
 bSuccess = ConvertBinary2Gray(lpBinImg, nImgWid, nImgHei, pbGrayImg );
 //SaveTo8bit_BMP(pbGrayImg, nImgWid, nImgHei, "d:\\testgray.bmp", 8);
 if( !bSuccess )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
  GlobalUnlock(m_hDib);
  return m_dirRect;
 }

 Line *Li  = new Line;
 if( !Li )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
  GlobalUnlock(m_hDib);
  return m_dirRect;
 }

 Li->StartY = 3;
 Li->StartX = 3;
 Li->EndX = nImgWid - 3;
 Li->EndY = nImgHei - 3;
 Li->cp  = NULL;

 BYTE  bCur;
 if( nMethod == 0 )  //黑色为背景
 {
  bCur = 0;
 }
 else
 { 
  bCur = 255;
 }

 ExtractComponent(Li, pbGrayImg, nGrayBytesPerLine, bCur );
 AnalysisDirtPos(Li, m_DirtSize, m_minDirtSize, nImgHei,  nCnt);

 if( pbGrayImg )
 {
  delete []pbGrayImg;
  pbGrayImg = NULL;
 }

 if( Li )
 {
  delete Li;
  Li = NULL;
 }

 GlobalUnlock(m_hDib);
 return m_dirRect;
}

bool CArdpsImg::AnalysisDirtPos(Line *Li, CSize maxSize, CSize minSize, int nHeight,  int *nCnt)
{
 if( Li == NULL || nCnt == NULL )
  return false;
 component *cp1= Li->cp;
 component *cp2; 
 int area;//, nCnt = 0;
 int minArea = minSize.cx  * minSize.cy;
 int maxArea = maxSize.cx * maxSize.cy;
 *nCnt = 0;
 while(cp1 != NULL )
 {
  cp1 = cp1->cnext; 
  (*nCnt)++;
 }
 if( m_dirRect )
 {
  delete []m_dirRect;
  m_dirRect = NULL;  
 }
 m_dirRect = new CRect[(*nCnt)];
 if( m_dirRect == NULL )
  return false;

 cp1= Li->cp;
 *nCnt = 0; 
 while( cp1 != NULL )
 {  
  area = cp1->bound.Height() * cp1->bound.Width();
  if( area <= maxArea && area >= minArea )
  {
   m_dirRect[*nCnt].top = nHeight - 1 - cp1->bound.bottom;
   m_dirRect[*nCnt].bottom = nHeight - 1 - cp1->bound.top;
   m_dirRect[*nCnt].left = cp1->bound.left;
   m_dirRect[*nCnt].right = cp1->bound.right;
   (*nCnt)++;
  } 

  cp2 = cp1;
  cp1 = cp1->cnext; 

  delete cp2;
 }

 Li->cp = NULL;  
 return true;
}

void CArdpsImg::SetBinary(CRect rect, BYTE *pbImg, UINT unBinaryBytesPerLine, const BYTE bValue)
{
 if( pbImg == NULL )
  return;

 LONG i, j, m; 
 
 for( i = rect.top, m = rect.top * unBinaryBytesPerLine; i <= rect.bottom; i++, m += unBinaryBytesPerLine )
 {  
  for( j = rect.left; j <= rect.right; j++ )
  {
   *(pbImg + m + j) = bValue;
  }
 }
}

bool CArdpsImg::ConvertBinary2Gray(const unsigned char * pbBinary, const int nWidth, const int nHeight, unsigned char *pbGray)
{
 if( pbBinary == NULL || pbGray == NULL || nWidth < 1 || nHeight < 1) 
  return false;

 LONG nBinaryBytesPerLine = MYWIDTHBYTES(nWidth);
 LONG nGrayBytesPerLine = MYWIDTHBYTES(nWidth << 3);
 LONG i, j, k, m;
 const unsigned char * pbIn = pbBinary;
 unsigned char * pbOut = pbGray;
 memset( pbGray, 0, nHeight * nGrayBytesPerLine * sizeof( unsigned char));
 BYTE btmp = 0;
 for( i = 0, k = 0, m = 0; i < nHeight; i++, k += nGrayBytesPerLine, m += nBinaryBytesPerLine )
 {
  pbIn = pbBinary + m;
  pbOut = pbGray + k;
  for( j = 0; j < (nWidth + 7)/8; j++, pbIn++)
  {
   btmp = *pbIn; 
   btmp = btmp & 128 ; 
   if( btmp == 128)
    *pbOut = 255;
   pbOut ++;  

   btmp = *pbIn; 
   btmp = btmp & 64 ; 
   if( btmp == 64)
    *pbOut = 255; 
   pbOut ++;

   btmp = *pbIn; 
   btmp = btmp & 32 ; 
   if( btmp == 32)
    *pbOut = 255;
   pbOut ++;


   btmp = *pbIn; 
   btmp = btmp & 16 ; 
   if( btmp == 16)
    *pbOut = 255;
   pbOut ++;

   btmp = *pbIn; 
   btmp = btmp & 8 ; 
   if( btmp == 8)
    *pbOut = 255;
   pbOut ++;

   btmp = *pbIn; 
   btmp = btmp & 4 ; 
   if( btmp == 4)
    *pbOut = 255;
   pbOut ++;

   btmp = *pbIn; 
   btmp = btmp & 2 ; 
   if( btmp == 2)
    *pbOut = 255;
   pbOut ++;

   btmp = *pbIn; 
   btmp = btmp & 1; 
   if( btmp == 1)
    *pbOut = 255;  
   pbOut ++;
  } 
 }
 return true;
}

 

bool CArdpsImg::FindTopEdge(BYTE *pbOutImage, BYTE *image, RECT rtROI, int nWidth)
{
 if( pbOutImage == NULL || image == NULL )
  return false;

 BYTE *pbIn1, *pbIn2;
 pbIn2 = pbIn1 = NULL;
 int i, j;
 int gd, n = 0, k = 0;
 int d; 

 gd = n = 0;
 pbIn1 = image + (rtROI.top + EDGE_STEP) * nWidth + rtROI.left;
 for (i = rtROI.top + EDGE_STEP; i < rtROI.bottom; i++, pbIn1 += nWidth)
 {
  pbIn2 = pbIn1;
  for (j = rtROI.left; j < rtROI.right; j++, pbIn2++)
  {
   d = pbIn2[-EDGE_STEP * nWidth] - pbIn2[0];
   if (d > 20)
   {
    gd += d;
    n++;
   }   
  }
 }
 if (n > 0)
 {
  gd = gd / n;
 }

 pbIn1 = image + rtROI.top * nWidth + rtROI.left;
 for (i = rtROI.top; i < rtROI.bottom; i++, pbIn1 += nWidth)
 {
  pbIn2 = pbIn1;
  for (j = rtROI.left; j < rtROI.right; j++, pbIn2++)
  {
   n = (int)(pbIn2 - image);
   pbOutImage[n] = 0;
   if (j > EDGE_STEP) 
   {
    d = pbIn2[nWidth - EDGE_STEP] - pbIn2[0];
    if (d > gd)
    {
     pbOutImage[n] = 255;
    }
   }   
  }
 }


 for (i = rtROI.top; i < rtROI.bottom; i++)
 {
  for (j = rtROI.right; j > rtROI.left; j--)
  {  
   if (pbOutImage[i * nWidth + j] <= pbOutImage[i * nWidth + j - 1]) 
   {
    pbOutImage[i * nWidth + j] = 0; 
   }     
  }
 }

 return true;
}

double CArdpsImg::DetectSkewAngle(BYTE *pbImage, RECT rtROI, int nWidth)
{
 double dK = 0.0;
 int i;
 int nValeSum, nValeMax, angle; 
 int *pnRamBuff = NULL;
 pnRamBuff = new int[nWidth * 2];
 if (pnRamBuff == NULL)
 {
  return 0.0;
 }

 angle = nValeMax = 0;
 for (i = -10; i <= 10; i ++)
 {
  nValeSum = CompVPrjValeSum(pnRamBuff, pbImage, nWidth, rtROI.bottom, i);
  if(nValeSum>nValeMax)
  {
   nValeMax = nValeSum;
   angle = i;
  }  
 } 
 dK = angle;// * 3.14159265 / 180;

 if( pnRamBuff)
 {
  delete []pnRamBuff;
  pnRamBuff = NULL;
 }

 return dK;
}
// compute all vale sum
int CArdpsImg::CompVPrjValeSum(int *pnRamBuff, const BYTE *pInImg, int nImgWidth, int nImgHeight, int nRotAngle)
{
 if( pnRamBuff == NULL || pInImg == NULL )
  return 0;
 
 int nValeSum = 0;
 int i, j;
 int *nVprj = pnRamBuff, *nPlot = NULL;
 int nPrjLen;
 int nPrjOffset;
 int nPx;
 int nSin, nCos;
 const BYTE *pb = NULL; 

 if((nRotAngle >= 0) && (nRotAngle <= 90))
 {
  nSin = g_nSin[nRotAngle];
  nCos = g_nCos[nRotAngle];
 }
 else if ((nRotAngle < 0)&&(nRotAngle > -90))
 {
  nSin = -g_nSin[-nRotAngle];
  nCos = g_nCos[-nRotAngle];
 }

 // compute max project length
 i = nSin > 0 ? nSin : -nSin;
 nPrjLen = nImgWidth*nCos+nImgHeight*i+32768;
 nPrjLen = nPrjLen>>16;
 if (nPrjLen>nImgWidth)
 {
  nPrjLen = nImgWidth;
 }
 if (nSin<0)
 {
  nPrjOffset = -nImgHeight*nSin+32768;
  nPrjOffset = nPrjOffset>>16;
 }
 else
 {
  nPrjOffset = 0;
 }

 memset(nVprj, 0, nPrjLen * 2 * sizeof(int));
 nPlot = nVprj + nPrjLen;

 pb = pInImg;
 for (i = 0; i < nImgHeight; i++)
 {  
  for (j = 0; j < nImgWidth; j++, pb++)
  {
   if (*pb < 8)
    continue;
   nPx = j * nCos + i * nSin + 32768;
   nPx = (nPx >> 16) + nPrjOffset;
   if((nPx < nPrjLen) && (nPx>=0)) 
   {
    nVprj[nPx] += *pb;
   }
  }
 }

 SmoothingArray(nPlot, 1, nVprj, 0, nPrjLen);
 nValeSum = 0;
 for(i = 0; i < nPrjLen; i++) 
 {
  if (nPlot[i]==0)
  {
   nValeSum++;
  }
 }

 return nValeSum;
}

//--------------------------------------------------------------------
// NAME:  SmoothingArray
//  PARAMS:  int *pIn, input array
//    int *pOut, output array
//    int nR, smooth radius
//    int nStart, int nEnd, the begin and end of smoothing
//  RETURN:  NULL
//  FUNCTION: Smoothing one-dimentional array
//---------------------------------------------------------------------
void CArdpsImg::SmoothingArray(int *pOut, int nR, const int *pIn, int nStart, int nEnd) 
{
 if( pIn == NULL || pOut == NULL )
  return;

 int i, nSum, nCount;
 int beg, end;

 nSum = 0;

 // the len is less than nR
 if (nEnd - nStart <= nR + 1)
 {
  nCount = nEnd - nStart;
  if (nCount <= 0) 
  {
   pOut[nStart] = pIn[nStart];
   return;
  }
  nSum = 0;
  for (i = nStart; i < nEnd; i ++) 
  {
   nSum += pIn[i];
  }
  nSum = (nSum + (nCount >> 1)) / nCount;
  for (i = nStart; i < nEnd; i ++) 
  {
   pOut[i] = nSum;
  }
  return;
 }

 // first nR len
 beg = nStart;
 end = nStart + nR;
 if (end > nEnd) end = nEnd;
 for (i = beg; i < end; i ++) 
 {
  nSum += pIn[i];
 }
 nCount = end - beg;

 // from start to start + nR, maybe less than start + nR
 if (end >= nEnd-nR)
  end = nEnd - nR - 1;
 for (i = nStart; i <= end; i ++) 
 {
  nSum += pIn[i + nR];
  nCount ++;
  pOut[i] = (nSum + (nCount >> 1)) / nCount;
 }

 // from start+nR to end - nR
 end = nEnd - nR;
 for (; i < end; i ++) 
 {
  nSum += pIn[i + nR] - pIn[i - nR - 1];
  pOut[i] = (nSum + nCount / 2) / nCount;
 }

 // from end-nR to start + nR
 end = nStart + nR + 1;
 if (end > nEnd) end = nEnd;
 for (; i < end; i++)
 {
  pOut[i] = (nSum + nCount / 2) / nCount;
 }

 // the last nR
 beg = i;
 if (beg < nStart + nR + 1) beg = nStart + nR + 1;
 for (i = beg; i < nEnd; i ++)
 {
  nCount --;
  nSum -= pIn[i - nR - 1];
  pOut[i] = (nSum + nCount / 2) / nCount;
 }

 return;
}

//-------------------------------------------------------
// delete short vertical stroke 
//-------------------------------------------------------
int CArdpsImg::DeleteVtlSmallLine(BYTE* pImg, BYTE* pBuf, int nWidth, int nHeight, int nTh)
{
 if( pImg == NULL || pBuf == NULL )
  return 0;

 int i, j, nTop, nBtm, nLen;
 int top, btm;
 BYTE *pIn, *pIn1, *pb, *pb1;
 pb = pb1 = pIn = pIn1 = NULL;
 int hr = 0;

 top = 0; 
 btm = nHeight;
 LONG nBytesPerLine = MYWIDTHBYTES(nWidth * 8);
 memset(pBuf, 0, nBytesPerLine * nHeight);

 pIn = pImg + top * nBytesPerLine;
 pb = pBuf + top * nBytesPerLine;
 for (i = top; i < btm; i++) 
 {
  for (j = 0; j < nWidth; j++, pIn++, pb++) 
  {
   if (*pIn == 0) 
   {
    continue;
   }
   nTop = i-1;
   pIn1 = pIn - nBytesPerLine;
   pb1 = pb - nBytesPerLine;
   for (; nTop >= 0 && *pIn1 != 0; nTop--, pIn1 -= nBytesPerLine, pb1 -= nBytesPerLine)
   {
    *pb1 = *pIn1;
    *pIn = 0;
   }
   nTop++;

   nBtm = i+1;
   pIn1 = pIn + nBytesPerLine;
   pb1 = pb + nBytesPerLine;   
   for (; nBtm < nHeight && *pIn1 != 0; nBtm++, pIn1 += nBytesPerLine, pb1 += nBytesPerLine)
   {
    *pb1 = *pIn1;
    *pIn1 = 0;
   }
   nBtm --;
   nLen = nBtm - nTop + 1;
   if (nLen < nTh) 
   {
    pb1 = pBuf + nTop * nBytesPerLine + j;
    for (; nTop <= nBtm; nTop++, pb1 += nBytesPerLine) 
    {
     *pb1 = 0;
    }
   }
  }
 }

 memcpy(pImg, pBuf, nBytesPerLine*nHeight);
 return 0;
}

RECT CArdpsImg::GetEdgeImageBoundary(BYTE *pbImage, int nWidth, int nHeight)
{
 RECT rect;
 BYTE *pbIn1, *pbIn2;
 LONG i, j, m;
 LONG n, d;
 int *pnProjX = NULL, *pnProjY = NULL;
 rect = SetRectValue(0, 0, 0, 0);
 if( pbImage == NULL )
  return rect;

 LONG nBytesPerLine = MYWIDTHBYTES(nWidth<< 3);
 pnProjX = new int [nWidth + nHeight];
 if (pnProjX == NULL)
 {
  return rect;
 }
 pnProjY = pnProjX + nWidth;
 memset(pnProjX, 0, (nWidth + nHeight) * sizeof(int));

 for (i = 0, m = 0; i < nHeight; i++, m+= nBytesPerLine )
 {
  pnProjY[i] = 0;
  pbIn1 = pbImage + m;
  for (j = 0; j < nWidth; j++, pbIn1++)
  {
   pnProjY[i] += pbIn1[0];
  }
 }

 pbIn1 = pbImage;
 for (i = 0; i < nWidth; i++, pbIn1++)
 {
  pbIn2 = pbIn1;
  pnProjX[i] = 0;
  for (j = 0; j < nHeight; j++, pbIn2 += nBytesPerLine)
  {
   pnProjX[i] += pbIn2[0];
  }
 }

 d = n = 0;
 for (i = 0; i < nHeight; i++)
 {
  d += pnProjY[i];
  n++;
 }
 d = d / (4 * n);

 rect.top = 0;
 for (i = 0; i < nHeight; i++)
 {
  if (pnProjY[i] > d) 
  {
   rect.top = i;
   break;
  }
 }
 rect.top = rect.top > 1 ? (rect.top - 1) : rect.top;

 rect.bottom = nHeight - 1;
 for (i = nHeight - 1; i >= 0; i--)
 {
  if (pnProjY[i] > d) 
  {
   rect.bottom = i;
   break;
  }
 }
 rect.bottom = rect.bottom < (nHeight - 2) ? (rect.bottom + 1) : rect.bottom;


 d = n = 0;
 for (i = 0; i < nWidth; i++)
 {
  d += pnProjX[i];
  n++;
 }
 d = d / (4 * n);

 rect.left = 0;
 for (i = 0; i < nWidth; i++)
 {
  if (pnProjX[i] > d) 
  {
   rect.left = i;
   break;
  }
 }
 rect.left = rect.left > 0 ? (rect.left - 1) : rect.left;

 rect.right = nWidth - 1;
 for (i = nWidth - 1; i >= 0; i--)
 {
  if (pnProjX[i] > d) 
  {
   rect.right = i;
   break;
  }
 }
 rect.right = rect.right < (nWidth - 2) ? (rect.right + 1) : rect.right;

 if (pnProjX != NULL)
 {
  delete []pnProjX;
  pnProjX = NULL;
 }

 return rect;
}

void CArdpsImg::MoveBin(BYTE *pbIn, int nWidth, int nHeight, RECT rtOld, RECT rtNew, int nMethod)
{
 if( !pbIn )
  return;
 LONG lBytesPerLine = MYWIDTHBYTES(nWidth << 3);
 LONG lImgSize = lBytesPerLine * nHeight;
 BYTE *pbOut = new BYTE[lImgSize];
 if( pbOut == NULL )
  return;

 memset(pbOut, 255, lImgSize * sizeof(BYTE));
 LONG i, j, m, n;
 BYTE *pbTmpIn = NULL;
 switch (nMethod)
 {
 case 1:   //水平调整
  j = rtOld.right - rtOld.left + 1;
  for (i = 0, m = 0; i < nHeight; i++, m += lBytesPerLine )
  {
   memcpy(pbOut + rtNew.left+ m, pbIn + rtOld.left + m, j * sizeof(BYTE) );  
  }
  break;
 case 2:   //垂直调整
  //i = rtOld.bottom - rtOld.top + 1;
  i = rtNew.top * lBytesPerLine;
  for( j = rtOld.top, m = j * lBytesPerLine; j <= rtOld.bottom; j++, m += lBytesPerLine)
  {
   memcpy(pbOut + i, pbIn + m, lBytesPerLine);
   i += lBytesPerLine;
  }
  break;
 case 0:   //垂直和水平调整
 default:

  i = rtNew.top * lBytesPerLine + rtNew.left;
  m = rtOld.top *lBytesPerLine + rtOld.left;
  n = rtOld.right - rtOld.left + 1;
  for( j = rtOld.top; j <= rtOld.bottom; j++)
  {
   memcpy(pbOut + i, pbIn + m, n * sizeof(BYTE));
   i += lBytesPerLine;
   m += lBytesPerLine;
  }
  break;
 }
 memcpy( pbIn, pbOut, lImgSize * sizeof(BYTE));

 if( pbOut )
  delete[]pbOut;
 pbOut = NULL;

}

RECT CArdpsImg::GetBinImageBoundary(BYTE *pbImage, int nWidth, int nHeight)
{
 RECT rect;
 BYTE *pbIn1, *pbIn2;
 LONG i, j, m;
 LONG n, d;
 int *pnProjX = NULL, *pnProjY = NULL;
 rect = SetRectValue(0, 0, 0, 0);
 if( pbImage == NULL )
  return rect;

 LONG nBytesPerLine = MYWIDTHBYTES(nWidth<< 3);
 pnProjX = new int [nWidth + nHeight];
 if (pnProjX == NULL)
 {
  return rect;
 }
 pnProjY = pnProjX + nWidth;
 memset(pnProjX, 0, (nWidth + nHeight) * sizeof(int));

 for (i = 0, m = 0; i < nHeight; i++, m+= nBytesPerLine )
 {
  pnProjY[i] = 0;
  pbIn1 = pbImage + m;
  for (j = 0; j < nWidth; j++, pbIn1++)
  {
   pnProjY[i] += 255- pbIn1[0];
  }
  pnProjY[i] /= 255;
 }

 pbIn1 = pbImage;
 for (i = 0; i < nWidth; i++, pbIn1++)
 {
  pbIn2 = pbIn1;
  pnProjX[i] = 0;
  for (j = 0; j < nHeight; j++, pbIn2 += nBytesPerLine)
  {
   pnProjX[i] += 255- pbIn2[0];
  }
  pnProjX[i] /= 255;
 }

 d = n = 0;
 for (i = 0; i < nHeight; i++)
 {
  d += pnProjY[i];
  n++;
 }
 d = d / (4 * n);

 rect.top = 0;
 for (i = 0; i < nHeight; i++)
 {
  if (pnProjY[i] > d) 
  {
   rect.top = i;
   break;
  }
 }
 rect.top = rect.top > 1 ? (rect.top - 1) : rect.top;

 rect.bottom = nHeight - 1;
 for (i = nHeight - 1; i >= 0; i--)
 {
  if (pnProjY[i] > d) 
  {
   rect.bottom = i;
   break;
  }
 }
 rect.bottom = rect.bottom < (nHeight - 2) ? (rect.bottom + 1) : rect.bottom;


 d = n = 0;
 for (i = 0; i < nWidth; i++)
 {
  d += pnProjX[i];
  n++;
 }
 d = d / (4 * n);

 rect.left = 0;
 for (i = 0; i < nWidth; i++)
 {
  if (pnProjX[i] > d) 
  {
   rect.left = i;
   break;
  }
 }
 rect.left = rect.left > 0 ? (rect.left - 1) : rect.left;

 rect.right = nWidth - 1;
 for (i = nWidth - 1; i >= 0; i--)
 {
  if (pnProjX[i] > d) 
  {
   rect.right = i;
   break;
  }
 }
 rect.right = rect.right < (nWidth - 2) ? (rect.right + 1) : rect.right;

 if (pnProjX != NULL)
 {
  delete []pnProjX;
  pnProjX = NULL;
 }

 return rect;

}

RECT CArdpsImg::SetRectValue(int left, int right, int top, int bottom)
{
 RECT rect;
 rect.left = left;
 rect.right = right;
 rect.top = top;
 rect.bottom = bottom;

 return rect;
}


int CArdpsImg::FindEdgeImage(BYTE *pbOutImage, BYTE *image, RECT rtROI, int nWidth)
{
 if( pbOutImage == NULL || image == NULL )
  return -1;
 
 BYTE *pbIn1, *pbIn2;
 int i, j, d;
 int gd, n = 0, k = 0;

 pbIn2 = pbIn1 = NULL;
 int nBytesPerLine = MYWIDTHBYTES(nWidth << 3);
 gd = n = 0;
 pbIn1 = image + rtROI.top * nBytesPerLine + rtROI.left;
 for (i = rtROI.top; i < rtROI.bottom; i++, pbIn1 += nBytesPerLine)
 {
  pbIn2 = pbIn1 + EDGE_STEP;
  for (j = rtROI.left + EDGE_STEP; j < rtROI.right; j++, pbIn2++)
  {
   d = pbIn2[-EDGE_STEP] - pbIn2[0];
   if (d < 10)
   {
    continue;
   }
   gd += d;
   n++;
  }
 }

 if (n < 100)
 {
  return -1;
 }
 gd = gd / n;

 pbIn1 = image + rtROI.top * nBytesPerLine + rtROI.left;
 for (i = rtROI.top; i < rtROI.bottom; i++, pbIn1 += nBytesPerLine)
 {
  pbIn2 = pbIn1;
  for (j = rtROI.left; j < rtROI.right; j++, pbIn2++)
  {
   n = (int)(pbIn2 - image);
   pbOutImage[n] = 0;
   if (j > EDGE_STEP) 
   {
    d = pbIn2[-EDGE_STEP] - pbIn2[0];
    if (d > gd)
    {
     pbOutImage[n] = d;
    }
   }

   if (j < nWidth - EDGE_STEP - 1)
   {
    d = pbIn2[EDGE_STEP] - pbIn2[0];
    if (d > gd && d > pbOutImage[i * nBytesPerLine + j])
    {
     pbOutImage[n] = d;
    }
   }
  }
 }

 return 0;
}

HDIB CArdpsImg::ReadBinTiff(char* szImgFileName)
{
 TIFF *image = NULL;
 uint32 height, buffsize, bytesPerLine, nImgWidth, nPalleteSize, nFileSize;
 uint32 row, k, i;
 HDIB hDIB = NULL; 
 

 // Open the TIFF image
 image = TIFFOpen(szImgFileName, "r");
 if( image == NULL )
  return hDIB;

 TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height);
 TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &nImgWidth);
 uint16 bitspersample=1;
 uint16 samplesperpixel=1;
 TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel); 
 TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample);
 
 //for debug
 uint16 compression = 0;
 TIFFGetField(image, TIFFTAG_COMPRESSION, &compression);

 
 uint16 photmeric = 0;
 TIFFGetField(image, TIFFTAG_PHOTOMETRIC, &photmeric);

 uint16 bitsperpixel = bitspersample * samplesperpixel;
 if( bitsperpixel == 1)
  nPalleteSize = 2;
 else if( bitsperpixel == 8 )
  nPalleteSize = 256;
 else if( bitsperpixel == 16)
  return hDIB;
 else
  nPalleteSize = 0;

 buffsize = TIFFScanlineSize(image); 
 uint32 buffTotalSize = buffsize * height; 
 BYTE * pbOriImg = new BYTE[buffTotalSize]; 
 if( pbOriImg == NULL )
 {
  TIFFClose(image);
  return hDIB;
 }

 for (row = 0 , k = (height - 1) * buffsize; row < height; row++ , k -= buffsize )
  TIFFReadScanline(image, (void*)(pbOriImg + k), row);

 TIFFClose(image);

 /*TIFF *newImg = TIFFOpen("d:\\testread.tif", "w");
 TIFFSetField(newImg, TIFFTAG_IMAGELENGTH, height);
 TIFFSetField(newImg, TIFFTAG_IMAGEWIDTH, nImgWidth);
 TIFFSetField(newImg, TIFFTAG_SAMPLESPERPIXEL,samplesperpixel); 
 TIFFSetField(newImg, TIFFTAG_BITSPERSAMPLE, bitspersample);
 TIFFSetField(newImg, TIFFTAG_COMPRESSION, compression);
 TIFFSetField(newImg, TIFFTAG_PHOTOMETRIC, photmeric);
 for (row = 0 , k = (height - 1) * buffsize; row < height; row++ , k -= buffsize )
  TIFFWriteScanline(newImg, (void*)(pbOriImg + k), row);*/
 
 //TIFFClose(newImg);
 
 if( bitsperpixel == 32 )
  bytesPerLine = MYWIDTHBYTES(nImgWidth * 24);
 else
  bytesPerLine = MYWIDTHBYTES(nImgWidth * bitsperpixel);

 buffTotalSize = bytesPerLine * height;
 nFileSize = buffTotalSize + nPalleteSize * sizeof(RGBQUAD) + sizeof(BITMAPINFOHEADER);
 hDIB = (HDIB)GlobalAlloc(GHND, nFileSize);
 if( hDIB == NULL )
 {
  delete []pbOriImg;
  pbOriImg = NULL;
  return hDIB;
 }

 LPBYTE lpNewDIB = (LPBYTE)GlobalLock(hDIB);
 memset( lpNewDIB, 0, nFileSize * sizeof(BYTE));

 LPBITMAPINFOHEADER pInfo = (LPBITMAPINFOHEADER) lpNewDIB;
 pInfo->biSize = sizeof(BITMAPINFOHEADER);
 pInfo->biWidth = nImgWidth;
 pInfo->biHeight = height;
 pInfo->biCompression = BI_RGB;
 pInfo->biClrUsed = 0;
 pInfo->biClrImportant = 0;
 pInfo->biPlanes = 1;
 pInfo->biSizeImage = buffTotalSize;
 pInfo->biXPelsPerMeter = 0;
 pInfo->biYPelsPerMeter = 0;
 if( bitsperpixel == 32 )
   pInfo->biBitCount = 24;
 else
   pInfo->biBitCount = bitsperpixel;

 RGBQUAD *pNewRGBQuad = (RGBQUAD *)( lpNewDIB + sizeof(BITMAPINFOHEADER));
 if( bitsperpixel == 1 )
 {
  pNewRGBQuad->rgbRed = pNewRGBQuad->rgbGreen = pNewRGBQuad->rgbBlue = 0;
  pNewRGBQuad->rgbReserved = 0;
  pNewRGBQuad ++;
  pNewRGBQuad->rgbRed = pNewRGBQuad->rgbGreen = pNewRGBQuad->rgbBlue = 255;
  pNewRGBQuad->rgbReserved = 0;
 }
 else
 {
  for( int i = 0; i < nPalleteSize; i++, pNewRGBQuad++ )
  {
   pNewRGBQuad->rgbRed = pNewRGBQuad->rgbGreen = pNewRGBQuad->rgbBlue = i;
   pNewRGBQuad->rgbReserved = 0;
  }
 }

 BYTE *pbNewImg = lpNewDIB + sizeof(BITMAPINFOHEADER) +  nPalleteSize * sizeof(RGBQUAD) ;
 if (bitsperpixel == 32)
 {
  Img32to24(pbOriImg, pbNewImg, nImgWidth, height);
 }
 else if (bitsperpixel == 24)
 {
  ReversColors(pbOriImg, pbNewImg, nImgWidth, height);  
 }
 else
 {
  for( i = 0, k = 0, row = 0; i < height; i++, k += bytesPerLine, row += buffsize )
  {
   memcpy(pbNewImg + k, pbOriImg + row, buffsize * sizeof(BYTE));
  }
 }
 
  
 if( pbOriImg )
  delete []pbOriImg;
 pbOriImg = NULL;

 return hDIB;

}

void CArdpsImg::Img32to24(BYTE *pbImg32, BYTE *pbImg24, int nWidth, int nHeight)
{
 if( pbImg32 == NULL || pbImg24 == NULL )
  return;

 int i, j, m, n;
 LONG nBytesLine24 = MYWIDTHBYTES(nWidth * 24);
 BYTE *pbIn = pbImg32;
 BYTE *pbOut = pbImg24;
 

 for( i = 0, m = 0, n = 0; i < nHeight; i++, m += nWidth * 4, n += nBytesLine24 )
 {
  pbIn = pbImg32 + m;
  pbOut = pbImg24 + n;
  for( j = 0; j < nWidth; j++ )
  {
   *pbOut = *(pbIn + 2);
   *(pbOut + 1) = *(pbIn + 1);
   *(pbOut + 2) =  *(pbIn);
   pbIn += 4;
   pbOut += 3;
  }
 }
}
void CArdpsImg::ReversColors(BYTE*pbIn, BYTE *pbOut, int nWidth, int nHeight)
{
 if( pbIn == NULL || pbOut == NULL )
  return;

 int i, j,n, m;
 LONG nBytesLine24 = MYWIDTHBYTES(nWidth * 24);
 BYTE *ptmp = pbIn;
 BYTE *ptmp2 = pbOut;

 for( i = 0, n = 0, m = 0; i < nHeight; i++, n += nBytesLine24, m += nWidth * 3 )
 {
  ptmp = pbIn + m;
  ptmp2 = pbOut + n;
  for( j = 0; j < nWidth; j++ )
  {
   *ptmp2 = *(ptmp + 2);
   *(ptmp2 + 1) = *(ptmp + 1);
   *(ptmp2 + 2) =  *(ptmp);
   ptmp2 += 3;
   ptmp += 3;
  }
 }
}

void CArdpsImg::ReversColors(BYTE*pbIn, int nWidth, int nHeight)
{
 if( pbIn == NULL )
  return;

 int i, j, m;
 LONG nBytesLine24 = MYWIDTHBYTES(nWidth * 24);
 BYTE *ptmp = pbIn;
 BYTE bValue = 0;
 for( i = 0, m = 0; i < nHeight; i++, m += nBytesLine24 )
 {
  ptmp = pbIn + m;
  for( j = 0; j < nWidth; j++ )
  {
   bValue = *(ptmp + 2);
   *(ptmp + 2) = *(ptmp);
   *ptmp =  bValue;
   ptmp += 3;
  }
 }
}

bool CArdpsImg::SaveTiff(char *szImgFileName)
{
 if( szImgFileName == NULL )
  return false;

 LPBYTE lpDIB = (LPBYTE) GlobalLock(m_hDib);
 if( lpDIB == NULL )
  return false;

 LPBYTE lpBinImg = FindDIBBits(lpDIB); 
 if( !lpBinImg )
 {
  GlobalUnlock(m_hDib);
  return false;
 }
 uint16 samplesperpixel, bitspersample, photometric, compression;

 LONG  nImgHei = GetHeight();
 LONG  nImgWid = GetWidth();
 WORD    nBitCnt = DIBBitCount(lpDIB);
 if( nBitCnt == 1)
 {
  samplesperpixel = 1;
  bitspersample = 1;
  photometric = PHOTOMETRIC_MINISBLACK;
  compression = COMPRESSION_CCITTFAX4;
  ReversBits(lpBinImg, nImgWid, nImgHei, nBitCnt);
 }
 else if (nBitCnt == 8)
 {
  samplesperpixel = 1;
  bitspersample = 8;
  photometric = 1;
  compression = COMPRESSION_LZW; 
 }
 else if (nBitCnt == 24)
 {
  samplesperpixel = 3;
  bitspersample = 8;
  photometric = 2;
  compression = COMPRESSION_JPEG;
  //compression = COMPRESSION_DEFLATE;
  ReversColors(lpBinImg, nImgWid, nImgHei);
 }
 else
 {
  GlobalUnlock(m_hDib);
  return false;
 }

 TIFF * newImg = TIFFOpen(szImgFileName, "w");
 if( newImg == NULL )
 {
  GlobalUnlock(m_hDib);
  return false;
 }

 TIFFSetField(newImg, TIFFTAG_IMAGELENGTH, nImgHei);
 TIFFSetField(newImg, TIFFTAG_IMAGEWIDTH, nImgWid);
 TIFFSetField(newImg, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel); 
 TIFFSetField(newImg, TIFFTAG_BITSPERSAMPLE,bitspersample);
 TIFFSetField(newImg, TIFFTAG_PHOTOMETRIC, photometric);
 //if(nBitCnt != 24 )  只能由程序读取,去掉该注释可以被通用程序读取,压缩比变小。
  TIFFSetField(newImg, TIFFTAG_COMPRESSION, compression);
 
 TIFFSetField(newImg, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);


 uint32 row, buffsize, k;
 buffsize = MYWIDTHBYTES(nImgWid * bitspersample * samplesperpixel );
 
 for (row = 0 , k = (nImgHei - 1) * buffsize; row < nImgHei; row++ , k -= buffsize )
  TIFFWriteScanline(newImg, (void*)(lpBinImg + k), row, 0);

 TIFFClose(newImg);
 GlobalUnlock(m_hDib);
 return true;
}
void CArdpsImg::ReversBits(BYTE *pbIn, int nWidth, int nHeight, DWORD nBitCnt)
{

 int nBytesPerLine = MYWIDTHBYTES(nBitCnt * nWidth );
 BYTE *ptmp = pbIn;
 for( int i = 0; i < nBytesPerLine * nHeight; i++, ptmp++ )
 {
  *ptmp = 255- (*ptmp);
 } 
}
bool CArdpsImg::SaveGrayDIB()
{
 LPBITMAPINFOHEADER lpDIBHdr = (LPBITMAPINFOHEADER)GlobalLock(m_hDib);

 //for color image
 LONG  nImgHei = GetHeight();
 LONG  nImgWid = GetWidth();
 //for gray image
 LONG  nGrayBytesPerLine = MYWIDTHBYTES( nImgWid << 3 );
 LONG nGrayImgSize = nGrayBytesPerLine * nImgHei;
 LONG nFileSize = nGrayImgSize + lpDIBHdr->biSize + 256 * sizeof(RGBQUAD);

 //allocate memory for gray image
 LPBYTE lpNewDIB = new BYTE[nFileSize];
 if( !lpNewDIB )
  return false;
 memset(lpNewDIB, 0, nFileSize*sizeof(BYTE));
 LPBITMAPINFOHEADER lpNewDIBHdr = (LPBITMAPINFOHEADER)lpNewDIB;
 memcpy(lpNewDIBHdr, lpDIBHdr, sizeof(BITMAPINFOHEADER));
 GlobalUnlock(m_hDib);

 lpNewDIBHdr->biBitCount = 8;
 lpNewDIBHdr->biCompression = BI_RGB;
 lpNewDIBHdr->biSizeImage = nGrayImgSize;
 lpNewDIBHdr->biClrUsed = 256;
 lpNewDIBHdr->biXPelsPerMeter = 2952;
 lpNewDIBHdr->biYPelsPerMeter = 2952;

 //create RGBQUARD
 RGBQUAD *pNewRGBQuad = (RGBQUAD*)(lpNewDIB +sizeof(BITMAPINFOHEADER));

 memset(pNewRGBQuad, 0, 256*sizeof(RGBQUAD));
 for( int i = 0; i < 256; ++i )
 {
  pNewRGBQuad->rgbBlue = pNewRGBQuad->rgbRed = 
   pNewRGBQuad->rgbGreen = i;
  pNewRGBQuad++;   
 }
 LPBYTE  lpDIB = (LPBYTE)GlobalLock(m_hDib);
 LPBYTE lpSrcImg = FindDIBBits(lpDIB); 
 LPBYTE lpNewImg = lpNewDIB + sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);

 bool bConvert = ConvertBinary2Gray(lpSrcImg, nImgWid, nImgHei, lpNewImg);

 GlobalUnlock(m_hDib);
 bool bCrate = Create(lpNewDIB);
 if( lpNewDIB )
  delete []lpNewDIB;

 lpNewDIB = NULL;

 return true;

}