English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Прошло уже некоторое время с начала работы, это первое мое письмо в блоге, я не знаю, как начать, пожалуйста, смиритесь и посмотрите, если что-то не так, пожалуйста, исправьте меня.
В последнее время в работе использовалась функция сжатия изображений. Найдены了一些 инструментов, но выбора не было. В конце концов, был выбран jdeli, но его эффективность снова стала проблемой. В отчаянии я решил изучить его исходный код и обнаружил, что мне очень интересна его функция уменьшения количества цветов. Но, к сожалению, я не понимаю, что он написал, и возникла идея реализовать свою собственную алгоритм квантализации цвета.
Найдены некоторые материалы, среди которых три наиболее часто используемых алгоритма обработки цветов:
Алгоритм流行ных цветов:
Конкретный алгоритм заключается в том, что сначала проводится статистический анализ всех цветовых значений изображения, из которых выбираются 256 самых часто встречающихся цветов в качестве цветов палитры изображения, затем снова пройдите по всем пикселям изображения и для каждого пикселя найдите наиболее близкий цвет в палитре (здесь я использую способ дисперсии), который записывается обратно в изображение. Реализация этого алгоритма довольно проста, но искажение довольно严重, и некоторые информация, которая появляется редко, но явно видна для человеческого глаза, может быть потеряна. Например, яркие пятна в изображении, которые встречаются редко, могут не быть выбраны алгоритмом и могут быть потеряны.
Метод мидисечения:
Этот алгоритм я не изучал, если кто-то хочет узнать о нем больше, пожалуйста, посмотритеЭта статья, в котором описаны три алгоритма.
Кд-дерево
Эта алгоритмическая модель - та, которую я в конечном итоге выбрал, ее основная идея - преобразовать цветовые значения RGB изображения в двоичное распределение в кд-дереве, например: (173, 234, 144)
Преобразование в двоичное представление равно (10101101, 11101010, 10010000), первый бит R, G, B собирается в (111), который является подузлом корневого узла, где 111 является индексом массива подузлов корневого узла, и так далее, до последнего бита. На листовых узлах хранится значение цвета и его частота встречаемости. Подробнее см. в изображении.
Одна из вещей, которые меня особенно интересовали, - это стратегия слияния листовых узлов. Здесь я использовал один из самых простых методов, то есть找到了 узел с最深шим уровнем и слил его, что несколько просто и грубо. Если у кого-то есть другие хорошие методы, пожалуйста, оставьте мне комментарий. Из-за того, что изображение слишком большое, чтобы загрузить, я просто приложу код. Код не был重构ирован, поэтому, пожалуйста, простите за это.
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> * * 16 декабря 2015 года 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, время обработки大概是 450 мс, если уровень -2, то大概是 100 мс.
Ну что ж, вот и все, что я хотел сказать. Вначале я чувствовал, что у меня было много что сказать, но когда я начал писать, я не знал, что и добавить. Пожалуйста, извините за это.
Обобщение
Вот и все, что касается примера кода на Java для простого реализации обработки изображений с помощью октаэдрального дерева. Надеюсь, это поможет вам. Те, кто интересуется, могут продолжить читать другие связанные темы на этом сайте. Если есть что-то неясное, пожалуйста, оставьте комментарий. Спасибо за поддержку сайта!
Заявление: содержимое этой статьи взято из интернета, авторские права принадлежат соответствующему автору. Контент предоставлен пользователями интернета, загружен ими самостоятельно, сайт не обладает правами собственности, не был отредактирован вручную и не несет ответственности за связанные с этим юридические последствия. Если вы обнаружите контент,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма замените # на @), чтобы сообщить о нарушении,并提供相关证据. При подтверждении факта нарушения сайт незамедлительно удалят涉嫌侵权的内容。