您的位置:首页 > 其它

图像的采样与量化及灰度直方图

2015-01-10 00:00 513 查看

图像的采样

采样是把空域上或时域上连续的图像(模拟图像)转换成离散采样点(像素)集合(数字图像)的操作。

采样越细,像素越小,越能精细地表现图像。不同采样间距的效果如下:







a.采样间隔16 b.采样间隔32 c.采样间隔64

图1

算法源代码1(java):

/**
* 对图像进行采样
*
* @param pix
*            保存图片像素
* @param iw
*            二维像素矩阵的宽
* @param ih
*            二维像素矩阵的高
* @param grey
*            采样间距
* @return
*/
private static int[] sample(int[] pix, int iw, int ih, int grey) {
// 对图像进行采样
ColorModel cm = ColorModel.getRGBdefault();

int d = (int) (256 / grey); // 采样间隔
int dd = d * d;
for (int i = 0; i < ih; i = i + d) {
for (int j = 0; j < iw; j = j + d) {
int r = 0, g = 0, b = 0;
for (int k = 0; k < d; k++)
for (int l = 0; l < d; l++) {
r = r + cm.getRed(pix[(i + k) * iw + (j + l)]);
g = g + cm.getGreen(pix[(i + k) * iw + (j + l)]);
b = b + cm.getBlue(pix[(i + k) * iw + (j + l)]);
}
r = (int) (r / dd);
g = (int) (g / dd);
b = (int) (b / dd);
for (int k = 0; k < d; k++)
for (int l = 0; l < d; l++)
// pix[(i+k)*iw+(j+l)] = 255<<24|r<<16|g<<8|b;
pix[(i + k) * iw + (j + l)] = new Color(r, g, b)
.getRGB();
}
}
return pix;
}


图像的量化

量化是把像素的灰度(浓淡)变换成离散的整数值的操作。最简单的量化是用黑(0)白(255)两个数值(即2级)来表示,成为二值图像。

量化越细致,灰度级数(浓淡层次)表现越丰富。计算机中一般用8bit(256级)来量化,这意味着像素的灰度(浓淡)是0—255之间的数值。化级数的效果图如下:







a.量化级数2 b.量化级数8 c.量化级数64
图2

算法源代码2(java):

/**
* 对图像进行量化
* @param srcPath 原图像文件路径
* @param distPath 目标图像文件路径
* @param grey 量化的级数
*/
public static void quantize(String srcPath, String distPath, int grey) {
OutputStream out = null;
try {
BufferedImage img = ImageIO.read(new File(srcPath));
int imgType = img.getType();
int w = img.getWidth();
int h = img.getHeight();
int pix[] = new int[w*h];
img.getRGB(0, 0, w, h, pix, 0, w);
int greyScope = 256/grey;
int r,g,b,temp;
r=b=g=temp=0;
ColorModel cm=ColorModel.getRGBdefault();
for(int i=0; i<w*h; i++) {
r = cm.getRed(pix[i]);
temp = r/greyScope;
r = temp*greyScope;
g = cm.getGreen(pix[i]);
temp = g/greyScope;
g = temp*greyScope;
b = cm.getBlue(pix[i]);
temp = b/greyScope;
b = temp*greyScope;
pix[i] = new Color(r, g, b).getRGB();
}
out = new FileOutputStream(distPath);
BufferedImage imgOut = new BufferedImage(w, h, imgType);
imgOut.setRGB(0, 0, w, h, pix, 0, w);
ImageIO.write(imgOut, "jpg", out);
System.out.println("test");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


图像直方图

直方图的定义

灰度直方图(histogram)是灰度级分布的函数,它表示图象中具有每种灰度级的象素的个数,反映图象中每种灰度出现的频率。灰度直方图的横坐标是灰度级,纵坐标是该灰度级出现的频率,是图象的最基本的统计特征。

生成图像灰度直方图的一般步骤:

1、统计各个灰度值的像素个数;

2、根据统计表画出直方图

如下图



图3



图4

算法源代码3:见下面算法源代码4中的drawHistogram()方法

灰度直方图的性质

1、只反映该图像中不同灰度值出现的次数(或频率),而不能反映某一灰度值像素所在的位置;

2、任何一张图像能唯一地确定一个与它对应的直方图,而一个直方图可以有多个不同的图像;

3、如果一张图片被剪裁成多张图片,各个子图的直方图之和就是这个全图的直方图。

直方图的用途

直方图有很多的用途,比如阀值分割,图像增强,还常常用于医疗影像。

图像的阀值(二值)处理

阀值处理的定义

图像的阀值处理是将图像的像素灰度值在某个定值(设为t)以上的点赋值为白色(或黑色),在这个定值t以下的点赋值为黑色(或白色)的处理过程。用公式表示成:



由于图像的阀值处理得到的是只有两个灰度值的二值图像,所以也将阀值处理称作二值化处理,得到的图像叫二值图像。如下图

图像阀值处理的步骤

1、 画出图像的灰度直方图;

2、 根据图像的灰度直方图确定阀值t,如图5的阀值为65;

3、 将像素灰度值小于等于t的点赋值为白色(或黑色),大于t的点赋值为黑色(或白色);效果如图6:



图5



图6

算法源代码4(java):

package cn.edu.jxau.luoweifu;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.File;
import java.io.IOException;
import java.util.Date;

import javax.imageio.ImageIO;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

/**
* 设置头像
* @author 罗伟富
*
*/
public class Histogram extends JFrame implements ChangeListener, ActionListener{
/**
* 头像界面的宽度
*/
public static final int WIDTH = 680;
/**
* 头像界面的高度
*/
public static final int HEIGHT = 400;
/**
* 显示图片版面的宽度
*/
public static final int PWIDTH = 350;
/**
* 显示图片版面的高度
*/
public static final int PHEIGHT = 300;
/**
* 直方图版面的宽度
*/
public static final int H_WIDTH = 350;
/**
* 直方图片版面的高度
*/
public static final int H_HEIGHT = 300;
/**
* 滑块的最大值
*/
public static final int JS_MAXIMUM = 100;

public static final String FRAMTITLE = "图像的灰度直方图";
private String imgSrc;
Image img;
JSlider jsliderH, jsliderV;	//水平和垂直滑块
JPanel uP, picP, uplodP, histP;
JButton openFile, histogram, threshold;
MyCanvas canvas;
Canvas histCanvas;
int imgW = PWIDTH, imgH = PHEIGHT;
int xcentre = PWIDTH/2, ycentre = PHEIGHT/2;
private int dx1 = xcentre-imgW/2, dy1 = ycentre-imgH/2, dx2 = xcentre + imgW/2, dy2 = ycentre + imgH/2;
private int sx1 = 0, sy1 = 0, sx2, sy2;
private float shx = 0, shy = 0;
/**
* 构造函数
*/
public Histogram() {
setTitle(FRAMTITLE);
launchDialog();
}

/**
* 返回canvas
* @return
*/
public Canvas getCanvas() {
return canvas;
}
/**
* 界面设计
*/
private void launchDialog() {
//初始化图片对象
imgSrc = "F:\\image processing\\baboom2_gray.jpg";
img = Toolkit.getDefaultToolkit().getImage(imgSrc);
//初始化组件
canvas = new MyCanvas();
jsliderH = new JSlider();
jsliderH.setMaximum(JS_MAXIMUM);
jsliderH.setValue(JS_MAXIMUM/2);
jsliderH.setMinimum(1);
jsliderH.setOrientation(JSlider.HORIZONTAL);
jsliderH.addChangeListener(this);
jsliderV = new JSlider();
jsliderV.setMaximum(JS_MAXIMUM);
jsliderV.setValue(JS_MAXIMUM/2);
jsliderV.setMinimum(1);
jsliderV.setOrientation(JSlider.VERTICAL);
jsliderV.addChangeListener(this);
picP = new JPanel();
picP.setPreferredSize(new Dimension(PWIDTH, PHEIGHT));
//picP.setBackground(Color.green);
uP = new JPanel();
uplodP = new JPanel();
openFile = new JButton("打开图片");
histogram = new JButton("显示直方图");
threshold = new JButton("显示二值图像");
openFile.addActionListener(this);
histogram.addActionListener(this);
threshold.addActionListener(this);
//添加组件
picP.setLayout(new BorderLayout());
picP.add(canvas, BorderLayout.CENTER);
uP.setLayout(new BorderLayout());
uP.add(picP, BorderLayout.CENTER);
uP.add(jsliderH, BorderLayout.SOUTH);
uP.add(jsliderV, BorderLayout.EAST);

histCanvas = new Canvas();
histP = new JPanel(new BorderLayout());
histP.add(histCanvas);
histP.setPreferredSize(new Dimension(H_WIDTH, H_HEIGHT));
histP.setBorder(new LineBorder(Color.blue));
//System.out.println("w:" + histP.getWidth() + "  h:" + histP.getHeight() + " " + histP.HEIGHT);

uplodP.setLayout(new FlowLayout());
uplodP.add(openFile);
uplodP.add(histogram);
uplodP.add(threshold);
Container c = getContentPane();
c.setLayout(new BoxLayout(c, BoxLayout.Y_AXIS));
//c.add(uP);
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS));
p.add(uP);
p.add(histP);
c.add(p);
c.add(uplodP);
setSize(WIDTH, HEIGHT);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setVisible(true);
}

public void drawHistogram() {
try {
BufferedImage bfImg = ImageIO.read(new File(imgSrc));
int w = bfImg.getWidth();
int h = bfImg.getHeight();
int pix[] = new int[w*h];
int hist[] = new int[256];
/*for(int i=0; i<hist.length; i++) {
hist[i] = 0;
}*/
int imgType = bfImg.getType();
int temp;
bfImg.getRGB(0, 0, w, h, pix, 0, w);
ColorModel cm = ColorModel.getRGBdefault();
for(int i=0; i<pix.length; i++) {
/*for(int j=0; j<hist.length; j++) {
if(j ==  cm.getRed(pix[i])) {
hist[j] ++;
}
}*/
temp = cm.getRed(pix[i]);
hist[temp] ++;
}
//System.out.println(hist.length);

int max = 0;
for(int i=0; i<hist.length; i++) {
if(hist[i] > max) {
max = hist[i];
}

}
for(int i=0; i<hist.length; i++) {
hist[i] = (int)(hist[i]/(float)max * 250);
/*System.out.print(hist[i] + "\t");
if(i%10 == 0) {
System.out.println();
}*/
}
//histCanvas.setHistPix(hist);
//histCanvas.repaint();

Graphics g = histCanvas.getGraphics();
Color c = g.getColor();
g.setColor(Color.red);
g.drawLine(10, H_HEIGHT-10, H_WIDTH-30, H_HEIGHT-10);
g.drawLine(H_WIDTH-35, H_HEIGHT-15, H_WIDTH-30, H_HEIGHT-10);
g.drawLine(H_WIDTH-35, H_HEIGHT-5, H_WIDTH-30, H_HEIGHT-10);
g.drawString("灰度级", H_WIDTH-80, H_HEIGHT);
g.drawLine(10,  H_HEIGHT-10, 10, 10);
g.drawLine(5, 15, 10, 10);
g.drawLine(15, 15, 10, 10);
g.drawString("像素个数", 15, 15);
g.setColor(Color.black);
for(int i=0; i<hist.length; i++) {
g.drawLine(10+i, H_HEIGHT-10, 10+i, H_HEIGHT-10-hist[i]);
if(i%30 == 0) {
g.drawString(i+"", 10+i, H_HEIGHT);
}
}
g.setColor(c);
} catch (IOException e) {
e.printStackTrace();
}
}

public void threshold(int threshold) {
try {
BufferedImage bfImg = ImageIO.read(new File(imgSrc));
int w = bfImg.getWidth();
int h = bfImg.getHeight();
int pix[] = new int[w*h];

int imgType = bfImg.getType();
bfImg.getRGB(0, 0, w, h, pix, 0, w);
int max = 0;
ColorModel cm = ColorModel.getRGBdefault();
for(int i=0; i<pix.length; i++) {
if(cm.getRed(pix[i]) <= threshold) {
pix[i] = new Color(255,255,255).getRGB();
} else {
pix[i] = new Color(0, 0, 0).getRGB();
}
}
bfImg.setRGB(0, 0, w, h, pix, 0, w);

//histCanvas.setHistPix(hist);
//histCanvas.repaint();

Graphics g = histCanvas.getGraphics();
g.clearRect(0, 0, H_WIDTH, H_HEIGHT);
Color c = g.getColor();
g.drawImage(bfImg, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, this);
g.setColor(c);
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 事件监听响应
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource() == openFile) {
FileDialog openFileDialog = new FileDialog(this, "打开图片");
openFileDialog.setMode(FileDialog.LOAD);	//设置此对话框为从文件加载内容
openFileDialog.setFile("*.jpg;*.jpeg;*.gif;*.png;*.bmp;*.tif;");		//设置可打开文件的类型为:.txt,.java

openFileDialog.setVisible(true);
String fileName = openFileDialog.getFile();
String directory = openFileDialog.getDirectory();
if(null != fileName) {
imgSrc = directory + fileName;
img = Toolkit.getDefaultToolkit().getImage(imgSrc);
histCanvas.repaint();
} else {
JOptionPane.showMessageDialog(this, "您已经取消选择了,请重新选择!");
}
} else if(e.getSource() == histogram) {
System.out.println(new Date());
drawHistogram();
System.out.println(new Date());
} else if(e.getSource() == threshold) {
threshold(65);
}
}
/**
* 滑动条滑动响应事件
*/
public void stateChanged(ChangeEvent e) {
if(e.getSource() == jsliderH) {
float valueH = jsliderH.getValue();
imgW = (int)(2*PWIDTH*(valueH/JS_MAXIMUM));
if(imgW < PWIDTH/4) {
imgW = PWIDTH/4;
}
dx1 = xcentre-imgW/2;
dy1 = ycentre-imgH/2;
dx2 = xcentre + imgW/2;
dy2 = ycentre + imgH/2;
canvas.repaint();
} else if(e.getSource() == jsliderV) {
float valueV = jsliderV.getValue();
imgH = (int)(2*PHEIGHT*(valueV/JS_MAXIMUM));
if(imgH < PHEIGHT/4) {
imgH = PHEIGHT/4;
}
dx1 = xcentre-imgW/2;
dy1 = ycentre-imgH/2;
dx2 = xcentre + imgW/2;
dy2 = ycentre + imgH/2;
canvas.repaint();
}
}

public static void main(String[] args) {
new Histogram();
}

/**
* 用于画图像的Canvas
*/
class MyCanvas extends Canvas {
public MyCanvas() {

}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
//g.drawImage(img, xcentre-imgW/2, ycentre-imgH/2, imgW, imgH, this);
sx2  = img.getWidth(this);
sy2  = img.getHeight(this);
g2.shear(shx, shy);
g2.drawImage(img, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, this);//Color.green,
}
}

}

/*class HistCanvas extends Canvas {
int histPix[] = new int[0];
public void setHistPix(int[] pix) {
histPix = pix;
}
public void paint(Graphics g) {
Color c = g.getColor();
g.setColor(Color.red);
g.drawLine(10, H_HEIGHT-10, H_WIDTH-30, H_HEIGHT-10);
g.drawLine(H_WIDTH-35, H_HEIGHT-15, H_WIDTH-30, H_HEIGHT-10);
g.drawLine(H_WIDTH-35, H_HEIGHT-5, H_WIDTH-30, H_HEIGHT-10);
g.drawString("灰度级", H_WIDTH-80, H_HEIGHT);
g.drawLine(10,  H_HEIGHT-10, 10, 10);
g.drawLine(5, 15, 10, 10);
g.drawLine(15, 15, 10, 10);
g.drawString("像素个数", 15, 15);
g.setColor(Color.black);
for(int i=0; i<histPix.length; i++) {
g.drawLine(10+i, H_HEIGHT-10, 10+i, H_HEIGHT-10-histPix[i]);
if(i%30 == 0) {
g.drawString(i+"", 10+i, H_
3ff0
HEIGHT);
}
}
g.setColor(c);
}
}*/







                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息