什麼是 MonsterID ?其實我的頭像,就是用 MonsterID 的其中一個實例,完全沒有用 photoshop 之類的圖形處理軟體,而是利用 ImageMagick 為指令工具把 這些身體部位的圖型,再加上不同的顏色,以隨機排列組合的方式,所疊出來的其中一個圖型。
上一篇文章提及 Don Park 提及利用基本圖型來做 IP 來源的辨識(好像也有提供是 java 的 source code ?),之後在這篇文章裡,提及利用同一觀念,而在其他程式語言環境做相關建置或延伸的連結:
http://www.docuverse.com/blog/donpark/2007/01/20/identicon-third-party-implementations
其中最有趣的是 Andi 利用 Combinatoric Critters version 裡(會走路的臉?組合而來的創造物?)的概念與做法,以 PHP 實作出 MonsterID 這個 Project 出來。
為什麼是 Monster 怪物
在最初的原型,是利用這些身體的部位,再加上顏色的不同,互相以排列組合的方式,就可畫出無數種的 critter (創造物?):
而這些是組合的順序:
就可畫出無數多的 critter:
且個個都不一樣的。這個原型,看起來是在 flash 上建置的;所以 Andi 把各個身體部位改為以圖型檔為元件,再加上顏色來做隨機的排列組合,基本上就可以為該身份畫出一個不會與其他重覆的 MonsterID 出來。
實作 MonsterID
Word Press 早就有了此 plugins,而網路上也找不到 ruby 版的 MonsterID 的實作,所以就只得利用 PHP + GD 的版本,想辦法改成 Ruby + ImageMagick 的版本。
先利用 command-line 端,做一個能隨機產生一個 MonsterID 出來試試:
# 編輯一個 monster.rb
require 'RMagick'
include Magick
# 刪掉之前產生的測試圖形檔
`rm test.png`
# 亂數取各身體部位的某一個
partno = [ rand(4) + 1 , rand(4) + 1, rand(4) + 1, rand(14) + 1, rand(14) + 1, rand(9) + 1 ]
image = Image.new(120,120) { self.background_color = "white" }
# 順序要對,不然把圖組合起來的時候,蓋圖形的時候會不順眼
@parts = ["legs_#{partno[0]}.png","hair_#{partno[1]}.png","arms_#{partno[2]}.png","body_#{partno[3]}.png","eyes_#{partno[4]}.png","mouth_#{partno[5]}.png"]
# 這個是在還沒以亂數取部位的測試,comment起來了
#images = ImageList.new('legs_1.png','hair_1.png','arms_1.png','body_2.png','eyes_2.png','mouth_1.png')
# 用 Rmagick 把各部位的圖疊起來,
@parts.each do |part|
imagepart = ImageList.new(part)
image=image.composite(imagepart,Magick::CenterGravity, Magick::OverCompositeOp)
end
image.write('test.png')
上述的程式碼就可以將身體個部位以亂數的方式,組合成一個圖;但問題是 body 的顏色卻沒變化,在以下就會也把 body 的顏色,也透過亂數來做不同顏色的配置。
在 RoR 上建置 MonsterID
先下載http://www.splitbrain.org/_media/projects/monsterid.tgz裡面有 monsterid 的 php 的程式碼,以及 parts 目錄,將 parts 目錄中的各身體部位的圖,放在 public/monster 裡頭就可開始實作看看。
def monster
require 'RMagick'
#算出各部位之亂數
srand Time.now.to_f
partno = [ rand(5) + 1 , rand(5) + 1, rand(5) + 1, rand(15) + 1, rand(15) + 1, rand(10) + 1 ]
image = Magick::Image.new(120,120) { self.background_color = "white" }
@parts = ["legs_#{partno[0]}.png","hair_#{partno[1]}.png","arms_#{partno[2]}.png","body_#{partno[3]}.png","eyes_#{partno[4]}.png","mouth_#{partno[5]}.png"]
# 各部位逐一畫上
@parts.each do |part|
file = 'public/monster/' + part
imagepart = Magick::ImageList.new(file)
# 若是body則再亂數上色
if part =~ /body/
rgb = random_rgb
imagepart=imagepart.color_floodfill(60,60,"##{rgb}")
end
image=image.composite(imagepart,Magick::CenterGravity, Magick::OverCompositeOp)
end
#image.write('monster.png') 把檔案寫到硬碟的測試
#send_file('monster.png', :type => 'image/png', :disposition => 'inline') 這個語法無效
# 不用寫到硬碟的方法:
# http://furui.org/blog/2006/12/18/rounded-corners-in-rails/
# http://furui.org/tools_controller.rb
# 將做出來的圖型以 blob 產生
blob = image.to_blob() {
self.format = 'PNG'
self.depth = 8
}
# 再將圖形寫到 chache 中再送出來
write_fragment(params, blob)
fragment = blob
send_data fragment, :type => 'image/png', :disposition => 'inline'
end
然後要在 private 中,加上 random_rgb 亂數隨機取 RGB 的數字出來 的子程式
def random_rgb
rgb = ''
rgbarray = %w[ 0 3 6 9 c f]
1.step(6,1) do
tmp = rand(6)
rgb = rgb + rgbarray[tmp]
end
return rgb
end
# 如果要符 216 安全色的話,則改為
# 1.step(3,1) do
# tmp = rand(6)
# rgb = rgb + rgbarray[tmp] + rgbarray[tmp]
# end
利用此程式碼,最後完成的隨機 Monster ID,就會是
每次 Reload 一下,就會畫出不一樣的 MonsterID ;後來也有 Wavatars ,也是 WordPress 的 plugins,用同樣的原理,及另外製作的身體部位所畫出另一種風格的 MonsterID,在我的 Blog 普連鐵克斯 的頭像 也是利用上述 Rmagick 所畫出的例子。
所以這也是讓網站 user 註冊時,自動提供預設的圖像,又不會互相重覆的有趣解決方案。
參考資料
Monster 的最早原型來源,太有趣的網頁,flash 圖都點一點看看,包括樹
http://www.levitated.net/bones/walkingFaces/
MonsterID 的 Project 的網頁
http://www.splitbrain.org/projects/monsterid
http://www.splitbrain.org/blog/2007-01/20_monsterid_as_gravatar_fallback
WordPress 的 MonsterID 的 plugins
http://scott.sherrillmix.com/blog/blogger/wp_monsterid/