1.问题描述
[问题描述]
栅格数据的四叉树编码是一种经典的压缩编码方式,最早应用于加拿大地理信息系统(CGIS)中,是GIS原理课程中的重要知识点之一,在GIS专业领域有广泛应用。因此,课程组将四叉树编码与解码问题纳入课程设计选题。具体选题设计包括问题描述、基本要求和实现提示三个方面:
区域型物体的四叉树表示方法将四叉树结构表达的信息以一种线性编码的方式存储起来,称之为四叉树编码,其在图像分割、数据压缩、地理信息系统等方面应用广泛。请实现二值栅格数据的四叉树压缩编码/解码算法,即设计四叉树数据结构及算法,将给定的二值图像转换为四叉树线性编码方式实现压缩存储,并设计解码算法将压缩编码文件解码还原为二值图像。
[实验要求]
(1)读取一个待压缩的栅格数据文件(BMP格式),文件中的每个像元仅有(0、1)两种取值(即二值图像),用一个矩阵存储原始数据;
(2)对该矩阵进行自上而下的四叉树分割,构建四叉树结构;
(3)对得到的所有叶子节点进行线性四叉树编码,将编码作为一个数组写入文件;
(4)实现线性四叉树编码的解码程序,即读取压缩编码文件将其还原为栅格图像数据文件。
[实现提示]
本题完成的关键在于对栅格数据四叉树剖分、四叉树构建以及线性四叉树编码几个知识点的理解。其中四叉树剖面和线性四叉树编码的基本思路见图4,解码过程与编码正好相反。相关概念与方法描述如下:
(1)四叉树剖分方法。将图像区域按大小相同的象限4等分,每个象限又可根据一定规则判断是否继续等分为次一层的4个象限。若子象限只含一种属性代码,则停止继续分割。图像区域的栅格阵列应为2n×2n,如果图像不是一个2n×2n的尺寸,通过边缘增补空白行、列的方式凑足。如图4中(a)(b)所示,一个8×8的栅格,经过3层四叉树剖分后,得到的每个单元只含有一种属性代码。
(2)线性四叉树编码方法。如图4(c)所示,四叉树剖分结果逻辑上可用一棵四叉树表示,树中每个非叶子结点有4个分支,每个叶子结点对应的是只含有一种属性代码的剖分区域。线性编码的目的就是记录每个叶结点在四叉树中的位置和值,其中值就是子区的属性代码,所以重点是如何在编码中表达结点的位置。常用的线性四叉树编码方式有:a.基于深度和层次码的方式;b.基于四进制的方式;c.基于十进制的方式。以第一种方式为例,用一个32位的二进制数表示节点的位置,具体分为两部分,最右边4位记录该叶子结点的深度,从右第5位开始往左的28位依次记录从叶节点到根结点的路径。用0、1、2、3分别表示北西(NW)、北东(NE)、南西(SW)和南东(SE)。例如,图4(c)中编号为10的叶子结点在树中位置信息的线性编码为:00000000 00000000 00000010 01110011。

图4 栅格数据四叉树构建及其线性编码
虽然,四叉树及其压缩编码的相关概念对GIS专业二年级的学生可能是全新的,通过查阅专业资料,相信同学们能够将四叉树及其编码方法相关问题与数据结构课程所学的知识点之间建立紧密的联系。例如,栅格数据的表达实际上就是数据结构中介绍的二维数组;同时,四叉树编码的结果也可用数组存储;需要构建的四叉树,其实就是二叉树的一种扩展。数据结构课程中对二叉树作了详细介绍,同学们应该可以根据对二叉树的理解举一反三,去实现四叉树的存储结构和相关操作算法。
为了降低难度,我们给学生提供了读写栅格数据文件(BMP格式)的源程序及使用说明,让学生能够从这种基础但不涉及核心问题的代码编写工作中解放出来,集中精力解决主要问题。
2.代码
我们的项目包含3个文件,一个头文件bmp.h和两个cpp文件:bmp.cpp和mainfile.cpp
老师给的源代码包括:bmp.h bmp.cpp 和mainfile.cpp里关于读取bmp的语句
我们完成的:项目里除去老师提供的代码
(1)bmp.h文件
#pragma once
#include <stdio.h>
typedef struct
{
// unsigned short bfType; //不单独读就出错 (2bytes)
unsigned long bfSize; //文件大小,字节单位表示(4bytes)
unsigned short bfReserved1;//保留,必须设0(2bytes)
unsigned short bfReserved2;//保留,必须设0(2bytes)
unsigned long bfOffBits;//说明从文件头到实际图像数据之间的偏移量(4bytes)
} ClBitMapFileHeader;
//C语言结构体大小计算规则:
//1、每个成员的偏移量都必须是当前成员所占内存大小的整数倍如果不是编译器会在成员之间加上填充字节。
//2、当所有成员大小计算完毕后,编译器判断当前结构体大小是否是结构体中最宽的成员变量大小的整数倍
//如果不是会在最后一个成员后做字节填充。
typedef struct
{
unsigned long biSize;//说明位图信息头结构所需要的字节数(4bytes)
long biWidth;//列出,像素为单位(4bytes)
long biHeight;//行数,像素为单位(4bytes)
unsigned short biPlanes;//为目标设备说明位面数,其值将总是被设为1(2bytes)
unsigned short biBitCount;//说明比特数/象素,其值为1、4、8、16、24、或32,平时用到的图像绝大部分是24位(真彩色)和8位256色(2bytes)
unsigned long biCompression;//说明图象数据压缩的类型,一般没有没有压缩的类型,一般为0 (4bytes)
unsigned long biSizeImage;//说明图象的大小,以字节为单位(4bytes)
long biXPelsPerMeter; //说明水平分辨率,用象素/米表示(4bytes)
long biYPelsPerMeter;//说明垂直分辨率,用象素/米表示(4bytes)
unsigned long biClrUsed;//说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。(4bytes)
unsigned long biClrImportant;//说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要(4bytes)
} ClBitMapInfoHeader;
typedef struct
{
unsigned char rgbBlue; //该颜色的蓝色分量
unsigned char rgbGreen; //该颜色的绿色分量
unsigned char rgbRed; //该颜色的红色分量
unsigned char rgbReserved; //保留值
} ClRgbQuad;
typedef struct
{
int width; //宽度,像素单位
int height;//高度,像素单位
int channels;//通道数
unsigned char* imageData;//图像数据
}ClImage;
//读取文件
ClImage* clLoadImage(char* path);
//保存文件
bool clSaveImage(char* path, ClImage* bmpImg);
(2)bmp.cpp文件
#include "bmp.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//获取一个字节中pos位的值(0或1)
unsigned char read_bit(char c, int pos)
{
char b_mask = 0x01;
b_mask = b_mask << pos;
//字符c和b_mask做位运算如果还是等于b_mask,说明该位为1
if ((c&b_mask) == b_mask)
return 1;
else
return 0;
}
//将一个Char的所有bit以单个数值(0、1)的形式解析出来
void get_bitsofchar(unsigned char bits[8], char c)
{
int k = 0;
unsigned char t;
for (k = 0; k < 8; k++)
{
bits[k] = read_bit(c, k);
}
//逆序排列各个bit位——计算机中顺序是从低位向高位排列的
for (k = 0; k < 4; k++)
{
t = bits[k];
bits[k] = bits[7 - k];
bits[7 - k] = t;
}
}
/*
读取BMP文件,返回一个ClImage对象
*/
ClImage* clLoadImage(char* path)
{
ClImage* bmpImg;
FILE* pFile;
unsigned short fileType;
ClBitMapFileHeader bmpFileHeader;
ClBitMapInfoHeader bmpInfoHeader;
int channels = 1;
int width = 0;
int height = 0;
int step = 0;
int offset = 0;
unsigned char pixVal;
ClRgbQuad* quad;
int i, j, k;
bmpImg = (ClImage*)malloc(sizeof(ClImage));
//pFile = fopen(path, "rb");
fopen_s(&pFile, path, "rb");
if (!pFile)
{
free(bmpImg);
return NULL;
}
//如果不先读取bifType,根据C语言结构体Sizeof运算规则——整体大于部分之和,从而导致读文件错位
fread(&fileType, sizeof(unsigned short), 1, pFile);
if (fileType == 0x4D42)
{
printf("文件类型标识正确! \n");
//读文件头(12bytes,除去bifType)
fread(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
printf("bmp文件头信息:\n");
printf("文件大小:%d \n", bmpFileHeader.bfSize);
printf("保留字:%d \n", bmpFileHeader.bfReserved1);
printf("保留字:%d \n", bmpFileHeader.bfReserved2);
printf("位图数据偏移字节数:%d \n", bmpFileHeader.bfOffBits);
//读位图信息头40bytes
fread(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
printf("bmp文件信息头\n");
printf("结构体长度:%d \n", bmpInfoHeader.biSize);
printf("位图宽度:%d \n", bmpInfoHeader.biWidth);
printf("位图高度:%d \n", bmpInfoHeader.biHeight);
printf("位图平面数:%d \n", bmpInfoHeader.biPlanes);
printf("颜色位数:%d \n", bmpInfoHeader.biBitCount);
printf("压缩方式:%d \n", bmpInfoHeader.biCompression);
printf("实际位图数据占用的字节数:%d \n", bmpInfoHeader.biSizeImage);
printf("X方向分辨率:%d \n", bmpInfoHeader.biXPelsPerMeter);
printf("Y方向分辨率:%d \n", bmpInfoHeader.biYPelsPerMeter);
printf("使用的颜色数:%d \n", bmpInfoHeader.biClrUsed);
printf("重要颜色数:%d \n", bmpInfoHeader.biClrImportant);
printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
//获取图像宽度,高度
width = bmpInfoHeader.biWidth;
height = bmpInfoHeader.biHeight;
bmpImg->width = width;
bmpImg->height = height;
if (bmpInfoHeader.biBitCount == 1) {//二值图
printf("该文件有调色板,即该位图二值图\n\n");
channels = 1;
offset = ((int)ceil(width / 8.0)) % 4; //一个字节存8个像素
//求每行末尾的空字节数
if (offset != 0)
{
offset = 4 - offset;
}
bmpImg->channels = 1;
bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*height);
step = channels*width; //每行宽度
quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad) * 2); //读取调色板
fread(quad, sizeof(ClRgbQuad), 2, pFile);
free(quad);
int w = (int)ceil(width / 8.0);//每行占的字节数
unsigned char bits[8];
int m = 0;
for (i = 0; i < height; i++)
{
m = 0;
for (j = 0; j < w; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
//获取字符中没一位的值,以一个unsigned char[8]的数组存储
get_bitsofchar(bits, pixVal);
//把每个字节的8位值解析出来,分别存入8个字节
for (int k = 0; k < 8; k++) {
if (m < width) {
// count[(height - 1 - i)*step + m] = bits[k];
if (bits[k] == 1) { //把值1映射为8位图中的255
bits[k] = 255;
}
// 坐标原点在左下角=》(height - 1 - i)*step+j,通过这个变换坐标原点变为左上角
bmpImg->imageData[(height - 1 - i)*step + m] = bits[k];
}
m++;
}
}
if (offset != 0)
{
for (j = 0; j < offset; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile); //读取每行的空字节
}
}
}
/*for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
printf("%d ", bmpImg->imageData[i*width + j]);
}
printf("\n");
}*/
}
else if (bmpInfoHeader.biBitCount == 4) {//十六色位图
//一般不用这种格式
}
else if (bmpInfoHeader.biBitCount == 8)//256色位图
{
printf("该文件有调色板,即该位图为非真彩色8位256色位图\n\n");
channels = 1;
offset = (channels*width) % 4;
if (offset != 0)
{
offset = 4 - offset;
}
bmpImg->channels = 1;
bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*height);
step = channels*width; //每行宽度
quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad) * 256); //读取调色板
fread(quad, sizeof(ClRgbQuad), 256, pFile);
free(quad);
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
// 坐标原点在左下角=》(height - 1 - i)*step+j,通过这个变换坐标原点变为左上角
bmpImg->imageData[(height - 1 - i)*step + j] = pixVal;
}
if (offset != 0)
{
for (j = 0; j < offset; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile); //读取每行的空字节
}
}
}
}
//16位高彩色图
else if (bmpInfoHeader.biBitCount == 16) {
//一般不用这种格式
}
//真彩色
else if (bmpInfoHeader.biBitCount == 24)
{
printf("该位图为位真彩色\n\n");
channels = 3;
bmpImg->channels = 3; //每个像元占3列
bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width * 3 * height);
step = channels*width;
offset = (channels*width) % 4;
if (offset != 0)
{
offset = 4 - offset; //计算空白数据,因为每行的长度只能是4的整数倍,如果不是,则以空白补上
}
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
for (k = 0; k < 3; k++) //三个通道分别读取
{
fread(&pixVal, sizeof(unsigned char), 1, pFile);
bmpImg->imageData[(height - 1 - i)*step + j * 3 + k] = pixVal;
}
//kzSetMat(bmpImg->mat, height-1-i, j, kzScalar(pixVal[0], pixVal[1], pixVal[2]));
}
if (offset != 0)
{
for (j = 0; j < offset; j++)
{
fread(&pixVal, sizeof(unsigned char), 1, pFile); //读空白
}
}
}
}
else if (bmpInfoHeader.biBitCount == 32) { //32位位图--具有透明透明通道
//一般不用这种格式
}
}
return bmpImg;
}
bool clSaveImage(char* path, ClImage* bmpImg)
{
FILE *pFile;
unsigned short fileType;
ClBitMapFileHeader bmpFileHeader;
ClBitMapInfoHeader bmpInfoHeader;
int step;
int offset;
unsigned char pixVal = '\0';
int i, j;
ClRgbQuad* quad;
//pFile = fopen(path, "wb");
fopen_s(&pFile, path, "wb");
if (!pFile)
{
return false;
}
//
fileType = 0x4D42;
fwrite(&fileType, sizeof(unsigned short), 1, pFile);
if (bmpImg->channels == 3)//24位,通道,彩图
{
step = bmpImg->channels*bmpImg->width;
offset = step % 4;
if (offset != 0)
{
offset = 4 - offset;
step += offset;
}
/*一个BUG
if (offset != 4) {
step += 4 - offset;
}*/
//bmpFileHeader.bfType = 0x4D42;
bmpFileHeader.bfSize = bmpImg->height*step + 54;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = 54;
fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
bmpInfoHeader.biSize = 40;
bmpInfoHeader.biWidth = bmpImg->width;
bmpInfoHeader.biHeight = bmpImg->height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 24;
bmpInfoHeader.biCompression = 0;
bmpInfoHeader.biSizeImage = bmpImg->height*step;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 0;
bmpInfoHeader.biClrImportant = 0;
fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
for (i = bmpImg->height - 1; i > -1; i--)
{
for (j = 0; j < bmpImg->width; j++)
{
pixVal = bmpImg->imageData[i*bmpImg->width * 3 + j * 3];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
pixVal = bmpImg->imageData[i*bmpImg->width * 3 + j * 3 + 1];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
pixVal = bmpImg->imageData[i*bmpImg->width * 3 + j * 3 + 2];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
if (offset != 0)
{
for (j = 0; j < offset; j++)
{
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
else if (bmpImg->channels == 1)//8位,单通道,灰度图
{
step = bmpImg->width;
offset = step % 4;
if (offset != 0)
{
offset = 4 - offset;
step += offset;
}
//bmpFileHeader.bfType = 0x4D42;
bmpFileHeader.bfSize = 54 + 256 * 4 + bmpImg->width;
bmpFileHeader.bfReserved1 = 0;
bmpFileHeader.bfReserved2 = 0;
bmpFileHeader.bfOffBits = 54 + 256 * 4;
fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
bmpInfoHeader.biSize = 40;
bmpInfoHeader.biWidth = bmpImg->width;
bmpInfoHeader.biHeight = bmpImg->height;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 8;
bmpInfoHeader.biCompression = 0;
bmpInfoHeader.biSizeImage = bmpImg->height*step;
bmpInfoHeader.biXPelsPerMeter = 0;
bmpInfoHeader.biYPelsPerMeter = 0;
bmpInfoHeader.biClrUsed = 256;
bmpInfoHeader.biClrImportant = 256;
fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
//调色板是一个灰度图
quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad) * 256);
for (i = 0; i < 256; i++)
{
quad[i].rgbBlue = i;
quad[i].rgbGreen = i;
quad[i].rgbRed = i;
quad[i].rgbReserved = 0;
}
fwrite(quad, sizeof(ClRgbQuad), 256, pFile);
free(quad);
for (i = bmpImg->height - 1; i > -1; i--)
{
for (j = 0; j < bmpImg->width; j++)
{
pixVal = bmpImg->imageData[i*bmpImg->width + j];
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
if (offset != 0)
{
for (j = 0; j < offset; j++)
{
pixVal = 0;
fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
}
}
}
}
fclose(pFile);
return true;
}
(3)mainfile.cpp文件
#define _CRT_SECURE_NO_WARNINGS
#include "bmp.h"
#include <stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct QuadNode
{
int rowStart; //区域的起始行
int colStart; //区域的起始列
int size; //区域大小
/*char code[32]; */ //线性编码,用一个32位的字符来表示,我们这里用一个字符型,蕴含图像位置,最后四位表示层数
ElemType value; //存0或1,-1表示此节点不是叶子节点
struct QuadNode* children[4]; //四个指针,指向他的四个孩子,指针为空代表没有孩子
}QuadTreeNode, * QuadTree; //定义四叉树结构体类型
//定义存放栅格单元值的二维数组
typedef struct reQuadTreeNode
{
int value; //节点的值
reQuadTreeNode* rechildren[4];
};
//根据伪代码构建四叉链表,从底层开始分割,采用递归的方式构建起来后
//遍历四个方向和层数
/*1.构建四叉树递归函数*/
QuadTreeNode* CreatTree(unsigned char** data, int rowStart, int colStart, int size)
//二维数组data存放bmp图像像元值,rowStart行的起始,colStart列的起始,size是行数或列数
{
QuadTreeNode* node = new QuadTreeNode();//给新四叉树节点分配存储空间
node->rowStart = rowStart;
node->colStart = colStart;
node->size = size;
if (size <= 1)
{
node->value = data[rowStart][colStart];
for (int i = 0; i < 4; i++) //给四个孩子指针指向,初始化
{
node->children[i] = NULL;
}
return node; //返回值类型应与函数类型相同,为QuadTreeNode,四叉树节点型
}
int halfsize = size / 2;
node->children[0] = CreatTree(data, rowStart, colStart, halfsize); //位置在数学第二象限
node->children[1] = CreatTree(data, rowStart, colStart+halfsize, halfsize);//第一象限
node->children[2] = CreatTree(data, rowStart+halfsize, colStart, halfsize);//第三象限
node->children[3] = CreatTree(data, rowStart + halfsize, colStart+halfsize, halfsize);//第四象限
int sum = 0;
for (int i = rowStart; i < rowStart + size; i++)
{
for (int j = colStart; j < colStart + size; j++)
{
sum += data[i][j]; //所有栅格数值之和
}
}
node->value = sum / (size * size); //先划分成四个象限
return node;
}
/*递归写入节点信息*/
void WriteQuadTreeNode(QuadTreeNode* node, FILE* fout, int depth)
{
fwrite(&depth, sizeof(depth), 1, fout);
int code = 0;
for (int i = 0; i < depth; i++)
{
code <<= 2; //int型是32位,code<<=2,代表向左移动两位
}
fwrite(&code, sizeof(code), 1, fout);
//写入节点信息
fwrite(&node->value, sizeof(code), 1, fout);
bool isleaf = (node->children[0] == NULL && node->children[1] == NULL &&
node->children[2] == NULL && node->children[3] == NULL);//注意是‘==’,不是‘=’
fwrite(&isleaf, sizeof(isleaf), 1, fout);
if (!isleaf)
{
for (int i = 0; i < 4; i++)
{
WriteQuadTreeNode(node->children[i], fout, depth + 1);
}
}
}
//将四叉树编码写入文件
void WriteCodeToFile(QuadTreeNode* root, const char* filename)
{
FILE* fout = fopen(filename, "wb");
//写入根节点信息
fwrite(&root->rowStart, sizeof(root->rowStart), 1, fout);
fwrite(&root->colStart, sizeof(root->colStart), 1, fout);
fwrite(&root->size, sizeof(root->size), 1, fout);
//递归地写入子节点信息
int depth = 0;
WriteQuadTreeNode(root, fout, depth);
fclose(fout);
}
//从文件中读取四叉树编码并构建四叉树
reQuadTreeNode* CreatQuadTreeFromCode(FILE* fin)
{
int value;
fread(&value, sizeof(value), 1, fin);
if (value == -1) //不是叶子节点
{
return NULL;
}
reQuadTreeNode* node = new reQuadTreeNode;
node->value = value;
for (int i = 0; i < 4; i++)
{
node->rechildren[i] = CreatQuadTreeFromCode(fin);
}
return node;
}
/*将四叉树还原为栅格数据*/
void ReturnRasterFromQuadTree(reQuadTreeNode* node, int** raster, int rowstart, int colstart, int size)
{
if (!node) //如果节点为空?里面没有东西
{
return;
}
if (size == 1)
{
raster[rowstart][colstart] = node->value;
return;
}
int halfsize = size / 2;
ReturnRasterFromQuadTree(node->rechildren[0], raster, rowstart, colstart, halfsize);
ReturnRasterFromQuadTree(node->rechildren[0], raster, rowstart, colstart+halfsize, halfsize);
ReturnRasterFromQuadTree(node->rechildren[0], raster, rowstart+halfsize, colstart, halfsize);
ReturnRasterFromQuadTree(node->rechildren[0], raster, rowstart+halfsize, colstart+halfsize, halfsize);
}
/*将栅格数据写入文件*/
void WriteRasterToFile(const char* filename, int** raster, int numRows, int numCols)
{
FILE* fout = fopen(filename, "wb");
if (!fout)
{
printf("Failed to open file for writing :%s\n", filename);
return;
}
//写入行数和列数
fwrite(&numRows, sizeof(int), 1, fout);
fwrite(&numCols, sizeof(int), 1, fout);
//写入栅格数据
for (int i = 0; i < numRows; i++)
{
fwrite(raster[i], sizeof(int), numCols, fout);
}
fclose(fout);
}
/*释放四叉树的内存*/
void FreeQuadTree(reQuadTreeNode* node)
{
if (!node)
{
return;
}
for (int i = 0; i < 4; i++)
{
FreeQuadTree(node->rechildren[i]);
}
delete node;
}
/*从四叉树编码文件中读取还原栅格文件*/
void ReturnToRasterFromTreeCode(const char* filename, const char* outputfilename)
{
FILE* fin = fopen(filename, "rb");
if (!fin)
{
printf("Failed to open file for reading:%s\n", filename);
return;
}
//读取根节点信息
int rowstart, colstart, size;
fread(&rowstart, sizeof(rowstart), 1, fin);
fread(&colstart, sizeof(colstart), 1, fin);
fread(&size, sizeof(size), 1, fin);
//构建四叉树
reQuadTreeNode* root = CreatQuadTreeFromCode(fin);
fclose(fin);
//还原为栅格数据
int numrows = rowstart + size;
int numcols = colstart + size;
int** raster = new int* [numrows];
for (int i = 0; i < numrows; i++)
{
raster[i] = new int[numrows];
}
ReturnRasterFromQuadTree(root, raster, rowstart, colstart, size);
//将栅格数据写入文件
WriteRasterToFile(outputfilename, raster, numrows, numcols);
//释放内存
FreeQuadTree(root);
for (int i = 0; i < numrows; i++)
{
delete[]raster[i];
}
delete[]raster;
}
void main()
{
unsigned char** imageArray; //矩阵名
ClImage* img = clLoadImage("bmp1.bmp");//读取
int ROWS = img->height;
int COLS = img->width;
imageArray = (unsigned char**)malloc(sizeof(unsigned char*) * ROWS);
for (int i = 0; i < ROWS; i++)
{
imageArray[i] = (unsigned char*)malloc(sizeof(unsigned char*) * COLS * img->channels);
}
for (int i = 0; i < ROWS; i++)
{
for (int j = 0; j < COLS * img->channels; j++)
{
imageArray[i][j] = img->imageData[i * COLS * img->channels + j];
}
}
for (int i = 0; i < ROWS; i++)
{
free(imageArray[i]);
}
free(imageArray);
//四叉树编码的二维数组构建
imageArray = new unsigned char* [ROWS];
for (int i = 0; i < ROWS; i++)
{
imageArray[i] = new unsigned char[COLS]; //行数组分配存储空间
for (int j = 0; i < COLS; j++)
{
imageArray[i][j] = img->imageData[i * COLS + j]; //给每个对应位置数组赋栅格像元值
}
}
QuadTreeNode* root;
root = CreatTree(imageArray, 0, 0, ROWS);
//对四叉树进行线性编码,并以数组的形式写入文件
WriteCodeToFile(root, "quadtree.bin");
//读取编码,还原为栅格图像数据
ReturnToRasterFromTreeCode("quadtree.bin", "rasterImage.bmp");
bool flag = clSaveImage("result.bmp", img);//存入
if (flag)
{
printf("save ok... \n");
}
}
3.报错显示

课程组将四叉树编码与解码问题纳入课程设计选题,要求实现二值栅格数据的四叉树压缩编码/解码算法,包括读取BMP文件、构建四叉树结构、进行线性编码及解码等。项目包含3个文件,老师提供部分源代码,学生完成其余代码。
2万+





