影象處理之簡單數字水印
影象處理之文字軋花效果- 簡單數字水印 @ gloomyfish
首先看一下效果,左邊是一張黑白的文字影象,右邊是混合之後的數字水印效果
實現原理
主要是利用點陣圖塊遷移演算法,首先提取文字骨架,寬度為一個畫素。然後將提取的骨架,按
照一定的畫素值填充到目標影象中即可。關於點陣圖塊遷移演算法說明請看這裡:
程式思路:
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);
}
}
轉載文章請註明出處!