八叉树算法将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位彩色图像的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 MFC 将 24 位彩色图像转换为二进制图像?

如何使用 OpenCV C++ 将 24 位深度的 PNG 图像转换为 8 位深度的 PNG 图像

彩色图像转变为灰度图像的matlab的程序代码是啥?

数字图像C++8位和24位BMP位图的平滑锐化二值化处理,以及24位真彩图的灰度化

使用 C 的 24 位 bmp 中的边缘检测

有关MATLAB和图像处理问题