`

灰度直方图

阅读更多

图像的统计特性

图像的基本统计分析量如下:
1.熵

一个 X 值域为{x1, ..., xn}的随机变量的熵值 H 定义为:H(X)  =  \operatorname{E}(I(X))即熵的公式可以表示为:H(X) = \sum_{i=1}^n {p(x_i)\,I(x_i)} = -\sum_{i=1}^n {p(x_i) \log_b p(x_i)}

上式我们取集合X为图像灰度值构成的集合,这样我们就可以得到图像灰度的熵值


2.灰度平均值,灰度中值已经灰度方差都能很容易得到

3.直方图的计算


我们来看一个灰度图像,让n_i表示灰度i出现的次数,这样图像中灰度为i 的像素的出现概率是

p_x(i) = \frac{n_i}{n}, i\in {0,..., L - 1}

L 是图像中所有不同的灰度值,n 是图像中所有的像素数, p 实际上是图像的直方图,归一化到 0..1


4.图像的质量评价标准(其实就是误差估计)


在编写程序之前,我们应该了解图片格式以及相应文件数据结构,java语言默认支持jpg,png和gif三种图片格式(没有考证),如果我们要处理bmp图片格式的,我们必须对bmp图片解码提取出图片的特征以及每个像素的数据,然后在java类库的方法表示出图像,才能进行进一步的处理,换句话说就是将图片(这里指bmp)内存储像素转换到java语言的存储图片的像素。下面附上java语言读取转换bmp图片的程序

 

 

//BMPReader.java, from Mark Wutka
//Revised by Xie-Hua Sun


import java.awt.*;
import java.io.*;
import java.awt.image.*;

/**
 *This class provides a public static method that takes an InputStream to a Windows
 * .bmp file and converts it into an ImageProducer via a MemoryImageSource.
 *You can fetch a .bmp throough a URL with the following code:
 *URL url = new URL(<wherever your URL is>)
 *Image img= createImage(BMPReader.getBMPImage(url.openStream()));
 */
public class BMPReader extends Object 
{  
    //Constants indication how the data is stored
    public static final int BI_RGB  = 0;
    public static final int BI_RLE8 = 1;
    public static final int BI_RLE4 = 2;
  
    public static MemoryImageSource getBMPImage(FileInputStream stream) throws IOException
    {  	
	  	//DataInputStream allows you to read in 16 and 32 bit numbers
	  	DataInputStream in=new DataInputStream(stream);
	  	
	  	//Verify that the header starts with 'BM'
	  	if(in.read() != 'B')
	  	  throw new IOException("Not a .BMP file!");  	  
	  		  	
	  	if(in.read() != 'M')
	  	  throw new IOException("Not a .BMP file!");  	  
	  		  	
	  	//Get the total file size
	  	int fileSize = intelInt(in.readInt());
	  	
	  	//Skip the 2 16-bit reserved words
	  	in.readUnsignedShort();
	  	in.readUnsignedShort();
	  	
	  	int bitmapOffset = intelInt(in.readInt());
	  	
	  	int bitmapInfoSize = intelInt(in.readInt());
	  	
	  	int width  = intelInt(in.readInt());
	  	int height = intelInt(in.readInt());
	  	
	  	//Skip the 16-bit bitplane size
	  	in.readUnsignedShort();
	  	
	  	int bitCount = intelShort(in.readUnsignedShort());
	  	
	  	int compressionType = intelInt(in.readInt());
	  	
	  	int imageSize = intelInt(in.readInt());
	    
	    //Skip pixels per meter
	    in.readInt();
	    in.readInt();
	    
	    int colorsUsed      = intelInt(in.readInt());
	    int colorsImportant = intelInt(in.readInt());
	    if(colorsUsed == 0) colorsUsed = 1<<bitCount;
	    
	    int colorTable[] = new int[colorsUsed];
	    
	    //Read the bitmap's color table
	    for(int i = 0; i < colorsUsed; i++)
	        colorTable[i] = (intelInt(in.readInt())&0xffffff)+0xff000000;
	    	    
	    //Create space for the pixels
	  	int pixels[] = new int[width*height];
	  	//Read the pixels from the stream based on the compression type
	  	if(compressionType == BI_RGB)
	  	    if(bitCount == 24)
	  	  	    readRGB24(width,height,pixels,in);
	  	    else
	  	  	    readRGB(width,height,colorTable,bitCount,pixels,in);	  	    
	  	else if(compressionType == BI_RLE8)
	  	    readRLE(width,height,colorTable,bitCount,pixels,in,imageSize,8);	
	  	else if(compressionType == BI_RLE4)
	  	    readRLE(width,height,colorTable,bitCount,pixels,in,imageSize,4);	
	  		  	
	  	//Create a memory image source from the pixels
	  	System.out.println(pixels[0]+" "+pixels[1]+" "+pixels[pixels.length-2]);
	  	return new MemoryImageSource(width,height,pixels,0,width);
	}
  
    /*
     *Reads in pixels in 24-bit format. There is no color table, and the pixels are
     *stored in 3-byte pairs. Oddly, all windows bitmaps are stored upside - the 
     *bottom line is stored first.
     **/
    protected static void readRGB24(int width,int height,int pixels[],
                                    DataInputStream in) throws IOException
    {  	
	  	//start storing at the bottom of the array
	  	for(int h = height-1; h >= 0; h--)
	  	{
	  	    int pos = h*width;
	  	    for(int w = 0; w < width; w++)
	  	    {
		  	  	//Read in the red, green and blue components
		  	  	int red   = in.read();
		  	  	int green = in.read();
		  	  	int blue  = in.read();
		  	  	//Turn the red,green and blue values into an RGB color with an alpha value
		  	  	//of 255 (fully opaque)
		  	  	pixels[pos++] = 0xff000000+(red<<16)+(green<<8)+blue;
	  	    }
	  	}
    }
  
    //readRGB reads in pixels values that are stored uncompressed. The bits represent 
    //indices into the color table
    protected static void readRGB(int width,int height,int colorTable[], int bitCount, 
                                  int pixels[], DataInputStream in) throws IOException
    {  	
	  	//How many pixels can be stored in a byte?
	  	int pixelsPerByte = 8/bitCount;
	  	
	  	//A bit mask containing the number of bits in a pixel
	  	int bitMask = (1<<bitCount)-1;
	  	
	  	//The shift values that will move each pixel to the far right
	  	int bitShifts[] = new int[pixelsPerByte];
	  	
	  	for(int i = 0; i < pixelsPerByte; i++)
	  	    bitShifts[i] = 8-((i+1)*bitCount);  	    
  	
	  	int whichBit = 0;
	  	
	  	//Read in the first byte
	  	int currByte = in.read();
	  	
	  	//Start at the bottom of the pixel array and work up
	  	for(int h = height-1;h >= 0; h--)
	  	{
	  	    int pos = h*width;
	  	    for(int w = 0; w < width; w++)
	  	    {	  	  	
		  	  	//Get the next pixel from the current byte
		  	  	pixels[pos] = colorTable[(currByte>>bitShifts[whichBit])&bitMask];
		  	  	pos++;
		  	  	whichBit++;
		  	  	
		  	  	//If the current bit position is past the number of pixels in
		  	  	//a byte, you advance to the next byte
		  	  	if(whichBit >= pixelsPerByte)
		  	  	{
		  	  	    whichBit = 0;
		  	  	    currByte = in.read();
		  	  	}
	  	    }
	  	}
    }
  
    //readRLE reads run-length encoded data in either RLE4 or RLE8 format
    protected static void readRLE(int width,int height,int colorTable[],
                                int bitCount,int pixels[],DataInputStream in,
                                int imageSize,int pixelSize) throws IOException
    {
	  	int x = 0;
	  	int y = height-1;
	  	
	  	//You already know how many bytes are in the image, so only go through that many
	  	for(int i = 0; i < imageSize; i++)
	  	{
	  	    //RLE encoding is defined by two bytes	
	  	    int byte1 = in.read();
	  	    int byte2 = in.read();
	  	    i += 2;
	  	  
	  	    //If byte0==0, this is an escape code
	  	    if(byte1 == 0)
	  	    {
	  	  	    //If escaped, byte2==0 means you are at end of line
	  	  	    if(byte2 == 0)
	  	  	    {
	  	  	        x = 0; y--;
	  	  	        //If escaped, byte2==1 means end of bitmap
	  	  	    }
	  	  	    else if(byte2 == 1)
	  	  	    {
	  	  	        return;
	  	  	        //if escaped, byte2==2 adjusts the current x and y by
	  	  	        //an offset stored in the next two words
	  	  	    }
		  	  	else if(byte2 == 2)
		  	  	{
		  	  	    int xoff = (char)intelShort(in.readUnsignedShort());
		  	  	    i += 2;
		  	  	    int yoff = (char)intelShort(in.readUnsignedShort());
		  	  	    i += 2;
		  	  	    x += xoff;
		  	  	    y -= yoff;
		  	  	    //If escaped, any other value for byte 2 is the number of bytes
		  	  	    //that you should read as pixel values (these pixels are not 
		  	  	    //run-length encoded)
		  	  	}
		  	  	else
		  	  	{
		  	  	    int whichBit = 0;
		  	  	    //Read in the next byte
		  	  	    int currByte = in.read();
		  	  	    i++;
		  	  	    for(int j = 0; j < byte2; j++)
		  	  	    {
		  	  	  	    if(pixelSize == 4)
		  	  	  	    {
			  	  	  	    //The pixels are 4-bits,so half the time you shift the current byte
			  	  	  	    //to the right as the pixel value	
			  	  	  	    if(whichBit == 0){
			  	  	  	  	    pixels[y*width+x] = colorTable[(currByte>>4)&0xf];
			  	  	  	    }
			  	  	  	    else
			  	  	  	    {
			  	  	  	  	    //The rest of the time, you mask out the upper 4 bits, save the 
			  	  	  	  	    //pixel value, then read in the next byte
			  	  	  	  	    pixels[y*width+x] = colorTable[currByte&0xf];
			  	  	  	  	    currByte = in.read();
			  	  	  	  	    i++;
			  	  	  	    }
		  	  	  	    }
		  	  	  	    else
		  	  	  	    {
		  	  	  	        pixels[y*width+x] = colorTable[currByte];
		  	  	  	        currByte = in.read();
		  	  	  	        i++;
		  	  	  	    }
		  	  	  	    x++;
		  	  	  	    if(x >= width)
		  	  	  	    {
		  	  	  	        x = 0; y--;
		  	  	  	    }
		  	  	    }
	  	  	        //The pixels must be word-aligned, so if you read an unevel number of 
	  	  	        // bytes, read and ignore a byte to get aligned again
	  	  	        if((byte2&1) == 1)
	  	  	        {
	  	  	  	        in.read(); i++;
	  	  	        }
	  	        }
	  	        //If the first byte was not 0, it is the number of pixels that 
	  	        //are encoded by byte 2
	  	    }
	  	    else
	  	    {
		  	  	for(int j = 0;j < byte1; j++)
		  	  	{
		  	  	    if(pixelSize == 4)
		  	  	    {
		  	  	  	    //If j is odd, use the upper 4 bits
		  	  	  	    if((j&1) == 0)
		  	  	  	        pixels[y*width+x] = colorTable[(byte2>>4)&0xf];
		  	  	  	    else
		  	  	  	        pixels[y*width+x+1] = colorTable[byte2&0xf];		  	  	  	    
		  	  	    }
		  	  	    else
		  	  	  	    pixels[y*width+x+1] = colorTable[byte2];
		  	  	    
		  	  	    x++;
		  	  	    if(x >= width)
		  	  	    {
		  	  	  	    x = 0; y--;
		  	  	    }
		  	  	}
	  	    }
	    }
    }
  
    //intelShort converts a 16-bit number stored in intel byte order into
    //the local host format
    protected static int intelShort(int i)
    {
  	    return((i>>8)&0xff)+((i<<8)&0xff00);
    } 
  
    //intelInt converts a 32-bit number stored in intel byte order into
    //the local host format  转换成小端存储的机器整数
    protected static int intelInt(int i)
    {
  	    return((i&0xff)<<24)+((i&0xff00)<<8)+((i&0xff0000)>>8)+((i>>24)&0xff);
    }
}
  
  

 

 程序中提供了一个静态方法getBMPImage将bmp图片的数据转换生成MemoryImageSource对象,这样就可以用来构造java的Image对象。查看相应文档,同样可以编写出其他图片格式的读取程序。然后将getBMPImage返回的MemoryImageSource对象传给createImage函数就生成了一个Image(jpg,png,gif可以直接构造Image),下面就进行统计直方图的处理:1.将刚才得到的Image对象传给grabber函数得到对应的像素数组

 

public int[] grabber(Image im, int iw, int ih)
	{
		int [] pix = new int[iw * ih];
		try
		{
		    PixelGrabber pg = new PixelGrabber(im, 0, 0, iw,  ih, pix, 0, iw);
		    pg.grabPixels();
		    System.out.println(pix[0]+" "+pix[1]);
		}
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		}	
		return pix;
	}

 2.根据1.中返回的像素数组统计不同灰度值的频率,如果对于一张本身就是灰度图像(8位灰度图像)来说,他的像素值就是它的灰度值(这就是我们程序采用的方法,也就是说我们处理的黑白图的灰度),如果是一张彩色图像,则它的灰度值需要经过函数映射来得到。灰度图像是由纯黑和纯白来过渡得到的,在黑色中加入白色就得到灰色,纯黑和纯白按不同的比例来混合就得到不同的灰度值。

public int[] getHist(int[] pix, int iw, int ih)
	{	
		int[] hist = new int[256];		
		for(int i = 0; i < iw*ih; i++) 
		{
			int grey = pix[i]&0xff;
			hist[grey]++;			
		}
		return hist;
	}

3.将灰度值频率显示出来

//显示直方图
    public void draw(Graphics g, int[] h, int max)
    {
    	g.clearRect(270, 0, 530, 350);    	
    	g.drawLine(270, 306, 525, 306); //x轴
    	g.drawLine(270, 50,  270, 306); //y轴
    	for(int i = 0; i < 256; i++)
    	    g.drawLine(270+i, 306, 270+i, 306-h[i]);
    	g.drawString(""+max, 275, 60);
       	g.drawString("直方图", 370, 320);
    }  

这是效果图:

其实灰度直方图反应了图片灰度值的一个分布情况,只是一个统计特性,并没有具体像素,但是我们可以从中得到图像的明亮程度,可以根据直方图知道我们照的照片的曝光程度,如果照片阴暗在低值的频率很大,过分曝光则在高灰度的频率很大。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 大小: 35.1 KB
1
5
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics