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

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

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


首先看一下效果,左边是一张黑白的文字图像,右边是混合之后的数字水印效果


0_13313063338dDF.png

实现原理


主要是利用位图块迁移算法,首先提取文字骨架,宽度为一个像素。然后将提取的骨架,按


照一定的像素值填充到目标图像中即可。关于位图块迁移算法说明请看这里:


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一个类中.



程序效果如下:

0_1331305941SvLB.png


位图块位移算法实现完全源代码如下:

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);
  }
}

转载文章请注明出处!

相关文章
|
24天前
|
算法 Java 计算机视觉
图像处理之基于NCC模板匹配识别
图像处理之基于NCC模板匹配识别
23 2
|
25天前
|
算法 计算机视觉
图像处理之移动模糊
图像处理之移动模糊
13 0
|
2月前
|
文字识别
[Halcon&识别] 车牌识别
[Halcon&识别] 车牌识别
46 0
|
机器学习/深度学习 传感器 算法
【图像隐藏】基于DCT的数字图像水印防伪附matlab代码
【图像隐藏】基于DCT的数字图像水印防伪附matlab代码
|
机器学习/深度学习
识别手绘数字图像
识别手绘数字图像
71 0
|
机器学习/深度学习 传感器 算法
【数字水印】基于小波变换算法DWT实现水印嵌入提取(含滤波攻击)含Matlab代码
【数字水印】基于小波变换算法DWT实现水印嵌入提取(含滤波攻击)含Matlab代码
【数字水印】基于小波变换算法DWT实现水印嵌入提取(含滤波攻击)含Matlab代码
|
机器学习/深度学习 人工智能 自然语言处理
文字、图片一键生成逼真音效,作者亲自揭秘音频AIGC模型
文字、图片一键生成逼真音效,作者亲自揭秘音频AIGC模型
191 0
|
计算机视觉
图像增强—彩色增强技术
图像增强—彩色增强技术
|
机器学习/深度学习 存储 传感器
【语音隐写】基于小波变换实现音频数字水印嵌入提取附Matlab代码
【语音隐写】基于小波变换实现音频数字水印嵌入提取附Matlab代码
|
算法 编译器
使用matlab机器视觉工具箱实现人脸特征的检测和定位,识别并标注眉毛,眼睛,鼻子,嘴巴
使用matlab机器视觉工具箱实现人脸特征的检测和定位,识别并标注眉毛,眼睛,鼻子,嘴巴
334 0
使用matlab机器视觉工具箱实现人脸特征的检测和定位,识别并标注眉毛,眼睛,鼻子,嘴巴