跳到主要內容

A Java De-flicker Class based on Donald Graft's idea

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

留言

熱門文章

差不多食譜:手工巧克力餅乾 Chocolate Cookies

又是手工餅乾,最近一連出了兩份餅乾食譜,這個「手工巧克力餅乾」已經是第三份了。會不會有更多呢?我可以告訴大家,這是肯定的。 要怪就怪這個陰鬱的冬季雨天,哪裡都不方便去,也懶得出去。餅乾櫃空在那邊已經很久了,雖然有時候會嘴饞,但也沒有迫切去補貨的必要。反正經常開伙,平常該有的材料都會有,自己弄個成分完全透明的零食,也是個不錯的選擇。再說,用烤箱進行烘焙時,房間會變得比較乾燥,也比較溫暖。在夏天是個折磨,但到了冬天,這種感覺還滿不錯的。 話不多說,開始進行這一道「手工巧克力餅乾」的準備工作。

差不多食譜:壽桃 Birthday Bunns

「壽桃」可不是老人家生日的專利,小巧玲瓏的壽桃超級受到小朋友歡迎,直說「好可愛喔!」其實壽桃就是一種造型饅頭/包子,只要掌握了這些方法,要做其他的造型都沒問題。

【豐原大蔥】免揉大蔥佛卡夏 No-knead Leek Focaccia - 差不多食譜

「豐原大蔥」的第二道食譜,就做 大蔥馬鈴薯濃湯 那篇提到的「大蔥佛卡夏」,而且用的還是懶人的免揉方法。不光是麵包,這份食譜還有一個衍生的副產品「大蔥油」,靈感來源就是蔥油拌麵。接著就來看看我是怎麼做的吧! 「大蔥佛卡夏」差不多需要這些材料:(20cm鑄鐵鍋) 豐原大蔥 ...... 1根 橄欖油 …… 適量(150ml左右) 高筋麵粉 …… 200g 鹽 …… 2g 酵母粉 …… 2-3g 水 …… 180-200g 「大蔥佛卡夏」差不多是這麼做的: Step 1. 製作「大蔥油」 說來你可能不相信,製作「大蔥佛卡夏」的「大蔥油」,靈感竟是來自於蔥油拌麵。但是大蔥油製作時需要人在旁邊顧著,而且炸過的大蔥也會拌進麵團裡面,正式製作佛卡夏前就先把這個大蔥油做好。 用小蔥製作蔥油的時候,只有切段丟進油裡去炸。可是我打算把炸過的大蔥一起揉到麵包裡,大蔥纖維比較不好咬斷,就先用刀子給它切碎。要注意的是,這裡我只用蔥白,以及稍微有點厚度的蔥綠,也就是蔥白和蔥綠交界那邊。 接下來,把切碎的大蔥放入鍋中,並倒入橄欖油,用中小火慢慢去炸大蔥。我不想要麵包裡黑黑的,所以炸到大蔥變軟,香味散出,顏色稍微黃一點的時候就可以關火,並將炸過的大蔥撈出放涼。 剩下的油就是大蔥油了,留下來炒菜、拌麵都很不錯。 Step 2. 製作佛卡夏麵團 拿個大碗,倒入高筋麵粉、鹽巴、酵母,再加上水攪拌成團。不用揉,只要成團就可以。 Step 3. 拌入大蔥發酵 往麵團中放入炸過而且放涼的大蔥,用湯匙或筷子拌進麵團。如果你的大蔥瀝的比較乾,再多補一兩匙大蔥油進去。當然,我這種差不多的作法,盛大蔥的碗底就差不多有一兩匙大蔥油了,整碗直接倒進去就好。 拌好後,找個蓋子蓋起來,室溫放一兩個鐘頭進行基礎發酵。也可以直接放進冰箱發酵,隔天再拿出來處理,就像其他的免揉麵包一樣。 Step 4. 移到烤盤 發酵完成,就可以把麵團轉移到烤盤上。我懶得洗鍋子,直接拿炸大蔥油的那個鑄鐵鍋當作烤盤來用。先在鑄鐵鍋(烤盤)底部抹些油,再灑些鹽巴,這樣麵包烤好之後表面就可以咬到一點鹹味。 接著就把麵團直接搬到烤盤,淋點大蔥油,開始用手指戳出佛卡夏特有的孔洞。操作之前記得洗手! 戳完孔後,切點蔥綠用大蔥油泡一下,均勻放在佛卡夏表面,並稍微壓進麵團裡。最後往表面再灑些鹽巴,同樣是在表面就可以嚐到些許的鹹味。這樣,就可以準備烘焙了! Step