java 图像智能字符识别技术——【专题二】

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

二值化

定义

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

方法

        全局二值化

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

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

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

        局部自适应二值化

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

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

分类

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

噪声去除

        定义

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

        噪声的产生及分类

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

        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与四、7与八、10与十一、13与1四、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

相关文章
相关标签/搜索