C++原始BMP转换——将BMP转换为MC像素画以及mcfunction文件

C++原始BMP转换——将BMP转换为MC像素画以及mcfunction文件_第1张图片

介绍

编译器:MinGW GCC 9.3.0,编译命令 -Wall -Wl,--stack=998244353 -std=c++14 -O2

由于在 Devcpp 上配置 OpenCV 过于麻烦,且 OpenCV 难以学习,我便通过 了解BMP格式 + fread读取字节直接在无 OpenCV 的 Devcpp 中用数组存储了 BMP 图像(从 title.bmp 输入),并进行了颜色的近似转换,输出到了大小相同的 BMP 中(fout.bmp),同时输出了该 BMP 的 8进制txt(test.txt),以及对应的mcfunction(title.mcfunction)。

注:只支持24位 BMP 或一些32位 BMP 。

Part 1. 输入BMP

C++原始BMP转换——将BMP转换为MC像素画以及mcfunction文件_第2张图片
BMP格式详见:BMP图像文件完全解析

按照字节顺序分别存到对应位置即可。

fread() 可以按照字节读入所有,不过返回的值不一定是正确的串长,所以正确的串长应该是上图中第 3~6 个字节表示的 BMP 大小。

编译器所限,目前可以转换的最大图片为 5000 X 5000 (宽和高都不应超过)。

Part 2. 颜色转换

我在 list.txt 中记录了 MC 中 38 种颜色较纯的方块的 I D \tt ID ID 以及 R G B \tt RGB RGB 颜色参数。

list.txt 文件第一行为方块数量,目前为 38 。后面每一行为一个字符串加上三个 0~255 的整数,分别表示:方块ID、R、G、B 。

以下 list.txt 只适用于 MC java edition 1.17 及以上(因为有“铜块”):

38
white_concrete 255 255 255
orange_concrete 226 97 0
magenta_concrete 171 47 161
light_blue_concrete 31 139 201
yellow_concrete 241 176 13
lime_concrete 95 171 19
pink_concrete 215 101 144
gray_concrete 51 55 59
light_gray_concrete 125 125 115
cyan_concrete 13 119 136
purple_concrete 101 26 158
blue_concrete 40 42 144
brown_concrete 97 58 26
green_concrete 71 90 31
red_concrete 144 27 27
black_concrete 2 3 7
gold_block 254 231 77
diamond_block 112 251 240
emerald_block 65 243 132
waxed_copper_block 214 123 91
waxed_oxidized_copper 110 197 159
purpur_block 186 149 186
terracotta 155 96 69
white_terracotta 208 177 160
orange_terracotta 166 86 34
magenta_terracotta 147 82 106
light_blue_terracotta 113 107 136
yellow_terracotta 187 133 27
lime_terracotta 98 114 45
pink_terracotta 165 74 78
gray_terracotta 55 36 27
light_gray_terracotta 131 102 92
cyan_terracotta 84 89 89
purple_terracotta 121 69 86
blue_terracotta 70 54 88
brown_terracotta 73 45 27
green_terracotta 71 78 33
red_terracotta 136 51 37

适合 1.17 版本前的 list.txt (用两种海晶石近似替换了铜块):

38
white_concrete 255 255 255
orange_concrete 226 97 0
magenta_concrete 171 47 161
light_blue_concrete 31 139 201
yellow_concrete 241 176 13
lime_concrete 95 171 19
pink_concrete 215 101 144
gray_concrete 51 55 59
light_gray_concrete 125 125 115
cyan_concrete 13 119 136
purple_concrete 101 26 158
blue_concrete 40 42 144
brown_concrete 97 58 26
green_concrete 71 90 31
red_concrete 144 27 27
black_concrete 2 3 7
gold_block 254 231 77
diamond_block 112 251 240
emerald_block 65 243 132
purpur_block 186 149 186
terracotta 155 96 69
white_terracotta 208 177 160
orange_terracotta 166 86 34
magenta_terracotta 147 82 106
light_blue_terracotta 113 107 136
yellow_terracotta 187 133 27
lime_terracotta 98 114 45
pink_terracotta 165 74 78
gray_terracotta 55 36 27
light_gray_terracotta 131 102 92
cyan_terracotta 84 89 89
purple_terracotta 121 69 86
blue_terracotta 70 54 88
brown_terracotta 73 45 27
green_terracotta 71 78 33
red_terracotta 136 51 37
prismarine_bricks 105 182 171
dark_prismarine 51 86 72

然后对于原图中的一个像素,将其映射到 RGB 颜色立方体中的一个坐标点,找到 38 种方块中与该像素欧几里得距离最短的方块,就可以比较近似地转换颜色。

Part 3. 输出函数

程序一开始需要在 stdin 中输入三个数,分别是图片面向西边时的右下角 X、Y、Z 坐标,

然后,把每个像素对应的方块通过 setblock [ID] 的指令形式输出到 fout.mcfunction 就好了。

CODE

代码有彩蛋

#include 
#include 
#define MAXN 5005 // max Hight - 1
#define MAXM 5005 // max Width - 1
#define UINT unsigned int
#define UCHA unsigned char
//---------  OI part  ---------
	#include
	#include
	#include
	#include
	#include
	#include
	#include
	#include
	#define LL long long
	#define ENDL putchar('\n')
	#define DB double
	#define lowbit(x) (-(x) & (x))
	#define FI first
	#define SE second
	LL read() {
		LL f = 1,x = 0;int s = getchar();
		while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
		while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
		return f*x;
	}
	void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
	void putnum(LL x) {
		if(!x) {putchar('0');return ;}
		if(x<0) putchar('-'),x = -x;
		return putpos(x);
	}
	void AIput(LL x,int c) {putnum(x);putchar(c);}
//-----------------------------
using namespace std;
void SetFont(int size = 30) { // Useless function
	CONSOLE_FONT_INFOEX cfi;
	cfi.cbSize = sizeof cfi;
	cfi.dwFontSize.X = 4;
	cfi.dwFontSize.Y = 8;
	SetCurrentConsoleFontEx(GetStdHandle(STD_OUTPUT_HANDLE), FALSE, &cfi);
}
void putbit(char c) {
	for(int i = (1<<7);i > 0;i >>= 1) {
		putchar((c&i) ? '1':'0');
	}
	putchar(' ');
	return ;
}
UINT n,m,s,o,k;
const int CMAXN = MAXN*MAXM+100;
UCHA a[CMAXN];
struct PT{
	UCHA B,G,R,A;
	PT(){B=G=R=A=0;}
	bool isblack() {return !B && !G && !R;}
	int Y() {return (0.299*R)+(0.587*G)+(0.114*B);}
	int S() {
		int mx = max(B,max(G,R));
		if(mx == 0) return 0;
		return (mx-min(B,min(G,R)))*255/mx;
	}
}mt[MAXN][MAXM],blc[200];
int rsb[MAXN][MAXM],cntb;
LL dis(PT a,PT b) {
	LL yd = 0;//a.S()-b.S(); //                                                                         模式开关!!!把 "0;//" 去掉可添加"颜色饱和度"作为参数
	return yd*yd+(a.R-b.R)*1ll*(a.R-b.R) + (a.G-b.G)*1ll*(a.G-b.G) + (a.B-b.B)*1ll*(a.B-b.B);
}
char nameid[200][100];
int xx0,yy0,zz0;
int main() {
	xx0 = read(); yy0 = read(); zz0 = read(); // Input initial 3-dimensional coordinate
	freopen("title.bmp","r",stdin); // "title" is replaceable 
	fread(a,1,CMAXN,stdin);
	UCHA tpe[2] = {a[0],a[1]};
	UINT SIZE = ((UINT)a[5]<<24) | ((UINT)a[4]<<16) | ((UINT)a[3]<<8) | ((UINT)a[2]);
	UINT len = SIZE;
	cerr<<"SIZE:"<<len<<endl;
	freopen("test.txt","w",stdout); // output BMP with 8-base numbers
	for(UINT i = 0;i < len;i ++) printf("%02x%s",a[i],i%2 ? " ":"");
	UINT pos = ((UINT)a[13]<<24) | ((UINT)a[12]<<16) | ((UINT)a[11]<<8) | ((UINT)a[10]);
	UINT header = ((UINT)a[17]<<24) | ((UINT)a[16]<<16) | ((UINT)a[15]<<8) | ((UINT)a[14]);
	m = ((UINT)a[21]<<24) | ((UINT)a[20]<<16) | ((UINT)a[19]<<8) | ((UINT)a[18]); // Width
	n = ((UINT)a[25]<<24) | ((UINT)a[24]<<16) | ((UINT)a[23]<<8) | ((UINT)a[22]); // Hight
	// a[26] = 1 , a[27] = 0
	UINT tp = ((UINT)a[29]<<8) | ((UINT)a[28]); // Color type , a[29] = 0
	// a[30~33] = 0, meaning no compress
	UINT picsize = ((UINT)a[37]<<24) | ((UINT)a[36]<<16) | ((UINT)a[35]<<8) | ((UINT)a[34]);
	// a[38~53] = 0
	UINT ad = 54,el = (4 - (tp>>3)*m%4)%4;
	cerr<<"color_type:"<<tp<<" tail_size:"<<el<<endl;
	for(UINT i = 1;i <= n;i ++) {
		for(UINT j = 1;j <= m;j ++) {
			mt[i][j].B = a[ad ++];
			mt[i][j].G = a[ad ++];
			mt[i][j].R = a[ad ++];
			if(tp == 32) mt[i][j].A = a[ad ++];
		}
		ad += el;
	}
//	printf("size: %d, %d x %d\n",SIZE,n,m);
//	for(UINT i = n;i > 0;i --) {
//		for(UINT j = 1;j <= m;j ++) {
//			if(mt[i][j].isblack()) printf("  ");
//			else printf("##");
//		}
//		ENDL;
//	}
	freopen("list.txt","r",stdin);
	cntb = read();
	for(UINT i = 0;i < cntb;i ++) {
		scanf("%s",nameid[i]);
		blc[i].R = read();
		blc[i].G = read();
		blc[i].B = read();
	}
	blc[cntb].R = blc[cntb].G = blc[cntb].B = 0;
	ad = 54;
	freopen("title.mcfunction","w",stdout); // "title" is replaceable
	a[28] = 24u;
	el = (4-3*m%4)%4;
//	printf("fill %d %d %d %d %d %d air\n",xx0,yy0,zz0,xx0+m-1,yy0+n-1,zz0); // X and Y
	printf("fill %d %d %d %d %d %d air\n",xx0,yy0+1,zz0,xx0,yy0+n,zz0-m+1); // Y and Z
	for(int i = 1;i <= n;i ++,ad += el) {
		for(int j = 1;j <= m;j ++,ad += 3) {
			if(mt[i][j].isblack()) continue;
			LL ds = dis(mt[i][j],blc[0]);
			UINT bli = 0;
			for(UINT k = 1;k < cntb;k ++) {
				LL d2 = dis(mt[i][j],blc[k]);
				if(d2 < ds) {
					ds = d2; bli = k;
				}
			}
			rsb[i][j] = bli;
			a[ad] = blc[bli].B;
			a[ad+1] = blc[bli].G;
			a[ad+2] = blc[bli].R;
//			printf("setblock %d %d %d %s\n",xx0+j-1,yy0+i-1,zz0,nameid[bli]); // X and Y
			printf("setblock %d %d %d %s\n",xx0,yy0+i,zz0-m+j,nameid[bli]); // Y and Z
		}
		for(UINT j = 0;j < el;j ++) a[ad+j] = 0;
	}
	picsize = ad-54; len = ad;
	a[34] = picsize & 255; picsize >>= 8;
	a[35] = picsize & 255; picsize >>= 8;
	a[36] = picsize & 255; picsize >>= 8;
	a[37] = picsize & 255;
//	cerr<
	freopen("fout.bmp","w",stdout); // preview
	fwrite(a,1,len,stdout);
	return 0;
}

你可能感兴趣的:(其它,c++,bmp,图像处理,minecraft)