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

مثال على تنفيذ معالجة صورة باستخدام شجرة الأكواز في Java

بعد از مدتی کار، اولین بار که بلاگ می‌نویسم، کمی نمی‌دانم چگونه بنویسم، لطفاً همه صبر کنند و ببینند، اگر چیزی نادرست گفتم، لطفاً تصحیح کنید.

In the recent work, I used a function for image compression. I found some tools, but did not find a good choice. Finally, I chose one called jdeli, but the efficiency problem still exists. Out of无奈, I had to study its source code and found that I was interested in its color quantization algorithm, but unfortunately, I did not understand what it wrote, so I had the idea of implementing my own quantization color algorithm.

I have found some information, and found three commonly used color processing algorithms:

Popular Color Algorithm:

The specific algorithm is to first count the frequency of all colors in an image, and then select the 256 most frequently occurring colors as the color palette of the image. Then traverse all pixels in the image again, and for each pixel, find the closest color in the palette (here I use the variance method) and write it back to the image. The implementation of this algorithm is relatively simple, but the distortion is serious, and some information that appears less frequently in the image, but is very obvious to the human eye, will be lost. For example, the high brightness spots in the image, due to the low frequency of occurrence, may not be selected by the algorithm and will be lost.

Median Splitting Algorithm:

I have not studied this algorithm. Those who want to learn about it can take a lookThis article, there are introductions to three algorithms.

Octree

This algorithm is the one I finally selected, and its main idea is to convert the RGB color value of the image into a binary distribution in the octree, for example: (173, 234, 144)

Converted to binary it is (10101101, 11101010, 10010000), taking the first bit of R, G, B to form (111) as the child node of the root node, where 111 is the index of the root child node array, and so on, up to the last bit. Then store this color component value and its frequency of occurrence on the leaf node. See the picture for details.

One of the things I am quite puzzled about is the merging strategy of leaf nodes. Here, I use the simplest method, which is to find the deepest level node and then merge it. It is a bit simple and rough. If there are better methods, please leave a message for me. The image is too large to upload, so I will upload the code directly. The code has not been refactored, so please bear with it.

package com.gys.pngquant.octree;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 
 *
 * @ClassName  Class name: Node
 * @Description Function description: 
 * <p>
 *   Octree implementation
 * </p>
 * 
 *  16-12-2015 guoys creates this function.
 *
 **********************************************************
 * </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) {
		إذا (!isLeaf) {
			throw new UnsupportedOperationException();
		}
		this.rNum = rNum;
	}
	public int getgNum() {
		return gNum;
	}
	public void setgNum(int gNum) {
		إذا (!isLeaf) {
			throw new UnsupportedOperationException();
		}
		this.gNum = gNum;
	}
	العمومية int getbNum() {
		الناتج bNum;
	}
	العمومية void setbNum(int bNum) {
		إذا (!isLeaf) {
			throw new UnsupportedOperationException();
		}
		this.bNum = bNum;
	}
	العمومية int getPiexls() {
		الناتج piexls;
	}
	العمومية void setPiexls(int piexls) {
		إذا (!isLeaf) {
			throw new UnsupportedOperationException();
		}
		this.piexls = piexls;
	}
	العمومية int getDepth() {
		الناتج depth;
	}
	// العودة إلى عدد الأبناء الأصلي للنود
	العمومية int mergerLeafNode() {
		إذا (this.isLeaf) {
			الناتج 1;
		}
		this.setLeaf(true);
		int rNum = 0;
		int gNum = 0;
		int bNum = 0;
		int pixel = 0;
		int i = 0;
		للمستوى 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 الأعمق
	العمومية Node getDepestNode() {
		للمستوى int i = 7; i > 0; i--) {
			List<Node> levelList = this.levelMapping.get(i);
			إذا (!levelList.isEmpty()) {
				الناتج levelList.remove(levelList.size() - 1);
			}
		}
		الناتج null;
	}
	// الحصول على عدد الأوراق
	العمومية int getLeafNum() {
		إذا (isLeaf) {
			الناتج 1;
		}
		int i = 0;
		للمستوى Node child : this.children) {
			إذا (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){
		إذا (node == null){
			return;
		}
		إذا (node.isLeaf()){
			result.add((byte) (node.getrNum() / node.getPiexls()));
			result.add((byte) (node.getgNum() / node.getPiexls()));
			result.add((byte) (node.getbNum() / node.getPiexls()));
		} else{
			للدوران (Node child : node.getChildren()) {
				fillArray(child, result, offset);
			}
		}
	}
}

بكل تأكيد، فقط دراستي الثلاثة للكلية الوحيدة التي فشلت فيها هي بنية البيانات. تم تنفيذ الكود فقط لشجرة الأكواخ، لصياغة صورة 1920x1080، يستغرق حوالي 450ms، وإذا كان المستوى -2 فإنه يستغرق حوالي 100ms.

حسنًا، هذا هو المقال، كنت أعتقد قبل كتابته أنني أردت قول الكثير، لكنني لم أستطع أن أعرف كيف أقولها، أرجو منكم التسامح.

الخلاصة

هذا هو محتوى المقال الكامل حول مثال بسيط لتنفيذ شجرة الأكواخ باستخدام لغة Java، آمل أن يكون مفيدًا لكم. يمكن للمشتركين في ذلك الاستمرار في مراجعة مواضيع أخرى ذات صلة على هذا الموقع، وترحيبًا بالتعليقات التي تشير إلى نقاط الضعف. شكرًا للدعم الذي يقدمونه أصدقاء الموقع!

بيان: محتوى هذا المقال تم جمعه من الإنترنت، يحق للمالك الأصلي حقوق النسخ، تم إضافة المحتوى من قبل مستخدمي الإنترنت بطرق مستقلة، هذا الموقع لا يمتلك حقوق الملكية، لم يتم تعديل المحتوى بشكل يدوي، ولا يتحمل أي مسؤولية قانونية متعلقة بذلك. إذا اكتشفت محتوى يشتبه في أنه مخالف للحقوق النسخية، فلا تتردد في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال # بـ @) للإبلاغ، وتقديم الأدلة ذات الصلة، إذا تم التحقق من ذلك، سيتم حذف المحتوى المزعوم فورًا.

الشئ الذي قد يعجبك