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

相關文章
相關標籤/搜索