图像处理算法_图像处理算法 和视频处理算法的区别 - CSDN
  • 数字图像处理实战

    2019-07-29 14:17:46
    本课程内容主要选取自stanford EE368/CS232 课程的...本次课程包含图像算法和机器学习,编程工具主要包括python,c++,android,matlab,opencv,Tesseract等。 由于个人时间精力有限,课程将每隔2-3周发布一次。
  • 贝叶斯分类器是机器学习的一个入门要点,如今已应用在图像处理、语音识别和自然语言处理等方面。本课程详细讲解贝叶斯方法的原理、推导过程、相关扩展方法和代码实战。包括:贝叶斯和概率、贝叶斯决策论、参数估计和...
  • 图像处理算法总结

    2018-07-15 12:04:38
    图像预处理图像预处理有很多过程。这里只介绍函数。方便使用。 图像读取原始 tf.gfile.FastGFile().read()图像格式的编码解码 :图像不直接记录图像上的不同位置,不同颜色的亮度。而是记录压缩编码之后的结果。...

    图像预处理

    图像预处理有很多过程。这里只介绍函数。方便使用。

     

    图像读取原始

    tf.gfile.FastGFile().read()

    图像格式的编码解码 :图像不直接记录图像上的不同位置,不同颜色的亮度。而是记录压缩编码之后的结果。所以要还原成三维矩阵,需要解码。

    tf.image.decode_jpeg()

    tf.image.encode_jpeg()

    转换函数 tf.image.convert_image_dtype

    图像大小调整

    tf.image.resize_images(image,[size],method)

    method 0:双线性插值 1:最近邻居法 2: 双三次插值法 3:面积差值法

    tf.image.resize_image_with_crop_pad 自动裁剪或者填充

    图像翻转

    tf.image.flip_up_down()

    tf.image.filp_left_right()

    tf.image.transpose_image()

    图像色彩调整

    亮度调整tf.image.adjust_brightness(image,brightness)

    随机亮度调整tf.image.random_brightness(image,max_delta)

    同理调整,tf.image.adjust_contrast,tf.image.adjust_hue,tf.image.

    saturation.

    图像标准化 tf.image.per_image_whitening(image)

    标注框

    tf.image.draw_bounding_boxes(batch,boxes) 这个函数要求图像矩阵的数字为实数,而且输入是一个batch的数据,即多张图像组成的四维矩阵,所以将编码后的图像矩阵加一维。

    tf.expand_dims() 这个加的维度大家自己要看api去理解

    tf.image.sample_distorted_bounding_box(size,boxes)随机截取图像信息

    随机翻转图像,随机调整颜色,随机截图图像中的有信息含量的部分,这些事提高模型健壮性的一种方式。这样可以使是训练得到的模型不受被识别物体大小的影响。

    TensorFlow 读取图片数据代码:

    reader = tf.WholeFileReader()

    key, value = reader.read(tf.train.string_input_producer(['cat.jpg']))

    image0 = tf.image.decode_jpeg(value)

    代码:

    resized_image = tf.image.resize_images(image0, [256, 256], \

            method=tf.image.ResizeMethod.AREA)

    其中 method 有四种选择:

    ResizeMethod.BILINEAR :双线性插值

    ResizeMethod.NEAREST_NEIGHBOR : 最近邻插值

    ResizeMethod.BICUBIC : 双三次插值

    ResizeMethod.AREA :面积插值

    代码裁剪:

    代码:

    cropped_image = tf.image.crop_to_bounding_box(image0, 20, 20,256, 256)

    代码:

    flipped_image = tf.image.flip_left_right(image0)

    除此之外还可以上下翻转:

    flipped_image = tf.image.flip_up_down(image0)

    代码:

    rotated_image = tf.image.rot90(image0, k=1)

    其中 k 值表示旋转 90 度的次数,读者可以尝试对原图旋转 180 度、270 度。

    代码:

    grayed_image = tf.image.rgb_to_grayscale(image0)

     

     


     


    展开全文
  • 1)将256*256分辨率的图像变为128*128分辨率可以将源图像划分成2*2的子图像块,然后将2*2的 子图像块的所有像素颜色均按照F(i,j)的颜色值进行设定,达到降低分辨率的目的。 如: F(i,j) F(i,j+1) F(i,j) F(i,j)...

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

    ● 减小对比度

    ● 改善对比度

    ● 增强对比度

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

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

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

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

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

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

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

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




    //实现

    一、基本实现如下功能:

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

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

    3)        图像去污功能;

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

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

    6)        支持部分磨白功能;

    7)        擦除图像功能;

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

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

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

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

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


    class CArdpsImg  : public CObject
    {
     DECLARE_SERIAL(CArdpsImg)

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

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


     bool    SaveGrayDIB(); // for debug

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


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

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

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

    public:
     virtual ~CArdpsImg(void);
    };

     

    2)源文件

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

     

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

    #define EDGE_STEP 4

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

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

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

    IMPLEMENT_SERIAL(CArdpsImg, CObject, 0)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     DWORD dwSize = DIBlockSize(lpDIB);

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

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

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

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

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

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

     return true;
    }

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

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


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

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

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

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

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

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

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

     // Fill in the fields of the file header

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

     bmfHdr.bfType = DIB_HEADER_MARKER;  // "BM"

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

     // Partial Calculation

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

     // Now calculate the size of the image

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

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

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

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

      dwDIBSize += dwBmBitsSize;

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

      lpBI->biSizeImage = dwBmBitsSize; 
     }


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

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

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

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

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

      GlobalUnlock(m_hDib); 
     WaitCursorEnd();

     return true;
    }


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

     if (! GlobalLock(m_hDib))
      return true;

     GlobalUnlock(m_hDib);
     return false;
    }

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

     CBitmap* pOldBmp = MemDC.SelectObject(m_pBitmap);

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

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

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

     return bSuccess;
    }

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

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

     return wBitCount;
    }

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

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

     return lWidth; 
    }

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

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

     return lHeight; 
    }

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

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

     return lpData;
    }

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

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

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

     return wColors;
    }

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

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

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

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

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

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

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

    */

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

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

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

     strlwr(szName);

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

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


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

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


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

     if(hConvertLib)
     {
      FreeLibrary(hConvertLib);
     }

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

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

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

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

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

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

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

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

      dwDIBSize += dwBmBitsSize;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     LPBITMAPINFOHEADER lpDIBHdr = (LPBITMAPINFOHEADER)GlobalLock(m_hDib);


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

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

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

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

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

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


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

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

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

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

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

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

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

     Th=threshold;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     GlobalUnlock(m_hDib);

     return UpdateInternal();
    }

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

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

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


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

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

     GlobalUnlock(m_hDib);

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

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

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

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

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

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

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

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

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

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

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


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

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


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

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

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

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

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

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

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

     WORD dwBitCnt = DIBBitCount(lpDIB);


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

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

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


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

     

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

     }

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

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

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

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


     LPBYTE lpDIBBits = FindDIBBits(lpDIB);

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

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


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

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

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

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


     LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);

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

     LPBYTE lpDIBBits = FindDIBBits(lpDIB);

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

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

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

     bSuccess = true;

    CleanUp:

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

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

     LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);

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


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

     HDIB hDib1, hDib2;

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

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

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

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

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

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

     LPBYTE lpSrcDIBBits = FindDIBBits(lpSrcDIB);

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

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

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

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

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

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


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

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

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

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

     }

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

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

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

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

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

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

     LPBYTE lpSrcDIBBits = FindDIBBits(lpSrcDIB);

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

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

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

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

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

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

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

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

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

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

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

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

      break;

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

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

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

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

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

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


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

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

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

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

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

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

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

     GlobalUnlock(m_hDib);

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

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

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

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

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

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

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

     GlobalUnlock(m_hDib);

     return UpdateInternal();
    }

     

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

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

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

     // return
     return UpdateInternal();
    }

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

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

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

     stack = InStack[topin];

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

     int i, j, k;

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

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

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

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

     delete []stack; 

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

     delete []flag;

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

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

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

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

      cp2 = cp1;
      cp1 = cp1->cnext; 

      delete cp2;
     }

     Li->cp = NULL;  
    }

     

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

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

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

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

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

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

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

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

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

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

     GlobalUnlock(m_hDib);
     return m_dirRect;
    }

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

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

      cp2 = cp1;
      cp1 = cp1->cnext; 

      delete cp2;
     }

     Li->cp = NULL;  
     return true;
    }

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

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

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

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

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

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


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

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

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

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

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

     

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

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

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

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


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

     return true;
    }

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

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

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

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

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

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

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

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

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

     return nValeSum;
    }

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

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

     nSum = 0;

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

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

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

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

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

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

     return;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

     return rect;
    }

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

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

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

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

    }

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

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

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

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

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

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

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


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

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

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

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

     return rect;

    }

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

     return rect;
    }


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

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

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

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

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

     return 0;
    }

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

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

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

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

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

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

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

     TIFFClose(image);

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

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

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

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

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

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

     return hDIB;

    }

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

     lpNewDIB = NULL;

     return true;

    }


    展开全文
  • 一些基本数字图像处理算法 所有的图像算法都在DIPAlgorithm类中,并且所有算法都为抽象成员函数。我已经按照java注释规范为所有方法添加使用说明注释,具体实现可见于DIPAlgorithm.java,这里只做算法说明。 1 图像...

    一些基本数字图像处理算法

    版权声明:本文为原创文章,未经博主允许不得用于商业用途。

    所有的图像算法都在DIPAlgorithm类中,并且所有算法都为抽象成员函数。我已经按照java注释规范为所有方法添加使用说明注释,具体实现可见于DIPAlgorithm.java,这里只做算法说明。

    1 图像扭曲

    在这里插入图片描述
    模仿PS的扭曲功能,通过建立一个三角形映射网格实现对图像的扭曲。

    如上图,一共设置了45个控制点围成74个三角形网格

    扭曲即形变处理其实是寻找一个函数,以所有网格顶点原始坐标为输入,扭曲后所有网格顶点坐标为输出。为了简化计算任务,采用控制栅格插值方法,对每个三角网格独立计算映射关系,如下图:

    在这里插入图片描述
    即求解矩阵MM满足MA=BMA = B,其中AA为原顶点的齐次矩阵:

    A=[x1y11x2y21x3y31] A = \begin{bmatrix} x_{1} & y_{1} & 1 \\ x_{2} & y_{2} & 1 \\ x_{3} & y_{3} & 1 \\ \end{bmatrix}

    B为形变后顶点的其次矩阵:

    B=[x1x2x3y1y2y3] B = \lbrack\begin{matrix} x_{1}^{'} & x_{2}^{'} & x_{3}^{'} \\ y_{1}^{'} & y_{2}^{'} & y_{3}^{'} \\ \end{matrix}\rbrack

    M即为2×32 \times 3的映射矩阵,且由于三角形三点不共线,因此A为可逆阵,

    M=BA1 M = BA^{- 1}

    对于三角形中的点p(x, y)p\left( x,\ y \right),其映射后坐标p=M[xy1]p^{'} = M\begin{bmatrix} x \\ y \\ 1 \\ \end{bmatrix}

    2 直方图计算

    直方图计算实际上即求图像的概率密度函数PDF,只需遍历一次所有像素点即可获得。

    3 直方图均衡化算法

    对于连续图像直方图均衡化其实是种点运算f,
    对不同灰度值做映射,使得所有像素频率相等。

    对于点运算f,有如下性质:

    DB=f(DA), HB(DB)ΔDB=HA(DA)ΔDA D_{B} = f\left( D_{A} \right),\ H_{B}\left( D_{B} \right)\Delta D_{B} = H_{A}\left( D_{A} \right)\Delta D_{A}

    其中D为灰度值,H即为灰度值在图像中的频数,整理可得

    HB(DB)=HA(DA)ΔDAΔDB=HA(DA)ΔDBΔDA=HA(DA)dDBdDA H_{B}\left( D_{B} \right) = \frac{H_{A}\left( D_{A} \right)\Delta D_{A}}{\Delta D_{B}} = \frac{H_{A}\left( D_{A} \right)}{\frac{\Delta D_{B}}{\Delta D_{A}}} = \frac{H_{A}\left( D_{A} \right)}{\frac{dD_{B}}{dD_{A}}}

    =HA(DA)f(DA)=HA(f1(DB))f(f1(DB)) = \frac{H_{A}\left( D_{A} \right)}{f'(D_{A})} = \frac{H_{A}\left( f^{- 1}\left( D_{B} \right) \right)}{f'(f^{- 1}(D_{B}))}

    即:

    寻找函数f使得HB(D)H_{B}(D)为常数A0Dm,A0,Dm\frac{A_{0}}{D_{m}},A_{0},D_{m}

    由(1)可知,KaTeX parse error: Undefined control sequence: \ at position 58: …\right)}{f'(D)}\̲ ̲\Rightarrow f^{…

    f(D)=DmCDF(D)f\left( D \right) = D_{m}CDF(D),CDF即累积分布函数

    因此只需求得直方图的前序和即可获得映射关系。

    4 图像灰度化

    目前比较符合人眼的灰度化权重为0.299、0.578和0.114,为了加速计算使用近似公式D=(3r+g+6b)/10D = (3r + g + 6b)/10

    5 图像二值化

    我使用的二值化算法为OSTU大律二值化算法。二值化操作即利用分割阈值u,将图片分为前景后景两部分。OSTU大律法认为使得前景像素和背景像素灰度方差g最大的阈值即为最佳分割阈值。

    g=w0w1(u0u1)2 g = w_{0}w_{1}\left( u_{0} - u_{1} \right)^{2}

    其中w0, w1w_{0},\ w_{1}为前景、后景在图像中的比例,KaTeX parse error: Undefined control sequence: \ at position 7: u_{0},\̲ ̲u_{1}为前景、后景的平均灰度。

    在实现时,只需遍历所有灰度,利用CDF求出每种灰度的方差,取最大者作为阈值即可。

    6 前景分离

    目前主流的前景分离为深度学习算法。这里只使用了最基本的阈值分离法,分别为RGB三个通道设置不同阈值,将小于阈值的像素作为背景,大于阈值的作为前景。

    7 滤波

    我使用的滤波方法是高斯滤波和中值滤波,高斯滤波即使用二维高斯函数作为滤波函数,中值滤波即使用邻域的中位数作为滤波函数。

    高斯滤波器为线性滤波器,可以有效消除高斯噪声。由于高斯函数离中值越近权重越大,因此相对于均值滤波器更加柔和,对边缘的保留效果更好。这里我使用的是如下矩阵做卷积:

    [1232124642367632464212321] \begin{bmatrix} 1 & 2 & 3 & 2 & 1 \\ 2 & 4 & 6 & 4 & 2 \\ 3 & 6 & 7 & 6 & 3 \\ 2 & 4 & 6 & 4 & 2 \\ 1 & 2 & 3 & 2 & 1 \\ \end{bmatrix}

    中值滤波器为非线性滤波器,可以有效的去除椒盐噪声和斑点噪声并且不会使图像变模糊。

    8 形态学扩张和腐蚀

    形态学腐蚀可记为AΘB\text{AΘB},其中A为输入图像,B为结构单元。对于二值图像,当且仅当当前像素点满足腐结构单元时才会被保留。对于灰度图像,则可类比为最小值,即

    fΘb(x,y)=min{f(xx, yy)b(x,y)(x,yDb)} f\Theta b\left( x,y \right) = min\{ f\left( x - x^{'},\ y - y^{'} \right) - b(x^{'},y')|(x^{'},y^{'} \in D_{b})\}

    形态学扩张可看作腐蚀的逆操作,记作ABA\bigoplus B,对于二值图像,将每个有效像素点的邻域结构单元置1,对于灰度图像则取最大值,即

    fb(x,y)=max{f(xx, yy)b(x,y)(x,yDb)} f\bigoplus b\left( x,y \right) = max\{ f\left( x - x^{'},\ y - y^{'} \right) - b(x^{'},y')|(x^{'},y^{'} \in D_{b})\}

    本程序将结构单元b统一设定为5*5矩形。

    通过扩张和腐蚀的结合可实现结构开运算(AoB=(AΘB)BAoB = \left( \text{AΘB} \right)\bigoplus B)和结构闭运算(AoB=(AB)ΘBAoB = \left( A\bigoplus B \right)\text{ΘB})对图像进行粗化、细化、滤波等处理

    9 傅里叶变换和滤波

    变换公式

    傅里叶变换可以将信号从时域转换到频域,因此可以看出许多时域中不明显的特征。二维傅里叶变换(CFT)公式如下:

    F(u,v)=f(x,y)e2πj(ux+vy)dxdy F\left( u,v \right) = \iint_{}^{}{f\left( x,y \right)e^{- 2\pi\overrightarrow{j}(ux + vy)}}\text{dxdy}

    其中j2=1,f,F{\overrightarrow{j}}^{2} = - 1,f,F,同样二维傅里叶逆变换公式如下:

    f(x,y)=F(u,v)e2πj(ux+vy)dudv f\left( x,y \right) = \iint_{}^{}{F\left( u,v \right)e^{2\pi\overrightarrow{j}(ux + vy)}}\text{dudv}

    对于离散函数,可以定义离散二维傅里叶变换(DFT)和逆变换:

    G(m,n)=1MN0 i  M10<k<N1 g(i,k)e2πj(imM+jnN) G\left( m,n \right) = \frac{1}{\sqrt{\text{MN}}}\sum_{\begin{matrix} 0 \leq \ i\ \leq \ M - 1 \\ 0 < k < N - 1\ \\ \end{matrix}}^{}{g\left( i,k \right)e^{- 2\pi\overrightarrow{j}(\frac{\text{im}}{M} + \frac{\text{jn}}{N})}}

    g(i,k)=1MN0 m  M10<n<N1 g(m,n)e2πj(imM+jnN) g\left( i,k \right) = \frac{1}{\sqrt{\text{MN}}}\sum_{\begin{matrix} 0 \leq \ m\ \leq \ M - 1 \\ 0 < n < N - 1\ \\ \end{matrix}}^{}{g\left( m,n \right)e^{2\pi\overrightarrow{j}(\frac{\text{im}}{M} + \frac{\text{jn}}{N})}}

    DFT可以理解为对连续二维信号进行了频率为M,
    N的采样,之后通过计算其和频域空间M*N个基向量的相关性(在该方向投影)将时域信号映射到频域。iDFT可以理解为通过M*N个基向量合成原始时域信号。

    矩阵表示

    傅里叶变换实际上是一种线性变换,因此在实际计算中常常将gg扩充为NNN*N方阵,此时DFT可以通过矩阵表示:G=W1gW,Wik=1Ne2πjikNG = \mathcal{W}^{- 1}g\mathcal{W},\mathcal{W}_{\text{ik}} = \frac{1}{N}e^{2\pi\overrightarrow{j}\frac{\text{ik}}{N}}

    易知Wik=Wki\mathcal{W}_{\text{ik}} = \mathcal{W}_{\text{ki}},且为正交矩阵,因此W\mathcal{W}为酉矩阵,即W1=(W)T=W\mathcal{W}^{- 1} = \left( \mathcal{W}^{*} \right)^{T} = \mathcal{W}^{*}G=WgWG = \mathcal{W}^{*}g\mathcal{W},其中FFF^{*}F

    由于傅里叶变换为酉变换,即Wt=W1\mathcal{W}^{t} = \mathcal{W}^{- 1}

    图像的傅里叶变换

    对于二维图片可以看作二维矩阵,因此可以进行DFT。二维图片经过DFT后获得的复矩阵的模矩阵可以表示每个频率信号的强度(也可看作先做自相关后再进行傅里叶变换),经过适当处理即可转化为灰度能量谱图片。

    线性噪声在频域中通常为点或线,因此可以通过傅里叶变换后进行滤波再通过逆变换复原图片。

    算法实现

    在实际实现时,根据欧拉公式,ejt=costjsint, ejt=cost+jsinte^{- \overrightarrow{j}t} = cost - \overrightarrow{j}\text{sint},\ e^{\overrightarrow{j}t} = cost + \overrightarrow{j}\text{sint},因此傅里叶变换的核矩阵可以表示为Wik=cos(2πik)jsin(2πik)N\mathcal{W}_{\text{ik}} = \frac{\cos\left( 2\pi ik \right) - \overrightarrow{j}\sin\left( 2\pi ik \right)}{N},为方便运算将W\mathcal{W}分解为虚部系数Wlm\mathcal{W}_{\text{lm}}和实部系数Wre\mathcal{W}_{\text{re}},其中则W=Wre+jWlm\mathcal{W} = \mathcal{W}_{\text{re}} + \overrightarrow{j}\mathcal{W}_{\text{lm}}。变换结果同样分解为G=Gre+jGlmG = G_{\text{re}} + \overrightarrow{j}G_{\text{lm}},则DFT可以表示为:

    G=WgW=(WrejWlm)g(Wre+jWlm)=WregWre+WlmgWlmj(WlmgWre+WregWlm) G = \mathcal{W}^{*}g\mathcal{W =}\left( \mathcal{W}_{\text{re}} - \overrightarrow{j}\mathcal{W}_{\text{lm}} \right)g\left( \mathcal{W}_{\text{re}} + \overrightarrow{j}\mathcal{W}_{\text{lm}} \right) = \mathcal{W}_{\text{re}}g\mathcal{W}_{\text{re}} + \mathcal{W}_{\text{lm}}g\mathcal{W}_{\text{lm}} - \overrightarrow{j}\left( \mathcal{W}_{\text{lm}}g\mathcal{W}_{\text{re}} + \mathcal{W}_{\text{re}}g\mathcal{W}_{\text{lm}} \right)

    {Gre=WregWre+WlmgWlmGlm=WlmgWreWregWlm  \left\{ \begin{matrix} G_{\text{re}} = \mathcal{W}_{\text{re}}g\mathcal{W}_{\text{re}} + \mathcal{W}_{\text{lm}}g\mathcal{W}_{\text{lm}} \\ G_{\text{lm}} = - \mathcal{W}_{\text{lm}}g\mathcal{W}_{\text{re}} - \mathcal{W}_{\text{re}}g\mathcal{W}_{\text{lm}} \\ \end{matrix} \right.\

    同理,iDFT可以表示为:

    g=(Wre+jWlm)(Gre+jGlm)(WrejWlm) g = \left( \mathcal{W}_{\text{re}} + \overrightarrow{j}\mathcal{W}_{\text{lm}} \right)(G_{\text{re}} + {\overrightarrow{j}G}_{\text{lm}})\left( \mathcal{W}_{\text{re}} - \overrightarrow{j}\mathcal{W}_{\text{lm}} \right)

    其中,为了将能量谱转化为可见的灰度图,为能量谱取对数值进行归一化。且由于在频域中两个维度频率都为0时(即W00\mathcal{W}_{00}处)为图像能量的总和,因此通过log(e+1)256log(W00+1)log(e + 1)*\frac{256}{\log\left( \mathcal{W}_{00} + 1 \right)}可以做进一步归一化。

    算法代码可见github

    展开全文
  • 图像处理算法之滤镜

    2017-04-04 16:29:09
    程序员拿到设计师的滤镜设计稿...这一点多多少少还是有些挑战,因为ps经过多年发展后,不同版本有些功能效果还不一样,比如最为常见的亮度、对比度调节,算法很简单,但是要做到和ps效果一样,还是要花时间摸索、调试。
           做了那么多滤镜,今天写一篇关于滤镜的博客。可能是现在滤镜太多了,现在所有的图像相关app,基本都有滤镜功能,同时还有很多开源滤镜程序。美食滤镜、风景滤镜、人像滤镜,每家滤镜都各有特色,可以说没有最好,只有更有特色的滤镜,不过身边很多人还是比较推崇VSCO的滤镜。
           那么滤镜程序都是如何实现的呢?我以前的工作流程是,滤镜效果由设计师使用ps设计,设计滤镜常用的ps功能是:调节饱和度及色相、调节曲线、叠加纹理等等,通过调节饱和度和色相,可以有针对性的调节某一个色系,使之变浓、变淡或者改变色调,而其他色系不变。同样,对rgb某一个通道进行曲线调节,可以非线性的拉伸或压缩某一颜色通道对整幅图像的影响,实现精细化的色调调节。叠加纹理要简单一些,将设计好的纹理,通过某种混合操作,叠加在原图上,实现各种特效,比如lomo滤镜,暗角除了可以通过算法生成外,也可以通过叠加纹理实现。而程序员拿到设计师的滤镜设计稿后,要做的就是编写程序高度还原设计效果。这一点多多少少还是有些挑战,因为ps经过多年发展后,不同版本有些功能效果还不一样,比如最为常见的亮度、对比度调节,算法很简单,但是要做到和ps效果一样,还是要花时间摸索、调试。我当时在实现过程中,比较难还原的是分色系:红、黄、绿、青、蓝、洋红进行饱和度及色相调节,比如整幅图像,只对红色部分进行调节,但是如果程序处理不佳,在红与黄、洋红颜色过渡带,会出现非常明显的色块、锯齿等各种非常差的效果,而ps就不会。这时就要反复推测ps的实现方式,努力做到自己的程序和ps效果一样。所以,滤镜其实也没什么复杂原理及公式推导,就是通过编写程序模拟ps各种操作,以还原设计师的设计效果。这里要提一下,对于图像处理算法工程师,如果ps不熟,那不及格的,哈哈。
           下面是一些滤镜效果图,主要包括黑白滤镜、美食滤镜、风景滤镜等。
        
        
        
           
           
           
        
        
        





    展开全文
  •  线性滤波可以说是图像处理最基本的方法,它可以允许我们对图像进行处理,产生很多不同的效果。做法很简单。首先,我们有一个二维的滤波器矩阵(有个高大上的名字叫卷积核)和一个要处理的二维图像。然后,对于图像...
  • 图像灰度不同,边界处一般会有明显的边缘,利用此特征可以分割图像。需要说明的是:边缘和物体间的边界并不等同,边缘指的是图像中像素的值有突变的地方,而物体间的边界指的是现实场景中的存在于物体之间的边界。...
  • &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...&a
  • 主要把图像处理的一部分经典基础算法梳理并实现了一遍,主要是用VS2013+OpenCV3.1.0做的。 主要目的:一是为了把基础巩固一下,以便更高效地看论文;二是为了工作基础。 因为以前在学习和研究的过程中发现自己基础...
  • 图像处理算法工程师

    2017-09-11 15:09:04
    整理了一下网上各个公司图像处理算法工程师的招聘要求:  图像处理算法工程师 职位要求 编程技能: 1、 具有较强的编程能力和良好的编程习惯, 精通c/c++编程,并熟练使用VS 或matlab开发环境; 2、 在计算机...
  • C/C++图像处理算法(一)

    2018-08-26 14:27:40
    图像处理算法1总结如下: //添加椒盐噪声 void salt(Mat&amp; src,int number) { for (int i = 0; i &lt; number; i++) { int r = static_cast&lt;int&gt;(rng.uniform(0, src.rows)); int ...
  • 有趣的图像处理算法

    2018-07-30 10:48:32
    之前见了一些文章讨论了一些有趣的图像处理算法 下面总结列出: 在研究的过程中,有时候会碰到很多有意思的图像处理算法,算法极具新意,并且能够产生非常有意思的结果。 算法简介 1、图像镶嵌 图像镶嵌也叫...
  • FPGA的图像处理算法

    2019-02-25 15:07:47
    下面简要分析了 FPGA 技术,包括 FPGA 技术原理和技术特点等,随后介绍一下FPGA 的图像处理系统算法的实现,包括存储模块、运算单元、控制模块以及数据传输模块等内容。 智能机器人、多媒体已经计算机的诞生都离不...
  • 嵌入式图像处理算法优化指南,适用于任何基于ARM或PC平台的视觉应用程序开发 转载2015-08-05 15:09:15 标签:目标识别与跟踪视觉定位与目标识别视觉算法研究与验证视频图像处理开发板it 第一章 绪论 ...
  • 主要涉及图形处理,包括形态学、图像质量、相机成像之3A算法、去雾处理、颜色空间转换、滤镜等,主要在安防公司或者机器视觉领域,包括缺陷检测; 二、现代图像算法工程师: 涉及模式识别,主要表现的经验为Adaboost...
  • Scriptol列出了几种神奇的图像处理算法,让我们一起来看一下。 一、像素图生成向量图的算法 数字时代早期的图片,分辨率很低。尤其是一些电子游戏的图片,放大后就是一个个像素方块。Depixelizing算法可以让低...
  • 真实记录本人第一次在android上面的图像处理算法开发经历。
  • 奇妙的图像处理算法

    2016-04-12 22:58:06
    导读 图像处理(image processing),用计算机对图像进行分析,以达到所需结果的技术。又称影像处理。...Scriptol列出了几种神奇的图像处理算法,让我们一起来看一下。 http://www.scriptol.com/program
  • 图像处理算法 面试题

    2019-04-08 16:46:36
    其主要用于边缘检测,在技术上它是以离散型的差分算子,用来运算图像亮度函数的梯度的近似值, Sobel算子是典型的基于一阶导数的边缘检测算子,由于该算子中引入了类似局部平均的运算,因此对噪声具有平滑作用,能很...
1 2 3 4 5 ... 20
收藏数 238,677
精华内容 95,470
关键字:

图像处理算法