1. 程式人生 > >BVH檔案 Java解析器

BVH檔案 Java解析器

BVH檔案解析器

簡介

對於BVH檔案的格式,可以參考:BVH檔案格式解析

解析器

使用java編寫,具體使用方法:

  • 匯入google的gson包,用來生成json:

    1. 如果是eclipse,下載jar匯入到專案中:gson jar下載
    2. 如果是使用IDEA,則通過File - Project Structure - Modules - Dependencies - +(加號) - Libraries - From Mave, 搜尋gson,新增com.google.gson.Gson;
  • 建立 BVHParser 的類物件:

BVHParser parser = new
BVHParser();
  • 傳入bvh檔案的絕對路徑,呼叫 parse(String) 方法解析得到json格式的字串:
String json = parser.parse("your absoulte path of the .bvh file's ");
  • 如果要寫入檔案:
File out = new File("out.json"); // print to a json file
try {
        PrintStream ps = new PrintStream(new FileOutputStream(out));
        ps.println(json);
} catch
(IOException e) { e.printStackTrace(); } finally { parser.release(); }
  • 最後釋放I/O
parser.release();

原始碼

對於邏輯,部分已在程式碼中註釋

import com.google.gson.Gson;

import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;

/**
 * Parsing the standard .bvh file, some infor cannot be tranfered to json if not a standard .bvh file
 * Only get segment's data without motion's data.
 * <p>
 * The parser need external library: com.google.code.gson:gson
 * <p>
 * Created by PentonBin on 16-3-23.
 */
public class BVHParser { private static final String TAG = "BVHParser"; private static final String KEYWORD_HIERARCHY = "HIERARCHY"; private static final String KEYWORD_ROOT = "ROOT"; private static final String KEYWORD_OFFSET = "OFFSET"; private static final String KEYWORD_CHANNELS = "CHANNELS"; private static final String KEYWORD_JOINT = "JOINT"; private static final String KEYWORD_END_SITE = "End Site"; private static final String KEYWORD_MOTION = "MOTION"; private static final String KEYWORD_FRAME = "Frames"; private static final String KEYWORD_FRAME_TIME = "Frame Time"; private static final int NUM_OFFSET = 3; private static final int NUM_ROOT_CHANNELS = 6; private static final int NUM_JOINT_CHANNELS = 3; private BufferedReader mParseReader; private boolean isReaderMarkConsume = true; enum SegmentType { Root, Joint, EndSite } enum ChannelType { Xposition, Yposition, Zposition, Zrotation, Xrotation, Yrotation } static class Segment { private String name; private SegmentType type; private ArrayList<Double> offsetValueList; private int channelsNum; private ArrayList<ChannelType> channelTypeList; private ArrayList<Segment> subSegments; public Segment() { } public void putOffset(double offset) { if (offsetValueList == null) { offsetValueList = new ArrayList<Double>(); } offsetValueList.add(offset); } public void setChannelsNum(int channelsNum) { this.channelsNum = channelsNum; } public void putChannelType(ChannelType type) { if (channelTypeList == null) { channelTypeList = new ArrayList<ChannelType>(); } channelTypeList.add(type); } public void setName(String name) { this.name = name; } public void setSegmentType(SegmentType type) { this.type = type; } public SegmentType getType() { return type; } public void putSubSegment(Segment subSegment) { if (subSegments == null) { subSegments = new ArrayList<Segment>(); } subSegments.add(subSegment); } } static class Motion { private int framesNum; private double framesTime; private ArrayList<Double> motionsData; public Motion() { } } /** * Parse the .bvh file provided by {@param filePath} * * @param filePath the path of the file * @return String of the json format, null if the file does not exist or is not file */ public String parse(String filePath) { File file = new File(filePath); if (file != null && file.exists() && file.isFile()) { Gson gson = new Gson(); Segment rootSegment = getSegmentFromFile(file); return gson.toJson(rootSegment); } return null; } /** * Must call this function to release the I/O after parsing the .bvh file */ public void release() { if (mParseReader != null) { try { mParseReader.close(); } catch (IOException e) { e.printStackTrace(); } } } private Segment getSegmentFromFile(File file) { mParseReader = getFileBufferedReader(file); if (mParseReader == null) { return null; } try { String header = mParseReader.readLine().trim(); if (!readFileHeader(header)) { throw new IllegalArgumentException("File : " + file.getName() + " is not a .bvh file"); } return parseSegment(mParseReader); } catch (Exception e) { e.printStackTrace(); } return null; } /** * Parsing the header of the .bvh file * * @param header the header string of the .bvh file * @return true if the header is real header of the .bvh file, else return false */ private static boolean readFileHeader(String header) { if (header == null || header.equalsIgnoreCase("")) { return false; } if (header.equalsIgnoreCase(KEYWORD_HIERARCHY)) { return true; } return false; } private Segment parseSegment(BufferedReader reader) { if (reader == null) { return null; } try { Segment segment = new Segment(); String line = reader.readLine().trim(); // Header of segment readSegmentFromeHeadString(segment, line); line = reader.readLine().trim(); if (line.equals("{")) { // left curly brace // do nothing } else { System.out.println("Error : left curly brace"); System.exit(-1); } line = reader.readLine().trim(); // offset readOffsetsFromString(segment, line); if (segment.type.equals(SegmentType.Root)) { line = reader.readLine().trim(); // channel readChannelsFromString(segment, line); while (true) { markReader(reader); // mark if need to call reset() line = reader.readLine().trim(); // get the next line to judge whether have a subsegment if (line.startsWith(KEYWORD_JOINT)) { // should call reset if has subsegment resetReader(reader); segment.putSubSegment(parseSegment(reader)); } else { consumeMark(); // has no subsegment, dismark the reader break; } } } else if (segment.type.equals(SegmentType.Joint)) { line = reader.readLine().trim(); // channel readChannelsFromString(segment, line); while (true) { markReader(reader); // mark line = reader.readLine().trim(); if (line.equalsIgnoreCase("}")) { // end joint consumeMark(); // no subsegment, dismark break; } if (line.startsWith(KEYWORD_JOINT)) { // has subsegment resetReader(reader); // reset segment.putSubSegment(parseSegment(reader)); // put joint } else if (line.startsWith(KEYWORD_END_SITE)) { resetReader(reader); segment.putSubSegment(parseSegment(reader)); // put endsite } } } else if (segment.type.equals(SegmentType.EndSite)) { // end parsing, no more data line = reader.readLine().trim(); // right curly brace if (line.equalsIgnoreCase("}")) { // do nothing } else { System.out.println("Error : right curly brace"); System.exit(-1); } } return segment; } catch (Exception e) { e.printStackTrace(); } return null; } /** * Parsing the string of a segment's head * * @param segment the header of this segment * @param segmentHeader the string of the segment's head */ private static void readSegmentFromeHeadString(Segment segment, String segmentHeader) { if (segmentHeader == null || segmentHeader.equalsIgnoreCase("")) { return; } String[] splits = segmentHeader.split(" "); if (splits[0].equalsIgnoreCase(KEYWORD_ROOT)) { segment.setSegmentType(SegmentType.Root); segment.setName(splits[1]); } else if (splits[0].equalsIgnoreCase(KEYWORD_JOINT)) { segment.setSegmentType(SegmentType.Joint); segment.setName(splits[1]); } else if (segmentHeader.equalsIgnoreCase(KEYWORD_END_SITE)) { segment.setSegmentType(SegmentType.EndSite); // End site has no name } } /** * Parsing the string of the segment's offset * * @param segment the offset of this segment * @param offsetString the string of the segment's offset */ private static void readOffsetsFromString(Segment segment, String offsetString) { if (offsetString == null || offsetString.equalsIgnoreCase("")) { return; } String[] splits = offsetString.split(" "); // may contains '\t' instead of ' ' if (!splits[0].equalsIgnoreCase(KEYWORD_OFFSET)) { splits = offsetString.split("\\t"); if (!splits[0].equalsIgnoreCase(KEYWORD_OFFSET)) { return; } } if (segment != null) { for (int i = 1; i <= NUM_OFFSET; ++i) { try { double offset = Double.parseDouble(splits[i]); segment.putOffset(offset); } catch (Exception e) { e.printStackTrace(); } } } } /** * Parsing the string of the segment's channel * * @param segment the channel of this segment * @param channelString the string of the segment's channel */ private static void readChannelsFromString(Segment segment, String channelString) { if (channelString == null || channelString.equalsIgnoreCase("")) { return; } String[] splits = channelString.split(" "); if (!splits[0].equalsIgnoreCase(KEYWORD_CHANNELS)) { return; } if (segment != null) { try { int channelsNum = Integer.parseInt(splits[1]); segment.setChannelsNum(channelsNum); for (int i = 2; i < 2 + channelsNum; ++i) { ChannelType channelType = ChannelType.valueOf(ChannelType.class, splits[i]); segment.putChannelType(channelType); } } catch (Exception e) { e.printStackTrace(); } } } /** * Get the BufferReader of the file * * @param file * @return the BufferReader of {@param file} */ private BufferedReader getFileBufferedReader(File file) { if (file == null || !file.exists() || !file.isFile()) { return null; } BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(file)); return reader; } catch (Exception e) { e.printStackTrace(); } return reader; } /** * To use the function reset() of the BufferReader, should call mark(int) first, * mark(int) need a param: readAheadLimit, it's recorded by a field of BufferReader, * using reflection to get the value of the field which named "nextChar" * * @param reader to get this reader's field * @return the field value of the {@param reader} */ private static int getReaderNextChar(BufferedReader reader) { Class<?> clazz = BufferedReader.class; try { Field nextCharField = clazz.getDeclaredField("nextChar"); nextCharField.setAccessible(true); return nextCharField.getInt(reader); } catch (Exception e) { e.printStackTrace(); } return -1; } /** * It's necessary to call BufferReader.mark(int) when call BufferReader.reset() * * To reset only once, using the field {@field isReaderMarkConsume} to control * * @param reader the reader we want to mark */ private void markReader(BufferedReader reader) { try { reader.mark(getReaderNextChar(reader)); isReaderMarkConsume = false; } catch (IOException e) { e.printStackTrace(); } } /** * If the reader was marked, using this function to dismark the reader */ private void consumeMark() { isReaderMarkConsume = true; } /** * When using BufferReader.readLine(), we need to call reset() when sometime need to rollback to the privious line, * it's necessary to call mark(int) before calling reset(), the function of the {@function markReader} is to mark the reader * * To reset only once, using the field {@field isReaderMarkConsume} to control * * @param reader the reader we want to reset */ private void resetReader(BufferedReader reader) { try { if (!isReaderMarkConsume) { reader.reset(); isReaderMarkConsume = true; } } catch (IOException e) { e.printStackTrace(); } } }

謝謝