八叉树算法的改进,可以设定固定的颜色数。

八叉树算法是用来提取图像中主要颜色的算法,可用于图像压缩,提取颜色板(比如生成油画)

八叉树算法有个缺点,就是颜色数设定的不固定,比如你设定40个颜色,可能算出来的是36色,38色,40色。原因是因为合并的时候一次合并了8个子节点,其中可能有5个子叶节点,可能有3个子叶节点,反正就是数量不固定。

本次改进可以设定严格的颜色数,比如设定15个颜色,就是15个,不会多一个也不会少一个,而且可以设定8以内的颜色数 5个、3个都可以(可用于刻绘),这是原来的八叉树算法不具备的能力。下面先介绍一下八叉树原本的算法。

八叉树算法的步骤

1,定义节点

    public class TreeNode
    {
        public int Counter = 0;
        public int Rcount = 0;
        public int Gcount = 0;
        public int Bcount = 0;
        public int State = 0; //0正常 1叶子节点
        public TreeNode[] ChildrenNodes = null;
        public string ListTag = "";
        public TreeNode Farthernode = null;
    }

 

2、循环遍历图像中的每一个像素。

3、计算像素对应的节点号。计算方法:将RGB值用二进制方式表示,然后从第0~7位分别提取RGB中的一个bit重组数字。

比如 RGB 0xFE, 0x77,0x4A,对应的二进制序列就是:

11111110

01110111

01001010

从0~7为分别取一个bit就是  100,111,110,110,101,110,111,010那么它的节点号就是4,7,6,6,5,6,7,2。对应8叉树中每一层的节点号的索引。

4、从8差数根节点开始搜索节点,接上面的例子第一层是4那么就选择第一层的第四个节点,如果改节点不存在,那么创建该节点,继续下一层。如果该节点已存在,判断该节点是否是子叶节点,如果是子叶节点,子叶节点的count加1,RGB值分别加上当前像素的RGB值(最后求平均使用)。如果已经是第8层,则该节点计入子叶节点。

5、判断子叶节点列表的个数,如果超过要求的颜色数,那么找到最深层的count最小的子叶节点合并。合并规则是,将子叶节点及其兄弟节点的count r g b值求和加到它们的父节点中,将父节点标记为子叶节点并加入子叶节点列表。

最后留下来的子叶节点列表即为选定的子叶节点,求它们的rgb平均值即可得到色板。

C#实现代码如下:

 //选颜色 八叉树法
            List<Color> listColor = new List<Color>();
            listAllNodes.Clear();
            ListEndNodes.Clear();
            TreeNode root = new TreeNode();
            for (int rd = 0; rd < ArrayIndexs.Length; rd++)
            {
                int x = rd % pixWidth;
                int y = rd / pixWidth;
                List<int> indexs = new List<int>();
                int r = bytes[y * bitmapData.Stride + x * channel + 2];
                int g = bytes[y * bitmapData.Stride + x * channel + 1];
                int b = bytes[y * bitmapData.Stride + x * channel];
                for (int k = 7; k >= 0; k--)
                {
                    indexs.Add(((r & 1 << k) > 0 ? 4 : 0) + ((g & 1 << k) > 0 ? 2 : 0) + ((b & 1 << k) > 0 ? 1 : 0));
                }
                TreeNode currentNode = root;
                for (int k = 0; k < colorDeep; k++)
                {
                    if (currentNode.ChildrenNodes == null)//正常节点没有子节点的情况
                    {
                        currentNode.ChildrenNodes = new TreeNode[8];
                        while (listAllNodes.Count <= k)
                        {
                            listAllNodes.Add(new List<TreeNode>());
                        }
                    }
                    if (currentNode.ChildrenNodes[indexs[k]] == null)
                    {
                        currentNode.ChildrenNodes[indexs[k]] = new TreeNode();
                        currentNode.ChildrenNodes[indexs[k]].Farthernode = currentNode;
                        currentNode.ChildrenNodes[indexs[k]].ListTag = currentNode.ListTag + indexs[k].ToString();
                        if (k == colorDeep - 1)
                        {
                            currentNode.ChildrenNodes[indexs[k]].State = 1;
                        }
                        listAllNodes[k].Add(currentNode.ChildrenNodes[indexs[k]]);
                    }
                    currentNode = currentNode.ChildrenNodes[indexs[k]];
                    if (currentNode.State == 1)//当前是叶子节点
                    {
                        currentNode.Counter += 1;
                        currentNode.Rcount += r;
                        currentNode.Gcount += g;
                        currentNode.Bcount += b;
                        if (!ListEndNodes.Contains(currentNode))
                        {
                            ListEndNodes.Add(currentNode);
                        }
                        break;
                    }
                    if (currentNode.State == 3)//已经删除的叶子结点
                    {
                        currentNode = currentNode.Farthernode;
                        currentNode.Counter += 1;
                        currentNode.Rcount += r;
                        currentNode.Gcount += g;
                        currentNode.Bcount += b;
                        if (!ListEndNodes.Contains(currentNode))
                        {
                            ListEndNodes.Add(currentNode);
                        }
                        break;
                    }
                }
                CheckAndRemoveChild(colorCount);
            }
            listAllNodes.Clear();
            ListEndNodes.Sort((x, y) => -x.Counter.CompareTo(y.Counter));

其中 CheckAndRemoveChild 方法 就是用来合并子叶节点。本章中使用的合并方法不是一次合并8个子节点,而是一次合并一个,父节点标记为(半子叶节点),如果合并后,父节点没有子叶节点了,那么该父节点才会被标记为真正的子叶节点。半子叶节点在遍历的时候,如果下一个子叶节点是已经合并删除的子叶节点,那么count和rgb值加入到该半子叶节点上,上面的代码中有体现。


        private static void CheckAndRemoveChild(int colorCount)
        {
            while (ListEndNodes.Count > colorCount)
            {
                int maxDeep = 0;
                for (int i = 0; i < listAllNodes.Count; i++)
                {
                    if (listAllNodes[i].Count != 0)
                    {
                        if (maxDeep < i)
                        {
                            maxDeep = i;
                        }
                    }
                }
                TreeNode child = null;
                foreach (TreeNode node in listAllNodes[maxDeep])
                {
                    if (child == null)
                    {
                        child = node;
                    }
                    if (child.Counter > node.Counter)
                    {
                        child = node;
                    }
                }
                TreeNode combineNode = child.Farthernode;
                combineNode.State = 2;//半子叶节点
                if (!ListEndNodes.Contains(combineNode))
                {
                    ListEndNodes.Add(combineNode);
                }
                foreach (List<TreeNode> list in listAllNodes)
                {
                    if (list.Contains(child))
                    {
                        list.Remove(child);
                    }
                }
                ListEndNodes.Remove(child);
                combineNode.Counter += child.Counter;
                combineNode.Rcount += child.Rcount;
                combineNode.Gcount += child.Gcount;
                combineNode.Bcount += child.Bcount;
                child.State = 3;
                bool has = false;
                foreach (TreeNode node in combineNode.ChildrenNodes)
                {
                    if (node != null && node.State != 3)
                    {
                        has = true;
                        break;
                    }
                }
                if (!has)
                {
                    //如果子叶节点全部删除
                    combineNode.ChildrenNodes = null;
                    combineNode.State = 1;
                }

            }
        }

交流加306128847.

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
八叉树颜色量化算法是一种用于处理图像颜色算法。该算法通过将原始图像中的颜色值分解为RGB三个分量,将每个分量的值映射到一个[0,255]的范围内。然后,将图像中的像素点分布到一个八叉树据结构中。 八叉树是一种树状据结构,每个节点最多可以有八个子节点。在进行颜色量化时,我们通过将颜色空间划分为八个子空间来构建这个八叉树。对于每个节点,我们计算子空间中所有像素点的平均颜色值,并将其用作该节点的颜色。 在构建完成八叉树后,我们可以根据需要对图像进行压缩或减小颜色深度。压缩时,将八叉树中的叶子节点合并为一个新的节点,以减小颜色量。减小颜色深度时,我们可以对八叉树进行剪切,使得树的深度减少,进而减小颜色级别。 对于每个图像像素点,我们可以使用八叉树来查找最接近的颜色节点,并将其替换为该节点的颜色值。这样一来,就实现了图像的颜色量化。 八叉树颜色量化算法具有如下优点:对于图像较为均匀的地方,可以保留更多的细节,因为这些细节可以对应到八叉树中较为细小的子空间中;同时,对于图像中颜色分布不均匀的地方,可以将颜色量化得更加精确,避免颜色丢失。 总体而言,八叉树颜色量化算法是一种高效且灵活的处理图像颜色算法,可以在保持图像质量的同时减小颜色深度和文件大小。该算法在很多图像处理应用中被广泛使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值