java 图像智能字符识别技术——【专题二】
java 图像智能字符识别技术——【专题二】
侠客人生 发表于5个月前
java 图像智能字符识别技术——【专题二】
  • 发表于 5个月前
  • 阅读 13
  • 收藏 2
  • 点赞 0
  • 评论 1

腾讯云 新注册用户 域名抢购1元起>>>   

本节我们对上节讲的二值化、噪声去除再详细讲一下

二值化

定义

 一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,最常用的方法就是设定一个阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。这是研究灰度变换的最特殊的方法,称为图像的二值化(BINARIZATION)

方法

        全局二值化

        一幅图像包括目标物体、背景还有噪声,要想从多值的数字图像中直接提取出目标物体,最常用的方法就是设定一个全局的阈值T,用T将图像的数据分成两部分:大于T的像素群和小于T的像素群。将大于T的像素群的像素值设定为白色(或者黑色),小于T的像素群的像素值设定为黑色(或者白色)。

全局二值化,在表现图像细节方面存在很大缺陷。为了弥补这个缺陷,出现了局部二值化方法。

局部二值化的方法就是按照一定的规则将整幅图像划分为N个窗口,对这N个窗口中的每一个窗口再按照一个统一的阈值T将该窗口内的像素划分为两部分,进行二值化处理。

        局部自适应二值化

        局部二值化也有一个缺陷。这个缺陷存在于那个统一阈值的选定。这个阈值是没有经过合理的运算得来,一般是取该窗口的平局值。这就导致在每一个窗口内仍然出现的是全局二值化的缺陷。为了解决这个问题,就出现了局部自适应二值化方法。

局部自适应二值化,该方法就是在局部二值化的基础之上,将阈值的设定更加合理化。该方法的阈值是通过对该窗口像素的平均值E,像素之间的差平方P,像素之间的均方根值Q等各种局部特征,设定一个参数方程进行阈值的计算,例如:T=a*E+b*P+c*Q,其中a,b,c是自由参数。这样得出来的二值化图像就更能表现出二值化图像中的细节。

分类

     根据阈值选取的不同,二值化的算法分为固定阈值和自适应阈值。 比较常用的二值化方法则有:双峰法、P参数法、迭代法和OTSU法等。

噪声去除

        定义

        图像去噪是指减少数字图像中噪声的过程称为图像去噪。现实中的数字图像在数字化和传输过程中常受到成像设备与外部环境噪声干扰等影响,称为含噪图像或噪声图像。噪声是图象干扰的重要原因。一幅图象在实际应用中可能存在各种各样的噪声,这些噪声可能在传输中产生,也可能在量化等处理中产生。

        噪声的产生及分类

        根据噪声和信号的关系可将其分为三种形式:(f(x,y)表示给定原始图象,g(x,y)表示图象信号,n(x,y)表示噪声。

        1) 加性噪声,此类噪声与输入图象信号无关,含噪图象可表示为f(x,y)=g(x,y)+n(x,y),信道噪声及光导摄像管的摄像机扫描图象时产生的噪声就属这类噪声;

        2) 乘性噪声,此类噪声与图象信号有关,含噪图象可表示为f(x,y)=g(x,y)+n(x,y)g(x,y),飞点扫描器扫描图象时的噪声,电视图象中的相关噪声,胶片中的颗粒噪声就属于此类噪声;

        3) 量化噪声,此类噪声与输入图象信号无关,是量化过程存在量化误差,再反映到接收端而产生。

       去除图像噪声的方法简介

        均值滤波器

        采用邻域平均法的均值滤波器非常适用于去除通过扫描得到的图像中的颗粒噪声。领域平均法有力地抑制了噪声,同时也由于平均而引起了模糊现象,模糊程度与领域半径成正比。

几何均值滤波器所达到的平滑度可以与算术均值滤波器相比,但在滤波过程中会丢失更少的图象细节。

谐波均值滤波器对“盐”噪声效果更好,但是不适用于“胡椒”噪声。它善于处理像高斯噪声那样的其他噪声。

逆谐波均值滤波器更适合于处理脉冲噪声,但它有个缺点,就是必须要知道噪声是暗噪声还是亮噪声,以便于选择合适的滤波器阶数符号,如果阶数的符号选择错了可能会引起灾难性的后果。

        自适应维纳滤波器

        它能根据图象的局部方差来调整滤波器的输出,局部方差越大,滤波器的平滑作用越强。它的最终目标是使恢复图像f^(x,y)与原始图像f(x,y)的均方误差e2=E[(f(x,y)-f^(x,y)2]最小。该方法的滤波效果比均值滤波器效果要好,对保留图像的边缘和其他高频部分很有用,不过计算量较大。维纳滤波器对具有白噪声的图象滤波效果最佳。

         中值滤波器

        它是一种常用的非线性平滑滤波器,其基本原理是把数字图像或数字序列中一点的值用该点的一个领域中各点值的中值代换其主要功能是让周围象素灰度值的差比较大的像素改取与周围的像素值接近的值,从而可以消除孤立的噪声点,所以中值滤波对于滤除图像的椒盐噪声非常有效。中值滤波器可以做到既去除噪声又能保护图像的边缘,从而获得较满意的复原效果,而且,在实际运算过程中不需要图象的统计特性,这也带来不少方便,但对一些细节多,特别是点、线、尖顶细节较多的图象不宜采用中值滤波的方法。

        形态学噪声滤除器

        将开启和闭合结合起来可用来滤除噪声,首先对有噪声图象进行开启操作,可选择结构要素矩阵比噪声的尺寸大,因而开启的结果是将背景上的噪声去除。最后是对前一步得到的图象进行闭合操作,将图象上的噪声去掉。根据此方法的特点可以知道,此方法适用的图像类型是图象中的对象尺寸都比较大,且没有细小的细节,对这种类型的图像除噪的效果会比较好。

        小波去噪

        这种方法保留了大部分包含信号的小波系数,因此可以较好地保持图象细节。小波分析进行图像去噪主要有3个步骤:(1)对图象信号进行小波分解。(2)对经过层次分解后的高频系数进行阈值量化。(3)利用二维小波重构图象信号。

RGB转灰度图

RGB转灰度图的几种算法

方法一

    对于彩色转灰度,有一个很著名的心理学公式:

    Gray = R*0.299 + G*0.587 + B*0.114

方法二

而实际应用时,希望避免低速的浮点运算,所以需要整数算法。
     注意到系数都是3位精度的没有,我们可以将它们缩放1000倍来实现整数运算算法:

Gray = (R*299 + G*587 + B*114 + 500) / 1000

RGB一般是8位精度,现在缩放1000倍,所以上面的运算是32位整型的运算。注意后面那个除法是整数 除法,所以需要加上500来实现四舍五入。
      就是由于该算法需要32位运算,所以该公式的另一个变种很流行:

Gray = (R*30 + G*59 + B*11 + 50) / 100

方法三

上面的整数算法已经很快了,但是有一点仍制约速度,就是最后的那个除法。移位比除法快多了,所以可以将系数缩放成 2的整数幂。
习惯上使用16位精度,2的16次幂是65536,所以这样计算系数:

0.299 * 65536 = 19595.264 ≈ 19595
                          0.587 * 65536 + (0.264) = 38469.632 + 0.264 = 38469.896 ≈ 38469
                          0.114 * 65536 + (0.896) =   7471.104 + 0.896 = 7472

可能很多人看见了,我所使用的舍入方式不是四舍五入。四舍五入会有较大的误差,应该将以前的计算结果的误差一起计算进去,舍入方式是去尾法:

写成表达式是:

Gray = (R*19595 + G*38469 + B*7472) >> 16

2至20位精度的系数:

Gray = (R*1 + G*2 + B*1) >> 2
                          Gray = (R*2 + G*5 + B*1) >> 3
                          Gray = (R*4 + G*10 + B*2) >> 4
                          Gray = (R*9 + G*19 + B*4) >> 5
                          Gray = (R*19 + G*37 + B*8) >> 6
                          Gray = (R*38 + G*75 + B*15) >> 7
                          Gray = (R*76 + G*150 + B*30) >> 8
                          Gray = (R*153 + G*300 + B*59) >> 9
                          Gray = (R*306 + G*601 + B*117) >> 10
                          Gray = (R*612 + G*1202 + B*234) >> 11
                          Gray = (R*1224 + G*2405 + B*467) >> 12
                          Gray = (R*2449 + G*4809 + B*934) >> 13
                          Gray = (R*4898 + G*9618 + B*1868) >> 14
                          Gray = (R*9797 + G*19235 + B*3736) >> 15
                          Gray = (R*19595 + G*38469 + B*7472) >> 16
                          Gray = (R*39190 + G*76939 + B*14943) >> 17
                          Gray = (R*78381 + G*153878 + B*29885) >> 18
                          Gray = (R*156762 + G*307757 + B*59769) >> 19
                          Gray = (R*313524 + G*615514 + B*119538) >> 20

仔细观察上面的表格,这些精度实际上是一样的:3与4、7与8、10与11、13与14、19与20
所以16位运算下最好的计算公式是使用7位精度,比先前那个系数缩放100倍的精度高,而且速度快:

Gray = (R*38 + G*75 + B*15) >> 7

其实最有意思的还是那个2位精度的,完全可以移位优化:

Gray = (R + (WORD)G<<1 + B) >> 2

**     另一种是 Adobe Photoshop 里的公式** 
**     Adobe RGB (1998) [gamma=2.20]** 
**     Gray = (R^2.2 * 0.2973 + G^2.2 * 0.6274 + B^2.2 * 0.0753)^(1/2.2)**

该方法运行速度稍慢,但是效果很好。

**      还有就是   平均值方法 **

**      GRAY = (RED+BLUE+GREEN)/3**

**    (GRAY,GRAY,GRAY ) 替代 (RED,GREEN,BLUE)**

实战

    前面为什么讲这么多理论,其实大家对这些图片处理的理论明白了,大家在看代码时就知道为什么要调图片的阀值。下面我们就来看看代码:

package com.jhaso.search.util;

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

/**
 * @author 侠客人生
 * @ClassName ClearImageHelper
 * @Description 图片识别之前先去噪,把图片中的干扰线清除
 * @create 2017-07-27 9:26
 * @version: V1.0.0
 **/
public class ClearImageHelper {

    public static void main(String[] args) throws IOException {

        File testDataDir = new File("E:\\imgData");
        final String destDir = testDataDir.getAbsolutePath() + "/tmp";
        for (File file : testDataDir.listFiles()) {
            cleanImage(file, destDir);
        }

    }

    /**
     * @param sfile   需要去噪的图像
     * @param destDir 去噪后的图像保存地址
     * @throws IOException
     */
    public static void cleanImage(File sfile, String destDir)
            throws IOException {
        File destF = new File(destDir);
        if (!destF.exists()) {
            destF.mkdirs();
        }

        BufferedImage bufferedImage = ImageIO.read(sfile);
        int h = bufferedImage.getHeight();
        int w = bufferedImage.getWidth();

        // 灰度化
        int[][] gray = new int[w][h];
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                int argb = bufferedImage.getRGB(x, y);
                // 图像加亮(调整亮度识别率非常高)
                int r = (int) (((argb >> 16) & 0xFF) * 1.1 + 30);
                int g = (int) (((argb >> 8) & 0xFF) * 1.1 + 30);
                int b = (int) (((argb >> 0) & 0xFF) * 1.1 + 30);
                if (r >= 255) {
                    r = 255;
                }
                if (g >= 255) {
                    g = 255;
                }
                if (b >= 255) {
                    b = 255;
                }
                /**
                 * RGB转灰度图中的第一种方法
                 */
                gray[x][y] = (int) Math.pow((Math.pow(r, 2.2) * 0.2973 + Math.pow(g, 2.2) * 0.6274 + Math.pow(b, 2.2) * 0.0753), 1 / 2.2);
            }
        }

        // 二值化
        int threshold = binaryzation(gray, w, h);
        BufferedImage binaryBufferedImage = new BufferedImage(w, h,
                BufferedImage.TYPE_BYTE_BINARY);
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                if (gray[x][y] > threshold) {
                    gray[x][y] |= 0x00FFFF;
                } else {
                    gray[x][y] &= 0xFF0000;
                }
                binaryBufferedImage.setRGB(x, y, gray[x][y]);
            }
        }

      /*  // 输出打印
        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                if (isBlack(binaryBufferedImage.getRGB(x, y))) {
                    System.out.print("*");
                } else {
                    System.out.print(" ");
                }
            }
            System.out.println();
        }*/
        /**
         * TODO 如果文件已经创建则覆盖未实行
         */
        ImageIO.write(binaryBufferedImage, "jpg", new File(destDir, sfile.getName()));
    }

    public static boolean isBlack(int colorInt) {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue() <= 300) {
            return true;
        }
        return false;
    }

    public static boolean isWhite(int colorInt) {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue() > 300) {
            return true;
        }
        return false;
    }

    public static int isBlackOrWhite(int colorInt) {
        if (getColorBright(colorInt) < 30 || getColorBright(colorInt) > 730) {
            return 1;
        }
        return 0;
    }

    public static int getColorBright(int colorInt) {
        Color color = new Color(colorInt);
        return color.getRed() + color.getGreen() + color.getBlue();
    }

    /**
     * 对摄像头拍摄的图片,大多数是彩色图像,彩色图像所含信息量巨大,对于图片的内容,我们可以简单的分为前景与背景,为了让计算机更快的,更好的识别文字,
     * 我们需要先对彩色图进行处理,使图片只前景信息与背景信息,可以简单的定义前景信息为黑色,背景信息为白色,这就是二值化图了。
     *
     * @param gray
     * @param w
     * @param h
     * @return
     */
    public static int binaryzation(int[][] gray, int w, int h) {
        int[] histData = new int[w * h];
        // 计算直方图
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                int red = 0xFF & gray[x][y];
                histData[red]++;
            }
        }

        // 统计像素点
        int total = w * h;

        float sum = 0;
        for (int t = 0; t < 256; t++)
            sum += t * histData[t];

        float sumB = 0;
        int wB = 0;
        int wF = 0;

        float varMax = 0;
        int threshold = 0;

        for (int t = 0; t < 256; t++) {
            wB += histData[t]; // 加重背景
            if (wB == 0)
                continue;

            wF = total - wB; // 加重前景
            if (wF == 0)
                break;

            sumB += (float) (t * histData[t]);

            float mB = sumB / wB; // Mean Background
            float mF = (sum - sumB) / wF; // Mean Foreground

            // Calculate Between Class Variance
            float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);

            // Check if new maximum found
            if (varBetween > varMax) {
                varMax = varBetween;
                threshold = t;
            }
        }

        return threshold;
    }
}

测试的图片:

运行后的图片:

【注】:对于上边a2.jpg图片中的7364 处理后数字少了一位这个原因,可能对于玩过PS 使用通道抠图的朋友比较清楚,当各个颜色调他们的阀值时效果都是不太一样的,最理想的是蓝色(也就为什么蒙娜丽莎抠图软件会使用蓝色背景抠图的原因)。该问题的解决方案可以借鉴PS 抠图,使用多个算法对图片进行处理,看那个效果好。我们就采用那个。对于不懂抠图的朋友可参考下面这篇文章:

        http://www.ps-xxw.cn/shiyongjiqiao/9128.html

 

共有 人打赏支持
粉丝 15
博文 43
码字总数 82954
评论 (1)
zcjlq
:+1:
×
侠客人生
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: