本文說(shuō)明如何通過(guò)實(shí)現(xiàn)
BufferedImageOp
接口來(lái)編寫自定義 Java 2D 圖像處理類。它使用一個(gè) 2D
細(xì)胞自動(dòng)機(jī)
(CA),即
循環(huán)空間
,來(lái)構(gòu)造圖像處理應(yīng)用程序。CA 會(huì) “操作” 圖像(例如,一個(gè) PEG 文件),使圖像不斷地按有趣的方式轉(zhuǎn)換。我希望本文能開闊您的視野,使您能編寫一個(gè)全新的圖像處理應(yīng)用程序類。
2D 細(xì)胞自動(dòng)機(jī)由分布在 2D 網(wǎng)格(通常稱為 布局 )中的 細(xì)胞 組成。每個(gè)細(xì)胞都有一個(gè) 狀態(tài) ,可以是 0 到 n 之間的任意整數(shù)。清單 1 顯示了如何用 Java 代碼聲明一個(gè)細(xì)胞自動(dòng)機(jī)布局:
清單 1. 定義
TwoDCellularAutomaton.universe
|
所有細(xì)胞每個(gè)時(shí)刻都同時(shí)更新狀態(tài)。一個(gè)細(xì)胞的新狀態(tài)取決于該細(xì)胞的當(dāng)前狀態(tài)和它相鄰細(xì)胞的當(dāng)前狀態(tài),狀態(tài)的轉(zhuǎn)換根據(jù)特定的規(guī)則進(jìn)行。清單 2 更新了下一時(shí)刻的布局:
清單 2.
TwoDCellularAutomaton
類(部分清單)
public void update() { |
不同類型的 CA 更新單個(gè)細(xì)胞所用的規(guī)則不相同。規(guī)則的定義由子類完成。
![]() |
|
在循環(huán)空間中,每個(gè)細(xì)胞都有一個(gè)狀態(tài),它是 n 種狀態(tài)中的一種。每個(gè)細(xì)胞的初始狀態(tài)通常是隨機(jī)定義的,也就是說(shuō),是 0 和 n - 1 (包括 0 和 n - 1)之間的一個(gè)隨機(jī)數(shù)字。細(xì)胞的鄰居定義為 von Neumann 鄰居 :包括它的上下左右 4 個(gè)鄰近細(xì)胞。
清單 3 通過(guò)給出每個(gè)細(xì)胞鄰居和細(xì)胞本身的不同坐標(biāo)來(lái)定義該細(xì)胞的 von Neumann 鄰居:
清單 3. 定義
TwoDCellularAutomaton.VON_NEUMANN_NEIGHBORHOOD
|
循環(huán)空間由以下規(guī)則定義:
如果一個(gè)細(xì)胞的狀態(tài)是 k ,它有一個(gè)鄰居的狀態(tài)是 k + 1 ,那么該狀態(tài)在下一時(shí)刻將會(huì)有一個(gè)新的狀態(tài) k + 1 。否則,該細(xì)胞的狀態(tài)將保持不變。
這個(gè)規(guī)則是循環(huán)的,因此,如果一個(gè)細(xì)胞處于狀態(tài) n - 1 ,而且有一個(gè)狀態(tài)為 0 的鄰居,那么該細(xì)胞在下一時(shí)刻的狀態(tài)將為 0 。
![]() |
|
這個(gè)簡(jiǎn)單規(guī)則會(huì)導(dǎo)致意想不到的復(fù)雜行為。清單 4 實(shí)現(xiàn)了在循環(huán)空間中更新細(xì)胞的規(guī)則:
清單 4. 定義
CyclicSpace.updateCell(int, int)
|
我曾說(shuō)過(guò),循環(huán)空間布局的初始狀態(tài)是隨機(jī)的。細(xì)胞會(huì)被 “更大的” 細(xì)胞 “吃掉”,最后會(huì)再次循環(huán)回到狀態(tài) 0 。在這個(gè)過(guò)程中,區(qū)域自行組織并展開,成為波浪形。最后,會(huì)出現(xiàn)一個(gè)穩(wěn)定的波浪圖案。這些波浪呈對(duì)角線在布局中移動(dòng),看上去有點(diǎn)像紙風(fēng)車。
![]() ![]() |
java.awt.image.BufferedImageOp
接口允許您創(chuàng)建自己的圖像操作器(也稱為
過(guò)濾器
)。本文只討論
BufferedImageOp
的一個(gè)方法:
BufferedImage filter(BufferedImage src, BufferedImage dest) |
src
和
dest
是 2D 像素網(wǎng)格。實(shí)現(xiàn)此方法時(shí),您可以按任意方式從
src
構(gòu)建
dest
。普遍做法是在
src
中迭代像素,并按照一定規(guī)則在
dest
中創(chuàng)建相應(yīng)的像素。這就是在圖像處理應(yīng)用程序中需要做的事情,我根據(jù)著名的法國(guó)畫家 Georges-Pierre Seurat 將它命名為
Seurat
(參見
下載
獲取完整的示例代碼)。
您可能知道圖像像素與 CA 中的細(xì)胞存在映射關(guān)系。它們都以 2D 網(wǎng)格形式存在,每個(gè)都有狀態(tài),對(duì)于像素就是它的紅綠藍(lán)(RGB)值。我將在
filter(BufferedImage src, BufferedImage dest)
實(shí)現(xiàn)中探討這種映射關(guān)系。對(duì)于
src
中的每個(gè)像素,我會(huì)根據(jù)一定規(guī)則將該像素的 RGB 值與 CA 中相應(yīng)細(xì)胞的狀態(tài)組合起來(lái),創(chuàng)建
dest
中相應(yīng)像素的新 RGB 值。這個(gè)規(guī)則將定義一個(gè)過(guò)濾器。
清單 5 顯示如何迭代
src
中的所有像素并在
dest
中構(gòu)建像素。抽象方法
getNewRGB(Color)
由單獨(dú)的過(guò)濾器定義。它為輸入顏色計(jì)算并返回經(jīng)過(guò)過(guò)濾的 RGB 值。
清單 5.
CellularAutomataFilter
類(部分清單)
|
您可能會(huì)發(fā)現(xiàn)我沒有利用圖像中的像素與 CA 中的細(xì)胞之間的一對(duì)一映射關(guān)系。更確切地講,CA 是粗粒度的(至少大多數(shù)情況如此)。我最初這樣做是出于性能考慮。但是,使用不同大小的 CA 布局可以獲得有趣的像素效果。
清單 6 顯示了
getNewRGB(Color)
的一種特殊實(shí)現(xiàn)。它計(jì)算 “RGB 互補(bǔ)(complement)”,但這不是實(shí)際的顏色互補(bǔ)(計(jì)算真正顏色互補(bǔ)的過(guò)濾器也很有趣,但將其編寫成代碼則沒有這么簡(jiǎn)單)。
清單 6.
RGBComplementFilter
類(部分清單)
|
我已經(jīng)擴(kuò)展
getNewRGB(Color)
,使其不僅可以傳入要轉(zhuǎn)換的像素顏色,而且可以傳入 8 個(gè)鄰居像素的顏色。這允許我創(chuàng)建某些效果,比如模糊效果或檢測(cè)邊緣,其中過(guò)濾的像素顏色取決于它的鄰居的顏色。這將是一個(gè)很好的增強(qiáng)功能。
最后,我將配合 CA 時(shí)鐘更新圖像來(lái)動(dòng)畫圖像。為此,我使用了一個(gè)
javax.swing.Timer
(這是制作變化圖像動(dòng)畫的簡(jiǎn)單方式,但不是最好的方式。Jonathan Knudsen 的著作
Java 2D Graphics
提供了一種更好更復(fù)雜的方式來(lái)創(chuàng)建動(dòng)畫。
圖 1 是 Georges Seurat 于 1884 年創(chuàng)作的點(diǎn)畫法名作 “A Sunday Afternoon on the Island of La Grand Jatte” 的照片:
圖 1. Georges Seurat 的 “A Sunday Afternoon on the Island of La Grand Jatte”
現(xiàn)在我將使用 RGB 互補(bǔ)過(guò)濾器在 Seurat 圖畫上運(yùn)行 Seurat 應(yīng)用程序。圖 2 顯示了過(guò)濾后的圖畫,此時(shí)循環(huán)空間處于它的初始隨機(jī)狀態(tài):
圖 2. 使用循環(huán)空間過(guò)濾圖畫的無(wú)組織隨機(jī)狀態(tài)
圖 3 顯示了過(guò)濾后的圖畫,此時(shí)循環(huán)空間開始進(jìn)入有序模式,但仍然帶有很大的隨機(jī)性:
圖 3. 使用循環(huán)空間過(guò)濾圖畫的中間狀態(tài)
圖 4 顯示了過(guò)濾圖畫的最終穩(wěn)定狀態(tài):
![]() |
|
圖 4. 使用循環(huán)空間在穩(wěn)定狀態(tài)下過(guò)濾的圖畫
不過(guò),靜態(tài)圖片不能真正實(shí)現(xiàn)過(guò)濾器/CA(畢竟,這個(gè)應(yīng)用程序是為 動(dòng)畫 靜態(tài)圖像而編寫的)。我建議您運(yùn)行實(shí)際的 Java applet 來(lái)查看運(yùn)行中的過(guò)濾器/CA(請(qǐng)參閱 參考資料 ,獲得即時(shí) demo 的鏈接)。
一 些人可能會(huì)認(rèn)為在 “A Sunday Afternoon on the Island of La Grand Jatte” 之類的偉大作品上運(yùn)行圖像過(guò)濾器應(yīng)用程序是一種褻瀆。我當(dāng)然很贊同此觀點(diǎn)。但我只是以這幅畫為例子。我的主要目標(biāo)是展示如何使用一種簡(jiǎn)單的細(xì)胞自動(dòng)機(jī)器, 以有趣而復(fù)雜的方式來(lái)制作圖像動(dòng)畫,以一副熟悉的名畫作為例子會(huì)比較好。
我曾在許多類型的畫上運(yùn)行過(guò) Seurat,在抽象藝術(shù)和具象藝術(shù)方面都得到了有趣的結(jié)果。但是,似乎在現(xiàn)代藝術(shù) — 特別是流行藝術(shù)方面效果更好。例如,當(dāng)您在 Jasper Johns 的 “Flag” 畫上運(yùn)行 Seurat 時(shí),會(huì)出現(xiàn)有趣的圖案。循環(huán)空間的對(duì)角線能根據(jù) “Flag” 畫中的直線很好地工作。在 Jackson Pollock 的水滴畫中,運(yùn)行 Seurat 時(shí)也會(huì)產(chǎn)生有趣的結(jié)果。例如,隨著循環(huán)空間 CA 越過(guò) Pollock 的 “Blue Poles”,它會(huì)隱藏、顯示、再隱藏這幅復(fù)雜畫作的細(xì)節(jié),讓您在不同時(shí)間集中注意不同的部位。這對(duì)照片同樣適用。我喜歡在 Ralph Eugene Meatyard 超現(xiàn)實(shí)主義的照片上運(yùn)行 Seurat。
在運(yùn)行 Seurat 這樣的應(yīng)用程序時(shí),您有 3 種選擇:2D 細(xì)胞自動(dòng)機(jī)類型、過(guò)濾器和原始圖像。在這篇文章中,我只使用了循環(huán)空間,但是也可以使用其他類型的 2D 細(xì)胞自動(dòng)機(jī)(如 Hodgepodge)。只要發(fā)揮您的想象力,就能編寫出各種過(guò)濾器程序。我主要實(shí)踐了操作顏色的過(guò)濾器,但更改圖像空間關(guān)系的過(guò)濾器也很有趣。例如,您 可以編寫一個(gè)歪曲圖像表面的過(guò)濾器程序,創(chuàng)建類似于披頭士的 Rubber Soul 專輯的封面那種效果。最后,您可以使用任意圖像,比如照片。對(duì)于給定的圖像,各種過(guò)濾器和 CA 類型的組合可以生成更好或更差的結(jié)果。我希望本文能鼓起您體驗(yàn)的欲望。
我對(duì) Julia Braswell 在視覺藝術(shù)方面的幫助表示衷心的感謝!
![]() |
||
![]() |
Paul Reiners 是一位 Sun 認(rèn)證的 Java 程序員和開發(fā)人員。他參與開發(fā)了幾個(gè)開放源碼程序,包括 Automatous Monk、Twisted Life 和 Leipzig。Reiners 于 1991 年 5 月獲得伊利諾斯大學(xué) Urbana-Champaign 分校的應(yīng)用數(shù)學(xué)(計(jì)算理論)碩士學(xué)位。他目前住在明尼蘇達(dá)州,在業(yè)余時(shí)間喜歡演奏低音電吉他,并且是一個(gè)爵士樂隊(duì)的成員。 |
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
