English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Пример кода обработки изображений с использованием octree в Java

Прошло уже некоторое время с начала работы, это первое мое письмо в блоге, я не знаю, как начать, пожалуйста, смиритесь и посмотрите, если что-то не так, пожалуйста, исправьте меня.

В последнее время в работе использовалась функция сжатия изображений. Найдены了一些 инструментов, но выбора не было. В конце концов, был выбран 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 (во время отправки письма замените # на @), чтобы сообщить о нарушении,并提供相关证据. При подтверждении факта нарушения сайт незамедлительно удалят涉嫌侵权的内容。

Рекомендуем к просмотру