首先看一下效果,左邊是一張黑白的文字圖像,右邊是混合之後的數字水印效果
實作原理
主要是利用位圖塊遷移算法,首先提取文字骨架,寬度為一個像素。然後将提取的骨架,按
照一定的像素值填充到目标圖像中即可。關于位圖塊遷移算法說明請看這裡:
<a target="_blank" href="http://en.wikipedia.org/wiki/bit_blit">http://en.wikipedia.org/wiki/bit_blit</a>
程式思路:
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);
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++) {
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 ta2 =0, tr2 = 0, tg2 = 0, tb2 = 0;
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 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 );
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 );
}
程式測試代碼如下:
import java.awt.borderlayout;
import java.awt.dimension;
import java.awt.graphics;
import java.awt.graphics2d;
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() {
public dimension getmaximumsize() {
public static void main(string[] args) {
jfilechooser chooser = new jfilechooser();
chooser.showopendialog(null);
file f = chooser.getselectedfile();
new bitbltfiltertest(f);