`
Marshal_R
  • 浏览: 130119 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

人工智能应用实例:图片降噪

阅读更多

人工智能应用实例:图片降噪

 

 

 场景设置

   对白色背景、黑色前景的黑白图片进行降噪处理,可以假定背景部分多于前景。

 
   
 图1 从左往右:原图、噪声图、降噪图 

 

 

降噪模型

    我们可以对图片建立这样一个两层的二维模型,底层表示原图,顶层表示任意的噪声图,xi为原图第i个像素,yi为噪声图第i个像素,xi、yi的取值只有1和-1,1表示白色,-1表示黑色。


图2 图片模型

 

    显然,我们对噪声图任取的一个像素yi可以有以下3个推测:

 

        1、这个像素更加可能是白色,因为白色背景多于黑色前景。

        2、这个像素应该和它周围的几个像素颜色一致。

        3、如果不能确定这个像素在原图到底是白色还是黑色,那么更有可能原图就是现在噪声图的这个颜色,因为毕竟噪声像素是少量的,大部分像素维持了原图的颜色。

 

    基于此,可以对噪声图定义一个能量公式:
    注意:这里的xi、yi颠倒过来了,xi为噪声图,yi为原图。

 

    公式中三个参数都是正实数,第一项表示对噪声图每个像素的值求和,第二项表示对噪声图每个像素与之周围像素差异的求和,第三项表示噪声图像素与原图像素的差异求和。显然基于我们以上的3点推测,越接近原图,噪声图的能量值应该越低。

 

 

降噪过程

    有了这样一个图片模型和噪声图能量公式,我们就可以对图片降噪了。图片降噪基于一个很简单的想法,我们对整个噪声图的每个像素扫描一次(也可以扫描多遍),看看每次改变该像素点的颜色能否使整个噪声图的能量值降低,如果能量值降低了,我们就接受这一次改动,否则,该像素颜色保持不变。

 

    不过,这里还存在一个问题,我们不可能取得能量公式中原图每个像素yi的值!其实这也无妨,可以简单地用每次降噪处理(一次只处理一个像素)得到的新的降噪图去逼近原图,而一开始采用的就是噪声图。因此,每次降噪处理都要计算两个能量值,一个是当前已经得到的降噪图(被用来逼近原图,即xi、yi都是自身)的能量值,另一个是改变一个像素颜色得到的图片(xi为自身,yi为前面得到的降噪图)的能量值。

 

 

Java代码实现

    由于某些限制,代码实现中图片读写使用了十分复杂的手段,实际上可以通过Java的imageio包进行简化。

另外,图片能量公式的3个参数这里采用了手动指定的方式:fa=0.0,fb=1.0,fc=2.1,还可以通过更加智能的算法自动学习得到,比如爬山算法、模拟退火算法、遗传算法。

 

// ImageIOer.java
/**
 * >  ImageIOer
 *   
 * ImageIOer is a class to read and save images,
 * and it implements IImageIO.
 * 
 * @author RuanShiHai
 */

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

import java.io.*;

import javax.imageio.*;


public class ImageIOer {
    /**
     * Read a bmp image
     * @param filePath - the image file to read
     * @return an Image object of the image
     */
    public Image myRead(String filePath) {
        try {
            // the length of BMP file header
            int bfLen = 14;

            // the length of BMP information header
            int biLen = 40;
            FileInputStream fs = new FileInputStream(filePath);
            byte[] bf = new byte[bfLen];
            byte[] bi = new byte[biLen];

            // read BMP file header
            fs.read(bf, 0, bfLen);
            // read BMP information header
            fs.read(bi, 0, biLen);

            // pixels value of width
            int biWidth = ((((int) bi[7] & 0xff) << 24) |
                (((int) bi[6] & 0xff) << 16) | (((int) bi[5] & 0xff) << 8) |
                ((int) bi[4] & 0xff));

            // pixels value of height
            int biHeight = (((int) bi[11] & 0xff) << 24) |
                (((int) bi[10] & 0xff) << 16) | (((int) bi[9] & 0xff) << 8) |
                ((int) bi[8] & 0xff);

            // the number of bits of each pixel
            int biBitCount = ((((int) bi[15] & 0xff) << 8) |
                ((int) bi[14] & 0xff));

            // the size of bytes of the image
            int biSizeImage = ((((int) bi[23] & 0xff) << 24) |
                (((int) bi[22] & 0xff) << 16) | (((int) bi[21] & 0xff) << 8) |
                ((int) bi[20] & 0xff));

            // only processes 24bits bmp image
            if (biBitCount != 24) {
                fs.close();

                return null;
            }

            // the number of bytes to be padded after each row
            int nPad = (biSizeImage / biHeight) - (biWidth * 3);

            // read pixels data from image into a byte array
            byte[] bRGB = new byte[biSizeImage];
            fs.read(bRGB, 0, biSizeImage);
            fs.close();

            int index = 0;
            int[] data = new int[biWidth * biHeight];

            // store pixels in an array of int type
            for (int i = biHeight - 1; i >= 0; i--) {
                for (int j = 0; j < biWidth; j++) {
                    data[(i * biWidth) + j] = (((int) 255 << 24) |
                        (((int) bRGB[index + 2] & 0xff) << 16) |
                        (((int) bRGB[index + 1] & 0xff) << 8) |
                        ((int) bRGB[index] & 0xff));
                    index += 3;
                }

                index += nPad;
            }

            // create an Image object
            Toolkit kit = Toolkit.getDefaultToolkit();
            Image image = kit.createImage(new MemoryImageSource(biWidth,
                        biHeight, data, 0, biWidth));

            return image;
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Caught exception in loadbitmap!");
        }

        return null;
    }

    /**
     * Store an Image as a bmp image file
     * @param image - Image object to be saved as a bmp image file
     * @param filePath - the name of the bmp image file
     * @return the Image object
     */
    public Image myWrite(Image image, String filePath) {
        try {
            File imgFile = new File(filePath + ".bmp");
            BufferedImage bi = new BufferedImage(image.getWidth(null),
                    image.getHeight(null), BufferedImage.TYPE_INT_RGB);
            Graphics2D biContext = bi.createGraphics();

            // get a BufferedImage object from an Image object
            biContext.drawImage(image, 0, 0, null);
            biContext.dispose();
            ImageIO.write(bi, "bmp", imgFile);

            return image;
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }

        return null;
    }
}

 

// PictureNoise.java
import java.awt.*;
import java.awt.image.*;

import java.io.*;
import java.util.Random;

import javax.imageio.*;

public class PictureNoise {
	private static final double rate = 0.1;
	private static final double fa = 0;
	private static final double fb = 1.0;
	private static final double fc = 2.1;

	public void generateAPicture() {
		try {
			int width = 640;
			int height = 480;
			// 创建BufferedImage对象
			Font font = new Font("微软雅黑", Font.BOLD, 160);
			BufferedImage image = new BufferedImage(width, height,
					BufferedImage.TYPE_INT_RGB);
			// 获取Graphics2D
			Graphics2D g2d = image.createGraphics();
			// 画图
			g2d.setBackground(new Color(255, 255, 255));
			g2d.setPaint(new Color(0, 0, 0));
			g2d.clearRect(0, 0, width, height);
			g2d.setFont(font);
			g2d.drawString("Java", 140, 200);
			g2d.drawString("Python", 40, 400);
			// 释放对象
			g2d.dispose();
			// 保存文件
			ImageIO.write(image, "bmp", new File("/home/ln/picture.bmp"));
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	// generate a picture with noise
	public Image getNoise(Image sourceImage) {
		BufferedImage bi = new BufferedImage(sourceImage.getWidth(null),
				sourceImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
		Graphics2D biContext = bi.createGraphics();

		// get a BufferedImage object from an Image object
		biContext.drawImage(sourceImage, 0, 0, null);
		biContext.dispose();

		// create an array of int type to store rgb values of each pixel
		int[] rgbs = new int[bi.getWidth() * bi.getHeight()];

		bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), rgbs, 0, bi.getWidth());

		Random rd = new Random();
		int index = 0;
		for (int i = 0; i < bi.getHeight(); i++) {
			for (int j = 0; j < bi.getWidth(); j++, index++) {
				if (rd.nextDouble() < rate)
					rgbs[index] = (rgbs[index] & 0xff000000)
							| (~rgbs[index] & 0x00ffffff);
			}
		}

		// create a new Image object
		Toolkit kit = Toolkit.getDefaultToolkit();
		Image image = kit.createImage(new MemoryImageSource(bi.getWidth(), bi
				.getHeight(), rgbs, 0, bi.getWidth()));

		return image;
	}

	public Image reduceNoise(Image sourceImage, double fa, double fb, double fc) {
		BufferedImage bi = new BufferedImage(sourceImage.getWidth(null),
				sourceImage.getHeight(null), BufferedImage.TYPE_INT_RGB);
		Graphics2D biContext = bi.createGraphics();

		// get a BufferedImage object from an Image object
		biContext.drawImage(sourceImage, 0, 0, null);
		biContext.dispose();

		// create an array of int type to store rgb values of each pixel
		int[] rgbs = new int[bi.getWidth() * bi.getHeight()];

		bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), rgbs, 0, bi.getWidth());

		int height = bi.getHeight(), width = bi.getWidth();
		for (int i = 0; i < height; i++) {
			for (int j = 0; j < width; j++) {
				double dd;
				int d1, d2, d3;

				d1 = 2;
				if ((rgbs[i * width + j] & 0x00ffffff) == 0)
					d1 = -2;
				d2 = 0;
				int rc[][] = { { -1, 0 }, { 0, 1 }, { 1, 0 }, { 0, -1 } };
				for (int k = 0; k < 4; k++) {
					if (isValid(i + rc[k][0], j + rc[k][1], height, width)) {
						if ((rgbs[(i + rc[k][0]) * width + j + rc[k][1]] & 0x00ffffff) == (rgbs[i
								* width + j] & 0x00ffffff))
							d2 += -2;
						else
							d2 += 2;
					}
				}
				d2 *= 2;
				d3 = -2;

				dd = fa * d1 - fb * d2 - fc * d3;
				if (dd < 0)
					rgbs[i * width + j] = (rgbs[i * width + j] & 0xff000000)
							| (~rgbs[i * width + j] & 0x00ffffff);
			}
		}

		// create a new Image object
		Toolkit kit = Toolkit.getDefaultToolkit();
		Image image = kit.createImage(new MemoryImageSource(bi.getWidth(), bi
				.getHeight(), rgbs, 0, bi.getWidth()));

		return image;
	}

	private boolean isValid(int row, int col, int height, int width) {
		return (row >= 0 && row < height && col >= 0 && col < width);
	}

	public static void main(String[] args) {
		PictureNoise pictureNoise = new PictureNoise();
		ImageIOer imageIOer = new ImageIOer();

		pictureNoise.generateAPicture();

		Image sourceImage = imageIOer.myRead("/picture.bmp");
		Image noiseImage = pictureNoise.getNoise(sourceImage);

		Image noNoiseImage = pictureNoise.reduceNoise(noiseImage, fa, fb, fc);

		imageIOer.myWrite(noiseImage, "/picture_with_noise");
		imageIOer.myWrite(noNoiseImage, "/picture_noise_processed");
	}

}

 

 

 

 

  • 大小: 2.2 KB
  • 大小: 24.5 KB
  • 大小: 3.4 KB
  • 大小: 22.1 KB
  • 大小: 15.2 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics