八叉树颜色量化的方法-----真彩图转化为256色图

真彩色:是指图像中的每个像素值都分成R、G、B三个基色分量,每个基色分量直接决定其基色的强度,这样产生的色彩称为真彩色。

256色图:仅表示256种颜色,在bmp文件格式种采用调色板(Palette)来保存图片的色彩信息,图片在进行色彩表达式需要进行索引。

介绍将真彩图转化为256色图的方法:八叉树颜色量化(octree color quantization)。

使用C以及opencv实现。

#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <opencv.hpp>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>

using namespace std;
using namespace cv;


static unsigned char mask[8] =
{ 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
unsigned int maxcolor = 16;
unsigned int currentcolor = 0;
bool pri = false;

typedef struct NODE
{
	bool IsLeaf;				//是否为叶子节点		
	bool Reduce;				//是否可归并
	unsigned int count;			//此节点记录了多少种像素颜色
	unsigned int level;			//层级,当 level = 7就是叶子节点

	unsigned int redsum;		//红色分量总和
	unsigned int greensum;		//绿色分量总和
	unsigned int bluesum;		//蓝色分量总和
	unsigned int mapto;			//映射到容器中,肯根据索引查找
	unsigned int childnum;		//不为空的子节点数量

	NODE* ptrChild[8];			//子节点初始化为空


	NODE(unsigned int lev)
	{
		level = lev;
		redsum = greensum = bluesum = count = childnum = 0;
		mapto = -1;
		for (int i = 0; i < 8; i++)
		{
			ptrChild[i] = NULL;
		}
		IsLeaf = false;
		Reduce = true;
	}

}*PNODE;

std::vector<PNODE> LeafNodes;
std::vector<int> HeadNodes[8];

void RecursiveReduce(int idx)
{
	if (LeafNodes[idx]->Reduce == false)
	{
		return;
	}
	LeafNodes[idx]->Reduce = false;
	for (int i = 0; i < 8; i++)
	{
		if (LeafNodes[idx]->ptrChild[i] != NULL)
			RecursiveReduce(LeafNodes[idx]->ptrChild[i]->mapto);
	}
}

//减少节点
void ReduceColor()
{
	int minidx = -1;
	for (int i = 7; i >= 0; i--)
	{
		int mincount = 0x7f7f7f;
		for (int j = 0; j < HeadNodes[i].size(); j++)
		{
			int idx = HeadNodes[i][j];
			if (LeafNodes[idx]->Reduce && LeafNodes[idx]->childnum >= 2 && mincount > LeafNodes[idx]->childnum)
			{
				mincount = LeafNodes[idx]->childnum;
				minidx = idx;
			}
		}
		if (minidx != -1)
			break;
	}
	RecursiveReduce(minidx);
	LeafNodes[minidx]->IsLeaf = true;
	currentcolor -= (LeafNodes[minidx]->childnum - 1);
	if (pri)
		cout << "得到索引" << minidx << "\t目前的颜色数量" << currentcolor << endl;
}

void addColor(PNODE &anode, unsigned int red, unsigned int green, unsigned int blue, bool newnode)
{
	if (anode->level < 8 && newnode)									//将该节点的索引放进所在层中,方便之后搜索
	{
		HeadNodes[anode->level].push_back(anode->mapto);
	}
	anode->redsum += red;
	anode->greensum += green;
	anode->bluesum += blue;
	anode->count += 1;													//即上图中的数量,即节点代表的像素数量
	if (anode->IsLeaf)
		return;

	if (anode->level == 8)												//如果是第8层
	{
		anode->IsLeaf = true;
		anode->Reduce = false;
		if (newnode)													//新颜色,需要新建一个叶子节点
		{
			currentcolor += 1;
			anode->IsLeaf = true;
			if (currentcolor > maxcolor)
			{
				if (pri)
					cout << "需要减去颜色" << endl;
				ReduceColor();
			}
		}
	}
	else
	{
		unsigned int shift = 7 - anode->level;								//用于计算7颜色的7位二进制
		unsigned int Idx = (((red & mask[anode->level]) >> shift) << 2) |
			(((green & mask[anode->level]) >> shift) << 1) |
			((blue & mask[anode->level]) >> shift);
		if (anode->ptrChild[Idx] == nullptr)								//子节点不存在,需要新建一个
		{

			anode->ptrChild[Idx] = new NODE(anode->level + 1);
			LeafNodes.push_back(anode->ptrChild[Idx]);
			anode->ptrChild[Idx]->mapto = LeafNodes.size() - 1;
			anode->childnum += 1;
			addColor(anode->ptrChild[Idx], red, green, blue, true);
		}
		else
		{
			addColor(anode->ptrChild[Idx], red, green, blue, false);
		}
	}
}

//建立调色盘
int QueryColor(PNODE& anode, unsigned int red, unsigned int green, unsigned int blue)
{
	if (anode->IsLeaf)
	{
		return anode->mapto;
	}
	else
	{
		unsigned int shift = 7 - anode->level;
		unsigned int Idx = (((red & mask[anode->level]) >> shift) << 2) |
			(((green & mask[anode->level]) >> shift) << 1) |
			((blue & mask[anode->level]) >> shift);
		if (pri)
			cout << "shift=" << shift << "\tidx=" << Idx << "下一层数是" << anode->level + 1 << endl;
		return QueryColor(anode->ptrChild[Idx], red, green, blue);
	}
}

int main()
{

	Mat srcImg = imread("bread.png");

	imshow("原图", srcImg);

	
	PNODE RootNode = new NODE(0);
	LeafNodes.push_back(RootNode);
	RootNode->mapto = 0;
	HeadNodes[0].push_back(0);
	for (int i = 0; i < srcImg.rows; i++)
	{
		for (int j = 0; j < srcImg.cols; j++)
		{
			addColor(RootNode, srcImg.at<Vec3b>(i, j)[2], srcImg.at<Vec3b>(i, j)[1], srcImg.at<Vec3b>(i, j)[0], false);

		}
	}

	for (int i = 0; i < srcImg.rows; i++)
	{
		for (int j = 0; j < srcImg.cols; j++)
		{
			int vectoridx = QueryColor(RootNode, srcImg.at<Vec3b>(i, j)[2], srcImg.at<Vec3b>(i, j)[1], srcImg.at<Vec3b>(i, j)[0]);
			if (pri)
				cout << "vectoridx =" << vectoridx << endl;
			srcImg.at<Vec3b>(i, j)[2] = LeafNodes[vectoridx]->redsum / LeafNodes[vectoridx]->count;
			srcImg.at<Vec3b>(i, j)[1] = LeafNodes[vectoridx]->greensum / LeafNodes[vectoridx]->count;
			srcImg.at<Vec3b>(i, j)[0] = LeafNodes[vectoridx]->bluesum / LeafNodes[vectoridx]->count;

		}
	}
	
	imshow("处理后的图", srcImg);
	imwrite("out.bmp", srcImg);
	waitKey(0);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值