图片的灰度化与二值化

为了提高Tesseract识别的准确性,需要对图片进行一些处理。

灰度化

RGB颜色模型

一种加色模型,将红(Red)、绿(Green)、蓝(Blue)三原色的色光以不同的比例相加,以产生多种多样的色光,且三原色的红绿蓝不可能用其他单色光合成。

RGB色彩模式使用RGB模型为图像中每个像素的RGB分量分配一个0~255范围内的强度值。RGB图像仅仅使用三种颜色,R(red)、G(green)、B(blue),就能够使它们依照不同的比例混合,在屏幕上呈现16777216(256 * 256 * 256)种颜色。

RGB模型

什么是灰度化

在RGB模型中,如果R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值,因此,灰度图像每个像素只需一个字节存放灰度值(又称强度值、亮度值),灰度范围为0-255。0%的灰度RGB数值是255,255,255;1%灰度的RGB数值是253,253,253;2%灰度RGB值为250,250,250。

灰度图像与黑白图像不同,在计算机图像领域中黑白图像只有黑白两种颜色,灰度图像在黑色与白色之间还有许多级的颜色深度。

灰度化的方法

约定
f(i,j) 为二维图片中坐标为 (i,j) 的点的灰度值,R(i,j)、G(i,j)、B(i,j)分别为坐标为 (i,j) 的点在red、green、bule分量上的值。

1. 分量法

将彩色图像中的三分量的亮度作为三个灰度图像的灰度值,可根据应用需要选取一种灰度图像。

    f1(i,j)=R(i,j)
    f2(i,j)=G(i,j)
    f3(i,j)=B(i,j)
2. 最大值法

将彩色图像中的三分量亮度的最大值作为灰度图的灰度值。

    f(i,j) = max(R(i,j),G(i,j),B(i,j))
3. 平均值法

将彩色图像中的三分量亮度求平均得到一个灰度图。

    f(i,j) = (R(i,j)+G(i,j)+B(i,j))/3
4. 加权平均法

根据重要性及其它指标,将三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感最高,对蓝色敏感最低,因此,按下式对RGB三分量进行加权平均能得到较合理的灰度图像。

    f(i,j)=0.30*R(i,j)+0.59*G(i,j)+0.11*B(i,j)

二值化

二值化(Thresholding)是图像分割的一种最简单的方法。二值化可以把灰度图像转换成二值图像。把大于某个临界灰度值的像素灰度设为灰度极大值,把小于这个值的像素灰度设为灰度极小值,从而实现二值化。

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

OTSU算法

用了OTSU算法来实现二值化,OTSU(大津法)是属于最大类间方差法,它是自适应计算单阈值的简单高效方法。

实现思路

  1. 统计灰度值的直方分布
  2. 分别计算背景图像、前景图像的平均灰度、背景图像像素数所占比例
  3. 遍历0~255各灰阶,计算并寻找类间方差极大值所对应灰度值

计算公式的推导:

  • 记i为前景与背景的分割阈值;
  • 前景点数占图像比例为w0,平均灰度为u0;
  • 背景点数占图像比例为w1,平均灰度为u1。
  • 图像的总平均灰度为:u=w0*u0+w1*u1。
  • 从最小灰度值到最大灰度值遍历i,当i使得值g=w0*(u0-u)^2+w1*(u1-u)^2 最大时i即为分割的最佳阈值。
  • 利用w0+w1=1,u=w0*u0+w1*u1可推导出等价公式:g=w0*w1*(u0-u1)^2。

编程实现

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

public class SolvePicture {

    public static void grayAndThresholding(String filename) throws IOException {
        File file = new File(filename);
        BufferedImage image = ImageIO.read(file);

        int w = image.getWidth();
        int h = image.getHeight();

        //灰度化
        int[][] gray = new int[w][h];
        for (int x = 0; x < w; x++) {
            for (int y = 0; y < h; y++) {
                gray[x][y] = getGray(image.getRGB(x, y));
            }
        }

        // 二值化
        int threshold = getThreshold(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]);
            }
        }

        File destfile = new File(filename+"-result.jpg");
        if (destfile.exists()){
            destfile.delete();
        }
        ImageIO.write(binaryBufferedImage, "jpg", destfile);
        System.out.println("ok");
    }

    //计算该点的灰度值
    public static int getGray(int rgb){
        Color color = new Color(rgb);
        //加权平均法
        return (int)(0.30*color.getRed()+0.59*color.getGreen()+0.11*color.getBlue());
    }

    //利用ostu算法获取分割的阀值
    public static int getThreshold(int gray[][],int w,int h){
        int threshold = 0;
        int[] hisData = new int[256];
        int graySum = 0;        //所有点的灰度值之和
        int x0 = 0, x1 = 0;     //前、后景点数
        int sum0 = 0, sum1 = 0; //前、后景点的平均灰度
        double w0 = 0, w1 = 0;  //前、后景点出现的概率
        double u0 = 0, u1 = 0;  //前、后景点的平均灰度
        double varMax = 0;      //函数的最大值
        int totalPoints = w * h;//所有像素点的个数

        //依次遍历每个点去统计灰度的分布,同时统计所有点的灰度值之和
        for(int i = 0; i < w; i++){
            for(int j = 0; j < h; j++){
                int graynum = gray[i][j];
                graySum += graynum;
                hisData[graynum]++;
            }
        }

        for(int i = 0; i < 256; i++){
            sum0 += i * hisData[i];
            x0 += hisData[i];
            if(x0 == 0) //无法分割
                continue;
            x1 = totalPoints - x0;
            sum1 = graySum - sum0;
            if(x1 == 0) //所有情况已经计算完
                break;

            w0 = (1.0 * x0) / totalPoints;
            w1 = 1 - w0;
            u0 = (1.0 * sum0) / x0;
            u1 = (1.0 * sum1) / x1;
            //带入公式
            double temp = w0*w1*(u0-u1)*(u0-u1);
            if(temp > varMax){
                varMax = temp;
                threshold = i;
            }
        }
        return threshold;
    }
}

测试

处理前后图片对比:
这里写图片描述

写在最后

关于对图像的处理,还有很多其他操作,请听下回分解。

猜你喜欢

转载自blog.csdn.net/ldx19980108/article/details/81254930