一晃工作有段时间了,第一次写博客,有点不知道怎么写,大家将就着看吧,说的有什么不正确的也请大家指正。
最近工作中用到了一个图像压缩的功能。找了一些工具,没有太好的选择。最后选了一个叫jdeli的,奈何效率又成了问题。我迫于无奈就只能研究了下它的源码,却发现自己对它的一个减色量化算法起了兴趣,可是尴尬的自己完全不明白它写的什么,就起了一个自己实现一个量化颜色算法的念头。
自己找了一些资料,找到三个比较常用的颜色处理算法:
流行色算法:
具体的算法就是,先对一个图像的所有颜色出现的次数进行统计,选举出出现次数最多的256个颜色作为图片的调色板的颜色,然后再次遍历图片的所有像素,对每个像素找出调色板中的最接近的颜色(这里我用的是方差的方式),写回到图片中。这个算法的实现比较简单,但是失真比较严重,图像中一些出现频率较低,但对人眼的视觉效挺明显的信息将丢失。比如,图像中存在的高亮度斑点,由于出现的次数少,很可能不能被算法选中,将被丢失。
中位切分算法:
这个算法我没有研究,想要了解的同学,可以看下这篇文章,里面有三种算法的介绍。
八叉树
这个算法就是我最后选用的算法,它的主要思想就是把图像的RGB颜色值转成二进制分布到八叉树中,例如:(173,234,144)
转成二进制就是(10101101,11101010,10010000),将R,G,B的第一位取出来组成(111),作为root节点的子节点,其中111作为root子节点数组的索引,以此类推,一直到最后一位,然后在叶子节点上存放这个颜色的分量值以及其出现的次数。具体看图。
其中我比较疑惑的有一个处理就是叶子节点的合并策略,这儿我用的最笨的一个方法,就是找到层次最深的节点,然后合并,有点简单粗暴,有别的比较好的方法,也请大家给我留言。图片太大上传不了了,直接上代码了,代码没有重构,大家凑合看吧。
package com.gys.pngquant.octree; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * * * @ClassName 类名:Node * @Description 功能说明: * <p> * 八叉树实现 * </p> * * 2015-12-16 guoys 创建该类功能。 * ********************************************************** * </p> */ public class Node{ private int depth = 0; // 为0时为root节点 private Node parent; private Node[] children = new Node[8]; private Boolean isLeaf = false; private int rNum = 0; private int gNum = 0; private int bNum = 0; private int piexls = 0; private Map<Integer, List<Node>> levelMapping; // 存放层次和node的关系 public int getRGBValue(){ int r = this.rNum / this.piexls; int g = this.gNum / this.piexls; int b = this.bNum / this.piexls; return (r << 16 | g << 8 | b); } public Map<Integer, List<Node>> getLevelMapping() { return levelMapping; } public void afterSetParam(){ if(this.getParent() == null && this.depth == 0){ levelMapping = new HashMap<Integer, List<Node>>(); for (int i = 1; i <= 8; i++) { levelMapping.put(i, new ArrayList<Node>()); } } } public int getrNum() { return rNum; } public void setrNum(int rNum) { if(!isLeaf){ throw new UnsupportedOperationException(); } this.rNum = rNum; } public int getgNum() { return gNum; } public void setgNum(int gNum) { if(!isLeaf){ throw new UnsupportedOperationException(); } this.gNum = gNum; } public int getbNum() { return bNum; } public void setbNum(int bNum) { if(!isLeaf){ throw new UnsupportedOperationException(); } this.bNum = bNum; } public int getPiexls() { return piexls; } public void setPiexls(int piexls) { if(!isLeaf){ throw new UnsupportedOperationException(); } this.piexls = piexls; } public int getDepth() { return depth; } // 返回节点原有的子节点数量 public int mergerLeafNode(){ if(this.isLeaf){ return 1; } this.setLeaf(true); int rNum = 0; int gNum = 0; int bNum = 0; int pixel = 0; int i = 0; for (Node child : this.children) { if(child == null){ continue; } rNum += child.getrNum(); gNum += child.getgNum(); bNum += child.getbNum(); pixel += child.getPiexls(); i += 1; } this.setrNum(rNum); this.setgNum(gNum); this.setbNum(bNum); this.setPiexls(pixel); this.children = null; return i; } // 获取最深层次的node public Node getDepestNode(){ for (int i = 7; i > 0; i--) { List<Node> levelList = this.levelMapping.get(i); if(!levelList.isEmpty()){ return levelList.remove(levelList.size() - 1); } } return null; } // 获取叶子节点的数量 public int getLeafNum(){ if(isLeaf){ return 1; } int i = 0; for (Node child : this.children) { if(child != null){ i += child.getLeafNum(); } } return i; } public void setDepth(int depth) { this.depth = depth; } public Node getParent() { return parent; } public void setParent(Node parent) { this.parent = parent; } public Node[] getChildren() { return children; } public Node getChild(int index){ return children[index]; } public void setChild(int index, Node node){ children[index] = node; } public Boolean isLeaf() { return isLeaf; } public void setPixel(int r, int g, int b){ this.rNum += r; this.gNum += g; this.bNum += b; this.piexls += 1; } public void setLeaf(Boolean isLeaf) { this.isLeaf = isLeaf; } public void add8Bite2Root(int _taget, int _speed){ if(depth != 0 || this.parent != null){ throw new UnsupportedOperationException(); } int speed = 7 + 1 - _speed; int r = _taget >> 16 & 0xFF; int g = _taget >> 8 & 0xFF; int b = _taget & 0xFF; Node proNode = this; for (int i=7;i>=speed;i--){ int item = ((r >> i & 1) << 2) + ((g >> i & 1) << 1) + (b >> i & 1); Node child = proNode.getChild(item); if(child == null){ child = new Node(); child.setDepth(8-i); child.setParent(proNode); child.afterSetParam(); this.levelMapping.get(child.getDepth()).add(child); proNode.setChild(item, child); } if(i == speed){ child.setLeaf(true); } if(child.isLeaf()){ child.setPixel(r, g, b); break; } proNode = child; } } public static Node build(int[][] matrix, int speed){ Node root = new Node(); root.afterSetParam(); for (int[] row : matrix) { for (int cell : row) { root.add8Bite2Root(cell, speed); } } return root; } public static byte[] mergeColors(Node root, int maxColors){ byte[] byteArray = new byte[maxColors * 3]; List<byte> result = new ArrayList<byte>(); int leafNum = root.getLeafNum(); try{ while(leafNum > maxColors){ int mergerLeafNode = root.getDepestNode().mergerLeafNode(); leafNum -= (mergerLeafNode - 1); } } catch(Exception e){ e.printStackTrace(); } fillArray(root, result, 0); int i = 0; for (byte byte1 : result) { byteArray[i++] = byte1; } return byteArray; } private static void fillArray(Node node, List<byte> result, int offset){ if(node == null){ return; } if(node.isLeaf()){ result.add((byte) (node.getrNum() / node.getPiexls())); result.add((byte) (node.getgNum() / node.getPiexls())); result.add((byte) (node.getbNum() / node.getPiexls())); } else{ for (Node child : node.getChildren()) { fillArray(child, result, offset); } } } }
可怜我大学唯二挂的数据结构。代码实现的只是八叉树,对一个1920*1080图片量化,耗时大概是450ms,如果层次-2的话大概是100ms左右。
好吧,这篇就这样吧,本来写之前,感觉自己想说的挺多的,结果写的时候就不知道怎么说了,大家见谅。
总结
以上就是本文关于java简单实现八叉树图像处理代码示例的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!
本文向大家介绍JS二叉树的简单实现方法示例,包括了JS二叉树的简单实现方法示例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了JS二叉树的简单实现方法。分享给大家供大家参考,具体如下: 今天学习了一下 二叉树的实现,在此记录一下 简单的二叉树实现,并且实现升序和降序排序输出 更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《JavaScript数据结构与算法技巧总结》、《J
本文向大家介绍java 实现简单圣诞树的示例代码(圣诞节快乐),包括了java 实现简单圣诞树的示例代码(圣诞节快乐)的使用技巧和注意事项,需要的朋友参考一下 代码如下: 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。
本文向大家介绍javascript实现二叉树的代码,包括了javascript实现二叉树的代码的使用技巧和注意事项,需要的朋友参考一下 前言: 二叉树的特点(例图只是二叉树的一种情况,不要尝试用例图推理以下结论) 除了最下面一层,每个节点都是父节点,每个节点都有且最多有两个子节点; 除了嘴上面一层,每个节点是子节点,每个节点都会有一个父节点; 最上面一层的节点(即例图中的节点50)为根节点; 最下
本文向大家介绍C++ HLSL实现简单的图像处理功能,包括了C++ HLSL实现简单的图像处理功能的使用技巧和注意事项,需要的朋友参考一下 由于对于dxva2解码得到的数据不宜copy回内存给CPU处理,所以最好的办法是在GPU上直接进行处理。D3D的像素着色器能够对像素直接进行操作,实现点运算极其简单方便,简单的卷积运算效果也非常好。但D3D9的限制也很多,对于过于复杂的图像处理则显得有些不能胜
本文向大家介绍Java 队列实现原理及简单实现代码,包括了Java 队列实现原理及简单实现代码的使用技巧和注意事项,需要的朋友参考一下 Java 队列实现原理 “队列”这个单词是英国人说的“排”。在英国“排队”的意思就是站到一排当中去。计算机科学中,队列是一种数据结构,有点类似栈,只是在队列中第一个插入的数据项也会最先被移除,而在栈中,最后插入的数据项最先移除。队列的作用就像电影院前的人们站成
本文向大家介绍python实现二叉查找树实例代码,包括了python实现二叉查找树实例代码的使用技巧和注意事项,需要的朋友参考一下 本文研究的主要是python实现二叉查找树的相关内容,具体介绍及实现如下。 1. 二叉查找树的定义: 左子树不为空的时候,左子树的结点值小于根节点,右子树不为空时,右子树的结点值大于根节点,左右子树分别为二叉查找树 2. 二叉查找树的最左边的结点即为最小值,要查找最小