第五十四章 图像→弹幕 试着制作变换工具吧(1)

>>点此回到教程目录

在本章中,我们来试着把图像变换为弹幕数据吧。

我们通过在有勾线的图像的勾线上放置子弹,来制作弹幕数据。

在这一章中使用的线画是这个。我先新建了一个叫做“AA画像”的文件夹然后把这张图片放进去。

这是拜托緑青黒羽先生绘制的图像。非常感谢。(緑青黒羽先生的站点 点此进入

这一次也是,先新建一个工程。如果觉得新建工程很麻烦的话,您可以直接使用教程附带的project中的项目,如果您不这么认为那么还是请自行新建工程。

接下来,这一次由于我们要处理图像,因此有必要先了解图像相关的知识。

有一种称为Bitmap文件(.bmp)的体积相当庞大的文件格式呢。

这是无压缩图像文件,数据被完完整整地写进文件。

因此虽然数据很庞大但是操作起来相当简单。

在DX Library中,颜色是用光的三原色的各自的亮度分量0~255来表示的。

Bitmap也是一样。每个像素都是红、蓝、绿三种颜色各自的亮度分量在0~255的范围内表示的。

也即是说,读入数据的话,

typedef struct{
        unsigned char col[3];
}img_t;

把颜色数据放入这种感觉的结构体中似乎是个不错的想法。

不过,文件有一个叫做“文件头”的东西。

“这幅图像的尺寸是○X○哦。容量是怎么样的哦。规格又是什么哦。”

这样子的信息放在了数据的开头。这个文件头部分大小为54bit,之后才是图像本身的数据。

因此,读入的时候我们扔掉54bit的数据之后再读入就行了。

关于Bitmap的内容,在 这里有详细的说明。

另外,我们要决定子弹是否放置在那里,并没有必要使用0~255的亮度分量。

倒不如说,这里只需要分为“是否有颜色”2个种类即可。

这种方法称为“二值化“。为了进行二值化,我们将所有亮度分量的平均值分为128以上和128一下即可。

总之,在这一章中

1、读入
2、把颜色信息放入保存颜色的结构体中
3、进行二值化
4、绘制

那么我们来试着实现吧。

/* Bitmap图像的宽度 */
#define BMP_YOKO 400
/* Bitmap图像的高度 */

#define BMP_TATE 400

/* Bitmap的文件头大小 */
#define HEAD 54
/* 总共大小 */
#define TOTAL (BMP_YOKO*BMP_TATE*3+HEAD)

/* 作成AA数据合计子弹数 */
#define BULLET_MAX 4000

/* 用于Bitmap颜色信息存储的结构体 */
typedef struct{
        unsigned char col[3];
}img_t;

/* TOTAL大小的数据 */
unsigned char data[TOTAL];

/* 图像的像素个数大小的颜色存储用结构体*/
img_t img[BMP_TATE][BMP_YOKO];

/* 存储二值化后的信息的数组*/
BYTE Pixel[BMP_TATE][BMP_YOKO];

总之我们先准备好这样一些变量和宏定义。

把图像设定为400×400。

TOTAL是包含文件头后的数据的比特大小。

BULLET_MAX现在暂时不使用。

/* 读入Bitmap并保存到data中的函数 */
int ReadBmp(){
    char name[256]="../AA画像/aisha.bmp";
    FILE *fp;
    fp = fopen( name , "rb" );
    if( fp == NULL ){
        printfDx( "找不到%s。",name);
        return -1;
    }
    fread( data, TOTAL, 1, fp );
    fclose(fp);
    return 0;
}

这样子就读入了Bitmap图像。这真是相当简单呢。

/* 将Bitmap的完整数据重新放入用于各个像素的颜色保存的结构体中 */
void ConvData(){
    int x,y,c,t;
    t=HEAD;
    for(y=BMP_TATE-1;y>=0;y--){
        for(x=0;x<BMP_YOKO;x++){
            for(c=0;c<3;c++){
                img[y][x].col[c]=data[t];
                t++;
            }
        }
    }
}

数据是像

“蓝绿红蓝绿红蓝绿红……”

这样子连续放入data中的,因此为了解析方便我们对将其重新放进img结构体中。

/* 二值化 */
void Binarization(){
    int x,y,c;
    int sum;
    for(y=0;y<BMP_TATE;y++){
        for(x=0;x<BMP_YOKO;x++){
            sum = 0;
            //计算颜色的平均亮度
            for(c=0;c<3;c++){
                sum += img[y][x].col[c];
            }
            sum /= 3;
            //0~255的平均亮度如果在128以上的话(亮的话)为0,不满128的话(暗的话)为1
            if( sum >= 128){
                Pixel[y][x] = 0;
            } else {
                Pixel[y][x] = 1;
            }
        }
    }
}

这样子就行了二值化。我们用sum来计算3个颜色分量的平均亮度,用这个平均亮度师傅在128以上来进行判断。

二值化后,保存到Pixel中的将会是这样:如果那里没有颜色(亮)为0,如果有颜色(暗)为1。

接下来就只是Pixel内容的绘制了。

那么我们来看看整个程序吧。

和鼠标、键盘相关的文件和上一章一样。

#include "../../../include/DxLib.h"
#include "Key_Mouse.h"

/* Bitmap图像宽度 */
#define BMP_YOKO 400
/* Bitmap图像高度 */
#define BMP_TATE 400

/* Bitmap文件头大小 */
#define HEAD 54
/* 总共大小 */
#define TOTAL (BMP_YOKO*BMP_TATE*3+HEAD)

/* 作成AA数据的总计子弹数 */
#define BULLET_MAX 4000

/* 保存Bitmap的颜色信息用的结构体 */
typedef struct{
        unsigned char col[3];
}img_t;

int Key[256];

/* TOTAL大小的数据 */
unsigned char data[TOTAL];
/* 图像的像素个数大小的颜色存储用结构体*/
img_t img[BMP_TATE][BMP_YOKO];
/* 存储二值化后的信息的数组 */
BYTE Pixel[BMP_TATE][BMP_YOKO];

/* 读入Bitmap并保存到data中的函数 */
int ReadBmp(){
    char name[256]="../AA画像/aisha.bmp";
    FILE *fp;
    fp = fopen( name , "rb" );
    if( fp == NULL ){
        printfDx( "找不到%s。",name);
        return -1;
    }
    fread( data, TOTAL, 1, fp );
    fclose(fp);
    return 0;
}

/* 将Bitmap的完整数据重新放入用于各个像素的颜色保存的结构体中 */
void ConvData(){
    int x,y,c,t;
    t=HEAD;
    for(y=BMP_TATE-1;y>=0;y--){
        for(x=0;x<BMP_YOKO;x++){
            for(c=0;c<3;c++){
                img[y][x].col[c]=data[t];
                t++;
            }
        }
    }
}

/* 二值化 */
void Binarization(){
    int x,y,c;
    int sum;
    for(y=0;y<BMP_TATE;y++){
        for(x=0;x<BMP_YOKO;x++){
            sum = 0;
            //计算颜色的平均亮度
            for(c=0;c<3;c++){
                sum += img[y][x].col[c];
            }
            sum /= 3;
            //0~255的平均亮度如果在128以上的话(亮的话)为0,不满128的话(暗的话)为1
            if( sum >= 128){
                Pixel[y][x] = 0;
            } else {
                Pixel[y][x] = 1;
            }
        }
    }
}

/* 描画 */
void Graph(){
    int x,y;
    static int Black = GetColor(0,0,0);
    DrawBox(0,0,BMP_YOKO,BMP_TATE,GetColor(255,255,255),TRUE);
    for(y=0;y<BMP_TATE;y++){
        for(x=0;x<BMP_YOKO;x++){
            if( Pixel[y][x] == 1){
                DrawPixel(x,y,Black);
            }
        }
    }
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){

    SetGraphMode(BMP_YOKO,BMP_TATE,16);//400x400に
    SetWindowSizeChangeEnableFlag(TRUE);//允许变更画面的大小
    ChangeWindowMode(TRUE);//窗口模式
    if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;//初始化和里画面化

    ReadBmp();//读入Bitmap
    ConvData();//保存到图像的颜色信息结构体中
    Binarization();//二值化

while(ProcessMessage()==0/*消息处理*/ && ClearDrawScreen()==0 /*清空画面*/&& GetHitKeyStateAll_2(Key)==0 /*保存输入状态*/&& Key[KEY_INPUT_ESCAPE]==0/* ESC键没有被按下*/){
        Graph();//绘制
        ScreenFlip();
    }

    DxLib_End();
    return 0;
}

运行结果


由于二值化后的Pixel数组的内容会作为进行DrawPixel的基础,因此应该可以理解从Biitmap图像中完整地读入数据然后变换得以进行的这一操作。

>>点此回到教程目录

results matching ""

    No results matching ""