图像处理之简单数字水印 - 文字轧花效果

简介: 图像处理之文字轧花效果- 简单数字水印 @ gloomyfish 首先看一下效果,左边是一张黑白的文字图像,右边是混合之后的数字水印效果   实现原理 主要是利用位图块迁移算法,首先提取文字骨架,宽度为一个像素。

图像处理之文字轧花效果- 简单数字水印 @ 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);
	}
}

转载文章请注明出处!
目录
相关文章
|
应用服务中间件 网络安全 容器
教你如何配置阿里云免费HTTPS证书(Tomcat版)
在互联网时代,我们生活中不管是衣食住行,早就已经离不开网络了。不过,大部分人只是这些网站的使用者,那么你有没有想过自己建网站呢?个人出于兴趣爱好或者是企业为了宣传推广都可以建立网站,这可以让你的信息迅速展示在互联网上让你的目标群体看到,从而带来大量的客户和财富。
5720 0
|
存储 数据可视化 安全
一张图的七十二变——阿里云OSS图片处理实践
      小张是某视频网站的新入职的UED,日常工作就是创作各式各样的海报banner。踌躇满志的小张,上了三天班就蔫了。因为他在完成一张图的创作后,还需要考虑:• 同一张图会以不同的形式应用于网站各处:有时候需裁剪成不同形状,有时需要加水印,有时需转换格式....• 为了风格统一,不同的图需要保持样式统一:不同图片排列组成成一组,每组图片风格(
2707 0
|
11月前
|
安全 网络安全 网络架构
私有IP地址详解:概念、分类与应用
私有IP地址详解:概念、分类与应用
886 0
|
数据安全/隐私保护
阿里云注册流程详解(适合新手小白)
本文主要讲解如何注册阿里云,实名阿里云,填写域名持有者信息模板,备案域名等。
|
存储 机器学习/深度学习 Cloud Native
阿里云产品免费试用活动可试用云产品配置、时长、规则及常见问题汇总
云产品免费试用活动是阿里云一直在做的一个活动,只是不同时间可申请试用的云产品配置和试用时长不一样,目前最新可申请试用的云服务器配置最低的是1核2G配置,配置最高的是4核16G,最长试用时长是3个月,下面是阿里云产品免费试用活动可申请试用的产品配置、时长及规则汇总。
阿里云产品免费试用活动可试用云产品配置、时长、规则及常见问题汇总
|
监控 Java 测试技术
盘点阿里巴巴 15 款开发者工具
更效率、更优雅 | 阿里巴巴开发者工具不完全盘点 从人工到自动化,从重复到创新,技术演进的历程中,伴随着开发者工具类产品的发展。 阿里巴巴将自身在各类业务场景下的技术积淀,通过开源、云上实现或工具等形式对外开放,本文将精选了一些阿里巴巴的开发者工具,希望能帮助开发者们提高开发效率、更优雅的写代码。
21168 117
|
XML Java 数据格式
Spring的Bean生命周期和扩展点
Spring 的核心是 IoC 容器,了解 Bean 的生命周期非常重要,这对 Spring 在项目中的应用和扩展都很有帮助。
481 0
如何解决不支持花呗的问题
付款收款不支持花呗,代表你没有使用花呗的权限,需要联系我们的人工客服进行咨询和开通权限,联系方式如下: 1.个人用户不支持     拨打电话:95188 2.商家不支持     拨打电话:0571-88158090,    或在线商户客服:https://cschannel.
1545 12
|
SQL 数据可视化 数据挖掘
|
网络协议 缓存 安全
《DNS攻击防范科普系列4》--遭遇DNS缓存投毒该怎么办?
        在《DNS攻击防范科普系列》的前几讲中,我们介绍了常见的DNS攻击、以及防范DDoS攻击、保障操作安全的方法。今天我们给大家带来的是对DNS缓存投毒攻击的防范。首先我们先来说说什么是“DNS缓存投毒”。