1、灰度与二值化
二值化(Binarization)是图像分割的一种最简单的方法。 二值化可以把灰度图像转换成二值图像。 把大于某个临界灰度值的像素灰度设为灰度極大值,把小于这个值的像素灰度设为灰度極小值,从而实现二值化。 根据阈值选取的不同,二值化的算法分为固定阈值和自适应阈值。
在RGB模型中,当R=G=B时,则彩色表示一种灰度颜色,其中R=G=B的值叫灰度值(又称强度值、亮度值),灰度范围为0-255。
灰度图像由不同灰度值的像素组成,灰度图像中灰度值的分布是该图像的一个重要特征。
图像的灰度直方图就描述了图像中灰度值分布情况,能够很直观的展示出图像中各个灰度值所占的多少。图像的灰度直方图是灰度值的函数,描述的是图像中具有该灰度值的像素的个数:其中,横坐标是灰度级,纵坐标是该灰度级出现的频率。
public static BufferedImage grayImage(BufferedImage bufferedImage) throws Exception {
int width = bufferedImage.getWidth();
int height = bufferedImage.getHeight();
BufferedImage grayBufferedImage = new BufferedImage(width, height, bufferedImage.getType());
for (int i = 0; i < bufferedImage.getWidth(); i++) {
for (int j = 0; j < bufferedImage.getHeight(); j++) {
final int color = bufferedImage.getRGB(i, j);
final int r = (color >> 16) & 0xff;
final int g = (color >> 8) & 0xff;
final int b = color & 0xff;
int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);
int newPixel = colorToRGB(255, gray, gray, gray);
grayBufferedImage.setRGB(i, j, newPixel);
}
}
return grayBufferedImage;
}
/**
* 颜色分量转换为RGB值
*
* @param alpha
* @param red
* @param green
* @param blue
* @return
*/
private static int colorToRGB(int alpha, int red, int green, int blue) {
int newPixel = 0;
newPixel += alpha;
newPixel = newPixel << 8;
newPixel += red;
newPixel = newPixel << 8;
newPixel += green;
newPixel = newPixel << 8;
newPixel += blue;
return newPixel;
}
public static BufferedImage binaryImage(BufferedImage image) throws Exception {
int w = image.getWidth();
int h = image.getHeight();
float[] rgb = new float[3];
double[][] zuobiao = new double[w][h];
int black = new Color(0, 0, 0).getRGB();
int white = new Color(255, 255, 255).getRGB();
BufferedImage bi= new BufferedImage(w, h,
BufferedImage.TYPE_BYTE_BINARY);;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
int pixel = image.getRGB(x, y);
rgb[0] = (pixel & 0xff0000) >> 16;
rgb[1] = (pixel & 0xff00) >> 8;
rgb[2] = (pixel & 0xff);
float avg = (rgb[0]+rgb[1]+rgb[2])/3;
zuobiao[x][y] = avg;
}
}
//这里是阈值,白底黑字还是黑底白字,大多数情况下建议白底黑字,后面都以白底黑字为例
double SW = 192;
for (int x = 0; x < w; x++) {
for (int y = 0; y < h; y++) {
if (zuobiao[x][y] < SW) {
bi.setRGB(x, y, black);
}else{
bi.setRGB(x, y, white);
}
}
}
return bi;
}
2、降噪
采用邻域平均法的均值滤波器非常适用于去除通过扫描得到的图像中的颗粒噪声。 领域平均法有力地抑制了噪声,同时也由于平均而引起了模糊现象,模糊程度与邻域半径成正比。 几何均值滤波器所达到的平滑度可以与算术均值滤波器相比,但在滤波过程中会丢失更少的图像细节。
/**
* 降噪,以1个像素点为单位(实际使用中可以循环降噪,或者把单位可以扩大为多个像素点)
* @param image
* @return
*/
public static BufferedImage denoise(BufferedImage image){
int w = image.getWidth();
int h = image.getHeight();
int white = new Color(255, 255, 255).getRGB();
if(isWhite(image.getRGB(1, 0)) && isWhite(image.getRGB(0, 1)) && isWhite(image.getRGB(1, 1))){
image.setRGB(0,0,white);
}
if(isWhite(image.getRGB(w-2, 0)) && isWhite(image.getRGB(w-1, 1)) && isWhite(image.getRGB(w-2, 1))){
image.setRGB(w-1,0,white);
}
if(isWhite(image.getRGB(0, h-2)) && isWhite(image.getRGB(1, h-1)) && isWhite(image.getRGB(1, h-2))){
image.setRGB(0,h-1,white);
}
if(isWhite(image.getRGB(w-2, h-1)) && isWhite(image.getRGB(w-1, h-2)) && isWhite(image.getRGB(w-2, h-2))){
image.setRGB(w-1,h-1,white);
}
for(int x = 1; x < w-1; x++){
int y = 0;
if(isBlack(image.getRGB(x, y))){
int size = 0;
if(isWhite(image.getRGB(x-1, y))){
size++;
}
if(isWhite(image.getRGB(x+1, y))){
size++;
}
if(isWhite(image.getRGB(x, y+1))){
size++;
}
if(isWhite(image.getRGB(x-1, y+1))){
size++;
}
if(isWhite(image.getRGB(x+1, y+1))){
size++;
}
if(size>=5){
image.setRGB(x,y,white);
}
}
}
for(int x = 1; x < w-1; x++){
int y = h-1;
if(isBlack(image.getRGB(x, y))){
int size = 0;
if(isWhite(image.getRGB(x-1, y))){
size++;
}
if(isWhite(image.getRGB(x+1, y))){
size++;
}
if(isWhite(image.getRGB(x, y-1))){
size++;
}
if(isWhite(image.getRGB(x+1, y-1))){
size++;
}
if(isWhite(image.getRGB(x-1, y-1))){
size++;
}
if(size>=5){
image.setRGB(x,y,white);
}
}
}
for(int y = 1; y < h-1; y++){
int x = 0;
if(isBlack(image.getRGB(x, y))){
int size = 0;
if(isWhite(image.getRGB(x+1, y))){
size++;
}
if(isWhite(image.getRGB(x, y+1))){
size++;
}
if(isWhite(image.getRGB(x, y-1))){
size++;
}
if(isWhite(image.getRGB(x+1, y-1))){
size++;
}
if(isWhite(image.getRGB(x+1, y+1))){
size++;
}
if(size>=5){
image.setRGB(x,y,white);
}
}
}
for(int y = 1; y < h-1; y++){
int x = w - 1;
if(isBlack(image.getRGB(x, y))){
int size = 0;
if(isWhite(image.getRGB(x-1, y))){
size++;
}
if(isWhite(image.getRGB(x, y+1))){
size++;
}
if(isWhite(image.getRGB(x, y-1))){
size++;
}
//斜上下为空时,去掉此点
if(isWhite(image.getRGB(x-1, y+1))){
size++;
}
if(isWhite(image.getRGB(x-1, y-1))){
size++;
}
if(size>=5){
image.setRGB(x,y,white);
}
}
}
//降噪,以1个像素点为单位
for(int y = 1; y < h-1; y++){
for(int x = 1; x < w-1; x++){
if(isBlack(image.getRGB(x, y))){
int size = 0;
//上下左右均为空时,去掉此点
if(isWhite(image.getRGB(x-1, y))){
size++;
}
if(isWhite(image.getRGB(x+1, y))){
size++;
}
//上下均为空时,去掉此点
if(isWhite(image.getRGB(x, y+1))){
size++;
}
if(isWhite(image.getRGB(x, y-1))){
size++;
}
//斜上下为空时,去掉此点
if(isWhite(image.getRGB(x-1, y+1))){
size++;
}
if(isWhite(image.getRGB(x+1, y-1))){
size++;
}
if(isWhite(image.getRGB(x+1, y+1))){
size++;
}
if(isWhite(image.getRGB(x-1, y-1))){
size++;
}
if(size>=8){
image.setRGB(x,y,white);
}
}
}
}
return image;
}
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 isBlack(int colorInt, int whiteThreshold) {
final Color color = new Color(colorInt);
if (color.getRed() + color.getGreen() + color.getBlue() <= whiteThreshold) {
return 1;
}
return 0;
}
3、膨胀与腐蚀
*图像的膨胀(dilation)和腐蚀(erosion)是两种基本的形态学运算,主要用来寻找图像中的极大区域和极小区域.
其中膨胀类似与 ‘领域扩张’ ,将图像的高亮区域或白色部分进行扩张,其运行结果图比原图的高亮区域更大.
*该公式表示用B来对图像A进行膨胀处理,其中B是一个卷积模板或卷积核,其形状可以为正方形或圆形,通过模板B与图像A进行卷积计算,扫描图像中的每一个像素点,用模板元素与二值图像元素做“与”运算,如果都为0,那么目标像素点为0,否则为1。从而计算B覆盖区域的像素点最大值,并用该值替换参考点的像素值实现膨胀。下图是将左边的原始图像A膨胀处理为右边的效果图A⊕B。
腐蚀类似 ‘领域被蚕食’ ,将图像中的高亮区域或白色部分进行缩减细化,其运行结果图比原图的高亮区域更小.**
该公式表示图像A用卷积模板B来进行腐蚀处理,通过模板B与图像A进行卷积计算,得出B覆盖区域的像素点最小值,并用这个最小值来替代参考点的像素值。如图所示,将左边的原始图像A腐蚀处理为右边的效果图A-B。
处理结果:
/**
* 图像辅助类
* @author Administrator
*
*/
public class Image_Utility {
///结构元素
private static int sData[]={
0,0,0,
0,1,0,
0,1,1
};
/**
* 图像的开运算: 先腐蚀再膨胀
* @param sourceImage 此处处理灰度图像或者二值图像
* @param shreshold :阈值————当膨胀结果小于阈值时,仍然设置图像位置的值为0;而进行腐蚀操作时,
* 当灰度值大于等于阈值(小于阈值)时并且结构元素为1(0)时,才认为对应位置匹配上;
* 如果为二值图像,则应该传入1。
* @return
*/
public static int[][] open(int [][]source,int threshold){
int width=source[0].length;
int height=source.length;
int[][] result=new int[height][width];
///先腐蚀运算
result=correde(source, threshold);
///后膨胀运算
result=dilate(result, threshold);
/*for(int j=0;j<height;j++){
for(int i=0;i<width;i++){
System.out.print(result[j][i]+",");
}
System.out.println();
}
*/
return result;
}
/**
* 腐蚀运算
* @param source
* @param shreshold 当灰度值大于阈值(小于阈值)时并且结构元素为1(0)时,才认为对应位置匹配上;
* @return
*/
private static int[][] correde(int[][] source,int threshold){
int width=source[0].length;
int height=source.length;
int[][] result=new int[height][width];
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
///边缘不进行操作,边缘内才操作
if(i>0&&j>0&&i<height-1&&j<width-1){
int max =0;
///对结构元素进行遍历
for(int k=0;k<sData.length;k++){
int x=k/3;///商表示x偏移量
int y=k%3;///余数表示y偏移量
if(sData[k]!=0){
///不为0时,必须全部大于阈值,否则就设置为0并结束遍历
if(source[i-1+x][j-1+y]>=threshold){
if(source[i-1+x][j-1+y]>max){
max=source[i-1+x][j-1+y];
}
}else{
与结构元素不匹配,赋值0,结束遍历
max=0;
break;
}
}
}
此处可以设置阈值,当max小于阈值的时候就赋为0
result[i][j]=max;
}else{
///直接赋值
result[i][j]=source[i][j];
}///end of the most out if-else clause .
}
}///end of outer for clause
return result;
}
/**
* 膨胀运算
* @param source
* @param threshold 当与运算结果值小于阈值时,图像点的值仍然设为0
* @return
*/
private static int[][] dilate(int[][] source,int threshold){
int width=source[0].length;
int height=source.length;
int[][] result=new int[height][width];
for(int i=0;i<height;i++){
for(int j=0;j<width;j++){
///边缘不进行操作
if(i>0&&j>0&&i<height-1&&j<width-1){
int max =0;
///对结构元素进行遍历
for(int k=0;k<sData.length;k++){
int x=k/3;///商表示x偏移量
int y=k%3;///余数表示y偏移量
if(sData[k]!=0){
///当结构元素中不为0时,取出图像中对应各项的最大值赋给图像当前位置作为灰度值
if(source[i-1+x][j-1+y]>max){
max=source[i-1+x][j-1+y];
}
}
}
此处可以设置阈值,当max小于阈值的时候就赋为0
if(max<threshold){
result[i][j]=0;
}else{
result[i][j]=max;
}
// result[i][j]=max;
}else{
///直接赋值
result[i][j]=source[i][j];
}
}
}
return result;
}
/**
* 灰度图像提取数组
* @param image
* @return int[][]数组
*/
public static int[][] imageToArray(BufferedImage image){
int width=image.getWidth();
int height=image.getHeight();
int[][] result=new int[height][width];
for(int j=0;j<height;j++){
for(int i=0;i<width;i++){
int rgb=image.getRGB(i, j);
int grey=(rgb>>16)&0xFF;
// System.out.println(grey);
result[j][i]=grey;
}
}
return result ;
}
/**
* 数组转为灰度图像
* @param sourceArray
* @return
*/
public static BufferedImage arrayToGreyImage(int[][] sourceArray){
int width=sourceArray[0].length;
int height=sourceArray.length;
BufferedImage targetImage=new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for(int j=0;j<height;j++){
for(int i=0;i<width;i++){
int greyRGB=sourceArray[j][i];
int rgb=(greyRGB<<16)|(greyRGB<<8)|greyRGB;
targetImage.setRGB(i, j, rgb);
}
}
return targetImage;
}
}```