https://blog.csdn.net/r250tgc/article/details/89604254 #define _CRT_SECURE_NO_DEPRECATE #include <windows.h> #include <cstdio> #include <cmath> #include<iostream> #include<string.h> #include<cstring>using namespace std; typedef unsigned char uint8;struct octNode { long long cnt, rSum, gSum, bSum; bool isLeaf; int depth; octNode* child[8]; octNode* pre; octNode* next; octNode() { cnt = 0; rSum = 0; gSum = 0; bSum = 0; isLeaf = false; depth = 0; pre = NULL; next = NULL; memset(child, NULL, sizeof(child)); } octNode(int d) { cnt = 0; rSum = 0; gSum = 0; bSum = 0; isLeaf = false; depth = d; pre = NULL; next = NULL; memset(child, NULL, sizeof(child)); }; octNode(uint8 r, uint8 g, uint8 b) { cnt = 1; rSum = r; gSum = g; bSum = b; isLeaf = true; depth = 9; pre = NULL; next = NULL; memset(child, NULL, sizeof(child)); }; }; class octTree {private: octNode* root; //八叉树的根 int colors; //当前的颜色总数 int maxColors; //最大颜色数 octNode** head;public: octTree(); octTree(int maxColorNum); ~octTree() ; void insertColor(uint8 r, uint8 g, uint8 b); //插入一个颜色 uint8 generatePalette(RGBQUAD* pal); //生成调色板 void deleteNode(octNode* p); void newNode(int d); };void octTree::deleteNode(octNode* p) { if (p == NULL) { return; } if (p->isLeaf == true) { colors--; delete p; return; } for (int i = 0; i < 8; i++) { deleteNode(p->child[i]); } delete p; }void octTree::newNode(int d) { root = new octNode(); colors = 0; maxColors = d; head = new octNode * [9]; for (int i = 0; i < 9; i++) { if (i == 0) { head[i] = root; } else { head[i] = NULL; } } } octTree::octTree() { newNode(0); } octTree::octTree(int maxColorNum) { newNode(maxColorNum); } octTree::~octTree() { deleteNode(root); }void octTree::insertColor(uint8 r, uint8 g, uint8 b) { int* num = new int[8]; for (int i = 0; i < 8; i++) { num[i] = 0; uint8 m = 1 << (7 - i); if (m & r) num[i] += 4; if (m & g) num[i] += 2; if (m & b) num[i] += 1; } octNode * p = root; for (int i = 0; i < 9; i++) { if (p->isLeaf == true) { p->cnt = p->cnt + 1; p->rSum = p->rSum + r; p->gSum = p->gSum + g; p->bSum = p->bSum + b; } else //p->isLeaf==false { if (p->child[num[i]] == NULL) { if (i < 7) { p->child[num[i]] = new octNode(i + 1); octNode * q = p->child[num[i]]; if (head[i + 1] == NULL) { head[i + 1] = q; } else { head[i + 1]->pre = q; q->next = head[i + 1]; head[i + 1] = q; } p = p->child[num[i]]; } else if (i == 7) { p->child[num[i]] = new octNode(r, g, b); colors++; p = p->child[num[i]]; i++; if (head[i] == NULL) { head[i] = p; } else { head[i]->pre = p; p->next = head[i]; head[i] = p; } break; } else { break; } } else //p->child[num[i]] != NULL { p = p->child[num[i]]; } } } double per = 0; int temp1 = colors - maxColors; int temp2 = colors; while (colors > maxColors) { double x= (double)(temp2 - colors) * 100 / temp1; if (x - per >= 10) { per = x; printf("%2d%%", per); } for (int u = 7; u > 0; u--) { if (head[u] == NULL) { continue; } octNode* p2 = head[u]; for (; p2 != NULL; p2 = p2->next) { if (p2->isLeaf==false) { break; } } if (p2 == NULL) { continue; } int childNum = 0; for (int i = 0; i < 8; i++) { if (p2->child[i] != NULL) { p2->cnt += p2->child[i]->cnt; p2->rSum += p2->child[i]->rSum; p2->gSum += p2->child[i]->gSum; p2->bSum += p2->child[i]->bSum; if (p2->child[i]->pre != NULL) { p2->child[i]->pre->next = p2->child[i]->next; } if (p2->child[i]->next != NULL) { p2->child[i]->next->pre = p2->child[i]->pre; } if (p2->child[i] == head[u + 1]) { head[u + 1] = p2->child[i]->next; } p2->child[i] = NULL; childNum++; } } p2->isLeaf = true; colors -= (childNum - 1); break; } } } int func(uint8 r, uint8 g, uint8 b, RGBQUAD * pal, int i) { int R = (int)(r - pal[i].rgbRed) * (r - pal[i].rgbRed); int G = (int)(g - pal[i].rgbGreen) * (g - pal[i].rgbGreen); int B = (int)(b - pal[i].rgbBlue) * (b - pal[i].rgbBlue); return R + G + B; } uint8 selectClosestColor(uint8 r, uint8 g, uint8 b, RGBQUAD * pal) { int I = 0; int V = func(r, g, b, pal, 0); for (int i = 0; i < 256; i++) { if (func(r, g, b, pal, i) < V) { I = i; V = func(r, g, b, pal, i); } } return (uint8)I; } uint8 octTree::generatePalette(RGBQUAD * pal) //生成调色板{ int number = 0; for (int L = 8; L >= 0; L--) { for (octNode* p = head[L]; p != NULL; p = p->next) { if (p == NULL) { return number; } if (!p->isLeaf) continue; pal[number].rgbRed = p->rSum / p->cnt; pal[number].rgbGreen = p->gSum / p->cnt; pal[number].rgbBlue = p->bSum / p->cnt; pal[number].rgbReserved = 0; number++; } } return number; }int main(int argc, char* argv[]) { if (argc < 3) { printf("using: exe[0], input file[1], output file[2] "); return -1; } BITMAPFILEHEADER bf, * pbf;//输入、输出文件的文件头 BITMAPINFOHEADER bi, * pbi;//输入、输出文件的信息头 RGBQUAD* pRGBQuad;//待生成的调色板指针 uint8* pImage;//转换后的图象数据 DWORD bfSize;//文件大小 LONG biWidth, biHeight;//图象宽度、高度 DWORD biSizeImage;//图象的大小,以字节为单位,每行字节数必须是4的整数倍 unsigned long biFullWidth;//每行字节数必须是4的整数倍 //打开输入文件 char* inputName, * outputName; FILE* fpIn, * fpOut; //inputName = (char*)"source.BMP"; //outputName = (char*)"result.BMP"; inputName = argv[1]; outputName = argv[2]; printf("Opening %s ... ", inputName); if (!(fpIn = fopen(inputName, "rb"))) { printf(" Can't open %s! ", inputName); return -1; } printf("Success! "); //创建输出文件 printf("Creating %s ... ", outputName); if (!(fpOut = fopen(outputName, "wb"))) { printf(" Can't create %s! ", outputName); return -1; } printf("Success! "); //读取输入文件的文件头、信息头 fread(&bf, sizeof(BITMAPFILEHEADER), 1, fpIn); fread(&bi, sizeof(BITMAPINFOHEADER), 1, fpIn); //读取文件信息 biWidth = bi.biWidth; biHeight = bi.biHeight; biFullWidth = ceil(biWidth / 4.) * 4;//bmp文件每一行的字节数必须是4的整数倍 biSizeImage = biFullWidth * biHeight; bfSize = biFullWidth * biHeight + 54 + 256 * 4;//图象文件的大小,包含文件头、信息头 //设置输出文件的BITMAPFILEHEADER pbf = new BITMAPFILEHEADER; pbf->bfType = 19778; pbf->bfSize = bfSize; pbf->bfReserved1 = 0; pbf->bfReserved2 = 0; pbf->bfOffBits = 54 + 256 * 4; //写出BITMAPFILEHEADER if (fwrite(pbf, sizeof(BITMAPFILEHEADER), 1, fpOut) != 1) { printf(" Can't write bitmap file header! "); fclose(fpOut); return -1; } //设置输出文件的BITMAPINFOHEADER pbi = new BITMAPINFOHEADER; pbi->biSize = 40; pbi->biWidth = biWidth; pbi->biHeight = biHeight; pbi->biPlanes = 1; pbi->biBitCount = 8; pbi->biCompression = 0; pbi->biSizeImage = biSizeImage; pbi->biXPelsPerMeter = 0; pbi->biYPelsPerMeter = 0; pbi->biClrUsed = 0; pbi->biClrImportant = 0; //写出BITMAPFILEHEADER if (fwrite(pbi, sizeof(BITMAPINFOHEADER), 1, fpOut) != 1) { printf(" Can't write bitmap info header! "); fclose(fpOut); return -1; } //构建颜色八叉树 printf("Building Color OctTree ... "); octTree* tree; tree = new octTree(256); uint8 RGB[3]; //读取图像中每个像素的颜色,并将其插入颜色八叉树 for (int i = 0; i < bi.biHeight; i++) { fseek(fpIn, bf.bfOffBits + i * ceil(biWidth * 3 / 4.) * 4, 0); for (int j = 0; j < bi.biWidth; j++) { //读取一个像素的颜色,并将其插入颜色八叉树 fread(&RGB, 3, 1, fpIn); tree->insertColor(RGB[2], RGB[1], RGB[0]); } } printf("Success! "); //生成并填充调色板 printf("Generating palette ... "); pRGBQuad = new RGBQUAD[256]; tree->generatePalette(pRGBQuad); //输出256色调色板 if (fwrite(pRGBQuad, 256 * sizeof(RGBQUAD), 1, fpOut) != 1) { printf(" Can't write palette! "); fclose(fpOut); return -1; } printf("Success! "); //填充图像数据 printf("Generating the output image ... "); pImage = new uint8[biSizeImage]; memset(pImage, 0, biSizeImage); for (int i = 0; i < bi.biHeight; i++) { fseek(fpIn, bf.bfOffBits + i * ceil(biWidth * 3 / 4.) * 4, 0); for (int j = 0; j < bi.biWidth; j++) { //读取一个像素的颜色,并将其转换位颜色索引值 fread(&RGB, 3, 1, fpIn); pImage[i * biFullWidth + j] = selectClosestColor(RGB[2], RGB[1], RGB[0], pRGBQuad); } } //输出图象数据 if (fwrite(pImage, biSizeImage, 1, fpOut) != 1) { printf(" Can't write image data! "); fclose(fpOut); return -1; } printf("Success! "); delete tree; delete pbf; delete pbi; delete[] pRGBQuad; delete[] pImage; fclose(fpIn); fclose(fpOut); printf("All done! "); return 0; }
https://blog.csdn.net/r250tgc/article/details/89604254
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <cstdio>
#include <cmath>
#include<iostream>
#include<string.h>
#include<cstring>using namespace std;
typedef unsigned char uint8;struct octNode
{
long long cnt, rSum, gSum, bSum;
bool isLeaf;
int depth;
octNode* child[8];
octNode* pre;
octNode* next;
octNode()
{
cnt = 0;
rSum = 0;
gSum = 0;
bSum = 0;
isLeaf = false;
depth = 0;
pre = NULL;
next = NULL;
memset(child, NULL, sizeof(child));
}
octNode(int d)
{
cnt = 0;
rSum = 0;
gSum = 0;
bSum = 0;
isLeaf = false;
depth = d;
pre = NULL;
next = NULL;
memset(child, NULL, sizeof(child));
};
octNode(uint8 r, uint8 g, uint8 b)
{
cnt = 1;
rSum = r;
gSum = g;
bSum = b;
isLeaf = true;
depth = 9;
pre = NULL;
next = NULL;
memset(child, NULL, sizeof(child));
};
};
class octTree
{private:
octNode* root; //八叉树的根
int colors; //当前的颜色总数
int maxColors; //最大颜色数
octNode** head;public:
octTree();
octTree(int maxColorNum);
~octTree() ;
void insertColor(uint8 r, uint8 g, uint8 b); //插入一个颜色
uint8 generatePalette(RGBQUAD* pal); //生成调色板
void deleteNode(octNode* p);
void newNode(int d);
};void octTree::deleteNode(octNode* p)
{
if (p == NULL)
{
return;
}
if (p->isLeaf == true)
{
colors--;
delete p;
return;
}
for (int i = 0; i < 8; i++)
{
deleteNode(p->child[i]);
}
delete p;
}void octTree::newNode(int d)
{
root = new octNode();
colors = 0;
maxColors = d;
head = new octNode * [9];
for (int i = 0; i < 9; i++)
{
if (i == 0)
{
head[i] = root;
}
else
{
head[i] = NULL;
}
}
}
octTree::octTree()
{
newNode(0);
}
octTree::octTree(int maxColorNum)
{
newNode(maxColorNum);
}
octTree::~octTree()
{
deleteNode(root);
}void octTree::insertColor(uint8 r, uint8 g, uint8 b)
{
int* num = new int[8];
for (int i = 0; i < 8; i++)
{
num[i] = 0;
uint8 m = 1 << (7 - i);
if (m & r)
num[i] += 4;
if (m & g)
num[i] += 2;
if (m & b)
num[i] += 1;
}
octNode * p = root;
for (int i = 0; i < 9; i++)
{
if (p->isLeaf == true)
{
p->cnt = p->cnt + 1;
p->rSum = p->rSum + r;
p->gSum = p->gSum + g;
p->bSum = p->bSum + b;
}
else //p->isLeaf==false {
if (p->child[num[i]] == NULL)
{
if (i < 7)
{
p->child[num[i]] = new octNode(i + 1);
octNode * q = p->child[num[i]];
if (head[i + 1] == NULL)
{
head[i + 1] = q;
}
else
{
head[i + 1]->pre = q;
q->next = head[i + 1];
head[i + 1] = q;
}
p = p->child[num[i]];
}
else if (i == 7)
{
p->child[num[i]] = new octNode(r, g, b);
colors++;
p = p->child[num[i]];
i++;
if (head[i] == NULL)
{
head[i] = p;
}
else
{
head[i]->pre = p;
p->next = head[i];
head[i] = p;
}
break;
}
else
{
break;
}
}
else //p->child[num[i]] != NULL {
p = p->child[num[i]];
}
}
}
double per = 0;
int temp1 = colors - maxColors;
int temp2 = colors;
while (colors > maxColors)
{
double x= (double)(temp2 - colors) * 100 / temp1;
if (x - per >= 10)
{
per = x;
printf("%2d%%", per);
}
for (int u = 7; u > 0; u--)
{
if (head[u] == NULL)
{
continue;
}
octNode* p2 = head[u];
for (; p2 != NULL; p2 = p2->next)
{
if (p2->isLeaf==false)
{
break;
}
}
if (p2 == NULL)
{
continue;
}
int childNum = 0;
for (int i = 0; i < 8; i++)
{
if (p2->child[i] != NULL)
{
p2->cnt += p2->child[i]->cnt;
p2->rSum += p2->child[i]->rSum;
p2->gSum += p2->child[i]->gSum;
p2->bSum += p2->child[i]->bSum;
if (p2->child[i]->pre != NULL)
{
p2->child[i]->pre->next = p2->child[i]->next;
}
if (p2->child[i]->next != NULL)
{
p2->child[i]->next->pre = p2->child[i]->pre;
}
if (p2->child[i] == head[u + 1])
{
head[u + 1] = p2->child[i]->next;
}
p2->child[i] = NULL;
childNum++;
}
}
p2->isLeaf = true;
colors -= (childNum - 1);
break;
}
}
}
int func(uint8 r, uint8 g, uint8 b, RGBQUAD * pal, int i)
{
int R = (int)(r - pal[i].rgbRed) * (r - pal[i].rgbRed);
int G = (int)(g - pal[i].rgbGreen) * (g - pal[i].rgbGreen);
int B = (int)(b - pal[i].rgbBlue) * (b - pal[i].rgbBlue);
return R + G + B;
}
uint8 selectClosestColor(uint8 r, uint8 g, uint8 b, RGBQUAD * pal)
{
int I = 0;
int V = func(r, g, b, pal, 0);
for (int i = 0; i < 256; i++)
{
if (func(r, g, b, pal, i) < V)
{
I = i;
V = func(r, g, b, pal, i);
}
}
return (uint8)I;
}
uint8 octTree::generatePalette(RGBQUAD * pal) //生成调色板{
int number = 0;
for (int L = 8; L >= 0; L--)
{
for (octNode* p = head[L]; p != NULL; p = p->next)
{
if (p == NULL)
{
return number;
}
if (!p->isLeaf)
continue;
pal[number].rgbRed = p->rSum / p->cnt;
pal[number].rgbGreen = p->gSum / p->cnt;
pal[number].rgbBlue = p->bSum / p->cnt;
pal[number].rgbReserved = 0;
number++;
}
}
return number;
}int main(int argc, char* argv[])
{
if (argc < 3)
{
printf("using: exe[0], input file[1], output file[2] ");
return -1;
}
BITMAPFILEHEADER bf, * pbf;//输入、输出文件的文件头
BITMAPINFOHEADER bi, * pbi;//输入、输出文件的信息头
RGBQUAD* pRGBQuad;//待生成的调色板指针
uint8* pImage;//转换后的图象数据
DWORD bfSize;//文件大小
LONG biWidth, biHeight;//图象宽度、高度
DWORD biSizeImage;//图象的大小,以字节为单位,每行字节数必须是4的整数倍
unsigned long biFullWidth;//每行字节数必须是4的整数倍
//打开输入文件
char* inputName, * outputName;
FILE* fpIn, * fpOut;
//inputName = (char*)"source.BMP";
//outputName = (char*)"result.BMP";
inputName = argv[1];
outputName = argv[2];
printf("Opening %s ... ", inputName);
if (!(fpIn = fopen(inputName, "rb")))
{
printf(" Can't open %s! ", inputName);
return -1;
}
printf("Success! ");
//创建输出文件
printf("Creating %s ... ", outputName);
if (!(fpOut = fopen(outputName, "wb")))
{
printf(" Can't create %s! ", outputName);
return -1;
}
printf("Success! ");
//读取输入文件的文件头、信息头
fread(&bf, sizeof(BITMAPFILEHEADER), 1, fpIn);
fread(&bi, sizeof(BITMAPINFOHEADER), 1, fpIn);
//读取文件信息
biWidth = bi.biWidth;
biHeight = bi.biHeight;
biFullWidth = ceil(biWidth / 4.) * 4;//bmp文件每一行的字节数必须是4的整数倍
biSizeImage = biFullWidth * biHeight;
bfSize = biFullWidth * biHeight + 54 + 256 * 4;//图象文件的大小,包含文件头、信息头
//设置输出文件的BITMAPFILEHEADER
pbf = new BITMAPFILEHEADER;
pbf->bfType = 19778;
pbf->bfSize = bfSize;
pbf->bfReserved1 = 0;
pbf->bfReserved2 = 0;
pbf->bfOffBits = 54 + 256 * 4;
//写出BITMAPFILEHEADER
if (fwrite(pbf, sizeof(BITMAPFILEHEADER), 1, fpOut) != 1)
{
printf(" Can't write bitmap file header! ");
fclose(fpOut);
return -1;
}
//设置输出文件的BITMAPINFOHEADER
pbi = new BITMAPINFOHEADER;
pbi->biSize = 40;
pbi->biWidth = biWidth;
pbi->biHeight = biHeight;
pbi->biPlanes = 1;
pbi->biBitCount = 8;
pbi->biCompression = 0;
pbi->biSizeImage = biSizeImage;
pbi->biXPelsPerMeter = 0;
pbi->biYPelsPerMeter = 0;
pbi->biClrUsed = 0;
pbi->biClrImportant = 0;
//写出BITMAPFILEHEADER
if (fwrite(pbi, sizeof(BITMAPINFOHEADER), 1, fpOut) != 1)
{
printf(" Can't write bitmap info header! ");
fclose(fpOut);
return -1;
}
//构建颜色八叉树
printf("Building Color OctTree ... ");
octTree* tree;
tree = new octTree(256);
uint8 RGB[3];
//读取图像中每个像素的颜色,并将其插入颜色八叉树
for (int i = 0; i < bi.biHeight; i++)
{
fseek(fpIn, bf.bfOffBits + i * ceil(biWidth * 3 / 4.) * 4, 0);
for (int j = 0; j < bi.biWidth; j++)
{
//读取一个像素的颜色,并将其插入颜色八叉树
fread(&RGB, 3, 1, fpIn);
tree->insertColor(RGB[2], RGB[1], RGB[0]);
}
}
printf("Success! ");
//生成并填充调色板
printf("Generating palette ... ");
pRGBQuad = new RGBQUAD[256];
tree->generatePalette(pRGBQuad);
//输出256色调色板
if (fwrite(pRGBQuad, 256 * sizeof(RGBQUAD), 1, fpOut) != 1)
{
printf(" Can't write palette! ");
fclose(fpOut);
return -1;
}
printf("Success! ");
//填充图像数据
printf("Generating the output image ... ");
pImage = new uint8[biSizeImage];
memset(pImage, 0, biSizeImage);
for (int i = 0; i < bi.biHeight; i++)
{
fseek(fpIn, bf.bfOffBits + i * ceil(biWidth * 3 / 4.) * 4, 0);
for (int j = 0; j < bi.biWidth; j++)
{
//读取一个像素的颜色,并将其转换位颜色索引值
fread(&RGB, 3, 1, fpIn);
pImage[i * biFullWidth + j] = selectClosestColor(RGB[2], RGB[1], RGB[0], pRGBQuad);
}
}
//输出图象数据
if (fwrite(pImage, biSizeImage, 1, fpOut) != 1)
{
printf(" Can't write image data! ");
fclose(fpOut);
return -1;
}
printf("Success! ");
delete tree;
delete pbf;
delete pbi;
delete[] pRGBQuad;
delete[] pImage;
fclose(fpIn);
fclose(fpOut);
printf("All done! ");
return 0;
}