八叉树算法将24位bmp图像转变为8位彩色图像
Posted h694879357
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八叉树算法将24位bmp图像转变为8位彩色图像相关的知识,希望对你有一定的参考价值。
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; }
以上是关于八叉树算法将24位bmp图像转变为8位彩色图像的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 OpenCV C++ 将 24 位深度的 PNG 图像转换为 8 位深度的 PNG 图像