JAVA图像处理系列(九)——艺术效果:火焰

艺术效果:火焰

火焰在3D作图中一般是通过粒子系统实现的,借鉴粒子系统的思想,我们也可以在图像中实现类似火焰的艺术效果。
粒子系统基本上都要通过循环迭代实现,下面先看看本文方法实现的火焰效果。
第一张为原始图像,第二张为循环迭代100次后的火焰效果,第三张为通过该算法生成的动态GIF图的显示效果。


cat.jpg

cat-fire.jpg

cat-fire.gif

算法实现

我们通过一个单独的类来保存粒子的状态,这里直接给出代码,感兴趣的朋友可直接通过阅读代码了解实现细节。

import java.awt.image.BufferedImage;

public class FireRoutine {
  java.util.Random r = new java.util.Random(System.currentTimeMillis());
  int       m_iFlameHeight;
  int       m_iWidth;
  int       m_iHeight;
  int       m_iFireSource;//The y position for the lit spots
  int       m_iFireChance;
  int       m_iAvgFlameWidth;
  int       m_iAlpha;

  int[]     m_FireColors;

  int[]         m_pFireBits;
  int[]         m_pPalletteBuffer;
  int[]         m_pYIndexes;

  public int getRGB(int r, int g, int b) {
    return (0x00ff&r)<<16 | (0x00ff&g) << 8 | (0x00ff & b);
  }

  public int getBValue(int color) {
    return color & 0x00ff;
  }

  public int getGValue(int color) {
    return (color >> 8) & 0x00ff;
  }

  public int getRValue(int color) {
    return (color >> 16) & 0x00ff;
  }

  public FireRoutine() {
    m_FireColors = new int[4];
    m_pPalletteBuffer = new int[256];

    m_iWidth = 0;
    m_iHeight = 0;
    m_pFireBits = null;
    m_pYIndexes = null;
    m_iFireSource = 2;
    m_iFireChance = 10;
    m_iFlameHeight = 50;
    m_iAlpha = 255;

    m_FireColors[0]  = getRGB(0,0,0);// Black
    m_FireColors[1]  = getRGB(255,0,0);// Red
    m_FireColors[2]  = getRGB(255,255,0);// Yellow
    m_FireColors[3]  = getRGB(255,255,255);// White
    m_iAvgFlameWidth = 35;
  }

  public void setParam(int iWidth, int iHeight, int iAlpha) {
    this.m_iWidth = iWidth;
    this.m_iHeight = iHeight;
    this.m_iAlpha = iAlpha;
  }

  public void initFire() {
    // Add three to the height
    m_iHeight+=3;

    m_pYIndexes = new int[m_iHeight];

    m_pFireBits = new int[m_iWidth*m_iHeight];

    // Clear the Fire bits
    for (int i=0; i < m_iWidth*m_iHeight; i++) {
      m_pFireBits[i] = 0;
    }
    // do all the y index pre-calc..
    for (int y = m_iHeight; y >0; y--)
      m_pYIndexes[y-1] = (y-1) * m_iWidth;

    // Create our pallete

    initPallette();
    clrHotSpots();
  }

  public void clrHotSpots() {
    for (int i = 0; i < m_iWidth; i++) {
      m_pFireBits[m_pYIndexes[m_iFireSource] + i] = 0;
    }
//    memset(&m_pFireBits[m_pYIndexes[m_iFireSource]],0,m_iWidth);
  }

  public void initPallette() {
    long iCount = 0;
    int clrStart;
    int clrEnd;

    for(int iColor = 1;iColor<4;iColor++)
    {
      clrStart = m_FireColors[iColor-1];
      clrEnd = m_FireColors[iColor];

      int r, g, b;                          // First distance, then starting value
      float rStep, gStep, bStep;        // Step size for each color

      // Get the color differences
      r = (getRValue(clrEnd) - getRValue(clrStart));
      g = (getGValue(clrEnd) - getGValue(clrStart));
      b =  (getBValue(clrEnd) - getBValue(clrStart));

      int nSteps = Math.max(Math.abs(r), Math.max(Math.abs(g), Math.abs(b)));
      float fStep = (float)(255/3)/ (float)nSteps;
      // Calculate the step size for each color
      rStep = r/(float)nSteps;
      gStep = g/(float)nSteps;
      bStep = b/(float)nSteps;

      // Reset the colors to the starting position
      r = getRValue(clrStart);
      g = getGValue(clrStart);
      b = getBValue(clrStart);

      for (int iOnBand = 0; iOnBand < nSteps; iOnBand++)
      {
        //COLORREF color = RGB(r+rStep*iOnBand, g + gStep*iOnBand, b + bStep *iOnBand);
        int color = getRGB((int)(r + rStep *iOnBand), (int)(g + gStep*iOnBand),(int)(b+bStep*iOnBand));

        int lIndex = (int)(iOnBand * fStep);

        if(lIndex+((iColor-1)*85) < 255)
          m_pPalletteBuffer[lIndex+((iColor-1)*85)] = color;
      }
    }
    // Step on the second color a little bit...
    clrStart = m_FireColors[0];
    clrEnd = m_FireColors[1];

    for(int kj=0;kj>8;
        g = ((g-getGValue(color))*m_iAlpha+(getGValue(color)<<8))>>8;
        b = ((b-getBValue(color))*m_iAlpha+(getBValue(color)<<8))>>8;

        color = getRGB(r, g, b);
        resultImage.setRGB(x, iHeight-i-1, color);
      }
    }
  }

  public int average(int x, int y) {

    int ave_color;
    int ave1, ave2, ave3, ave4, ave5, ave6, ave7;

    // Make sure we are not at the last line...
    if(y == m_iHeight)
      ave1 = m_pFireBits[m_pYIndexes[y-1] + x];
    else
      ave1 = m_pFireBits[m_pYIndexes[y + 1] + x];

    ave2 = m_pFireBits[m_pYIndexes[y - 1] + x];
    ave3 = m_pFireBits[m_pYIndexes[y] + x + 1];
    ave4 = m_pFireBits[m_pYIndexes[y] + x - 1];
    ave5 = m_pFireBits[m_pYIndexes[y] + x + 2];
    ave6 = m_pFireBits[m_pYIndexes[y] + x - 2];
    ave7 = m_pFireBits[m_pYIndexes[y] + x];

    ave_color = (int)((ave1 + ave2 + ave3 + ave4 + ave5 + ave6 + ave7) / 7);

    return(ave_color);
  }
}

测试代码

下面是测试代码:

public class GifEffectTest {
    public static void fireImage() throws IOException {
        BufferedImage img = ImageIO.read(new File("cat.jpg"));
        
        FireRoutine fr = new FireRoutine();
        fr.setParam(img.getWidth(), img.getHeight(), 50);
        fr.initFire();

        for (int i = 0; i < 100; i++) {
            fr.nullPos();
        }
        BufferedImage resultImage = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
        fr.render(img, img.getWidth(), img.getHeight(), resultImage);

        ImageIO.write(resultImage, "jpeg", new File("cat-fire.jpg"));
    }
    
    public static Vector fireEffect(BufferedImage img) {
        
        Vector images = new Vector<>();
        
        FireRoutine fr = new FireRoutine();
        fr.setParam(img.getWidth(), img.getHeight(), 50);
        fr.initFire();

        for (int i = 0; i < 100; i++) {
            if (i % 5 == 0) {
                BufferedImage tmpImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
                fr.render(img, img.getWidth(), img.getHeight(), tmpImg);
                images.add(tmpImg);
            } else {
                fr.nullPos();
            }
        }

        return images;
    }
    
    public static void fireImageGif() throws IOException {
        BufferedImage img = ImageIO.read(new File("cat.jpg"));
        OutputStream out = new FileOutputStream("cat-fire.gif");
                
        GifEncoder gif = new GifEncoder();
        if (!gif.isInit()) {
            gif.init(img.getWidth(), img.getHeight(), out, true);
            gif.setDelay(10);
        }
        
        Vector images = fireEffect(img);
        
        for (BufferedImage item : images) {
            gif.addImage(item);
        }
        
        gif.setCount(0);
        gif.encodeMultiple();
        out.close();
    }
    
    public static void main(String[] argv) throws IOException {
        fireImage();
        fireImageGif();
    }
}

其中fireEffect实现迭代100次后的火焰效果,fireImageGif实现每迭代5次生成一张图片,叠加或生成动态GIF图片。GIF图片生成类GifEncoder请参考文章《JAVA实现GIF动画》

你可能感兴趣的:(JAVA图像处理系列(九)——艺术效果:火焰)