程序员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也可以得到类似效果。
(完)
zython 说:
给女朋友的礼物不知道合不合适
2011年11月26日 21:44 | # | 引用
原村小和和 说:
难道真的用骰子摆出来?
2011年11月26日 21:50 | # | 引用
Brooklyn 说:
我觉得,大学的C语言就该布置这种题目,既学到东西,又能让同学们处理自己和同学的照片获得成就感。
2011年11月26日 22:40 | # | 引用
laf 说:
在使用骰子之前的部分,非常像asciiart作画,然而当前支持telnet登录的bbs的asciiart版基本都非常小众了。
2011年11月27日 01:05 | # | 引用
link 说:
要是我们的作业也这么有趣就好了
2011年11月27日 08:31 | # | 引用
降落在心灵汤的鸡 说:
if (x < 206 && x <= 247) return 6;
程序错误
2011年11月27日 22:09 | # | 引用
viperchaos 说:
楼主的文章很棒,感谢分享
2011年11月27日 22:27 | # | 引用
阮一峰 说:
谢谢指出,已更正。
2011年11月28日 07:40 | # | 引用
leoluo 说:
博主的文章很不错,看了很多篇博客,第一次留言,感谢分享,支持!!!
2011年11月28日 12:55 | # | 引用
ypchen 说:
博主的博客是怎么做的呢? 不是wordpress吧
2011年11月28日 22:44 | # | 引用
up 说:
看源代码中有此定义
PImage img;
PImage cropped_img;
PImage patch;
不知道PImage是什么?属于哪个库?想自己编译试试,看来没得玩了。
2011年11月29日 19:41 | # | 引用
阮一峰 说:
看这里 http://processing.org/reference/PImage.html
用的是Processing语言,基本上是Java的一个开源库。
2011年11月29日 20:32 | # | 引用
M16 说:
貌似是一个好点子,阮兄,不妨做个骰子图片接口,让传进来的图片生成一张筛子图,再提供个骰子样本,供爱好者拼图使用
2011年11月30日 09:17 | # | 引用
sokoban 说:
骰子作画没有用魔方作画流行啊。有没有用魔方作画的算法?
2011年12月 1日 22:38 | # | 引用
大余 说:
是 Movable Type,另一个开源的博客系统。
2011年12月 3日 16:43 | # | 引用
chaos 说:
骰子部分的图是用C画出来还是用photoshop等其他的软件制作出来的?我试着用matlab画骰子图但是无法拼出每个骰子对应单元矩阵,求教了
2011年12月 3日 22:08 | # | 引用
无人 说:
具体如何执行。请指教!
2011年12月20日 21:00 | # | 引用
乎阮醉 说:
赞成!学以致用可以激发学习兴趣,而兴趣是最好的老师
2012年3月31日 20:05 | # | 引用
牧马人 说:
昨天晚上看到这个点子,今天早上用c#实现了,不过有两个想法,一个是这个如果用在亚洲人身上的话,rgb最好越靠近255越集中,因为亚洲人眼窝没欧美那么深,如果256平均分6份的话,脸部细节可能会丢失严重。另一个就是改进色子的算法,因为直接将16x16个像素(或者类似的2^n)转为一个色子还是太笼统,比如说可以用把一个色子分成四份,看作四个色子,因为同一个色子不同摆放也是有不同形态的(比如说3可以摆成两种),如果合理地计算的话,很可能可以把实际的分辨率提高不少,当然这种算法会比较复杂……
2013年11月30日 21:53 | # | 引用
qq313066598 说:
请问能教我弄吗? 小白完全不懂但是很喜欢
2014年1月18日 11:18 | # | 引用
张彦斌 说:
2014年7月28日 16:39 | # | 引用
张彦斌 说:
请加我QQ231237456
2014年7月28日 16:41 | # | 引用
小李 说:
有趣
2015年2月28日 17:41 | # | 引用
洪先生 说:
峰哥你好。我看到你做的骰子图文章,我小白,研究了一周都弄不明白,但是又急着要图,把图发给您直接帮我换成骰子图样式的图片可以吗,有偿!急要!
2020年9月24日 02:11 | # | 引用
深井烈阳 说:
吸收思想,用canvas和JavaScript实现了,感谢大佬
2021年11月16日 11:34 | # | 引用