`

基于二次误差度量的网格简化

    博客分类:
  • Java
阅读更多
直接贴结果了:







界面:



这个还有点价值吧,麻烦下的人给贡献点分了:

http://download.csdn.net/source/2019425
or

http://www.pudn.com/downloads223/sourcecode/graph/detail1049804.html



主要的类:
package MeshSimpliefication_QuadErr;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.ObjectInputStream.GetField;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.TreeSet;

import javax.vecmath.Point3d;
import javax.xml.ws.FaultAction;

/**
 * 由j3d中的ObjectFile修改而来,仅用了其读文件的部分(也有修改)
 * @author Chao Liang(梁超),TsingHua university; Student ID:2009310399
 *         {@link essay1986@yahoo.com.cn}
 * 
 */
public class ObjFileSimplified_v2 {

	ArrayList<VertexComplicated> vertexList; // 顶点列表
	int vertexNumber;// 现在的顶点的真实数量
	TreeSet<EdgeWithCost> costSet;// 边折叠损耗的列表,用于排序(半自动)
	// HashSet<FaceTriangle> faces;//不需要,只在输出时用到,到时才创建
	private long time;

	public ObjFileSimplified_v2(String fileName) {
		try {
			time = System.currentTimeMillis();
			load(fileName);// 载入文件
			time = System.currentTimeMillis() - time;
			System.out.println("读文件“" + fileName + "”用时: " + ((double) time) / 1000 + " s");
		} catch (Exception e) {
			e.printStackTrace();
		}
		time = System.currentTimeMillis();
		init_Q_Costs();// 初始化损耗函数
		System.out.println("初始化用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");
	}

	/**
	 * 读取一个顶点
	 * @param st
	 * @throws ParsingErrorException
	 */
	void readVertex(ObjectFileParser st) throws ParsingErrorException {
		Point3d p = new Point3d();
		st.getNumber();
		p.x = st.nval;
		st.getNumber();
		p.y = st.nval;
		st.getNumber();
		p.z = st.nval;
		st.skipToNextLine();
		// Add this vertex to the array
		vertexList.add(new VertexComplicated(vertexList.size(), p));
	} // End of readVertex

	/**
	 * readFace 将各个面的边分别存到对应的顶点中;修改过
	 */
	void readFace(ObjectFileParser st) throws ParsingErrorException {
		ArrayList<Integer> points = new ArrayList<Integer>();

		while (st.ttype != StreamTokenizer.TT_EOL) {
			st.getNumber();
			points.add((int) st.nval - 1);
			// st.getNumber();
			st.getToken();
			if (st.ttype == StreamTokenizer.TT_EOL)
				break;
			else
				st.pushBack();
		}

		FaceTriangle face = new FaceTriangle(points);
		// faces.add(face);//不需要,只在输出时用到,到时才创建
		// for (FaceTriangle f : faces) {
		// System.out.println(f);
		// }
		for (int i = 0; i < points.size(); i++) {// 增加边和面片,其中边以另一顶点的索引的方式存储在该顶点中
			int point1 = points.get(i);
			vertexList.get(point1).addFace(face);// 加入面片
			TreeSet<Integer> edgeInd = vertexList.get(point1).getEdgeIndexes();
			for (int j = i + 1; j < points.size(); j++) {
				int point2 = points.get(j);
				if (edgeInd.contains(point2))
					continue;
				EdgeWithCost edge = new EdgeWithCost(point1, point2);
				vertexList.get(point1).edges.add(edge);
				vertexList.get(point2).edges.add(edge);
			}
		}
		st.skipToNextLine();
	} // End of readFace

	/**
	 * readFile
	 * 
	 * Read the model data from the file.
	 */
	void readFile(ObjectFileParser st) throws ParsingErrorException {

		st.getToken();
		while (st.ttype != ObjectFileParser.TT_EOF) {
			if (st.ttype == ObjectFileParser.TT_WORD) {
				if (st.sval.equals("v")) {
					readVertex(st);
				} else if (st.sval.equals("f")) {
					readFace(st);
				} else {
				}
			}
			st.skipToNextLine();
			// Get next token
			st.getToken();
		}

		vertexNumber = vertexList.size();
	} // End of readFile

	/**
	 * The Object File is loaded from the .obj file specified by the filename.
	 * To attach the model to your scene, call getSceneGroup() on the Scene
	 * object passed back, and attach the returned BranchGroup to your scene
	 * graph. For an example, see j3d-examples/ObjLoad/ObjLoad.java.
	 */
	public void load(String filename) throws FileNotFoundException, IncorrectFormatException,
			ParsingErrorException {
		Reader reader = new BufferedReader(new FileReader(filename));
		load(reader);
	} // End of load(String)

	/**
	 * The Object File is loaded from the already opened file. To attach the
	 * model to your scene, call getSceneGroup() on the Scene object passed
	 * back, and attach the returned BranchGroup to your scene graph. For an
	 * example, see j3d-examples/ObjLoad/ObjLoad.java.
	 */
	public void load(Reader reader) throws FileNotFoundException, IncorrectFormatException,
			ParsingErrorException {
		ObjectFileParser st = new ObjectFileParser(reader);
		vertexList = new ArrayList<VertexComplicated>();
		// faces = new HashSet<FaceTriangle>();//不需要,只在输出时用到,到时才创建
		readFile(st);
	} // End of load(Reader)

	/**
	 * 打印该模型文件,输出顶点和边表
	 */
	void print() {
		int indexFrom1 = 1;
		System.out.print("points={");
		for (int i = 0; i < vertexList.size(); i++) {
			if (vertexList.get(i) != null) {
				if (i % 4 == 0) {
					System.out.println();
					System.out.print("\t");
				}
				Point3d p = vertexList.get(i).vertex;
				System.out.print("p(" + (i + indexFrom1) + ")=[ " + p.x + ", " + p.y + ", " + p.z
						+ "];   ");
			}
		}
		System.out.println();
		System.out.println("}\\end of points");

		System.out.println("edges={");// /////edges
		for (int i = 0; i < vertexList.size(); i++) {
			if (vertexList.get(i) != null) {
				System.out.print("\t" + (i + indexFrom1) + ":  ");
				// TreeSet<Integer> edges = vertexList.get(i).edgeIndexes;
				// for (int integer : edges) {
				// System.out.print((integer + indexFrom1) + ",  ");
				// }
				ArrayList<EdgeWithCost> edges = vertexList.get(i).edges;
				for (EdgeWithCost edge : edges) {
					System.out.print((edge.getOtherVertex(i) + indexFrom1) + ",  ");
				}
				System.out.println();
			}
		}
		System.out.println("}end of edges");
	}

	void init_Q_Costs() {
		for (VertexComplicated v : vertexList) {
			v.calculateQ(vertexList);// 初始化Q
		}

		// 计算各个边的边折叠损耗
		for (VertexComplicated v : vertexList) {
			ArrayList<EdgeWithCost> edges = v.edges;
			for (EdgeWithCost edge : edges) {
				if (edge.isSmallVertexIndex(v.index))
					edge.cal_v_cost(vertexList);
			}
		}
		costSet = new TreeSet<EdgeWithCost>();
		for (VertexComplicated v : vertexList) {
			ArrayList<EdgeWithCost> edges = v.edges;
			for (EdgeWithCost edge : edges) {

				if (edge.isSmallVertexIndex(v.index)) {
					boolean b = costSet.add(edge);
					// if(!b) System.out.println(edge);;
				}
			}
			// if(costSet.size()>0&&e!=null)System.out.println(costSet.contains(e));

		}

		// for (VertexComplicated v : vertexList) {
		// ArrayList<EdgeWithCost> edges = v.edges;
		// EdgeWithCost e = null;
		// for (EdgeWithCost edge : edges) {
		// if (!costSet.contains(edge))
		// System.out.println(edge);
		// else
		// System.out.println("                               正确            " +
		// edge);
		// // System.out.println(costSet.contains(edge));
		// }
		//
		// }
		// System.out.println(costSet);
		// // System.out.println();
		// System.out.println();
		// System.out.println(vertexList.get(0).edges.get(0));
		// System.out.println(costSet.contains(vertexList.get(0).edges.get(0)));
	}

	/**
	 * 边收缩操作一次,合并一条消耗最小的边的两个顶点, 快速,只更新本顶点的边折叠损耗
	 */
	void contractEdgeFast() {
		EdgeWithCost edgeLeastCost = costSet.pollFirst();// 取出消耗最小的边;已从消耗列表中删除
		VertexComplicated vSmall = vertexList.get(edgeLeastCost.getSmallVertex());// 编号小的顶点
		VertexComplicated vBig = vertexList.get(edgeLeastCost.getBigVertex());// 编号大的顶点
		vSmall.setErrorPairContraction(edgeLeastCost.cost);// step2:更新顶点的消耗
		ArrayList<EdgeWithCost> edgeSmall = vSmall.edges;

		TreeSet<Integer> edgeIndSmall = vSmall.getEdgeIndexes();// 获取临边的定点标号
		edgeSmall.remove(edgeLeastCost);// 从自己的边中删除消耗最小的边
		vBig.edges.remove(edgeLeastCost);

		// 更新Faces: 将vBig中的面片的索引换为vSmall的索引,若vSmall也含有该面片则删除它
		for (FaceTriangle face : vBig.faces) {
			if (face.isVertex(vSmall.index)) {// vSmall也含有该面片,从vSmall的面片中删除,并从面片列表中删除
				vSmall.removeFace(face);
				vertexList.get(face.getAnothorVertex(vSmall.index, vBig.index)).removeFace(face);
				// faces.remove(face);//不需要,只在输出时用到,到时才创建
			} else {
				face.setVertex(vBig.index, vSmall.index);
				vSmall.addFace(face);
			}
		}

		for (EdgeWithCost e : vBig.edges) {
			boolean b = costSet.remove(e);// 先从消耗函数中移除,以改动后删除免出问题
			if ((!edgeIndSmall.contains(e.getOtherVertex(vBig.index)))) {
				e.setVertex(vBig.index, vSmall.index);// 更新边链接坐标,因而不用重新算边
				edgeSmall.add(e);
			} else {
				b = vertexList.get(e.getOtherVertex(vBig.index)).edges.remove(e);// 重复的边,从另一顶点移除
			}
		}

		// 更新vSmall及周围顶点的Q
		vSmall.calculateQ(vertexList);
		for (EdgeWithCost e : edgeSmall) {
			int vInd = e.getOtherVertex(vSmall.index);
			vertexList.get(vInd).calculateQ(vertexList);
		}

		// 更新vSmall的边压缩损耗及周围点的边的的边压缩损耗;
		// 注意:vSmall的边也含在周围的顶点的边中,无需重复更新。
		// 另外,由于不一定这些顶点为较小节点,因而必须全部更新,可能产生重复计算
		for (EdgeWithCost e : edgeSmall) {
			boolean b = costSet.remove(e);// 先删除,否则排序会有错;由于vSmall的周围顶点间可能有边,因而可能重复删除而返回false,不影响
			e.cal_v_cost(vertexList);// 更新边压缩损耗
			costSet.add(e);// 重新加入

		}
		vertexList.set(vBig.index, null);// 标定为null,相当于移除
		vertexNumber--;
	}

	/**
	 * 边收缩操作一次,合并一条消耗最小的边的两个顶点 慢速,更新所有受到影响的边的折叠损耗
	 */
	void contractEdge() {
		// System.err.println(vertexList.size()-vertexNumber+1);
		EdgeWithCost edgeLeastCost = costSet.pollFirst();// 取出消耗最小的边;已从消耗列表中删除
		VertexComplicated vSmall = vertexList.get(edgeLeastCost.getSmallVertex());// 编号小的顶点
		VertexComplicated vBig = vertexList.get(edgeLeastCost.getBigVertex());// 编号大的顶点
		vSmall.setErrorPairContraction(edgeLeastCost.cost);// step2:更新顶点的消耗
		ArrayList<EdgeWithCost> edgeSmall = vSmall.edges;

		TreeSet<Integer> edgeIndSmall = vSmall.getEdgeIndexes();// 获取临边的定点标号

		edgeSmall.remove(edgeLeastCost);// 从自己的边中删除消耗最小的边
		vBig.edges.remove(edgeLeastCost);// 从另一顶点的边中删除消耗最小的边

		// 更新Faces: 将vBig中的面片的索引换为vSmall的索引,若vSmall也含有该面片则删除它
		for (FaceTriangle face : vBig.faces) {
			if (face.isVertex(vSmall.index)) {// vSmall也含有该面片,从vSmall的面片中删除,并从面片列表中删除
				vSmall.removeFace(face);
				vertexList.get(face.getAnothorVertex(vSmall.index, vBig.index)).removeFace(face);
				// faces.remove(face);//不需要,只在输出时用到,到时才创建
			} else {
				face.setVertex(vBig.index, vSmall.index);
				vSmall.addFace(face);
			}
		}

		for (EdgeWithCost e : vBig.edges) {
			boolean b = costSet.remove(e);// 先从消耗函数列表中移除,以免改动后删除出问题
			// if (!b) {
			// // System.out.println(costSet.contains(e));
			// // System.out.flush();
			// // System.err.println(costSet);
			// System.err.println("位置1   移除失败" + e);
			// System.err.flush();
			// }
			if ((!edgeIndSmall.contains(e.getOtherVertex(vBig.index)))) {
				e.setVertex(vBig.index, vSmall.index);// 更新边链接坐标,因而不用重新算边
				edgeSmall.add(e);// 重新加入到消耗函数列表中
			} else {
				b = vertexList.get(e.getOtherVertex(vBig.index)).edges.remove(e);// 重复的边,从另一顶点移除
				// if (!b) {
				// System.err.println("位置3     移除失败" + e);
				// System.err.flush();
				// }
			}
		}

		// 更新vSmall及周围顶点的Q
		vSmall.calculateQ(vertexList);
		for (EdgeWithCost e : edgeSmall) {
			int vInd = e.getOtherVertex(vSmall.index);
			vertexList.get(vInd).calculateQ(vertexList);
		}
		// 更新vSmall的边压缩损耗及周围点的边的的边压缩损耗;
		// 注意:vSmall的边也含在周围的顶点的边中,无需重复更新。
		// 另外,由于不一定这些顶点为较小节点,因而必须全部更新,可能产生重复计算
		for (EdgeWithCost e : edgeSmall) {
			int vInd = e.getOtherVertex(vSmall.index);
			for (EdgeWithCost edge : vertexList.get(vInd).edges) {
				boolean b = costSet.remove(edge);// 先删除,否则排序会有错;由于vSmall的周围顶点间可能有边,因而可能重复删除而返回false,不影响

				// if (!b &&
				// (!vSmall.getEdgeIndexes().contains(edge.getOtherVertex(vSmall.index))))
				// {
				// System.out.flush();
				// System.err.println(costSet);
				// System.err.println("2:移除失败" + edge);
				// System.err.flush();
				// }
				// else{
				// System.err.println("2:                                          移除成功"
				// + edge);
				// }
				edge.cal_v_cost(vertexList);// 更新边压缩损耗
				costSet.add(edge);// 重新加入
			}
		}
		vertexList.set(vBig.index, null);// 标定为null,相当于移除
		vertexNumber--; // 修正当前的点数
	}

	/**
	 * @return 物体的三角面片
	 */
	public HashSet<FaceTriangle> getFaces() {
		HashSet<FaceTriangle> faces = new HashSet<FaceTriangle>(3 * vertexNumber);// 假定面片数量约为顶点的两倍,则最后hashset的loadFactor约为0.7
		for (VertexComplicated v1 : vertexList) {
			if (v1 == null)// 该顶点已被删除
				continue;
			for (FaceTriangle face : v1.faces) {
				faces.add(face);
			}
		}

//		System.out.println("------------面片数量:" + faces.size());
//
//		for (FaceTriangle face : faces) {
//			System.out.println(face);
//		}
		return faces;
	}

	/**
	 * @return 返回顶点索引映射表,第一个为原索引(从0开始),第二个为简化后网格的顶点索引(从1开始)
	 */
	public HashMap<Integer, Integer> getIndexMap() {
		HashMap<Integer, Integer> indexMap = new HashMap<Integer, Integer>(2 * vertexNumber);
		int j = 1;
		for (int i = 0; i < vertexList.size(); i++) {
			if (vertexList.get(i) != null) {
				indexMap.put(i, j);
				j++;
			}
		}

		// for (int i = 0; i < vertexList.size(); i++) {
		// System.out.println(i + "~" + indexMap.get(i));
		// }

		return indexMap;
	}

	/**
	 * 将当前的模型输出到文件
	 * @param fileName 要写入的文件名
	 * @throws IOException
	 */
	public void writeObj(String fileName) throws IOException {
		PrintWriter bw = new PrintWriter(new File(fileName));
		HashMap<Integer, Integer> indexMap = getIndexMap();// 获取索引映射表
		HashSet<FaceTriangle> faces = getFaces();// 获取三角面片的原始索引
		bw.println("# " + vertexNumber + " " + faces.size());// 第一行输出顶点数量和三角面片数量
		for (VertexComplicated v : vertexList) {// 输出顶点
			if (v != null)
				bw.println(v);
		}

		for (FaceTriangle face : faces) {// 输出三角面片,索引要经过修正
			bw.println("f " + indexMap.get(face.v1Ind) + " " + indexMap.get(face.v2Ind) + " "
					+ indexMap.get(face.v3Ind));
		}
		bw.close();
	}

	// EdgeWithCost getEdge(int v1ind, int v2ind) {
	// VertexComplicated v1 = vertexList.get(v1ind);
	// for (EdgeWithCost e : v1.edges) {
	// if (e.getOtherVertex(v1ind) == v2ind)
	// return e;
	// }
	//
	// return null;
	//
	// }

	/**
	 * 简化网格
	 * @param fileName 原始模型的文件名(含路径)
	 * @param fileNameOut 输出模型的文件名(含路径)
	 * @param goalVertex 输出模型的目标顶点数量
	 * @throws IOException
	 */
	public static void simplifyMesh(String fileName, String fileNameOut, int goalVertex)
			throws IOException {
		System
				.setOut(new PrintStream(new File(fileNameOut).getPath() + File.separator
						+ "log.txt"));
		ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(fileName);
		long time = System.currentTimeMillis();
		while (obj.vertexNumber > goalVertex)
			obj.contractEdge();

		System.out.println("边压缩用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");
		time = System.currentTimeMillis();

		obj.writeObj(fileNameOut);

		System.out.println("写文件用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");

	}

	/**
	 * 简化网格,快速
	 * @param fileName 原始模型的文件名(含路径)
	 * @param fileNameOut 输出模型的文件名(含路径)
	 * @param goalVertex 输出模型的目标顶点数量
	 * @throws IOException
	 */
	public static void simplifyMeshFast(String fileName, String fileNameOut, int goalVertex)
			throws IOException {
		// System
		// .setOut(new PrintStream(new File(fileNameOut).getPath() +
		// File.separator
		// + "log.txt"));
		ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(fileName);
		long time = System.currentTimeMillis();
		while (obj.vertexNumber > goalVertex)
			obj.contractEdgeFast();

		System.out.println("边压缩用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");
		time = System.currentTimeMillis();

		obj.writeObj(fileNameOut);

		System.out.println("写文件用时:" + (double) (System.currentTimeMillis() - time) / 1000 + "s");

	}

	public static void main(String[] args) throws IOException {
		// String path = "F:\\课件\\计算机图形学\\模型\\";
		// simplifyMesh(path + "unitcone.obj", path + "LChah.obj", 46);
		test();

		// FaceTriangle fa = new FaceTriangle(1, 2, 4), fc = fa;
		// FaceTriangle fb = new FaceTriangle(1, 2, 4);
		// HashSet<FaceTriangle> faces = new HashSet<FaceTriangle>(100);
		// System.out.println("faces.add(fa)=" + faces.add(fa));
		// System.out.println("faces.add(fa)=" + faces.add(fa));
		//
		// System.out.println(fa);
		//
		// fa.v1Ind = 1060;
		// System.out.println("faces.contains(fa)" + faces.contains(fa));
		// // System.out.println(fa);
		// // System.out.println(fc==fa);
		// System.out.println(fa == fb);
		// for (FaceTriangle face : faces) {
		// System.out.println(face);
		// }
	}

	/**
	 * 编程过程中的一些测试
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	private static void test() throws FileNotFoundException, IOException {

		String path = "F:\\课件\\计算机图形学\\模型\\";
		// System.setOut(new PrintStream(new File(path + "log.txt")));

		// ObjFileSimplified obj = new ObjFileSimplified(path + "LC_testQ.obj");
		// ObjFileSimplified obj = new ObjFileSimplified(path +
		// "LCdoubletriangle_testCost.txt");
		// ObjFileSimplified obj = new ObjFileSimplified(path + "unitcone.obj");
		// ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(path +
		// "unitcone.obj");
		ObjFileSimplified_v2 obj = new ObjFileSimplified_v2(path + "cube.obj");
		// obj.getFaces();
		// boolean b = obj.costSet.remove(obj.getEdge(63,86));
		// System.out.println(b);
		// obj.print();
		// System.out.println(obj.costSet);
		obj.getFaces();

		long t = System.currentTimeMillis();
		for (int i = 0; i < obj.vertexList.size() / 2; i++) {
			obj.contractEdge();
			// obj.print();
			// System.out.println(obj.costSet);
		}
		System.out.println("边压缩用时:" + (double) (System.currentTimeMillis() - t) / 1000 + "s");
		t = System.currentTimeMillis();
		obj.writeObj(obj.vertexNumber + ".obj");
		System.out.println("写文件用时:" + (double) (System.currentTimeMillis() - t) / 1000 + "s");

		//		
		//		
		// obj.getIndexMap();
		// float f=0.8888888888888888f;
		// double d=0.8888888888888888;
		// System.out.println(f);
		// System.out.println(d);
		// System.out.println(d-f);
	}

} // End of class ObjectFile

// End of file ObjectFile.java
  • 大小: 30.6 KB
  • 大小: 154.2 KB
  • 大小: 46.3 KB
0
0
分享到:
评论

相关推荐

    基于二次误差的网格简化

    总结来说,基于二次误差的网格简化是计算机图形学中的一种高效技术,通过四元数误差度量和优化算法,实现了在保持模型视觉质量的同时,有效地减少模型的多边形数量。这种方法在现代3D图形处理中扮演着重要的角色,是...

    基于离散曲率的二次误差度量网格简化算法 (2010年)

    提出了一种基于离散曲率的二次误差度量网格简化算法。在代价函数中引入顶点离散曲率,通过将代价函数作为顶点对的权值来控制顶点对合并次序,更好地保留了原模型的细节特征,同时修改模型特征点与特征线的权值,使得简化...

    三角网格简化程序源代码

    三角网格简化程序是一种在计算机图形学中用于降低三维模型复杂度的技术,主要目的是优化渲染效率,使得复杂的3D模型在有限的计算资源下仍能流畅显示。本程序源自1998年,由一位国外专家编写,对于初学者来说,它提供...

    基于GPU的网格模型简化算法研究.pdf

    其中,基于二次误差度量的简化算法因其在简化速度和质量之间的良好平衡而受到青睐,但在处理大量网格模型时,其速度受到限制。 基于这一背景,研究者提出利用DirectX 11的计算着色器将简化算法移植到GPU中执行。...

    网格简化算法

    误差通常是根据模型表面到简化后表面的最短距离平方计算的,因此称为“二次误差”。QEM算法试图最小化整体的误差,以确保简化后的模型尽可能接近原始模型。 Edge Collapse是另一种常用的网格简化策略,它通过选择...

    快速Quadric网格简化

    标签中的"Quadric"强调了算法是基于二次曲面误差度量的,而"Mesh Simplificat"则直指我们讨论的主题——网格简化。这个特定的实现可能包含源代码、示例文件和其他相关资源,这些都可以在名为"Fast-Quadric-Mesh-...

    基于3D空间中的二次误差度量的自动超像素生成算法

    关键词包括三角网格、网格简化、图像分割和二次误差度量。超像素分割方法与聚类方法类似,都是基于像素的相似性将图像中的像素分组到不同的集合中。它们捕捉图像的冗余信息,并大大减少图像元素的数量。基于区域计算...

    体积平方度量下的特征保持网格简化方法

    描述部分指出,该论文提出了一种新的基于体积平方度量的三角形折叠网格简化方法。该方法通过最小化一个误差目标函数来简化三角形网格。在该方法中,简化误差被定义为三角形简化后产生的模型的平方体积变化,并且考虑...

    顾及角度误差的三维建筑模型边折叠简化算法.docx

    Garland等人在此基础上发展了二次误差度量(QEM)算法,通过新顶点到一阶邻域三角形的距离平方和作为误差度量,简化过程更为高效。然而,QEM算法在保持模型细节方面可能不够理想,尤其是在处理尖锐特征时。 针对...

    视觉传感器网络中基于运动特征的动画网格简化

    基于运动特征,创建了动画二次误差度量,并建立了二次误差矩阵。 与其它方法相比,本文方法的一个重要优势是高效率的简化过程和更平滑的简化效果,适合于实时应用。实验结果表明,采用本文方法得到的三维动画网格...

    论文研究-针灸铜人三维可视化研究与应用.pdf

    然后使用XML语言对其承载的传统中医知识进行存储与表达,重点利用基于改进的二次误差度量和递进网格算法实现铜人多分辨率简化模型的自动生成;最后利用Unity引擎开发了一套针灸铜人虚拟交互展示系统,实现了穴位、...

Global site tag (gtag.js) - Google Analytics