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));
}
}
留言
張貼留言