使用 Java 2D API 製作藝術動畫

Paul Reiners 展示瞭如何通過 Java 2D API 和細胞自動機(cellular automata)以獨特的藝術方式製作圖像動畫。在這個過程中,他演示了用 Java 代碼實現圖像操作器並介紹了循環空間(cyclic space ),循環空間是一種 2D 細胞自動機。您可以根據本文的思路創建自己的圖像操作器,並使用 Java 技術創建藝術應用程序。

本文說明如何通過實現 BufferedImageOp 接口來編寫自定義 Java 2D 圖像處理類。它使用一個 2D 細胞自動機(CA),即循環空間,來構造圖像處理應用程序。CA 會 “操作” 圖像(例如,一個 PEG 文件),使圖像不斷地按有趣的方式轉換。我希望本文能開闊您的視野,使您能編寫一個全新的圖像處理應用程序類。

2D 細胞自動機

2D 細胞自動機由分佈在 2D 網格(通常稱爲佈局)中的細胞 組成。每個細胞都有一個狀態,可以是 0 到 n 之間的任意整數。清單 1 顯示瞭如何用 Java 代碼聲明一個細胞自動機佈局:


清單 1. 定義 TwoDCellularAutomaton.universe
				
protected int[][] universe;

所有細胞每個時刻都同時更新狀態。一個細胞的新狀態取決於該細胞的當前狀態和它相鄰細胞的當前狀態,狀態的轉換根據特定的規則進行。清單 2 更新了下一時刻的佈局:


清單 2. TwoDCellularAutomaton 類(部分清單)
				public void update() {
int[][] newUniverse = new int[rowCount][colCount];
for (int row = 0; row < rowCount; row++) {
for (int col = 0; col < colCount; col++) {
newUniverse[row][col] = updateCell(row, col);
}
}
for (int row = 0; row < rowCount; row++) {
for (int col = 0; col < colCount; col++) {
universe[row][col] = newUniverse[row][col];
}
}
}

protected abstract int updateCell(int row, int col);

不同類型的 CA 更新單個細胞所用的規則不相同。規則的定義由子類完成。

循環空間

循環空間是由麥迪遜市威斯康星大學數學系的 David Griffeath 發現的,並由 A. K. Dewdney 在 Scientific American 的一個專欄中推廣(請參閱 參考資料)。

在循環空間中,每個細胞都有一個狀態,它是 n 種狀態中的一種。每個細胞的初始狀態通常是隨機定義的,也就是說,是 0 和 n - 1(包括 0 和 n - 1)之間的一個隨機數字。細胞的鄰居定義爲 von Neumann 鄰居:包括它的上下左右 4 個鄰近細胞。

清單 3 通過給出每個細胞鄰居和細胞本身的不同座標來定義該細胞的 von Neumann 鄰居:


清單 3. 定義 TwoDCellularAutomaton.VON_NEUMANN_NEIGHBORHOOD
				
protected static final int[][] VON_NEUMANN_NEIGHBORHOOD = { { -1, 0 },
{ 1, 0 }, { 0, -1 }, { 0, 1 } };

循環空間由以下規則定義:

如果一個細胞的狀態是 k,它有一個鄰居的狀態是 k + 1,那麼該狀態在下一時刻將會有一個新的狀態 k + 1。否則,該細胞的狀態將保持不變。

這個規則是循環的,因此,如果一個細胞處於狀態 n - 1,而且有一個狀態爲 0 的鄰居,那麼該細胞在下一時刻的狀態將爲 0

ConvolveOp 算是一個細胞自動機

Java 2D API 的 ConvolveOp 類代表一個空間螺旋:每個目標像素的顏色通過對應的源像素及其鄰居像素的顏色來確定。

您是不是覺得這個定義很熟悉呢?這與 2D 細胞自動機基本上是同一個東西,但不盡相同。例如,狀態(顏色)是連續的而不是分散的(也不完全如此:RGB 值的個數是無限的,但很接近連續)。這使得該類更像是一個連續自動機。而且您不能使用像 CA 那樣細的粒度控制基於細胞及其鄰居細胞當前狀態的新狀態。

因此,您無法使用一個 ConvolveOp 定義循環空間,但它仍然是很有趣的。它是查看 ConvolveOp 的另一種方式。

這個簡單規則會導致意想不到的複雜行爲。清單 4 實現了在循環空間中更新細胞的規則:


清單 4. 定義 CyclicSpace.updateCell(int, int)
				
protected int updateCell(int row, int col) {
int[] neighborStates = getNeighborStates(row, col, neighborhood);
int currentState = universe[row][col];
for (int i = 0; i < neighborStates.length; i++) {
int neighborState = neighborStates[i];
if (neighborState == (currentState + 1) % n) {
return neighborState;
}
}

return currentState;
}

我曾說過,循環空間佈局的初始狀態是隨機的。細胞會被 “更大的” 細胞 “喫掉”,最後會再次循環回到狀態 0。在這個過程中,區域自行組織並展開,成爲波浪形。最後,會出現一個穩定的波浪圖案。這些波浪呈對角線在佈局中移動,看上去有點像紙風車。





回頁首


創建圖像操作器

java.awt.image.BufferedImageOp 接口允許您創建自己的圖像操作器(也稱爲過濾器)。本文只討論 BufferedImageOp 的一個方法:

BufferedImage filter(BufferedImage src, BufferedImage dest)

srcdest 是 2D 像素網格。實現此方法時,您可以按任意方式從 src 構建 dest。普遍做法是在 src 中迭代像素,並按照一定規則在 dest 中創建相應的像素。這就是在圖像處理應用程序中需要做的事情,我根據著名的法國畫家 Georges-Pierre Seurat 將它命名爲 Seurat(參見 下載 獲取完整的示例代碼)。

Seurat 應用程序

您可能知道圖像像素與 CA 中的細胞存在映射關係。它們都以 2D 網格形式存在,每個都有狀態,對於像素就是它的紅綠藍(RGB)值。我將在 filter(BufferedImage src, BufferedImage dest) 實現中探討這種映射關係。對於 src 中的每個像素,我會根據一定規則將該像素的 RGB 值與 CA 中相應細胞的狀態組合起來,創建 dest 中相應像素的新 RGB 值。這個規則將定義一個過濾器。

清單 5 顯示如何迭代 src 中的所有像素並在 dest 中構建像素。抽象方法 getNewRGB(Color) 由單獨的過濾器定義。它爲輸入顏色計算並返回經過過濾的 RGB 值。


清單 5. CellularAutomataFilter 類(部分清單)


本文轉自IBM Developerworks中國

      請點擊此處查看全文

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章