Since QuickTime for Java has been depreciated in year 2009, and JMF (Java Media Framework) has to call for external library, there is a need to search alternative way assembling Jpeg images into a QuickTime movie. I know Time Lapse Assembler has done a excellent job at this under Mac OS X, and it is free. But Time Lapse Assembler fails to deal with the flickering problem commonly seen in Time-Lapse videos. As a result, I have to find a pure-java way to write QuickTime file, and to deal with the flickering problem under Mac OS X.
Werner Randelshofer's Blog has a good solution writing QuickTime movies. All I need to do myself is de-flickering. Again, open-source resources help a lot. Donald Graft kindly provides his source code of Deflicker Filter for VirtualDub. We can learn his algorithm from the code. Graft's deflicker was written in C++. However, what I need is Java. So I re-write part of Graft's deflicker. (Sorry, I still have no idea about the soften phase….)
The basic idea is simply. As Graft wrote inside his code, "The adjustment factor is determined from a moving average of the luminance's of the past frames in the window." Thus, what I need to do is quite simple, calculating the luminance (sum of each pixel's r+g+b divided by width*height*3) of each frame. And then, just following Graft's idea. Here is my deflicker filter code.
package org.sustudio.deflicker; import java.awt.image.BufferedImage; public class DonaldGraftDeflicker { private int window; private int scene_change_threshold; private int lumnData[]; public DonaldGraftDeflicker(int window, int threshold) { this.window = window; this.scene_change_threshold = threshold; this.lumnData = new int[window]; // first frame infication. this.lumnData[0] = 256; } public BufferedImage deflicker(BufferedImage img) { BufferedImage image = img; int w = image.getWidth(); int h = image.getHeight(); int lum_sum = 0; // Calculate the luminance of the current frame. for (int y=0; y> 16) & 0xff; int green = (pixel >> 8) & 0xff; int blue = (pixel >> 0) & 0xff; //lum_sum += red + green + blue; lum_sum += (int) Math.ceil((0.299 * red) + (0.587 * green) + (0.114 * blue)); } } //lum_sum /= (w * h * 3.0); lum_sum /= (w * h); System.out.print(lum_sum + "\t"); // Do scene change processing. //boolean scene_change = false; if (this.scene_change_threshold < 256 && this.lumnData[0] != 256 && Math.abs((int)lum_sum - (int)this.lumnData[window-1]) >= this.scene_change_threshold) { this.lumnData[0] = 256; //scene_change = true; } // Calculate the adjustment factor for the current frame. // The adjustment factor is determined from a moving average // of the luminances of the past frames in the window. double scale = 1.0; if (this.lumnData[0] > 255) { for (int i=0; i 0) { scale = 1.0 / (double) lum_sum; double filt = 0.0; for (int i=0; i > 16) & 0xff; int green = (pixel >> 8) & 0xff; int blue = (pixel >> 0) & 0xff; int max = Math.max(Math.max(red, blue), green); if (scale * max > 255.0) scale = 255.0 / (double)max; red = (int) (scale * red); green = (int) (scale * green); blue = (int) (scale * blue); pixel = (red & 0xff) << 16 | (green & 0xff) << 8 | (blue & 0xff) << 0; image.setRGB(x, y, pixel); lum_sum += (int) Math.ceil((0.299 * red) + (0.587 * green) + (0.114 * blue)); } } lum_sum /= (w * h); System.out.println(lum_sum); return image; } private int getPixelGrayValue(int rgb) { int red = (rgb >> 16) & 0xff; int green = (rgb >> 8) & 0xff; int blue = (rgb >> 0) & 0xff; //return (int) Math.ceil((0.2126 * red) + (0.7152 * green) + (0.0722 * blue)); return (int) Math.ceil((0.299 * red) + (0.587 * green) + (0.114 * blue)); } }
留言
張貼留言