天天看點

圖像處理之簡單數字水印 - 文字軋花效果

圖像處理之文字軋花效果- 簡單數字水印 @ gloomyfish

首先看一下效果,左邊是一張黑白的文字圖像,右邊是混合之後的數字水印效果

圖像處理之簡單數字水印 - 文字軋花效果

實作原理

主要是利用位圖塊遷移算法,首先提取文字骨架,寬度為一個像素。然後将提取的骨架,按

照一定的像素值填充到目标圖像中即可。關于位圖塊遷移算法說明請看這裡:

http://en.wikipedia.org/wiki/Bit_blit

程式思路:

1.      首先建立兩張白闆的單色位圖,讀入黑白文字圖檔,

2.      移動一個像素位開始讀取文字圖檔中的像素,将每個對應像素與白闆單×××片疊加,直

至黑白文字圖檔完全copy到單色白闆中。

3.      重複上面操作,唯一不同的,将白闆像素移動一個像素為,以後開始填充

4.      分别将兩張位圖塊遷移圖檔與原黑白文字圖檔像素完成一個或操作,則得到左上和右下

的文字骨架。

5.      将兩個文字骨架的像素填充到目标彩×××片中,即得到軋花效果的圖檔

根據輸入參數不同,還可得到雕刻效果圖檔。

關鍵代碼解釋:

實作位圖塊遷移算法的代碼如下:

// one pixel transfer

for(int row=1; row<height; row++) {

    int ta = 0, tr = 0, tg = 0, tb = 0;

    for(int col=1; col<width; col++) {

       index = row * width + col;

       index2 = (row-1) * width + (col-1);

       ta = (inPixels[isTop?index:index2] >> 24) & 0xff;

        tr = (inPixels[isTop?index:index2] >> 16) & 0xff;

        tg = (inPixels[isTop?index:index2] >> 8) & 0xff;

        tb = inPixels[isTop?index:index2] & 0xff;

        outPixels[isTop?index2:index] = (ta << 24) | (tr<< 16) | (tg << 8) | tb;

    }

}

布爾變量isTop決定是否填充單色白闆位移(Offset)是零還是一。

擷取一個像素寬度骨架的方法為processonePixelWidth()主要是利用文字圖檔是一個二值圖像,

進而remove掉多餘的像素。

混合軋花的方法為embossImage()主要是簡單的像素填充,布爾變量主要是用來控制是凹軋花

還是凸軋花效果。所有對文字圖像的處理和軋花效果的處理封裝在BitBltFilter一個類中.

程式效果如下:

圖像處理之簡單數字水印 - 文字軋花效果
位圖塊位移算法實作完全源代碼如下:

package com.gloomyfish.zoom.study;  import java.awt.image.BufferedImage;  import com.process.blur.study.AbstractBufferedImageOp;  public class BitBltFilter extends AbstractBufferedImageOp { 	// raster operation - bit block transfer. 	// 1975 for the Smalltalk-72 system, For the Smalltalk-74 system 	private boolean isTop = true;  	/** 	 * left - top skeleton or right - bottom. 	 *  	 * @param isTop 	 */ 	public void setTop(boolean isTop) { 		this.isTop = isTop; 	} 	 	/** 	 * blend the pixels and get the final output image 	 *  	 * @param textImage 	 * @param targetImage 	 */ 	public void emboss(BufferedImage textImage, BufferedImage targetImage) { 		// BitBltFilter filter = new BitBltFilter(); 		BufferedImage topImage = filter(textImage, null); 		setTop(false); 		BufferedImage buttomImage = filter(textImage, null);  		int width = textImage.getWidth();         int height = textImage.getHeight();                  int[] inPixels = new int[width*height];         int[] outPixels = new int[width*height];         getRGB( textImage, 0, 0, width, height, inPixels );         getRGB( topImage, 0, 0, width, height, outPixels );         processonePixelWidth(width, height, inPixels, outPixels, topImage);         getRGB( buttomImage, 0, 0, width, height, outPixels );         processonePixelWidth(width, height, inPixels, outPixels, buttomImage);                  // emboss now         embossImage(topImage, targetImage, true);         embossImage(buttomImage, targetImage, false); 	}  	@Override 	public BufferedImage filter(BufferedImage src, BufferedImage dest) { 		int width = src.getWidth();         int height = src.getHeight();          if ( dest == null )         	dest = createCompatibleDestImage(src, null);          int[] inPixels = new int[width*height];         int[] outPixels = new int[width*height];         getRGB( src, 0, 0, width, height, inPixels );         int index = 0;         int index2 = 0;         // initialization outPixels         for(int row=0; row<height; row++) {         	for(int col=0; col<width; col++) {         		index = row * width + col;         		outPixels[index] = (255 << 24) | (255 << 16) | (255 << 8) | 255;         	}         }                  // one pixel transfer         for(int row=1; row<height; row++) {         	int ta = 0, tr = 0, tg = 0, tb = 0;         	for(int col=1; col<width; col++) {         		index = row * width + col;         		index2 = (row-1) * width + (col-1);         		ta = (inPixels[isTop?index:index2] >> 24) & 0xff;                 tr = (inPixels[isTop?index:index2] >> 16) & 0xff;                 tg = (inPixels[isTop?index:index2] >> 8) & 0xff;                 tb = inPixels[isTop?index:index2] & 0xff;                 outPixels[isTop?index2:index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;         	}         }         setRGB( dest, 0, 0, width, height, outPixels );         return dest; 	} 	 	/** 	 *  	 * @param width 	 * @param height 	 * @param inPixels 	 * @param outPixels 	 * @param destImage 	 */ 	private void processonePixelWidth(int width, int height, int[] inPixels, int[] outPixels, BufferedImage destImage) { 		// now get one pixel data         int index = 0;         for(int row=0; row<height; row++) {         	int ta = 0, tr = 0, tg = 0, tb = 0;         	int ta2 =0, tr2 = 0, tg2 = 0, tb2 = 0;         	for(int col=0; col<width; col++) {         		index = row * width + col;         		ta = (inPixels[index] >> 24) & 0xff;                 tr = (inPixels[index] >> 16) & 0xff;                 tg = (inPixels[index] >> 8) & 0xff;                 tb = inPixels[index] & 0xff;                                  ta2 = (outPixels[index] >> 24) & 0xff;                 tr2 = (outPixels[index] >> 16) & 0xff;                 tg2 = (outPixels[index] >> 8) & 0xff;                 tb2 = outPixels[index] & 0xff;                                  if(tr2 == tr && tg == tg2 && tb == tb2) {                 	outPixels[index] = (255 << 24) | (255 << 16) | (255 << 8) | 255;                 } else {                 	if(tr2 < 5 && tg2 < 5 && tb2 < 5) {                 		outPixels[index] = (ta2 << 24) | (tr2 << 16) | (tg2 << 8) | tb2;                 	} else {                 		outPixels[index] = (255 << 24) | (255 << 16) | (255 << 8) | 255;                 	}                 }         	}         }         setRGB( destImage, 0, 0, width, height, outPixels ); 	} 	 	/** 	 *  	 * @param src 	 * @param dest 	 * @param colorInverse - must be setted here!!! 	 */ 	private void embossImage(BufferedImage src, BufferedImage dest, boolean colorInverse) 	{ 		int width = src.getWidth();         int height = src.getHeight();         int dw = dest.getWidth();         int dh = dest.getHeight();                  int[] sinPixels = new int[width*height];         int[] dinPixels = new int[dw*dh];         src.getRGB( 0, 0, width, height, sinPixels, 0, width );         dest.getRGB( 0, 0, dw, dh, dinPixels, 0, dw ); 		int index = 0; 		int index2 = 0; 		for ( int y = 0; y < height; y++ ) { 			for ( int x = 0; x < width; x++ ) { 				index = y * width + x; 				int srgb = sinPixels[index]; 				int r1 = (srgb >> 16) & 0xff; 				int g1 = (srgb >> 8) & 0xff; 				int b1 = srgb & 0xff; 				if(r1 > 200 || g1 >=200 || b1 >=200) { 					continue; 				} 				index2 = y * dw + x; 				if(colorInverse) { 					r1 = 255 - r1; 					g1 = 255 - g1; 					b1 = 255 - b1; 				} 				dinPixels[index2] = (255 << 24) | (r1 << 16) | (g1 << 8) | b1; 			} 		} 		dest.setRGB( 0, 0, dw, dh, dinPixels, 0, dw ); 	} }       

程式測試代碼如下:

package com.gloomyfish.zoom.study;  import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException;  import javax.imageio.ImageIO; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JFrame;   public class BitBltFilterTest extends JComponent { 	/** 	 *  	 */ 	private static final long serialVersionUID = 7462704254856439832L; 	private BufferedImage rawImg; 	private BufferedImage modImg; 	private Dimension mySize; 	public BitBltFilterTest(File f) { 		try { 			rawImg = ImageIO.read(f); 			modImg = ImageIO.read(new File("D:\\resource\\geanmm.png")); 			// modImg = ImageIO.read(new File("D:\\resource\\gloomyfish.png")); 		} catch (IOException e) { 			e.printStackTrace(); 		} 		mySize = new Dimension(2*modImg.getWidth() + 20, modImg.getHeight()+ 100); 		filterImage(); 		final JFrame imageFrame = new JFrame("Emboss Text - gloomyfish"); 		imageFrame.getContentPane().setLayout(new BorderLayout()); 		imageFrame.getContentPane().add(this, BorderLayout.CENTER); 		imageFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 		imageFrame.pack(); 		imageFrame.setVisible(true); 	} 	 	private void filterImage() { 		BitBltFilter filter = new BitBltFilter(); 		filter.emboss(rawImg, modImg); 	} 	 	public void paint(Graphics g) { 		Graphics2D g2 = (Graphics2D) g; 		g2.drawImage(rawImg, 0, 0, rawImg.getWidth(), rawImg.getHeight(), null); 		g2.drawImage(modImg, rawImg.getWidth()+10, 0, modImg.getWidth(), modImg.getHeight(), null); 		g2.drawString("text image", rawImg.getWidth()/2, rawImg.getHeight()+10); 		g2.drawString("sharped text in image", modImg.getWidth() + 10, modImg.getHeight()+10); 	} 	public Dimension getPreferredSize() { 		return mySize; 	} 	 	public Dimension getMinimumSize() { 		return mySize; 	} 	 	public Dimension getMaximumSize() { 		return mySize; 	} 	 	public static void main(String[] args) { 		JFileChooser chooser = new JFileChooser(); 		chooser.showOpenDialog(null); 		File f = chooser.getSelectedFile(); 		new BitBltFilterTest(f); 	} }       

轉載文章請注明出處!