2018-08-12 23:15:22 echo_1994 阅读数 27635

同图像灰度不同,边界处一般会有明显的边缘,利用此特征可以分割图像。需要说明的是:边缘和物体间的边界并不等同,边缘指的是图像中像素的值有突变的地方,而物体间的边界指的是现实场景中的存在于物体之间的边界。有可能有边缘的地方并非边界,也有可能边界的地方并无边缘,因为现实世界中的物体是三维的,而图像只具有二维信息,从三维到二维的投影成像不可避免的会丢失一部分信息;另外,成像过程中的光照和噪声也是不可避免的重要因素。正是因为这些原因,基于边缘的图像分割仍然是当前图像研究中的世界级难题,目前研究者正在试图在边缘提取中加入高层的语义信息。

        在实际的图像分割中,往往只用到一阶和二阶导数,虽然,原理上,可以用更高阶的导数,但是,因为噪声的影响,在纯粹二阶的导数操作中就会出现对噪声的敏感现象,三阶以上的导数信息往往失去了应用价值。二阶导数还可以说明灰度突变的类型。在有些情况下,如灰度变化均匀的图像,只利用一阶导数可能找不到边界,此时二阶导数就能提供很有用的信息。二阶导数对噪声也比较敏感,解决的方法是先对图像进行平滑滤波,消除部分噪声,再进行边缘检测。不过,利用二阶导数信息的算法是基于过零检测的,因此得到的边缘点数比较少,有利于后继的处理和识别工作。

      各种算子的存在就是对这种导数分割原理进行的实例化计算,是为了在计算过程中直接使用的一种计算单位。

 

 

1.Sobel算子

        其主要用于边缘检测,在技术上它是以离散型的差分算子,用来运算图像亮度函数的梯度的近似值, Sobel算子是典型的基于一阶导数的边缘检测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很好的消除噪声的影响。Sobel算子对于象素的位置的影响做了加权,与Prewitt算子、Roberts算子相比因此效果更好。

       Sobel算子包含两组3x3的矩阵,分别为横向及纵向模板,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。实际使用中,常用如下两个模板来检测图像边缘。

                       

检测水平边沿 横向模板 :           检测垂直平边沿 纵向模板:

图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小。

                                                                             

然后可用以下公式计算梯度方向。

                                                                           

 

在以上例子中,如果以上的角度Θ等于零,即代表图像该处拥有纵向边缘,左方较右方暗。

缺点是Sobel算子并没有将图像的主题与背景严格地区分开来,换言之就是Sobel算子并没有基于图像灰度进行处理,由于Sobel算子并没有严格地模拟人的视觉生理特征,所以提取的图像轮廓有时并不能令人满意。

 

2. Isotropic Sobel算子

 

        Sobel算子另一种形式是(Isotropic Sobel)算子,加权平均算子,权值反比于邻点与中心点的距离,当沿不同方向检测边缘时梯度幅度一致,就是通常所说的各向同性Sobel(Isotropic Sobel)算子。模板也有两个,一个是检测水平边沿的 ,另一个是检测垂直平边沿的 。各向同性Sobel算子和普通Sobel算子相比,它的位置加权系数更为准确,在检测不同方向的边沿时梯度的幅度一致。

 

3. Roberts算子

罗伯茨算子、Roberts算子是一种最简单的算子,是一种利用局部差分算子寻找边缘的算子,他采用对角线方向相邻两象素之差近似梯度幅值检测边缘。检测垂直边缘的效果好于斜向边缘,定位精度高,对噪声敏感,无法抑制噪声的影响。1963年,Roberts提出了这种寻找边缘的算子。

Roberts边缘算子是一个2x2的模板,采用的是对角方向相邻的两个像素之差。从图像处理的实际效果来看,边缘定位较准,对噪声敏感。适用于边缘明显且噪声较少的图像分割。Roberts边缘检测算子是一种利用局部差分算子寻找边缘的算子,Robert算子图像处理后结果边缘不是很平滑。经分析,由于Robert算子通常会在图像边缘附近的区域内产生较宽的响应,故采用上述算子检测的边缘图像常需做细化处理,边缘定位的精度不是很高。

 

4. Prewitt算子

        Prewitt算子是一种一阶微分算子的边缘检测,利用像素点上下、左右邻点的灰度差,在边缘处达到极值检测边缘,去掉部分伪边缘,对噪声具有平滑作用 。其原理是在图像空间利用两个方向模板与图像进行邻域卷积来完成的,这两个方向模板一个检测水平边缘,一个检测垂直边缘。

 对数字图像f(x,y),Prewitt算子的定义如下:

G(i)=|[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)]|

G(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)]|

则 P(i,j)=max[G(i),G(j)]或 P(i,j)=G(i)+G(j)

经典Prewitt算子认为:凡灰度新值大于或等于阈值的像素点都是边缘点。即选择适当的阈值T,若P(i,j)≥T,则(i,j)为边缘点,P(i,j)为边缘图像。这种判定是欠合理的,会造成边缘点的误判,因为许多噪声点的灰度值也很大,而且对于幅值较小的边缘点,其边缘反而丢失了。

Prewitt算子对噪声有抑制作用,抑制噪声的原理是通过像素平均,但是像素平均相当于对图像的低通滤波,所以Prewitt算子对边缘的定位不如Roberts算子。

 

因为平均能减少或消除噪声,Prewitt梯度算子法就是先求平均,再求差分来求梯度。水平和垂直梯度模板分别为:

检测水平边沿 横向模板                 检测垂直平边沿 纵向模板:

该算子与Sobel算子类似,只是权值有所变化,但两者实现起来功能还是有差距的,据经验得知Sobel要比Prewitt更能准确检测图像边缘。

 

5.Laplacian算子

         Laplace算子是一种各向同性算子,二阶微分算子,在只关心边缘的位置而不考虑其周围的象素灰度差值时比较合适。Laplace算子对孤立象素的响应要比对边缘或线的响应要更强烈,因此只适用于无噪声图象。存在噪声情况下,使用Laplacian算子检测边缘之前需要先进行低通滤波。所以,通常的分割算法都是把Laplacian算子和平滑算子结合起来生成一个新的模板。

 

拉普拉斯算子也是最简单的各向同性微分算子,具有旋转不变性。一个二维图像函数的拉普拉斯变换是各向同性的二阶导数,定义

                                                                           

了更适合于数字图像处理,将拉式算子表示为离散形式:

另外,拉普拉斯算子还可以表示成模板的形式,如下图所示,

 

离散拉普拉斯算子的模板:, 其扩展模板: 。

 

      拉式算子用来改善因扩散效应的模糊特别有效,因为它符合降制模型。扩散效应是成像过程中经常发生的现象。

      Laplacian算子一般不以其原始形式用于边缘检测,因为其作为一个二阶导数,Laplacian算子对噪声具有无法接受的敏感性;同时其幅值产生算边缘,这是复杂的分割不希望有的结果;最后Laplacian算子不能检测边缘的方向;所以Laplacian在分割中所起的作用包括:(1)利用它的零交叉性质进行边缘定位;(2)确定一个像素是在一条边缘暗的一面还是亮的一面;一般使用的是高斯型拉普拉斯算子(Laplacian of a Gaussian,LoG),由于二阶导数是线性运算,利用LoG卷积一幅图像与首先使用高斯型平滑函数卷积改图像,然后计算所得结果的拉普拉斯是一样的。所以在LoG公式中使用高斯函数的目的就是对图像进行平滑处理,使用Laplacian算子的目的是提供一幅用零交叉确定边缘位置的图像;图像的平滑处理减少了噪声的影响并且它的主要作用还是抵消由Laplacian算子的二阶导数引起的逐渐增加的噪声影响。

 

 

6.Canny算子

      该算子功能比前面几种都要好,但是它实现起来较为麻烦,Canny算子是一个具有滤波,增强,检测的多阶段的优化算子,在进行处理前,Canny算子先利用高斯平滑滤波器来平滑图像以除去噪声,Canny分割算法采用一阶偏导的有限差分来计算梯度幅值和方向,在处理过程中,Canny算子还将经过一个非极大值抑制的过程,最后Canny算子还采用两个阈值来连接边缘。

Canny边缘检测算法

step1: 用高斯滤波器平滑图象;

step2: 用一阶偏导的有限差分来计算梯度的幅值和方向;

step3: 对梯度幅值进行非极大值抑制

step4: 用双阈值算法检测和连接边缘

详解:http://www.cnblogs.com/cfantaisie/archive/2011/06/05/2073168.html

 

 

(1)图象边缘检测必须满足两个条件:一能有效地抑制噪声;二必须尽量精确确定边缘的位置。

(2)根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny边缘检测算子。

(3)类似与Marr(LoG)边缘检测方法,也属于先平滑后求导数的方法。

2012-12-14 20:59:40 luckydongbin 阅读数 13049

这是以前应朋友邀请写的算法,大多数是基础的,只是由于长时间不写,显得生疏,不知道是否有用,贴出来帮给初学者参考。算法是在MFC框架下编写的,我会把主文件和相关文件相继贴出来。

一、基本实现如下功能:

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;

}

 

 

2013-10-16 10:04:43 luying12345 阅读数 7916

最近面试了几个图像的职位,汇总一下面试题。


图像基础知识:

1. 常用的图像空间。

2. 简述你熟悉的聚类算法并说明其优缺点。

3. 请描述以下任一概念:SIFT/SURF  LDA/PCA

4. 请说出使用过的分类器和实现原理。

5. Random Forest的随机性表现在哪里。

6. Graph-cut的基本原理和应用。

7. GMM的基本原理和应用。

8. 用具体算法举例说明监督学习和非监督学习的区别。


数学知识:

1. 贝叶斯全概率公式题。

2. 最小二乘拟合的公式推导和代码实现。

3. 数论or组合数学题。


白板编程:

1. 图的遍历

    思路:深度搜索DFS和广度搜搜BFS

2. 网格搜索:给一张二值化图片,用1~n标记不同的连通域。

   思路:可以采用最简单的四领域搜索。

3. 代码实现HSV图的直方图表示,已知H bins=8 S bins=4 V bins=2 


开放问答:

1. 怎样在一张街拍图像中识别明星的衣着服饰信息?

2. 上衣纯色,裙子花色,怎样做区分?

3. 怎样判断一张广告图片中是否有文字信息?是否用到OCR技术?怎样应用?

4. 给一张二值化图片(包含一个正方形),怎样识别图片中的正方形?如果图片污损严重,怎样识别并恢复?

5. 简述图像识别在移动互联网中的应用。

2019-01-07 10:21:04 u010608296 阅读数 4231

数字图像处理中常用图像分割算法有哪些?

1.多数的图像分割算法

2.图像边缘分割

3.图像阈值分割

4.基于区域的分割

5.形态学分水岭算法

多数的图像分割算法 均是基于灰度值的不连续相似的性质。在前者中,算法以灰度突变为基础分割一幅图像,如图像边缘分割。假设图像不同区域的边界彼此完全不同,且与背景不同,从而允许基于灰度的局部不连续性来进行边界检测。后者是根据一组预定义的准则将一幅图像分割为相似区域,如阈值处理、区域生长、区域分裂区域聚合都是基于这种方法形成的。下面将对每类算法进行详细说明。

 

图像边缘分割:边缘是图像中灰度突变像素的集合,一般用微分进行检测。基本的边缘检测算法有:Roberts算子、Prewitt算子、Sobel算子。稍高级的算法有:Marr-Hilderth边缘检测器、Canny边缘检测器。

图像阈值分割:由于阈值处理直观、实现简单且计算速度快,因此阈值处理在分割应用中处于核心地位。阈值处理可以分为单阈值处理与多阈值处理。在单阈值处理中最常用且分割效果也不错的算法是Otsu(最大类间方差算法)算法。多阈值处理:K类由K-1个阈值来分离,即计算图像的多个类间方差,多阈值处理的分割结果相较于单阈值的结果虽然会更好一些,但分类数量增加时它会开始失去意义,因为我们仅仅处理一个变量(灰度),此时可以通过增加变量如彩色来进行解决。

基于区域的分割:区域生长算法和区域分裂与聚合都是属于基于区域的分割算法。

区域生长算法是根据预先定义的生长准则将像素或子区域组合为更大的区域的过程。

基本方法是从一组“种子”点开始,将与种子预先定义的性质相似的那些邻域像素添加到每个种子上来形成这些生长区域(如特定范围的灰度或颜色)。区域分裂与聚合是首先将一幅图像细分为一组任意的不相交区域,然后按照一定规则聚合、分裂这些区域。

形态学分水岭算法:分水岭的概念是以三维形象化一幅图像为基础的。

在图中,我们主要考虑三种类型的点:

(1)属于一个区域最小值的点;

(2)把一点看成是一个水滴,如果把这些点放在任意位置上,水滴一定会下落到一个单一的最小值点;

(3)处在该点的水会等可能性地流向不止一个这样的最小值点。

对于一个特定的区域最小值,满足条件(2)的点的集合称为该最小值的汇水盆地分水岭

满足条件(3)的点形成地表面的峰线,称之为分割线分水线

为了达到更好的分割效果,常常将分水岭算法应用到梯度图像上,而不是图像本身。

 

(二)

个人认为图像分割的算法可以从分割目标入手:通常是要将图像分成目标区域和背景。

需要从图像的特征入手,以灰度图像为例(其余类型的图像处理均类似),图像图形很明显的特征有:

图像灰度值特征、目标边界特征、纹理特征、形态学特征等等;

还有一些基于这些特征所计算提取出的特征,比如信息熵、能量泛函等等。

最为简单的就是灰度值特征了,一幅图中有时候目标区域与背景区域有很明显的亮度区别,基于这个认识,只要试图找到某个亮度的值,我们假设低于该值的认为是背景,高于该值的认为是目标。关于找这个值的算法就是阈值分割算法了,像OTSU、迭代法、最大熵法等等都是属于这一范畴。

同时也可以注意到,在空域内,目标的边界是区分目标与背景的重要依据,因此区分边界也是一个重要的手段,通常边界点周围灰度值变化率很高,因此可以基于图像灰度梯度来识别。这就有一些sobel算子、canny算子等等方式,都是通过找到边界来确定目标区域背景的。

在有些图像中,目标区域具有一定的连续性,基于区域连续性的一些方法像区域生长法、分水岭算法等(本人对这一块不是很熟悉)。

另外,基于图像原始的特征进行提取获得“精炼”的二级特征,并据此分割也是一种好的方法。像SNAKE算法,该算法认为目标区域的边界是“外力”,内力共同作用的结果,因此当外力内力平衡时找到边界,基于这种平衡,提出了判断能量泛函最小的判断原则。此外,还有基于几何活动轮廓模型的水平集方法,该方法是借助于目标区域的几何度量参数,可以比较好的处理一些拓扑变化。

除去经典的阈值分水岭分割算法外,有主动轮廓及衍生的水平集,图割及相关算法(例如GrabCut),交互式分割,以及Cosegmentation。

所有分割算法的核心目的是解决目标区域语义合并难题。例如交互式分割,通过精准交互来解决该问题。Cosegmentation通过分割相同或相似目标来处理该问题。

(三)

(1)基于阈值的分割方法:可在各种颜色空间或不同通道中完成阈值、自适应阈值、

(2)基于边缘的分割方法:各种边缘检测算子

(3)基于区域的分割方法:分水岭、区域归并与分裂

(4)图割分割:最大流(最小割)算法

(5)基于深度信息的分割

(6)基于先验信息的分割

 

基于特定理论的分割方法等。

特定理论大概有:聚类分析、模糊集理论、基因编码、小波变换等。

2019-10-17 23:23:33 tyfwin 阅读数 217

图像算法工程师 转行之路

 

传统图像 + 深度学习图像 + 语言工具 + 光学基础知识

 

1、图像处理的基础:冈萨雷斯的《数字图像处理》这本书必看。里面算法虽然老,但是还是很实用的。搭配它拍套的《数字图像处理的Matlab实现》方便Matlab实现。

2、机器学习相关的,机器学习的常用算法最好都知道,推荐周志华的书《机器学习》,还有吴恩达的机器学习视频,b站上随便一搜就应该有了。我学习的时候还看了唐宇迪的各种深度学习、tensorflow教程。反正这种教程网易云课堂啊、b站啊、慕课啊、某宝上都能找到很多。跟着人家视频边学边写写代码。一般这种代码用Python写比较常见,Python常用的库都会在视频中学到,包括NumPy、Scipy、Pandas、Matplotlib、Seaborn、Sklearn等。常见的机器学习算法可能很多也用不到图像,但是最好知道。后面侧重图像的主要都是深度网络了,学习各种Net,图像的分类、识别、探测、分割等等。中间会在教程中学到TensorflowPyTorch这种深度学习框架。跟着视频肯定会自己训练一些深度学习网络的。

3、图像算法最后的实现大都是在硬件上,比如手机、相机。所以要学C/C++,最好能学好C++,这个感觉入门容易,精通难。因为Python用库比较方便,机器学习常用,所以Python要会,本身也不难。最后还有Matlab最好也会,处理一点简单的图像算法用起来超方便,也非常简单。还有就是数据结构这门课,在实际项目中,非常有必要。

4、最后,就是关于图像获取过程的了解。如果光学出身的话就是非常优势的,因为计算机的门槛变得越来越低,但是光学的门槛并不低。计算机的人才也太多了,所以光学的话要抓住优势。首先是几何光学(工程光学),这个是必须的。如果是光学出身,一定要从原理上搞清楚成像过程中的各种概念以及之间的关系:光圈、焦距、视场、数值孔径、各种像差等摄影中出现频率很高的词汇。然后就是色度学,色彩要了解一下。再复杂的就是波动光学的知识了,比如物理光学、傅里叶光学(信息光学),尤其是对衍射的理解。因为一些照片的现象是波动光学的体现。比如紫边、星芒。最后学有余力可以看看光电技术,它详细讲解了传感器的原理构造,对于原生图像的转化以及图像去噪等都会有很大帮助。最后,如果不是光学出身想学这一块的话,可以买个单反微单玩玩学学摄影,后期学习PS,有精力有兴趣再看看上面光学相关的书。

 

总结一下需要学的:

冈萨雷斯的《数字图像处理》 

周志华《机器学习》

吴恩达的机器学习视频、唐宇迪的各种教程

C++、Python、Matlab、数据结构

几何光学、色度学、傅里叶光学、光电技术

有兴趣的话可以买个单反或者微单玩玩,后期修修图

 

加油!

我也还没正式工作,也只是自己的体会,还望大家多多批评指正。

图像分割常用算法

阅读数 4299

没有更多推荐了,返回首页