程式設計師Scott MacDonald做了一個很有趣的專案----骰子作畫。
他用黑底白點的骰子。
模擬出一張人像照片。
把影像放大,就可以看得更清楚。
他一共用了2500多顆骰子。
最後的成品就是這樣。
任何一張圖片都可以用骰子模擬出來,演算法非常簡單:將圖片分成若干個區域,每個區域經過計算以後,用1-6之間的一個整數表示,代表骰子的一個面。這種將連續的量轉化成不連續的整數的演算法,屬於vector quantization(向量量化)的一個應用。
具體來說,
第一步,將圖片分割成16畫素x16畫素的小方塊。
for (int i=0; i < (pic_width/16); ++i) {
for (int j=0; j < (pic_height/16); ++j) {
patch = cropped_img.get(i*16, j*16, 16, 16);
}
}
第二步,每個小方塊內共有256個畫素,將每個畫素點的灰度值,存入一個陣列。
for (int k=0; k < patch.pixels.length; ++k) {
x[k] = rgb2gray(patch.pixels[k]);
}
int rgb2gray(int argb) {
int _alpha = (argb >> 24) & 0xFF;
int _red = (argb >> 16) & 0xFF;
int _green = (argb >> 8 ) & 0xFF;
int _blue = (argb) & 0xFF;
return int(0.3*_red + 0.59*_green + 0.11*_blue);
}
第三步,計算該陣列的平均值,並用1-6之間的一個整數來表示。
int dice_num = six_step_gray(mean(x));
int mean(int[] x) {
float m = 0;
for (int i=0; i < x.length; ++i) {
m += x[i];
}
m = m/x.length;
return int(m);
}
int six_step_gray(int x) {
if (0 <= x && x <= 41) return 1;
if (41 < x && x <= 83) return 2;
if (83 < x && x <= 124) return 3;
if (124 < x && x <= 165) return 4;
if (165 < x && x <= 206) return 5;
if (206 < x && x <= 247) return 6;
else return 6;
}
整數1,表示骰子朝上的一面有1個白點;整數2,表示有2個白點;以此類推。白點越少,表示這個區域越接近全黑;白點越多,表示越接近全白。根據白點值,將骰子依次放入,就能模擬出全圖。
這種演算法早在1981年就有人提出,當時用的是1~9個白點的多米諾骨牌。
如果區域劃分得越小,模擬圖的生成效果就越好。
此外,不用程式設計,使用Photoshop也可以得到類似效果。
(完)