骰子作画

阮一峰博客中描述了骰子作画算法。
骰子作画_第1张图片
思想非常简单,也就是矢量量化:将图片分成若干个区域,每个区域经过计算以后,用1-6之间的一个整数表示,代表骰子的一个面,算法总共分4步。

  1. 将图片分割成16像素x16像素的小方块。

  2. 每个小方块内共有256个像素,将每个像素点的灰度值,存入一个数组。

  3. 计算该数组的平均值,并用1-6之间的一个整数来表示。

  4. 根据白点值,将骰子依次放入,就能模拟出全图

这种算法与使用的纹理密切相关,纹理越好,越是逼近,另外分割的小块越小越好。我在github上看到了haneuma0628的一个项目ImageToDice,下载下来,修改了一下,可以运行。

#include <iostream>
#include <math.h>

#include <opencv/cv.h>
#include <opencv/highgui.h>

using namespace cv;
using namespace std;

Mat trim(Mat src, int mag)
{
    int cutC, cutR;
    cutC = src.cols % mag;
    cutR = src.rows % mag;

    Mat roi;
    Size rsize(src.cols - cutC, src.rows - cutR);
    resize(src, roi, rsize, INTER_CUBIC);

    return roi;
}

Mat mozaic(Mat src)
{
    int sum = 0, mag = 12;
    Mat dst(src.size(), src.type());

    for (int y = 0; y < src.rows; y += mag)
    {
        for (int x = 0; x < src.cols; x += mag)
        {
            for (int i = 0; i < mag; i++)
            {
                for (int j = 0; j < mag; j++)
                {
                    sum += src.data[(y + i) * src.step + (x + j)];
                }
            }

            for (int i = 0; i < mag; i++)
            {
                for (int j = 0; j < mag; j++)
                {
                    dst.data[(y + i) * dst.step + (x + j)] = (int)(sum/(12*12));
                }
            }
            sum = 0;
        }
    }

    return dst;
}

int main(int argc, char *argv[])
{
    string filename = "lena.jpg";

    int cnum, rnum, mag = 12, lnum = 7;
    string fd[7];
    Mat dmat[7];

    for (int i = 0; i < lnum; i++)
        fd[i] = to_string((_ULonglong)i) + ".png";
    for (int i = 0; i < lnum; i++)
        dmat[i] = imread(fd[i], 0);

    Mat src = imread(filename, 0);
    Mat dst(src.size(), src.type());

    src = trim(src, mag);
    dst = mozaic(src);
    cnum = src.cols / mag;
    rnum = src.rows / mag;

    int *result= new int[rnum*cnum];

    Mat *himg = new Mat[cnum];
    Mat *vimg = new Mat[rnum];

    for (int y = 0; y < src.rows; y += mag)
    {
        for (int x = 0; x < src.cols; x += mag)
        {
            result[y/mag * rnum + x/mag] = dst.data[(y) * dst.step + (x)];
        }
    }

    int max = 0, min = 255;
    for (int i = 0; i < rnum; i++)
    {
        for (int j = 0; j < cnum; j++)
        {
            if (result[i*rnum+j] >= max)
                max = result[i*rnum+j];
            if (result[i*rnum+j] <= min)
                min = result[i*rnum+j];
        }
    }
    int ld;
    int level[256] = {0};
    ld = (int)((max - min) / lnum);

    int end = 0, cnt = 0;
    for (int i = max; i > min+ld; i -= ld)
    {
        for (int j = 0; j < ld; j++)
        {
            level[i-j] = cnt;
            end = i - j;
        }
        cnt++;
    }
    while (end >= min)
    {
        level[end] = 6;
        end--;
    }

    cnt=0;
    Mat vdst;
    for (int i = 0; i < rnum; i++)
    {
        for (int j = 0; j < cnum; j++)
        {
            himg[j] = imread(fd[level[result[i*rnum+j]]], 0);
        }
        hconcat(himg, cnum, vimg[i]);
    }

    vconcat(vimg, rnum, vdst);

    namedWindow("gray", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
    imshow("gray", src);

    namedWindow("mozaic", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
    imshow("mozaic", dst);

    namedWindow("dice", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
    imshow("dice", vdst);

    delete[] himg;
    delete[] vimg;

    waitKey(0);
}

注意,该代码只能处理,比较规范大小的图片,比如512*512,若是使用时候需要对边界做一些处理,建议裁剪图像到整数倍。这里使用的纹理图不是很好,影响了效果。这里使用了7个纹理。
另外,和骰子作画很相似的一个算法,是ASCII作画,github上面有源代码。
骰子作画_第2张图片
骰子作画_第3张图片

效果

骰子作画_第4张图片 骰子作画_第5张图片 骰子作画_第6张图片

Licenses

作者 日期 联系方式
风吹夏天 2015年8月16日 [email protected]

你可能感兴趣的:(算法,阮一峰,骰子作画)