骰子作画的算法

作者: 阮一峰

日期: 2011年11月26日

程序员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也可以得到类似效果。

(完)

留言(25条)

给女朋友的礼物不知道合不合适

难道真的用骰子摆出来?

我觉得,大学的C语言就该布置这种题目,既学到东西,又能让同学们处理自己和同学的照片获得成就感。

在使用骰子之前的部分,非常像asciiart作画,然而当前支持telnet登录的bbs的asciiart版基本都非常小众了。

要是我们的作业也这么有趣就好了

if (x < 206 && x <= 247) return 6;

程序错误

楼主的文章很棒,感谢分享

引用降落在心灵汤的鸡的发言:

if (x < 206 && x <= 247) return 6;

程序错误

谢谢指出,已更正。

博主的文章很不错,看了很多篇博客,第一次留言,感谢分享,支持!!!

博主的博客是怎么做的呢? 不是wordpress吧

看源代码中有此定义
PImage img;
PImage cropped_img;
PImage patch;
不知道PImage是什么?属于哪个库?想自己编译试试,看来没得玩了。

引用up的发言:

不知道PImage是什么?属于哪个库?想自己编译试试,看来没得玩了。

看这里 http://processing.org/reference/PImage.html

用的是Processing语言,基本上是Java的一个开源库。

貌似是一个好点子,阮兄,不妨做个骰子图片接口,让传进来的图片生成一张筛子图,再提供个骰子样本,供爱好者拼图使用

骰子作画没有用魔方作画流行啊。有没有用魔方作画的算法?

引用ypchen的发言:

博主的博客是怎么做的呢? 不是wordpress吧

是 Movable Type,另一个开源的博客系统。

骰子部分的图是用C画出来还是用photoshop等其他的软件制作出来的?我试着用matlab画骰子图但是无法拼出每个骰子对应单元矩阵,求教了

具体如何执行。请指教!

引用Brooklyn的发言:

我觉得,大学的C语言就该布置这种题目,既学到东西,又能让同学们处理自己和同学的照片获得成就感。

赞成!学以致用可以激发学习兴趣,而兴趣是最好的老师

昨天晚上看到这个点子,今天早上用c#实现了,不过有两个想法,一个是这个如果用在亚洲人身上的话,rgb最好越靠近255越集中,因为亚洲人眼窝没欧美那么深,如果256平均分6份的话,脸部细节可能会丢失严重。另一个就是改进色子的算法,因为直接将16x16个像素(或者类似的2^n)转为一个色子还是太笼统,比如说可以用把一个色子分成四份,看作四个色子,因为同一个色子不同摆放也是有不同形态的(比如说3可以摆成两种),如果合理地计算的话,很可能可以把实际的分辨率提高不少,当然这种算法会比较复杂……

请问能教我弄吗? 小白完全不懂但是很喜欢

引用牧马人的发言:

昨天晚上看到这个点子,今天早上用c#实现了,不过有两个想法,一个是这个如果用在亚洲人身上的话,rgb最好越靠近255越集中,因为亚洲人眼窝没欧美那么深,如果256平均分6份的话,脸部细节可能会丢失严重。另一个就是改进色子的算法,因为直接将16x16个像素(或者类似的2^n)转为一个色子还是太笼统,比如说可以用把一个色子分成四份,看作四个色子,因为同一个色子不同摆放也是有不同形态的(比如说3可以摆成两种),如果合理地计算的话,很可能可以把实际的分辨率提高不少,当然这种算法会比较复杂……


请加我QQ231237456 跪谢

请加我QQ231237456

有趣

峰哥你好。我看到你做的骰子图文章,我小白,研究了一周都弄不明白,但是又急着要图,把图发给您直接帮我换成骰子图样式的图片可以吗,有偿!急要!

吸收思想,用canvas和JavaScript实现了,感谢大佬

我要发表看法

«-必填

«-必填,不公开

«-我信任你,不会填写广告链接