diff --git a/.gitignore b/.gitignore index 8d1d48b2..afe5b0e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,16 @@ +/Debug/ +/Release/ +exe/FaceLandmarkImg/Debug/ +exe/FaceLandmarkVid/Debug/ +exe/FaceLandmarkVidMulti/Debug/ +exe/FeatureExtraction/Debug/ +exe/Recording/Debug/ +lib/3rdParty/dlib/Debug/ +lib/local/FaceAnalyser/Debug/ +lib/local/LandmarkDetector/Debug/ +matlab_runners/Head Pose Experiments/experiments/biwi_out/ +matlab_runners/Head Pose Experiments/experiments/bu_out/ +matlab_runners/Head Pose Experiments/experiments/ict_out/ exe/Recording/x64/ exe/FaceLandmarkVidMulti/x64/ lib/3rdParty/dlib/x64/ @@ -21,7 +34,6 @@ matlab_runners/Feature Point Experiments/yt_features_clm/ matlab_runners/Gaze Experiments/mpii_out/ build/ Release/AU_predictors/ -Release/ exe/Recording/recording/ ipch/ exe/FeatureExtraction/out_bp4d/ @@ -72,4 +84,4 @@ matlab_version/pdm_generation/menpo_pdm/old_models/ matlab_version/pdm_generation/menpo_pdm/ matlab_version/experiments_menpo/menpo_challenge_helpers/out_profile/ matlab_version/experiments_menpo/menpo_challenge_helpers/out_semifrontal/ -matlab_version/experiments_menpo/menpo_challenge_helpers/out_semifrontal.zip +matlab_version/experiments_menpo/menpo_challenge_helpers/out_semifrontal.zip \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 6b0918d5..fde65bbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,50 +14,57 @@ os: - linux before_install: + + # OpenCV dependencies and boost + - if [ ${TRAVIS_OS_NAME} = linux ]; then + sudo apt-get install git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev; + sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev checkinstall; + sudo add-apt-repository -y ppa:boost-latest/ppa; + sudo apt-get update; + sudo apt-get install libboost1.55-all-dev; + wget https://github.com/Itseez/opencv/archive/3.1.0.zip; + sudo unzip 3.1.0.zip; + cd opencv-3.1.0; + sudo mkdir build; + cd build; + fi + # g++4.8.1 - - if [ "$CXX" == "g++" ]; then - if [[ ${TRAVIS_OS_NAME} = linux ]]; then + - if [ "$CXX" = "g++" ]; then + if [ ${TRAVIS_OS_NAME} = linux ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; + sudo apt-get update -qq; + sudo apt-get install -qq g++-4.8; + export CXX="g++-4.8"; + sudo cmake -D CMAKE_BUILD_TYPE=RELEASE -D WITH_V4L=ON -D WITH_OPENCL=OFF -D INSTALL_C_EXAMPLES=OFF -D BUILD_EXAMPLES=OFF -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D BUILD_EXAMPLES=OFF -D INSTALL_PYTHON_EXAMPLES=OFF ..; fi fi # clang 3.4 - - if [ "$CXX" == "clang++" ]; then - if [[ ${TRAVIS_OS_NAME} = linux ]]; then - sudo add-apt-repository -y ppa:h-rayflood/llvm; + - if [ "$CXX" = "clang++" ]; then + if [ ${TRAVIS_OS_NAME} = linux ]; then + $CXX --version; + sudo sed -i -e 's/^Defaults\tsecure_path.*$//' /etc/sudoers; + sudo cmake -D CMAKE_BUILD_TYPE=RELEASE -D WITH_V4L=ON -D WITH_OPENCL=OFF -D INSTALL_C_EXAMPLES=OFF -D BUILD_EXAMPLES=OFF -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D BUILD_EXAMPLES=OFF -D INSTALL_PYTHON_EXAMPLES=OFF ..; fi fi - - if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo apt-get update -qq; fi - -install: - - if [ "$CXX" == "g++" ]; then if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo apt-get install -qq g++-4.8; fi fi - - if [ "$CXX" == "g++" ]; then if [[ ${TRAVIS_OS_NAME} = linux ]]; then export CXX="g++-4.8"; fi fi - - if [ "$CXX" == "clang++" ]; then if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo apt-get install -qq clang-3.4; fi fi - - if [ "$CXX" == "clang++" ]; then if [[ ${TRAVIS_OS_NAME} = linux ]]; then export CXX="clang++-3.4"; fi fi + - if [ ${TRAVIS_OS_NAME} = osx ]; then + brew update; + brew install tbb; + wget https://github.com/Itseez/opencv/archive/3.1.0.zip; + sudo unzip 3.1.0.zip; + cd opencv-3.1.0; + sudo mkdir build; + cd build; + sudo cmake -D CMAKE_BUILD_TYPE=RELEASE -D WITH_V4L=ON ..; + fi - - if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo apt-get install git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev; fi - - if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev checkinstall; fi - - # Getting newest boost - - if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo add-apt-repository -y ppa:boost-latest/ppa; fi - - if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo apt-get update; fi - - if [[ ${TRAVIS_OS_NAME} = linux ]]; then sudo apt-get install libboost1.55-all-dev; fi - - - if [[ ${TRAVIS_OS_NAME} = osx ]]; then brew update; fi - - if [[ ${TRAVIS_OS_NAME} = osx ]]; then brew tap homebrew/science; fi - - if [[ ${TRAVIS_OS_NAME} = osx ]]; then brew install tbb opencv3; fi - before_script: - - git clone https://github.com/Itseez/opencv.git - - cd opencv - - mkdir build - - cd build - - cmake .. - - make -j2 - - sudo make -j2 install - - cd ../.. - + - sudo make -j4 + - sudo make install + - cd ../.. + script: - $CXX --version - mkdir build @@ -68,4 +75,5 @@ script: - ../build/bin/FaceLandmarkImg -inroot ../videos -f Obama.jpg -outroot data -of obama.txt -op obama.3d -oi obama.bmp -multi_view 1 -wild -q - ../build/bin/FaceLandmarkVidMulti -inroot ../videos -f multi_face.avi -outroot output -ov multi_face.avi -q - ../build/bin/FeatureExtraction -f "../videos/1815_01_008_tony_blair.avi" -outroot output_features -ov blair.avi -of "1815_01_008_tony_blair.txt" -simalign aligned -ov feat_test.avi -hogalign hog_test.dat -q - - ../build/bin/FaceLandmarkVid -inroot ../videos -f 1815_01_008_tony_blair.avi -f 0188_03_021_al_pacino.avi -f 0217_03_006_alanis_morissette.avi -outroot output_data -ov 1.avi -ov 2.avi -ov 3.avi -q \ No newline at end of file + - ../build/bin/FeatureExtraction -f "../videos/1815_01_008_tony_blair.avi" -outroot output_features -simsize 200 -simscale 0.5 -ov blair.avi -of "1815_01_008_tony_blair.txt" -simalign aligned -ov feat_test.avi -hogalign hog_test.dat -q + - ../build/bin/FaceLandmarkVid -inroot ../videos -f 1815_01_008_tony_blair.avi -f 0188_03_021_al_pacino.avi -f 0217_03_006_alanis_morissette.avi -outroot output_data -ov 1.avi -ov 2.avi -ov 3.avi -q diff --git a/CMakeLists.txt b/CMakeLists.txt index 210bb753..3d53bc6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,12 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules/") +set(CMAKE_CONFIG_DIR etc/OpenFace) +set(CONFIG_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_CONFIG_DIR}") +add_definitions(-DCONFIG_DIR="${CONFIG_DIR}") + find_package( OpenCV REQUIRED ) + MESSAGE("OpenCV information:") MESSAGE(" OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") MESSAGE(" OpenCV_LIBRARIES: ${OpenCV_LIBRARIES}") @@ -33,6 +38,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/model) endif(MSVC) + + install(FILES ${file} DESTINATION ${CMAKE_CONFIG_DIR}/model) endforeach() # Move the hierarchical LandmarkDetector models @@ -44,6 +51,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/model) endif(MSVC) + + install(DIRECTORY ${file} DESTINATION ${CMAKE_CONFIG_DIR}/model) endforeach() file(GLOB files "lib/local/LandmarkDetector/model/detection_validation/*.txt") @@ -54,6 +63,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/model/detection_validation) endif(MSVC) + + install(FILES ${file} DESTINATION ${CMAKE_CONFIG_DIR}/model/detection_validation) endforeach() file(GLOB files "lib/local/LandmarkDetector/model/patch_experts/*.txt") @@ -64,6 +75,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/model/patch_experts) endif(MSVC) + + install(FILES ${file} DESTINATION ${CMAKE_CONFIG_DIR}/model/patch_experts) endforeach() file(GLOB files "lib/local/LandmarkDetector/model/pdms/*.txt") @@ -74,6 +87,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/model/pdms) endif(MSVC) + + install(FILES ${file} DESTINATION ${CMAKE_CONFIG_DIR}/model/pdms) endforeach() # Move OpenCV classifiers @@ -85,6 +100,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/classifiers) endif(MSVC) + + install(FILES ${file} DESTINATION ${CMAKE_CONFIG_DIR}/classifiers) endforeach() # Move AU prediction modules @@ -96,6 +113,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/AU_predictors) endif(MSVC) + + install(FILES ${file} DESTINATION ${CMAKE_CONFIG_DIR}/AU_predictors) endforeach() # Move AU prediction modules @@ -107,6 +126,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/AU_predictors) endif(MSVC) + + install(DIRECTORY ${file} DESTINATION ${CMAKE_CONFIG_DIR}/AU_predictors) endforeach() # Move AU prediction modules @@ -118,6 +139,8 @@ foreach(file ${files}) else(MSVC) file(COPY ${file} DESTINATION ${CMAKE_BINARY_DIR}/bin/AU_predictors) endif(MSVC) + + install(DIRECTORY ${file} DESTINATION ${CMAKE_CONFIG_DIR}/AU_predictors) endforeach() if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") diff --git a/README.md b/README.md index 015fc4e3..bf2ed803 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,8 @@ The code was written mainly by Tadas Baltrusaitis during his time at the Languag Special thanks goes to Louis-Philippe Morency and his MultiComp Lab at Institute for Creative Technologies for help in writing and testing the code, and Erroll Wood for the gaze estimation work. -**For instructions of how to install/compile/use the project please see [wiki](https://github.com/TadasBaltrusaitis/OpenFace/wiki)** +## WIKI +**For instructions of how to install/compile/use the project please see [WIKI](https://github.com/TadasBaltrusaitis/OpenFace/wiki)** More details about the project - http://www.cl.cam.ac.uk/research/rainbow/projects/openface/ diff --git a/appveyor.yml b/appveyor.yml index dcba59bf..5c90d1f5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,4 +25,5 @@ test_script: - cmd: if exist "../videos" (FaceLandmarkImg.exe -inroot ../videos -f obama.jpg -outroot out_data -of obama.pts -op obama.3d -oi obama.bmp -q) else (FaceLandmarkImg.exe -inroot ../../videos -f obama.jpg -outroot out_data -of obama.pts -op obama.3d -oi obama.bmp -q) - cmd: if exist "../videos" (FaceLandmarkVidMulti.exe -inroot ../videos -f multi_face.avi -ov multi_face.avi -q) else (FaceLandmarkVidMulti.exe -inroot ../../videos -f multi_face.avi -ov multi_face.avi -q) - cmd: if exist "../videos" (FeatureExtraction.exe -f "../videos/1815_01_008_tony_blair.avi" -outroot output_features -of "1815_01_008_tony_blair.txt" -simalign aligned -ov feat_track.avi -hogalign hog_test.dat -q) else (FeatureExtraction.exe -f "../../videos/1815_01_008_tony_blair.avi" -outroot output_features -of "1815_01_008_tony_blair.txt" -simalign aligned -ov feat_track.avi -hogalign hog_test.dat -q) + - cmd: if exist "../videos" (FeatureExtraction.exe -f "../videos/1815_01_008_tony_blair.avi" -outroot output_features -of "1815_01_008_tony_blair.txt" -simalign aligned -simsize 200 -simscale 0.5 -ov feat_track.avi -hogalign hog_test.dat -q) else (FeatureExtraction.exe -f "../../videos/1815_01_008_tony_blair.avi" -outroot output_features -of "1815_01_008_tony_blair.txt" -simalign aligned -simsize 200 -simscale 0.5 -ov feat_track.avi -hogalign hog_test.dat -q) - cmd: if exist "../videos" (FaceLandmarkVid.exe -f "../videos/1815_01_008_tony_blair.avi" -ov track.avi -q) else (FaceLandmarkVid.exe -f "../../videos/1815_01_008_tony_blair.avi" -ov track.avi -q) diff --git a/exe/FaceLandmarkImg/FaceLandmarkImg.cpp b/exe/FaceLandmarkImg/FaceLandmarkImg.cpp index 15da1761..bf9f716c 100644 --- a/exe/FaceLandmarkImg/FaceLandmarkImg.cpp +++ b/exe/FaceLandmarkImg/FaceLandmarkImg.cpp @@ -78,8 +78,9 @@ #include #include -// OpenBLAS -#include +#ifndef CONFIG_DIR +#define CONFIG_DIR "~" +#endif using namespace std; @@ -316,11 +317,14 @@ void create_display_image(const cv::Mat& orig, cv::Mat& display_image, LandmarkD int main (int argc, char **argv) { - openblas_set_num_threads(1); - + //Convert arguments to more convenient vector form vector arguments = get_arguments(argc, argv); + // Search paths + boost::filesystem::path config_path = boost::filesystem::path(CONFIG_DIR); + boost::filesystem::path parent_path = boost::filesystem::path(arguments[0]).parent_path(); + // Some initial parameters that can be overriden from command line vector files, depth_files, output_images, output_landmark_locations, output_pose_locations; @@ -360,38 +364,44 @@ int main (int argc, char **argv) // Loading the AU prediction models string au_loc = "AU_predictors/AU_all_static.txt"; - if (!boost::filesystem::exists(boost::filesystem::path(au_loc))) + boost::filesystem::path au_loc_path = boost::filesystem::path(au_loc); + if (boost::filesystem::exists(au_loc_path)) { - boost::filesystem::path loc = boost::filesystem::path(arguments[0]).parent_path() / au_loc; - - if (boost::filesystem::exists(loc)) - { - au_loc = loc.string(); - } - else - { - cout << "Can't find AU prediction files, exiting" << endl; - return 1; - } + au_loc = au_loc_path.string(); + } + else if (boost::filesystem::exists(parent_path/au_loc_path)) + { + au_loc = (parent_path/au_loc_path).string(); + } + else if (boost::filesystem::exists(config_path/au_loc_path)) + { + au_loc = (config_path/au_loc_path).string(); + } + else + { + cout << "Can't find AU prediction files, exiting" << endl; + return 1; } // Used for image masking for AUs string tri_loc; - if (boost::filesystem::exists(boost::filesystem::path("model/tris_68_full.txt"))) + boost::filesystem::path tri_loc_path = boost::filesystem::path("model/tris_68_full.txt"); + if (boost::filesystem::exists(tri_loc_path)) { - std::ifstream triangulation_file("model/tris_68_full.txt"); - tri_loc = "model/tris_68_full.txt"; + tri_loc = tri_loc_path.string(); + } + else if (boost::filesystem::exists(parent_path/tri_loc_path)) + { + tri_loc = (parent_path/tri_loc_path).string(); + } + else if (boost::filesystem::exists(config_path/tri_loc_path)) + { + tri_loc = (config_path/tri_loc_path).string(); } else { - boost::filesystem::path loc = boost::filesystem::path(arguments[0]).parent_path() / "model/tris_68_full.txt"; - tri_loc = loc.string(); - - if (!exists(loc)) - { - cout << "Can't find triangulation files, exiting" << endl; - return 1; - } + cout << "Can't find triangulation files, exiting" << endl; + return 1; } FaceAnalysis::FaceAnalyser face_analyser(vector(), 0.7, 112, 112, au_loc, tri_loc); diff --git a/exe/FaceLandmarkVid/FaceLandmarkVid.cpp b/exe/FaceLandmarkVid/FaceLandmarkVid.cpp index 96742081..a60826ae 100644 --- a/exe/FaceLandmarkVid/FaceLandmarkVid.cpp +++ b/exe/FaceLandmarkVid/FaceLandmarkVid.cpp @@ -198,7 +198,8 @@ int main (int argc, char **argv) // Indicates that rotation should be with respect to world or camera coordinates bool u; - LandmarkDetector::get_video_input_output_params(files, depth_directories, out_dummy, output_video_files, u, arguments); + string output_codec; + LandmarkDetector::get_video_input_output_params(files, depth_directories, out_dummy, output_video_files, u, output_codec, arguments); // The modules that are being used for tracking LandmarkDetector::CLNF clnf_model(det_parameters.model_location); @@ -302,7 +303,14 @@ int main (int argc, char **argv) cv::VideoWriter writerFace; if (!output_video_files.empty()) { - writerFace = cv::VideoWriter(output_video_files[f_n], CV_FOURCC('D', 'I', 'V', 'X'), 30, captured_image.size(), true); + try + { + writerFace = cv::VideoWriter(output_video_files[f_n], CV_FOURCC(output_codec[0], output_codec[1], output_codec[2], output_codec[3]), 30, captured_image.size(), true); + } + catch(cv::Exception e) + { + WARN_STREAM( "Could not open VideoWriter, OUTPUT FILE WILL NOT BE WRITTEN. Currently using codec " << output_codec << ", try using an other one (-oc option)"); + } } // Use for timestamping if using a webcam diff --git a/exe/FaceLandmarkVidMulti/FaceLandmarkVidMulti.cpp b/exe/FaceLandmarkVidMulti/FaceLandmarkVidMulti.cpp index 58fdabc9..dac2b594 100644 --- a/exe/FaceLandmarkVidMulti/FaceLandmarkVidMulti.cpp +++ b/exe/FaceLandmarkVidMulti/FaceLandmarkVidMulti.cpp @@ -154,7 +154,8 @@ int main (int argc, char **argv) // Get the input output file parameters bool u; - LandmarkDetector::get_video_input_output_params(files, depth_directories, dummy_out, tracked_videos_output, u, arguments); + string output_codec; + LandmarkDetector::get_video_input_output_params(files, depth_directories, dummy_out, tracked_videos_output, u, output_codec, arguments); // Get camera parameters LandmarkDetector::get_camera_params(device, fx, fy, cx, cy, arguments); @@ -246,7 +247,14 @@ int main (int argc, char **argv) cv::VideoWriter writerFace; if(!tracked_videos_output.empty()) { - writerFace = cv::VideoWriter(tracked_videos_output[f_n], CV_FOURCC('D','I','V','X'), 30, captured_image.size(), true); + try + { + writerFace = cv::VideoWriter(tracked_videos_output[f_n], CV_FOURCC(output_codec[0],output_codec[1],output_codec[2],output_codec[3]), 30, captured_image.size(), true); + } + catch(cv::Exception e) + { + WARN_STREAM( "Could not open VideoWriter, OUTPUT FILE WILL NOT BE WRITTEN. Currently using codec " << output_codec << ", try using an other one (-oc option)"); + } } // For measuring the timings diff --git a/exe/FeatureExtraction/CMakeLists.txt b/exe/FeatureExtraction/CMakeLists.txt index 3c561493..5d6d72dc 100644 --- a/exe/FeatureExtraction/CMakeLists.txt +++ b/exe/FeatureExtraction/CMakeLists.txt @@ -14,3 +14,5 @@ target_link_libraries(FeatureExtraction FaceAnalyser) target_link_libraries(FeatureExtraction dlib) target_link_libraries(FeatureExtraction ${OpenCV_LIBS} ${Boost_LIBRARIES} ${TBB_LIBRARIES}) + +install (TARGETS FeatureExtraction DESTINATION bin) diff --git a/exe/FeatureExtraction/FeatureExtraction.cpp b/exe/FeatureExtraction/FeatureExtraction.cpp index 79fe43c6..ca4ea973 100644 --- a/exe/FeatureExtraction/FeatureExtraction.cpp +++ b/exe/FeatureExtraction/FeatureExtraction.cpp @@ -2,7 +2,7 @@ // Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, // all rights reserved. // -// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS // OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS @@ -15,13 +15,13 @@ // POSSIBILITY OF SUCH DAMAGE. // // Notwithstanding the license granted herein, Licensee acknowledges that certain components -// of the Software may be covered by so-called “open source” software licenses (“Open Source -// Components”), which means any software licenses approved as open source licenses by the +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the // Open Source Initiative or any substantially similar licenses, including without limitation any // license that, as a condition of distribution of the software licensed under such license, // requires that the distributor make the software available in source code format. Licensor shall // provide a list of Open Source Components for a particular version of the Software upon -// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to // the extent required by the licenses covering Open Source Components, the terms of such // licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the // licenses applicable to Open Source Components prohibit any of the restrictions in this @@ -38,20 +38,20 @@ // reports and manuals, must cite at least one of the following works: // // OpenFace: an open source facial behavior analysis toolkit -// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency // in IEEE Winter Conference on Applications of Computer Vision, 2016 // // Rendering of Eyes for Eye-Shape Registration and Gaze Estimation -// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling // in IEEE International. Conference on Computer Vision (ICCV), 2015 // // Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection -// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson // in Facial Expression Recognition and Analysis Challenge, // IEEE International Conference on Automatic Face and Gesture Recognition, 2015 // // Constrained Local Neural Fields for robust facial landmark detection in the wild. -// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. // in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. // /////////////////////////////////////////////////////////////////////////////// @@ -81,8 +81,9 @@ #include #include -// OpenBLAS -#include +#ifndef CONFIG_DIR +#define CONFIG_DIR "~" +#endif #define INFO_STREAM( stream ) \ std::cout << stream << std::endl @@ -241,10 +242,13 @@ void post_process_output_file(FaceAnalysis::FaceAnalyser& face_analyser, string int main (int argc, char **argv) { - openblas_set_num_threads(1); vector arguments = get_arguments(argc, argv); + // Search paths + boost::filesystem::path config_path = boost::filesystem::path(CONFIG_DIR); + boost::filesystem::path parent_path = boost::filesystem::path(arguments[0]).parent_path(); + // Some initial parameters that can be overriden from command line vector input_files, depth_directories, output_files, tracked_videos_output; @@ -256,7 +260,8 @@ int main (int argc, char **argv) // Indicates that rotation should be with respect to camera or world coordinates bool use_world_coordinates; - LandmarkDetector::get_video_input_output_params(input_files, depth_directories, output_files, tracked_videos_output, use_world_coordinates, arguments); + string output_codec; //not used but should + LandmarkDetector::get_video_input_output_params(input_files, depth_directories, output_files, tracked_videos_output, use_world_coordinates, output_codec, arguments); bool video_input = true; bool verbose = true; @@ -303,9 +308,9 @@ int main (int argc, char **argv) vector output_similarity_align; vector output_hog_align_files; - double sim_scale = 0.7; + double sim_scale = -1; int sim_size = 112; - bool grayscale = false; + bool grayscale = false; bool video_output = false; bool dynamic = true; // Indicates if a dynamic AU model should be used (dynamic is useful if the video is long enough to include neutral expressions) int num_hog_rows; @@ -316,31 +321,33 @@ int main (int argc, char **argv) bool output_2D_landmarks = true; bool output_3D_landmarks = true; bool output_model_params = true; - bool output_pose = true; + bool output_pose = true; bool output_AUs = true; bool output_gaze = true; get_output_feature_params(output_similarity_align, output_hog_align_files, sim_scale, sim_size, grayscale, verbose, dynamic, output_2D_landmarks, output_3D_landmarks, output_model_params, output_pose, output_AUs, output_gaze, arguments); - - // Used for image masking + // Used for image masking string tri_loc; - if(boost::filesystem::exists(path("model/tris_68_full.txt"))) + boost::filesystem::path tri_loc_path = boost::filesystem::path("model/tris_68_full.txt"); + if (boost::filesystem::exists(tri_loc_path)) { - tri_loc = "model/tris_68_full.txt"; + tri_loc = tri_loc_path.string(); + } + else if (boost::filesystem::exists(parent_path/tri_loc_path)) + { + tri_loc = (parent_path/tri_loc_path).string(); + } + else if (boost::filesystem::exists(config_path/tri_loc_path)) + { + tri_loc = (config_path/tri_loc_path).string(); } else { - path loc = path(arguments[0]).parent_path() / "model/tris_68_full.txt"; - tri_loc = loc.string(); - - if(!exists(loc)) - { - cout << "Can't find triangulation files, exiting" << endl; - return 1; - } - } + cout << "Can't find triangulation files, exiting" << endl; + return 1; + } // Will warp to scaled mean shape cv::Mat_ similarity_normalised_shape = face_model.pdm.mean_shape * sim_scale; @@ -364,27 +371,30 @@ int main (int argc, char **argv) au_loc_local = "AU_predictors/AU_all_static.txt"; } - if(boost::filesystem::exists(path(au_loc_local))) + boost::filesystem::path au_loc_path = boost::filesystem::path(au_loc_local); + if (boost::filesystem::exists(au_loc_path)) { - au_loc = au_loc_local; + au_loc = au_loc_path.string(); + } + else if (boost::filesystem::exists(parent_path/au_loc_path)) + { + au_loc = (parent_path/au_loc_path).string(); + } + else if (boost::filesystem::exists(config_path/au_loc_path)) + { + au_loc = (config_path/au_loc_path).string(); } else { - path loc = path(arguments[0]).parent_path() / au_loc_local; - - if(exists(loc)) - { - au_loc = loc.string(); - } - else - { - cout << "Can't find AU prediction files, exiting" << endl; - return 1; - } - } + cout << "Can't find AU prediction files, exiting" << endl; + return 1; + } // Creating a face analyser that will be used for AU extraction - FaceAnalysis::FaceAnalyser face_analyser(vector(), 0.7, 112, 112, au_loc, tri_loc); + // Make sure sim_scale is proportional to sim_size if not set + if (sim_scale == -1) sim_scale = sim_size * (0.7 / 112.0); + + FaceAnalysis::FaceAnalyser face_analyser(vector(), sim_scale, sim_size, sim_size, au_loc, tri_loc); while(!done) // this is not a for loop as we might also be reading from a webcam { @@ -493,7 +503,16 @@ int main (int argc, char **argv) cv::VideoWriter writerFace; if(!tracked_videos_output.empty()) { - writerFace = cv::VideoWriter(tracked_videos_output[f_n], CV_FOURCC('D', 'I', 'V', 'X'), fps_vid_in, captured_image.size(), true); + try + { + writerFace = cv::VideoWriter(tracked_videos_output[f_n], CV_FOURCC(output_codec[0],output_codec[1],output_codec[2],output_codec[3]), fps_vid_in, captured_image.size(), true); + } + catch(cv::Exception e) + { + WARN_STREAM( "Could not open VideoWriter, OUTPUT FILE WILL NOT BE WRITTEN. Currently using codec " << output_codec << ", try using an other one (-oc option)"); + } + + } int frame_count = 0; @@ -577,7 +596,7 @@ int main (int argc, char **argv) } if(hog_output_file.is_open()) { - FaceAnalysis::Extract_FHOG_descriptor(hog_descriptor, sim_warped_img, num_hog_rows, num_hog_cols); + face_analyser.GetLatestHOG(hog_descriptor, num_hog_rows, num_hog_cols); if(visualise_hog && !det_parameters.quiet_mode) { @@ -599,13 +618,13 @@ int main (int argc, char **argv) pose_estimate = LandmarkDetector::GetCorrectedPoseCamera(face_model, fx, fy, cx, cy); } - if(hog_output_file.is_open()) + if (hog_output_file.is_open()) { output_HOG_frame(&hog_output_file, detection_success, hog_descriptor, num_hog_rows, num_hog_cols); } // Write the similarity normalised output - if(!output_similarity_align.empty()) + if (!output_similarity_align.empty()) { if (sim_warped_img.channels() == 3 && grayscale) @@ -614,18 +633,18 @@ int main (int argc, char **argv) } char name[100]; - - // output the frame number - std::sprintf(name, "frame_det_%06d.bmp", frame_count); + + // Filename is based on frame number + std::sprintf(name, "frame_det_%06d.bmp", frame_count + 1); // Construct the output filename boost::filesystem::path slash("/"); - + std::string preferredSlash = slash.make_preferred().string(); - + string out_file = output_similarity_align[f_n] + preferredSlash + string(name); bool write_success = imwrite(out_file, sim_warped_img); - + if (!write_success) { cout << "Could not output similarity aligned image image" << endl; @@ -664,20 +683,24 @@ int main (int argc, char **argv) captured_image = cv::Mat(); } } - // detect key presses - char character_press = cv::waitKey(1); - // restart the tracker - if(character_press == 'r') + if (!det_parameters.quiet_mode) { - face_model.Reset(); + // detect key presses + char character_press = cv::waitKey(1); + + // restart the tracker + if(character_press == 'r') + { + face_model.Reset(); + } + // quit the application + else if(character_press=='q') + { + return(0); + } } - // quit the application - else if(character_press=='q') - { - return(0); - } - + // Update the frame count frame_count++; @@ -694,10 +717,10 @@ int main (int argc, char **argv) output_file.close(); - if(output_files.size() > 0 && output_AUs) + if (output_files.size() > 0 && output_AUs) { cout << "Postprocessing the Action Unit predictions" << endl; - post_process_output_file(face_analyser, output_files[f_n], dynamic); + face_analyser.PostprocessOutputFile(output_files[f_n], dynamic); } // Reset the models for the next video face_analyser.Reset(); @@ -721,121 +744,6 @@ int main (int argc, char **argv) return 0; } -// Allows for post processing of the AU signal -void post_process_output_file(FaceAnalysis::FaceAnalyser& face_analyser, string output_file, bool dynamic) -{ - - vector certainties; - vector successes; - vector timestamps; - vector>> predictions_reg; - vector>> predictions_class; - - // Construct the new values to overwrite the output file with - face_analyser.ExtractAllPredictionsOfflineReg(predictions_reg, certainties, successes, timestamps, dynamic); - face_analyser.ExtractAllPredictionsOfflineClass(predictions_class, certainties, successes, timestamps, dynamic); - - int num_class = predictions_class.size(); - int num_reg = predictions_reg.size(); - - // Extract the indices of writing out first - vector au_reg_names = face_analyser.GetAURegNames(); - std::sort(au_reg_names.begin(), au_reg_names.end()); - vector inds_reg; - - // write out ar the correct index - for (string au_name : au_reg_names) - { - for (int i = 0; i < num_reg; ++i) - { - if (au_name.compare(predictions_reg[i].first) == 0) - { - inds_reg.push_back(i); - break; - } - } - } - - vector au_class_names = face_analyser.GetAUClassNames(); - std::sort(au_class_names.begin(), au_class_names.end()); - vector inds_class; - - // write out ar the correct index - for (string au_name : au_class_names) - { - for (int i = 0; i < num_class; ++i) - { - if (au_name.compare(predictions_class[i].first) == 0) - { - inds_class.push_back(i); - break; - } - } - } - // Read all of the output file in - vector output_file_contents; - - std::ifstream infile(output_file); - string line; - - while (std::getline(infile, line)) - output_file_contents.push_back(line); - - infile.close(); - - // Read the header and find all _r and _c parts in a file and use their indices - std::vector tokens; - boost::split(tokens, output_file_contents[0], boost::is_any_of(",")); - - int begin_ind = -1; - - for (size_t i = 0; i < tokens.size(); ++i) - { - if (tokens[i].find("AU") != string::npos && begin_ind == -1) - { - begin_ind = i; - break; - } - } - int end_ind = begin_ind + num_class + num_reg; - - // Now overwrite the whole file - std::ofstream outfile(output_file, ios_base::out); - // Write the header - outfile << output_file_contents[0].c_str() << endl; - - // Write the contents - for (int i = 1; i < (int)output_file_contents.size(); ++i) - { - std::vector tokens; - boost::split(tokens, output_file_contents[i], boost::is_any_of(",")); - - outfile << tokens[0]; - - for (int t = 1; t < (int)tokens.size(); ++t) - { - if (t >= begin_ind && t < end_ind) - { - if(t - begin_ind < num_reg) - { - outfile << ", " << predictions_reg[inds_reg[t - begin_ind]].second[i - 1]; - } - else - { - outfile << ", " << predictions_class[inds_class[t - begin_ind - num_reg]].second[i - 1]; - } - } - else - { - outfile << ", " << tokens[t]; - } - } - outfile << endl; - } - - -} - void prepareOutputFile(std::ofstream* output_file, bool output_2D_landmarks, bool output_3D_landmarks, bool output_model_params, bool output_pose, bool output_AUs, bool output_gaze, int num_landmarks, int num_model_modes, vector au_names_class, vector au_names_reg) @@ -1186,6 +1094,7 @@ void get_output_feature_params(vector &output_similarity_aligned, vector } + // Can process images via directories creating a separate output file per directory void get_image_input_output_params_feats(vector > &input_image_files, bool& as_video, vector &arguments) { diff --git a/imgs/frame_det_000000_112x112_0.7.bmp b/imgs/frame_det_000000_112x112_0.7.bmp new file mode 100644 index 00000000..9bb5fefb Binary files /dev/null and b/imgs/frame_det_000000_112x112_0.7.bmp differ diff --git a/imgs/frame_det_000001_112x112_0.5.bmp b/imgs/frame_det_000001_112x112_0.5.bmp new file mode 100644 index 00000000..5b0bfc6f Binary files /dev/null and b/imgs/frame_det_000001_112x112_0.5.bmp differ diff --git a/imgs/frame_det_000001_200x200_0.7.bmp b/imgs/frame_det_000001_200x200_0.7.bmp new file mode 100644 index 00000000..fddb315d Binary files /dev/null and b/imgs/frame_det_000001_200x200_0.7.bmp differ diff --git a/install.sh b/install.sh new file mode 100755 index 00000000..ccbf879d --- /dev/null +++ b/install.sh @@ -0,0 +1,86 @@ +#!/bin/bash +#============================================================================== +# Title: install.sh +# Description: Install everything necessary for OpenFace to compile. +# Author: Daniyal Shahrokhian +# Date: 20170310 +# Version : 1.0 +# Usage: bash install.sh +# NOTES: There are certain steps to be taken in the system before installing +# via this script (refer to README): Run +# `sudo gedit /etc/apt/sources.list` and change the line +# `deb http://us.archive.ubuntu.com/ubuntu/ xenial main restricted` to +# `deb http://us.archive.ubuntu.com/ubuntu/ xenial main universe` +#============================================================================== + +# Exit script if any command fails +set -e +set -o pipefail + +if [ $# -ne 1 ] + then + echo "Usage: install.sh " + exit 1 +fi + +DIRECTORY="$1" +cd "$DIRECTORY" +echo "Installation under ${DIRECTORY}" + +# Essential Dependencies +echo "Installing Essential dependencies..." +sudo apt-get update +sudo apt-get install build-essential +sudo apt-get install llvm + +sudo apt-get update +sudo apt-get install clang-3.7 libc++-dev libc++abi-dev +sudo apt-get install cmake +sudo apt-get install libopenblas-dev liblapack-dev +sudo apt-get install git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev +sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev checkinstall +echo "Essential dependencies installed." + +# OpenCV Dependency +echo "Downloading OpenCV..." +if [ -d "opencv-3.1.0" ]; then + sudo rm -r "opencv-3.1.0" +fi + +wget https://github.com/Itseez/opencv/archive/3.1.0.zip +unzip 3.1.0.zip +rm 3.1.0.zip +cd opencv-3.1.0 +mkdir build +cd build +echo "Installing OpenCV..." +cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D WITH_TBB=ON -D BUILD_SHARED_LIBS=OFF .. +make -j4 +sudo make install +cd "../.." +echo "OpenCV installed." + +# Boost C++ Dependency +echo "Installing Boost..." +sudo apt-get install libboost-all-dev +echo "Boost installed." + +# OpenFace installation +echo "Downloading OpenFace..." +if [ -d "OpenFace" ]; then + sudo rm -r "OpenFace" +fi + +git clone https://github.com/TadasBaltrusaitis/OpenFace.git +cd OpenFace +mkdir build +cd build +echo "Installing OpenFace..." +cmake -D CMAKE_BUILD_TYPE=RELEASE .. +make +echo "OpenFace installed." + +# Installation test +echo "Testing installation..." +./bin/FaceLandmarkVid -f "../videos/changeLighting.wmv" +echo "Installation tested." diff --git a/lib/local/FaceAnalyser/CMakeLists.txt b/lib/local/FaceAnalyser/CMakeLists.txt index ce334505..1652e9a3 100644 --- a/lib/local/FaceAnalyser/CMakeLists.txt +++ b/lib/local/FaceAnalyser/CMakeLists.txt @@ -29,5 +29,5 @@ include_directories(../LandmarkDetector/include) add_library( FaceAnalyser ${SOURCE} ${HEADERS}) -install (TARGETS FaceAnalyser DESTINATION bin) -install (FILES ${HEADERS} DESTINATION include) +install (TARGETS FaceAnalyser DESTINATION lib) +install (FILES ${HEADERS} DESTINATION include/OpenFace) diff --git a/lib/local/FaceAnalyser/include/FaceAnalyser.h b/lib/local/FaceAnalyser/include/FaceAnalyser.h index e5bd9f85..f4d262de 100644 --- a/lib/local/FaceAnalyser/include/FaceAnalyser.h +++ b/lib/local/FaceAnalyser/include/FaceAnalyser.h @@ -74,184 +74,183 @@ namespace FaceAnalysis { -class FaceAnalyser{ + class FaceAnalyser { -public: + public: - enum RegressorType{ SVR_appearance_static_linear = 0, SVR_appearance_dynamic_linear = 1, SVR_dynamic_geom_linear = 2, SVR_combined_linear = 3, SVM_linear_stat = 4, SVM_linear_dyn = 5, SVR_linear_static_seg = 6, SVR_linear_dynamic_seg =7}; + enum RegressorType { SVR_appearance_static_linear = 0, SVR_appearance_dynamic_linear = 1, SVR_dynamic_geom_linear = 2, SVR_combined_linear = 3, SVM_linear_stat = 4, SVM_linear_dyn = 5, SVR_linear_static_seg = 6, SVR_linear_dynamic_seg = 7 }; - // Constructor from a model file (or a default one if not provided - // TODO scale width and height should be read in as part of the model as opposed to being here? - FaceAnalyser(vector orientation_bins = vector(), double scale = 0.7, int width = 112, int height = 112, std::string au_location = "AU_predictors/AU_all_best.txt", std::string tri_location = "model/tris_68_full.txt"); + // Constructor from a model file (or a default one if not provided + // TODO scale width and height should be read in as part of the model as opposed to being here? + FaceAnalyser(vector orientation_bins = vector(), double scale = 0.7, int width = 112, int height = 112, std::string au_location = "AU_predictors/AU_all_best.txt", std::string tri_location = "model/tris_68_full.txt"); - void AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CLNF& clnf, double timestamp_seconds, bool online = false, bool visualise = true); + void AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CLNF& clnf, double timestamp_seconds, bool online = false, bool visualise = true); - // If the features are extracted manually (shouldn't really be used) - void PredictAUs(const cv::Mat_& hog_features, const cv::Mat_& geom_features, const LandmarkDetector::CLNF& clnf_model, bool online); + // If the features are extracted manually (shouldn't really be used) + void PredictAUs(const cv::Mat_& hog_features, const cv::Mat_& geom_features, const LandmarkDetector::CLNF& clnf_model, bool online); - cv::Mat GetLatestHOGDescriptorVisualisation(); + cv::Mat GetLatestHOGDescriptorVisualisation(); - double GetCurrentTimeSeconds(); - - // Grab the current predictions about AUs from the face analyser - std::vector> GetCurrentAUsClass() const; // AU presence - std::vector> GetCurrentAUsReg() const; // AU intensity - std::vector> GetCurrentAUsCombined() const; // Both presense and intensity + double GetCurrentTimeSeconds(); - // A standalone call for predicting AUs from a static image, the first element in the pair represents occurence the second intensity - // This call is useful for detecting action units in images - std::pair>, std::vector>> PredictStaticAUs(const cv::Mat& frame, const LandmarkDetector::CLNF& clnf, bool visualise = true); + // Grab the current predictions about AUs from the face analyser + std::vector> GetCurrentAUsClass() const; // AU presence + std::vector> GetCurrentAUsReg() const; // AU intensity + std::vector> GetCurrentAUsCombined() const; // Both presense and intensity - void Reset(); + // A standalone call for predicting AUs from a static image, the first element in the pair represents occurence the second intensity + // This call is useful for detecting action units in images + std::pair>, std::vector>> PredictStaticAUs(const cv::Mat& frame, const LandmarkDetector::CLNF& clnf, bool visualise = true); - void GetLatestHOG(cv::Mat_& hog_descriptor, int& num_rows, int& num_cols); - void GetLatestAlignedFace(cv::Mat& image); - - void GetLatestNeutralHOG(cv::Mat_& hog_descriptor, int& num_rows, int& num_cols); - - cv::Mat_ GetTriangulation(); + void Reset(); - cv::Mat_ GetLatestAlignedFaceGrayscale(); - - void GetGeomDescriptor(cv::Mat_& geom_desc); + void GetLatestHOG(cv::Mat_& hog_descriptor, int& num_rows, int& num_cols); + void GetLatestAlignedFace(cv::Mat& image); - void ExtractCurrentMedians(vector& hog_medians, vector& face_image_medians, vector& orientations); + void GetLatestNeutralHOG(cv::Mat_& hog_descriptor, int& num_rows, int& num_cols); - // Grab the names of AUs being predicted - std::vector GetAUClassNames() const; // Presence - std::vector GetAURegNames() const; // Intensity + cv::Mat_ GetTriangulation(); - // Identify if models are static or dynamic (useful for correction and shifting) - std::vector GetDynamicAUClass() const; // Presence - std::vector> GetDynamicAUReg() const; // Intensity + void GetGeomDescriptor(cv::Mat_& geom_desc); + + // Grab the names of AUs being predicted + std::vector GetAUClassNames() const; // Presence + std::vector GetAURegNames() const; // Intensity + + // Identify if models are static or dynamic (useful for correction and shifting) + std::vector GetDynamicAUClass() const; // Presence + std::vector> GetDynamicAUReg() const; // Intensity - void ExtractAllPredictionsOfflineReg(vector>>& au_predictions, vector& confidences, vector& successes, vector& timestamps, bool dynamic); - void ExtractAllPredictionsOfflineClass(vector>>& au_predictions, vector& confidences, vector& successes, vector& timestamps, bool dynamic); + void ExtractAllPredictionsOfflineReg(vector>>& au_predictions, vector& confidences, vector& successes, vector& timestamps, bool dynamic); + void ExtractAllPredictionsOfflineClass(vector>>& au_predictions, vector& confidences, vector& successes, vector& timestamps, bool dynamic); -private: + // Helper function for post-processing AU output files + void PostprocessOutputFile(string output_file, bool dynamic); - // Where the predictions are kept - std::vector> AU_predictions_reg; - std::vector> AU_predictions_class; + private: - std::vector> AU_predictions_combined; + // Where the predictions are kept + std::vector> AU_predictions_reg; + std::vector> AU_predictions_class; - // Keeping track of AU predictions over time (useful for post-processing) - vector timestamps; - std::map> AU_predictions_reg_all_hist; - std::map> AU_predictions_class_all_hist; - std::vector confidences; - std::vector valid_preds; + std::vector> AU_predictions_combined; - int frames_tracking; + // Keeping track of AU predictions over time (useful for post-processing) + vector timestamps; + std::map> AU_predictions_reg_all_hist; + std::map> AU_predictions_class_all_hist; + std::vector confidences; + std::vector valid_preds; - // Cache of intermediate images - cv::Mat_ aligned_face_grayscale; - cv::Mat aligned_face; - cv::Mat hog_descriptor_visualisation; + int frames_tracking; - // Private members to be used for predictions - // The HOG descriptor of the last frame - cv::Mat_ hog_desc_frame; - int num_hog_rows; - int num_hog_cols; + // Cache of intermediate images + cv::Mat aligned_face_for_au; + cv::Mat aligned_face_for_output; + cv::Mat hog_descriptor_visualisation; - // Keep a running median of the hog descriptors and a aligned images - cv::Mat_ hog_desc_median; - cv::Mat_ face_image_median; + // Private members to be used for predictions + // The HOG descriptor of the last frame + cv::Mat_ hog_desc_frame; + int num_hog_rows; + int num_hog_cols; - // Use histograms for quick (but approximate) median computation - // Use the same for - vector > hog_desc_hist; + // Keep a running median of the hog descriptors and a aligned images + cv::Mat_ hog_desc_median; + cv::Mat_ face_image_median; - // This is not being used at the moment as it is a bit slow - vector > face_image_hist; - vector face_image_hist_sum; + // Use histograms for quick (but approximate) median computation + // Use the same for + vector > hog_desc_hist; - vector head_orientations; + // This is not being used at the moment as it is a bit slow + vector > face_image_hist; + vector face_image_hist_sum; - int num_bins_hog; - double min_val_hog; - double max_val_hog; - vector hog_hist_sum; - int view_used; + vector head_orientations; - // The geometry descriptor (rigid followed by non-rigid shape parameters from CLNF) - cv::Mat_ geom_descriptor_frame; - cv::Mat_ geom_descriptor_median; - - int geom_hist_sum; - cv::Mat_ geom_desc_hist; - int num_bins_geom; - double min_val_geom; - double max_val_geom; - - // Using the bounding box of previous analysed frame to determine if a reset is needed - cv::Rect_ face_bounding_box; - - // The AU predictions internally - std::vector> PredictCurrentAUs(int view); - std::vector> PredictCurrentAUsClass(int view); + int num_bins_hog; + double min_val_hog; + double max_val_hog; + vector hog_hist_sum; + int view_used; - // special step for online (rather than offline AU prediction) - std::vector> CorrectOnlineAUs(std::vector> predictions_orig, int view, bool dyn_shift = false, bool dyn_scale = false, bool update_track = true, bool clip_values = false); + // The geometry descriptor (rigid followed by non-rigid shape parameters from CLNF) + cv::Mat_ geom_descriptor_frame; + cv::Mat_ geom_descriptor_median; - void ReadAU(std::string au_location); + int geom_hist_sum; + cv::Mat_ geom_desc_hist; + int num_bins_geom; + double min_val_geom; + double max_val_geom; - void ReadRegressor(std::string fname, const vector& au_names); + // Using the bounding box of previous analysed frame to determine if a reset is needed + cv::Rect_ face_bounding_box; - // A utility function for keeping track of approximate running medians used for AU and emotion inference using a set of histograms (the histograms are evenly spaced from min_val to max_val) - // Descriptor has to be a row vector - // TODO this duplicates some other code - void UpdateRunningMedian(cv::Mat_& histogram, int& hist_sum, cv::Mat_& median, const cv::Mat_& descriptor, bool update, int num_bins, double min_val, double max_val); - void ExtractMedian(cv::Mat_& histogram, int hist_count, cv::Mat_& median, int num_bins, double min_val, double max_val); - - // The linear SVR regressors - SVR_static_lin_regressors AU_SVR_static_appearance_lin_regressors; - SVR_dynamic_lin_regressors AU_SVR_dynamic_appearance_lin_regressors; - - // The linear SVM classifiers - SVM_static_lin AU_SVM_static_appearance_lin; - SVM_dynamic_lin AU_SVM_dynamic_appearance_lin; + // The AU predictions internally + std::vector> PredictCurrentAUs(int view); + std::vector> PredictCurrentAUsClass(int view); - // The AUs predicted by the model are not always 0 calibrated to a person. That is they don't always predict 0 for a neutral expression - // Keeping track of the predictions we can correct for this, by assuming that at least "ratio" of frames are neutral and subtract that value of prediction, only perform the correction after min_frames - void UpdatePredictionTrack(cv::Mat_& prediction_corr_histogram, int& prediction_correction_count, vector& correction, const vector>& predictions, double ratio=0.25, int num_bins = 200, double min_val = -3, double max_val = 5, int min_frames = 10); - void GetSampleHist(cv::Mat_& prediction_corr_histogram, int prediction_correction_count, vector& sample, double ratio, int num_bins = 200, double min_val = 0, double max_val = 5); + // special step for online (rather than offline AU prediction) + std::vector> CorrectOnlineAUs(std::vector> predictions_orig, int view, bool dyn_shift = false, bool dyn_scale = false, bool update_track = true, bool clip_values = false); - void PostprocessPredictions(); + void ReadAU(std::string au_location); - vector> au_prediction_correction_histogram; - vector au_prediction_correction_count; + void ReadRegressor(std::string fname, const vector& au_names); - // Some dynamic scaling (the logic is that before the extreme versions of expression or emotion are shown, - // it is hard to tell the boundaries, this allows us to scale the model to the most extreme seen) - // They have to be view specific - vector> dyn_scaling; - - // Keeping track of predictions for summary stats - cv::Mat_ AU_prediction_track; - cv::Mat_ geom_desc_track; + // A utility function for keeping track of approximate running medians used for AU and emotion inference using a set of histograms (the histograms are evenly spaced from min_val to max_val) + // Descriptor has to be a row vector + // TODO this duplicates some other code + void UpdateRunningMedian(cv::Mat_& histogram, int& hist_sum, cv::Mat_& median, const cv::Mat_& descriptor, bool update, int num_bins, double min_val, double max_val); + void ExtractMedian(cv::Mat_& histogram, int hist_count, cv::Mat_& median, int num_bins, double min_val, double max_val); - double current_time_seconds; + // The linear SVR regressors + SVR_static_lin_regressors AU_SVR_static_appearance_lin_regressors; + SVR_dynamic_lin_regressors AU_SVR_dynamic_appearance_lin_regressors; - // Used for face alignment - cv::Mat_ triangulation; - double align_scale; - int align_width; - int align_height; + // The linear SVM classifiers + SVM_static_lin AU_SVM_static_appearance_lin; + SVM_dynamic_lin AU_SVM_dynamic_appearance_lin; - // Useful placeholder for renormalizing the initial frames of shorter videos - int max_init_frames = 3000; - vector> hog_desc_frames_init; - vector> geom_descriptor_frames_init; - vector views; - bool postprocessed = false; - int frames_tracking_succ = 0; + // The AUs predicted by the model are not always 0 calibrated to a person. That is they don't always predict 0 for a neutral expression + // Keeping track of the predictions we can correct for this, by assuming that at least "ratio" of frames are neutral and subtract that value of prediction, only perform the correction after min_frames + void UpdatePredictionTrack(cv::Mat_& prediction_corr_histogram, int& prediction_correction_count, vector& correction, const vector>& predictions, double ratio = 0.25, int num_bins = 200, double min_val = -3, double max_val = 5, int min_frames = 10); + void GetSampleHist(cv::Mat_& prediction_corr_histogram, int prediction_correction_count, vector& sample, double ratio, int num_bins = 200, double min_val = 0, double max_val = 5); -}; - //=========================================================================== + void PostprocessPredictions(); + + vector> au_prediction_correction_histogram; + vector au_prediction_correction_count; + + // Some dynamic scaling (the logic is that before the extreme versions of expression or emotion are shown, + // it is hard to tell the boundaries, this allows us to scale the model to the most extreme seen) + // They have to be view specific + vector> dyn_scaling; + + // Keeping track of predictions for summary stats + cv::Mat_ AU_prediction_track; + cv::Mat_ geom_desc_track; + + double current_time_seconds; + + // Used for face alignment + cv::Mat_ triangulation; + double align_scale; + int align_width; + int align_height; + + // Useful placeholder for renormalizing the initial frames of shorter videos + int max_init_frames = 3000; + vector> hog_desc_frames_init; + vector> geom_descriptor_frames_init; + vector views; + bool postprocessed = false; + int frames_tracking_succ = 0; + + }; + //=========================================================================== } #endif diff --git a/lib/local/FaceAnalyser/src/FaceAnalyser.cpp b/lib/local/FaceAnalyser/src/FaceAnalyser.cpp index ae26d73c..5de97f7a 100644 --- a/lib/local/FaceAnalyser/src/FaceAnalyser.cpp +++ b/lib/local/FaceAnalyser/src/FaceAnalyser.cpp @@ -226,7 +226,7 @@ void FaceAnalyser::GetLatestHOG(cv::Mat_& hog_descriptor, int& num_rows, void FaceAnalyser::GetLatestAlignedFace(cv::Mat& image) { - image = this->aligned_face.clone(); + image = this->aligned_face_for_output.clone(); } void FaceAnalyser::GetLatestNeutralHOG(cv::Mat_& hog_descriptor, int& num_rows, int& num_cols) @@ -267,57 +267,22 @@ int GetViewId(const vector orientations_all, const cv::Vec3d& orienta } -void FaceAnalyser::ExtractCurrentMedians(vector& hog_medians, vector& face_image_medians, vector& orientations) -{ - - orientations = this->head_orientations; - - for(size_t i = 0; i < orientations.size(); ++i) - { - cv::Mat_ median_face(this->face_image_median.rows, this->face_image_median.cols, 0.0); - cv::Mat_ median_hog(this->hog_desc_median.rows, this->hog_desc_median.cols, 0.0); - - ExtractMedian(this->face_image_hist[i], this->face_image_hist_sum[i], median_face, 256, 0, 255); - ExtractMedian(this->hog_desc_hist[i], this->hog_hist_sum[i], median_hog, this->num_bins_hog, 0, 1); - - // Add the HOG sample - hog_medians.push_back(median_hog.clone()); - - // For the face image need to convert it to suitable format - cv::Mat_ aligned_face_cols_uchar; - median_face.convertTo(aligned_face_cols_uchar, CV_8U); - - cv::Mat aligned_face_uchar; - if(aligned_face.channels() == 1) - { - aligned_face_uchar = cv::Mat(aligned_face.rows, aligned_face.cols, CV_8U, aligned_face_cols_uchar.data); - } - else - { - aligned_face_uchar = cv::Mat(aligned_face.rows, aligned_face.cols, CV_8UC3, aligned_face_cols_uchar.data); - } - - face_image_medians.push_back(aligned_face_uchar.clone()); - - } -} - std::pair>, std::vector>> FaceAnalyser::PredictStaticAUs(const cv::Mat& frame, const LandmarkDetector::CLNF& clnf, bool visualise) { - + // First align the face - AlignFaceMask(aligned_face, frame, clnf, triangulation, true, align_scale, align_width, align_height); - + AlignFaceMask(aligned_face_for_au, frame, clnf, triangulation, true, 0.7, 112, 112); + // Extract HOG descriptor from the frame and convert it to a useable format cv::Mat_ hog_descriptor; - Extract_FHOG_descriptor(hog_descriptor, aligned_face, this->num_hog_rows, this->num_hog_cols); + Extract_FHOG_descriptor(hog_descriptor, aligned_face_for_au, this->num_hog_rows, this->num_hog_cols); // Store the descriptor hog_desc_frame = hog_descriptor; cv::Vec3d curr_orient(clnf.params_global[1], clnf.params_global[2], clnf.params_global[3]); int orientation_to_use = GetViewId(this->head_orientations, curr_orient); - + // Geom descriptor and its median geom_descriptor_frame = clnf.params_local.t(); @@ -325,11 +290,11 @@ std::pair>, std::vector locs = clnf.pdm.princ_comp * geom_descriptor_frame.t(); cv::hconcat(locs.t(), geom_descriptor_frame.clone(), geom_descriptor_frame); - - // First convert the face image to double representation as a row vector - cv::Mat_ aligned_face_cols(1, aligned_face.cols * aligned_face.rows * aligned_face.channels(), aligned_face.data, 1); - cv::Mat_ aligned_face_cols_double; - aligned_face_cols.convertTo(aligned_face_cols_double, CV_64F); + + // First convert the face image to double representation as a row vector, TODO rem + //cv::Mat_ aligned_face_cols(1, aligned_face_for_au.cols * aligned_face_for_au.rows * aligned_face_for_au.channels(), aligned_face_for_au.data, 1); + //cv::Mat_ aligned_face_cols_double; + //aligned_face_cols.convertTo(aligned_face_cols_double, CV_64F); // Visualising the median HOG if (visualise) @@ -361,29 +326,34 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL frames_tracking++; // First align the face if tracking was successfull - if(clnf_model.detection_success) + if (clnf_model.detection_success) { - AlignFaceMask(aligned_face, frame, clnf_model, triangulation, true, align_scale, align_width, align_height); - } - else - { - aligned_face = cv::Mat(align_height, align_width, CV_8UC3); - aligned_face.setTo(0); - } - if(aligned_face.channels() == 3) - { - cv::cvtColor(aligned_face, aligned_face_grayscale, CV_BGR2GRAY); + // The aligned face requirement for AUs + AlignFaceMask(aligned_face_for_au, frame, clnf_model, triangulation, true, 0.7, 112, 112); + + // If the output requirement matches use the already computed one, else compute it again + if (align_scale == 0.7 && align_width == 112 && align_height == 112) + { + aligned_face_for_output = aligned_face_for_au.clone(); + } + else + { + AlignFaceMask(aligned_face_for_output, frame, clnf_model, triangulation, true, align_scale, align_width, align_height); + } } else { - aligned_face_grayscale = aligned_face.clone(); + aligned_face_for_output = cv::Mat(align_height, align_width, CV_8UC3); + aligned_face_for_au = cv::Mat(112, 112, CV_8UC3); + aligned_face_for_output.setTo(0); + aligned_face_for_au.setTo(0); } // Extract HOG descriptor from the frame and convert it to a useable format cv::Mat_ hog_descriptor; - Extract_FHOG_descriptor(hog_descriptor, aligned_face, this->num_hog_rows, this->num_hog_cols); - + Extract_FHOG_descriptor(hog_descriptor, aligned_face_for_au, this->num_hog_rows, this->num_hog_cols); + // Store the descriptor hog_desc_frame = hog_descriptor; @@ -425,41 +395,38 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL frames_tracking_succ++; // A small speedup - if(frames_tracking % 2 == 1) + if (frames_tracking % 2 == 1) { UpdateRunningMedian(this->hog_desc_hist[orientation_to_use], this->hog_hist_sum[orientation_to_use], this->hog_desc_median, hog_descriptor, update_median, this->num_bins_hog, this->min_val_hog, this->max_val_hog); this->hog_desc_median.setTo(0, this->hog_desc_median < 0); - } + } // Geom descriptor and its median geom_descriptor_frame = clnf_model.params_local.t(); - - if(!clnf_model.detection_success) + + if (!clnf_model.detection_success) { geom_descriptor_frame.setTo(0); } // Stack with the actual feature point locations (without mean) cv::Mat_ locs = clnf_model.pdm.princ_comp * geom_descriptor_frame.t(); - + cv::hconcat(locs.t(), geom_descriptor_frame.clone(), geom_descriptor_frame); - + // A small speedup - if(frames_tracking % 2 == 1) + if (frames_tracking % 2 == 1) { UpdateRunningMedian(this->geom_desc_hist, this->geom_hist_sum, this->geom_descriptor_median, geom_descriptor_frame, update_median, this->num_bins_geom, this->min_val_geom, this->max_val_geom); } - // First convert the face image to double representation as a row vector - cv::Mat_ aligned_face_cols(1, aligned_face.cols * aligned_face.rows * aligned_face.channels(), aligned_face.data, 1); - cv::Mat_ aligned_face_cols_double; - aligned_face_cols.convertTo(aligned_face_cols_double, CV_64F); - - // TODO get rid of this completely as it takes too long? - //UpdateRunningMedian(this->face_image_hist[orientation_to_use], this->face_image_hist_sum[orientation_to_use], this->face_image_median, aligned_face_cols_double, update_median, 256, 0, 255); + // First convert the face image to double representation as a row vector, TODO rem? + //cv::Mat_ aligned_face_cols(1, aligned_face.cols * aligned_face.rows * aligned_face.channels(), aligned_face.data, 1); + //cv::Mat_ aligned_face_cols_double; + //aligned_face_cols.convertTo(aligned_face_cols_double, CV_64F); // Visualising the median HOG - if(visualise) + if (visualise) { FaceAnalysis::Visualise_FHOG(hog_descriptor, num_hog_rows, num_hog_cols, hog_descriptor_visualisation); } @@ -468,9 +435,9 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL AU_predictions_reg = PredictCurrentAUs(orientation_to_use); std::vector> AU_predictions_reg_corrected; - if(online) + if (online) { - AU_predictions_reg_corrected = CorrectOnlineAUs(AU_predictions_reg, orientation_to_use, true, false, clnf_model.detection_success); + AU_predictions_reg_corrected = CorrectOnlineAUs(AU_predictions_reg, orientation_to_use, true, false, clnf_model.detection_success, true); } // Add the reg predictions to the historic data @@ -479,7 +446,7 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL // Find the appropriate AU (if not found add it) // Only add if the detection was successful - if(clnf_model.detection_success) + if (clnf_model.detection_success) { AU_predictions_reg_all_hist[AU_predictions_reg[au].first].push_back(AU_predictions_reg[au].second); } @@ -488,7 +455,7 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL AU_predictions_reg_all_hist[AU_predictions_reg[au].first].push_back(0); } } - + AU_predictions_class = PredictCurrentAUsClass(orientation_to_use); for (size_t au = 0; au < AU_predictions_class.size(); ++au) @@ -496,7 +463,7 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL // Find the appropriate AU (if not found add it) // Only add if the detection was successful - if(clnf_model.detection_success) + if (clnf_model.detection_success) { AU_predictions_class_all_hist[AU_predictions_class[au].first].push_back(AU_predictions_class[au].second); } @@ -505,9 +472,9 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL AU_predictions_class_all_hist[AU_predictions_class[au].first].push_back(0); } } - - if(online) + + if (online) { AU_predictions_reg = AU_predictions_reg_corrected; } @@ -524,15 +491,13 @@ void FaceAnalyser::AddNextFrame(const cv::Mat& frame, const LandmarkDetector::CL this->current_time_seconds = timestamp_seconds; view_used = orientation_to_use; - + bool success = clnf_model.detection_success; confidences.push_back(clnf_model.detection_certainty); valid_preds.push_back(success); timestamps.push_back(timestamp_seconds); - - } void FaceAnalyser::GetGeomDescriptor(cv::Mat_& geom_desc) @@ -764,14 +729,16 @@ void FaceAnalyser::ExtractAllPredictionsOfflineReg(vector au_vals_tmp = au_iter->second; - for (size_t i = (window_size - 1) / 2; i < au_iter->second.size() - (window_size - 1) / 2; ++i) + for (int i = (window_size - 1) / 2; i < (int)au_iter->second.size() - (window_size - 1) / 2; ++i) { double sum = 0; + int count_over = 0; for (int w = -(window_size - 1) / 2; w <= (window_size - 1) / 2; ++w) { sum += au_vals_tmp[i + w]; + count_over++; } - sum = sum / window_size; + sum = sum / count_over; au_iter->second[i] = sum; } @@ -798,14 +765,16 @@ void FaceAnalyser::ExtractAllPredictionsOfflineClass(vector au_vals_tmp = au_vals; - for (size_t i = (window_size - 1)/2; i < au_vals.size() - (window_size - 1) / 2; ++i) + for (int i = (window_size - 1)/2; i < (int)au_vals.size() - (window_size - 1) / 2; ++i) { double sum = 0; + int count_over = 0; for (int w = -(window_size - 1) / 2; w <= (window_size - 1) / 2; ++w) { sum += au_vals_tmp[i + w]; + count_over++; } - sum = sum / window_size; + sum = sum / count_over; if (sum < 0.5) sum = 0; else @@ -1097,12 +1066,6 @@ vector> FaceAnalyser::PredictCurrentAUsClass(int view) return predictions; } - -cv::Mat_ FaceAnalyser::GetLatestAlignedFaceGrayscale() -{ - return aligned_face_grayscale.clone(); -} - cv::Mat FaceAnalyser::GetLatestHOGDescriptorVisualisation() { return hog_descriptor_visualisation; @@ -1294,4 +1257,122 @@ void FaceAnalyser::ReadRegressor(std::string fname, const vector& au_nam double FaceAnalyser::GetCurrentTimeSeconds() { return current_time_seconds; -} \ No newline at end of file +} + +// Allows for post processing of the AU signal +void FaceAnalyser::PostprocessOutputFile(string output_file, bool dynamic) +{ + + vector certainties; + vector successes; + vector timestamps; + vector>> predictions_reg; + vector>> predictions_class; + + // Construct the new values to overwrite the output file with + ExtractAllPredictionsOfflineReg(predictions_reg, certainties, successes, timestamps, dynamic); + ExtractAllPredictionsOfflineClass(predictions_class, certainties, successes, timestamps, dynamic); + + int num_class = predictions_class.size(); + int num_reg = predictions_reg.size(); + + // Extract the indices of writing out first + vector au_reg_names = GetAURegNames(); + std::sort(au_reg_names.begin(), au_reg_names.end()); + vector inds_reg; + + // write out ar the correct index + for (string au_name : au_reg_names) + { + for (int i = 0; i < num_reg; ++i) + { + if (au_name.compare(predictions_reg[i].first) == 0) + { + inds_reg.push_back(i); + break; + } + } + } + + vector au_class_names = GetAUClassNames(); + std::sort(au_class_names.begin(), au_class_names.end()); + vector inds_class; + + // write out ar the correct index + for (string au_name : au_class_names) + { + for (int i = 0; i < num_class; ++i) + { + if (au_name.compare(predictions_class[i].first) == 0) + { + inds_class.push_back(i); + break; + } + } + } + // Read all of the output file in + vector output_file_contents; + + std::ifstream infile(output_file); + string line; + + while (std::getline(infile, line)) + output_file_contents.push_back(line); + + infile.close(); + + // Read the header and find all _r and _c parts in a file and use their indices + std::vector tokens; + boost::split(tokens, output_file_contents[0], boost::is_any_of(",")); + + int begin_ind = -1; + + for (size_t i = 0; i < tokens.size(); ++i) + { + if (tokens[i].find("AU") != string::npos && begin_ind == -1) + { + begin_ind = i; + break; + } + } + int end_ind = begin_ind + num_class + num_reg; + + // Now overwrite the whole file + std::ofstream outfile(output_file, ios_base::out); + // Write the header + outfile << std::setprecision(4); + outfile << output_file_contents[0].c_str() << endl; + + // Write the contents + for (int i = 1; i < (int)output_file_contents.size(); ++i) + { + std::vector tokens; + boost::split(tokens, output_file_contents[i], boost::is_any_of(",")); + + boost::trim(tokens[0]); + outfile << tokens[0]; + + for (int t = 1; t < (int)tokens.size(); ++t) + { + if (t >= begin_ind && t < end_ind) + { + if (t - begin_ind < num_reg) + { + outfile << ", " << predictions_reg[inds_reg[t - begin_ind]].second[i - 1]; + } + else + { + outfile << ", " << predictions_class[inds_class[t - begin_ind - num_reg]].second[i - 1]; + } + } + else + { + boost::trim(tokens[t]); + outfile << ", " << tokens[t]; + } + } + outfile << endl; + } + + +} diff --git a/lib/local/FaceAnalyser/src/Face_utils.cpp b/lib/local/FaceAnalyser/src/Face_utils.cpp index 52e00940..b6d4eb58 100644 --- a/lib/local/FaceAnalyser/src/Face_utils.cpp +++ b/lib/local/FaceAnalyser/src/Face_utils.cpp @@ -221,19 +221,19 @@ namespace FaceAnalysis destination_landmarks.col(1) = destination_landmarks.col(1) + warp_matrix(1,2); // Move the eyebrows up to include more of upper face - destination_landmarks.at(0,1) -= 30; - destination_landmarks.at(16,1) -= 30; + destination_landmarks.at(0,1) -= (30/0.7)*sim_scale; + destination_landmarks.at(16,1) -= (30 / 0.7)*sim_scale; - destination_landmarks.at(17,1) -= 30; - destination_landmarks.at(18,1) -= 30; - destination_landmarks.at(19,1) -= 30; - destination_landmarks.at(20,1) -= 30; - destination_landmarks.at(21,1) -= 30; - destination_landmarks.at(22,1) -= 30; - destination_landmarks.at(23,1) -= 30; - destination_landmarks.at(24,1) -= 30; - destination_landmarks.at(25,1) -= 30; - destination_landmarks.at(26,1) -= 30; + destination_landmarks.at(17,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(18,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(19,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(20,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(21,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(22,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(23,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(24,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(25,1) -= (30 / 0.7)*sim_scale; + destination_landmarks.at(26,1) -= (30 / 0.7)*sim_scale; destination_landmarks = cv::Mat(destination_landmarks.t()).reshape(1, 1).t(); diff --git a/lib/local/LandmarkDetector/CMakeLists.txt b/lib/local/LandmarkDetector/CMakeLists.txt index a5e1a502..9d5055c6 100644 --- a/lib/local/LandmarkDetector/CMakeLists.txt +++ b/lib/local/LandmarkDetector/CMakeLists.txt @@ -35,7 +35,7 @@ SET(HEADERS include_directories(./include) include_directories(${LandmarkDetector_SOURCE_DIR}/include) -add_library( LandmarkDetector ${SOURCE} ${HEADERS}) +add_library( LandmarkDetector ${SOURCE} ${HEADERS} ) -install (TARGETS LandmarkDetector DESTINATION bin) -install (FILES ${HEADERS} DESTINATION include) +install (TARGETS LandmarkDetector DESTINATION lib) +install (FILES ${HEADERS} DESTINATION include/OpenFace) diff --git a/lib/local/LandmarkDetector/include/LandmarkDetectorModel.h b/lib/local/LandmarkDetector/include/LandmarkDetectorModel.h index b5fb8e45..b34d3f94 100644 --- a/lib/local/LandmarkDetector/include/LandmarkDetectorModel.h +++ b/lib/local/LandmarkDetector/include/LandmarkDetectorModel.h @@ -2,7 +2,7 @@ // Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, // all rights reserved. // -// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS // OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS @@ -15,13 +15,13 @@ // POSSIBILITY OF SUCH DAMAGE. // // Notwithstanding the license granted herein, Licensee acknowledges that certain components -// of the Software may be covered by so-called “open source” software licenses (“Open Source -// Components”), which means any software licenses approved as open source licenses by the +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the // Open Source Initiative or any substantially similar licenses, including without limitation any // license that, as a condition of distribution of the software licensed under such license, // requires that the distributor make the software available in source code format. Licensor shall // provide a list of Open Source Components for a particular version of the Software upon -// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to // the extent required by the licenses covering Open Source Components, the terms of such // licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the // licenses applicable to Open Source Components prohibit any of the restrictions in this @@ -38,20 +38,20 @@ // reports and manuals, must cite at least one of the following works: // // OpenFace: an open source facial behavior analysis toolkit -// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency // in IEEE Winter Conference on Applications of Computer Vision, 2016 // // Rendering of Eyes for Eye-Shape Registration and Gaze Estimation -// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling // in IEEE International. Conference on Computer Vision (ICCV), 2015 // // Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection -// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson // in Facial Expression Recognition and Analysis Challenge, // IEEE International Conference on Automatic Face and Gesture Recognition, 2015 // // Constrained Local Neural Fields for robust facial landmark detection in the wild. -// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. // in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. // /////////////////////////////////////////////////////////////////////////////// @@ -130,7 +130,7 @@ public: double detection_certainty; // Indicator if eye model is there for eye detection - bool eye_model = false; + bool eye_model; // the triangulation per each view (for drawing purposes only) vector > triangulations; diff --git a/lib/local/LandmarkDetector/include/LandmarkDetectorUtils.h b/lib/local/LandmarkDetector/include/LandmarkDetectorUtils.h index e74cf49a..cdd6ab3a 100644 --- a/lib/local/LandmarkDetector/include/LandmarkDetectorUtils.h +++ b/lib/local/LandmarkDetector/include/LandmarkDetectorUtils.h @@ -77,7 +77,7 @@ namespace LandmarkDetector // Helper functions for parsing the inputs //============================================================================================= void get_video_input_output_params(vector &input_video_file, vector &depth_dir, vector &output_files, - vector &output_video_files, bool& world_coordinates_pose, vector &arguments); + vector &output_video_files, bool& world_coordinates_pose, string &output_codec, vector &arguments); void get_camera_params(int &device, float &fx, float &fy, float &cx, float &cy, vector &arguments); diff --git a/lib/local/LandmarkDetector/src/LandmarkDetectorFunc.cpp b/lib/local/LandmarkDetector/src/LandmarkDetectorFunc.cpp index a0bd2982..39710b9c 100644 --- a/lib/local/LandmarkDetector/src/LandmarkDetectorFunc.cpp +++ b/lib/local/LandmarkDetector/src/LandmarkDetectorFunc.cpp @@ -447,7 +447,7 @@ bool LandmarkDetector::DetectLandmarksInVideo(const cv::Mat_ &grayscale_i bool LandmarkDetector::DetectLandmarksInVideo(const cv::Mat_ &grayscale_image, const cv::Rect_ bounding_box, CLNF& clnf_model, FaceModelParameters& params) { - return DetectLandmarksInVideo(grayscale_image, cv::Mat_(), clnf_model, params); + return DetectLandmarksInVideo(grayscale_image, cv::Mat_(), bounding_box, clnf_model, params); } //================================================================================================================ diff --git a/lib/local/LandmarkDetector/src/LandmarkDetectorModel.cpp b/lib/local/LandmarkDetector/src/LandmarkDetectorModel.cpp index 0920e528..80d2c3b0 100644 --- a/lib/local/LandmarkDetector/src/LandmarkDetectorModel.cpp +++ b/lib/local/LandmarkDetector/src/LandmarkDetectorModel.cpp @@ -2,7 +2,7 @@ // Copyright (C) 2016, Carnegie Mellon University and University of Cambridge, // all rights reserved. // -// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS +// THIS SOFTWARE IS PROVIDED “AS IS” FOR ACADEMIC USE ONLY AND ANY EXPRESS // OR IMPLIED WARRANTIES WARRANTIES, INCLUDING, BUT NOT LIMITED TO, // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS @@ -15,13 +15,13 @@ // POSSIBILITY OF SUCH DAMAGE. // // Notwithstanding the license granted herein, Licensee acknowledges that certain components -// of the Software may be covered by so-called “open source” software licenses (“Open Source -// Components”), which means any software licenses approved as open source licenses by the +// of the Software may be covered by so-called “open source” software licenses (“Open Source +// Components”), which means any software licenses approved as open source licenses by the // Open Source Initiative or any substantially similar licenses, including without limitation any // license that, as a condition of distribution of the software licensed under such license, // requires that the distributor make the software available in source code format. Licensor shall // provide a list of Open Source Components for a particular version of the Software upon -// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to +// Licensee’s request. Licensee will comply with the applicable terms of such licenses and to // the extent required by the licenses covering Open Source Components, the terms of such // licenses will apply in lieu of the terms of this Agreement. To the extent the terms of the // licenses applicable to Open Source Components prohibit any of the restrictions in this @@ -38,20 +38,20 @@ // reports and manuals, must cite at least one of the following works: // // OpenFace: an open source facial behavior analysis toolkit -// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency // in IEEE Winter Conference on Applications of Computer Vision, 2016 // // Rendering of Eyes for Eye-Shape Registration and Gaze Estimation -// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling +// Erroll Wood, Tadas Baltrušaitis, Xucong Zhang, Yusuke Sugano, Peter Robinson, and Andreas Bulling // in IEEE International. Conference on Computer Vision (ICCV), 2015 // // Cross-dataset learning and person-speci?c normalisation for automatic Action Unit detection -// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson +// Tadas Baltrušaitis, Marwa Mahmoud, and Peter Robinson // in Facial Expression Recognition and Analysis Challenge, // IEEE International Conference on Automatic Face and Gesture Recognition, 2015 // // Constrained Local Neural Fields for robust facial landmark detection in the wild. -// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. +// Tadas Baltrušaitis, Peter Robinson, and Louis-Philippe Morency. // in IEEE Int. Conference on Computer Vision Workshops, 300 Faces in-the-Wild Challenge, 2013. // /////////////////////////////////////////////////////////////////////////////// @@ -371,6 +371,9 @@ void CLNF::Read(string main_location) // The other module locations should be defined as relative paths from the main model boost::filesystem::path root = boost::filesystem::path(main_location).parent_path(); + // Assume no eye model, unless read-in + eye_model = false; + // The main file contains the references to other files while (!locations.eof()) { @@ -392,6 +395,7 @@ void CLNF::Read(string main_location) location = location.substr(0, location.size()-1); } + // append to root location = (root / location).string(); if (module.compare("LandmarkDetector") == 0) diff --git a/lib/local/LandmarkDetector/src/LandmarkDetectorParameters.cpp b/lib/local/LandmarkDetector/src/LandmarkDetectorParameters.cpp index 82ddc500..d71ddd5a 100644 --- a/lib/local/LandmarkDetector/src/LandmarkDetectorParameters.cpp +++ b/lib/local/LandmarkDetector/src/LandmarkDetectorParameters.cpp @@ -67,6 +67,11 @@ // System includes #include #include +#include + +#ifndef CONFIG_DIR +#define CONFIG_DIR "~" +#endif using namespace std; @@ -200,15 +205,25 @@ FaceModelParameters::FaceModelParameters(vector &arguments) } // Make sure model_location is valid - if (!boost::filesystem::exists(boost::filesystem::path(model_location))) + // First check working directory, then the executable's directory, then the config path set by the build process. + boost::filesystem::path config_path = boost::filesystem::path(CONFIG_DIR); + boost::filesystem::path model_path = boost::filesystem::path(model_location); + if (boost::filesystem::exists(model_path)) { - model_location = (root / model_location).string(); - if (!boost::filesystem::exists(boost::filesystem::path(model_location))) - { - std::cout << "Could not find the landmark detection model to load" << std::endl; - } + model_location = model_path.string(); + } + else if (boost::filesystem::exists(root/model_path)) + { + model_location = (root/model_path).string(); + } + else if (boost::filesystem::exists(config_path/model_path)) + { + model_location = (config_path/model_path).string(); + } + else + { + std::cout << "Could not find the landmark detection model to load" << std::endl; } - } void FaceModelParameters::init() diff --git a/lib/local/LandmarkDetector/src/LandmarkDetectorUtils.cpp b/lib/local/LandmarkDetector/src/LandmarkDetectorUtils.cpp index c952e516..caf366aa 100644 --- a/lib/local/LandmarkDetector/src/LandmarkDetectorUtils.cpp +++ b/lib/local/LandmarkDetector/src/LandmarkDetectorUtils.cpp @@ -121,7 +121,7 @@ void create_directories(string output_path) // Extracting the following command line arguments -f, -fd, -op, -of, -ov (and possible ordered repetitions) void get_video_input_output_params(vector &input_video_files, vector &depth_dirs, vector &output_files, - vector &output_video_files, bool& world_coordinates_pose, vector &arguments) + vector &output_video_files, bool& world_coordinates_pose, string& output_codec, vector &arguments) { bool* valid = new bool[arguments.size()]; @@ -133,6 +133,9 @@ void get_video_input_output_params(vector &input_video_files, vector &input_video_files, vector= 0; --i) diff --git a/matlab_runners/Action Unit Experiments/helpers/ParseSEMAINEAnnotations.m b/matlab_runners/Action Unit Experiments/helpers/ParseSEMAINEAnnotations.m new file mode 100644 index 00000000..bc8911dc --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/ParseSEMAINEAnnotations.m @@ -0,0 +1,181 @@ + +% Function ParseSEMAINEAnnotations is intended to demonstrate example usage +% of SEMAINE Action Unit annotations made with ELAN annotation toolbox. +% This function loads the XML structure from an ELAN annotation file with +% ".eaf" extension, parses it and returns a numerical matrix called +% "activations" of size NUMBER OF FRAMES X NUMBER OF ACTION UNITS. The +% matrix holds binary activation status for each frame / AU combination. +% The matrix also has a row header showing which AU corresponds to which +% row as well as a column header displaying original frame indexes. + +% The function takes 1 compulsory and 2 optional arguments: + +% - "filepath" (compulsory) - complete path to an annotation file to parse. +% For example, "/matlab/annotation.eaf" or "C:\matlab\annotation.eaf" on +% Windows. + +% - "startFrame" (optional) - ignore all annotations before "startFrame". +% Default is 1. + +% - "endFrame" (optional) - ignore all annotations after "endFrame". +% Default is the last frame of a video. + +% The function requires XML IO Toolbox +% (http://www.mathworks.com/matlabcentral/fileexchange/12907-xml-io-tools) +% to run properly (supplied). + +function activations = ParseSEMAINEAnnotations (filepath, startFrame, endFrame) + activations = []; + + % Framerate value used to convert ELAN millisecond time slots to more + % usual frames. 50 is a valid framerate for all SEMAINE videos. + framerate = 50; + + % A fixed set of 6 Action Units selected for the challenge from the + % SEMAINE annotations + aus = [2 12 17 25 28 45]; + + % Total number of AUs. + naus = length(aus); + + % Load XML structure from the file, return in case of a problem. + [success, XML] = OpenXML(filepath); + if ~success + return + end + + % Parse annotation time slots + tslots = ParseTimeSlots(XML); + + % Init start and end frames with default values + if nargin < 2 + startFrame = 1; + end + + if nargin < 3 + % Get total number of time slots + ntslots = length(tslots); + % Get last slot ID + lastID = strcat('ts', num2str(ntslots)); + % Get last time slot value in ms + lastValue = tslots(lastID); + % Convert last time slot value in ms to frames + endFrame = floor((lastValue / 1000) * framerate); + end + + % Get total number of tiers. There are 65 of them, 1 for speech, 32 for + % activations (1 per AU) and 32 for intensities. We are going to ignore + % intensity tiers. + ntiers = length(XML.TIER); + + % Compose vector of frame indexes to extract annotations from + frames = (startFrame:endFrame); + + % Preallocate activations matrix + activations = zeros(length(frames), naus); + + indx = 1; + % Go through all tiers skipping the first one (speech) as well as every + % intensity tier. A single activation tier is processed at every + % iteration. + for k = 2:2:ntiers + tier = XML.TIER(k); + % Only extract annotations of selected AUs, skip the rest + au = strcat('AU', num2str(aus(indx))); + if strcmp(au, tier.ATTRIBUTE.TIER_ID) + % Read all activation periods from the current tier + activationTier = ParseActivationTier(tier, tslots); + % Convert of all activation periods into frame level numerical + % representation + activations(:, indx) = ParseOccurrences(activationTier, frames, framerate); + + indx = indx + 1; + end + + if indx > naus + break + end + end + + activations = [frames' activations]; + activations = [[0 aus]; activations]; +end + +function occurrences = ParseOccurrences (activations, frames, framerate) + % Preallocate activations vector + occurrences = zeros(length(frames), 1); + % Go through all activation periods, convert ms into frames and init + % corresponding values of activations vector with 1 leaving the rest be 0 + for i = 1:length(activations) + % Convert ms into frames + sframe = floor((activations(i).start / 1000) * framerate); + eframe = floor((activations(i).end / 1000) * framerate); + + % Determine indexes of frames vector corresponding to the above + % time frame + sindx = find(frames == sframe); + eindx = find(frames == eframe); + + % Mark active set of frames with 1 + occurrences(sindx:eindx) = 1; + end +end + +function activationTier = ParseActivationTier (tier, tslots) + % Get total number of activation periods + nactivations = length(tier.ANNOTATION); + % Preallocate activation tier structure holding start and end time + % stamps of all activation periods for the given AU + activationTier = repmat(struct('start', 0, 'end', 0), nactivations, 1); + % Go through all activation periods and init activation tier + % structure array + for i = 1:nactivations + % Read start time slot ID of the current activation period + t = tier.ANNOTATION(i).ALIGNABLE_ANNOTATION.ATTRIBUTE.TIME_SLOT_REF1; + % Read time in ms corresponding to the time slot ID + activationTier(i).start = tslots(t); + + % Read end time slot ID of the current activation period + t = tier.ANNOTATION(i).ALIGNABLE_ANNOTATION.ATTRIBUTE.TIME_SLOT_REF2; + % Read time in ms corresponding to the time slot ID + activationTier(i).end = tslots(t); + end +end + +function tslots = ParseTimeSlots (xmlObject) + % Get total number of time slots + nslots = length(xmlObject.TIME_ORDER.TIME_SLOT); + % Preallocate cell arrays of time slot IDs and values + tids = cell(nslots, 1); + tvalues = zeros(nslots, 1); + % Read all time slot IDs and numerical values (in ms) + for i = 1:nslots + tids{i} = xmlObject.TIME_ORDER.TIME_SLOT(i).ATTRIBUTE.TIME_SLOT_ID; + tvalues(i) = xmlObject.TIME_ORDER.TIME_SLOT(i).ATTRIBUTE.TIME_VALUE; + end + % Map time slot IDs and values together so that values are accessible + % by their IDs + tslots = containers.Map(tids, tvalues); +end + +function [success, xmlObject] = OpenXML (xmlPath) + fprintf(' *** Attempting to load \"%s\" ... ', xmlPath); + xmlObject = []; + success = false; + % Check if the specified file exists and return error otherwise + if exist(xmlPath, 'file') + % Load XML structure + xmlObject = xml_read(xmlPath); + % Check if XML object loaded correctly, return error otherwise + if isempty(xmlObject) + fprintf(' ERROR - unable to read xml tree *** \n'); + return + else + success = true; + end + else + fprintf(' ERROR - specified path does not exist *** \n'); + return + end + fprintf(' Done *** \n'); +end \ No newline at end of file diff --git a/matlab_runners/Action Unit Experiments/helpers/extract_SEMAINE_labels.m b/matlab_runners/Action Unit Experiments/helpers/extract_SEMAINE_labels.m index c7f122b4..76ff8391 100644 --- a/matlab_runners/Action Unit Experiments/helpers/extract_SEMAINE_labels.m +++ b/matlab_runners/Action Unit Experiments/helpers/extract_SEMAINE_labels.m @@ -26,14 +26,9 @@ function [ labels, valid_ids, vid_ids ] = extract_SEMAINE_labels( SEMAINE_dir, xml_file = [SEMAINE_dir, recs{i}, '\' file.name]; [root_xml, name_xml, ~] = fileparts(xml_file); - m_file = [root_xml, name_xml, '.mat']; - if(~exist(m_file, 'file')) - activations = ParseSEMAINEAnnotations([SEMAINE_dir, recs{i}, '\' file.name]); - save(m_file, 'activations'); - else - load(m_file); - end + activations = ParseSEMAINEAnnotations([SEMAINE_dir, recs{i}, '/' file.name]); + if(size(activations,1) < vid_ids(i,2)) vid_ids(i,2) = size(activations,1); if(vid_ids(i,2) > 2999) diff --git a/matlab_runners/Action Unit Experiments/helpers/find_FERA2011.m b/matlab_runners/Action Unit Experiments/helpers/find_FERA2011.m index 411fcba8..fd5bdbdd 100644 --- a/matlab_runners/Action Unit Experiments/helpers/find_FERA2011.m +++ b/matlab_runners/Action Unit Experiments/helpers/find_FERA2011.m @@ -1,6 +1,7 @@ if(exist('D:\Datasets\fera/au_training', 'file')) FERA2011_dir = 'D:\Datasets\fera/au_training/'; - hog_data_dir = 'D:\Datasets\face_datasets\hog_aligned_rigid/'; +elseif(exist('/multicomp/datasets/fera2011/', 'file')) + FERA2011_dir = '/multicomp/datasets/fera2011/au_training/'; else fprintf('FERA2011 location not found (or not defined)\n'); end diff --git a/matlab_runners/Action Unit Experiments/helpers/find_SEMAINE.m b/matlab_runners/Action Unit Experiments/helpers/find_SEMAINE.m index b7c6ed2b..6c44599b 100644 --- a/matlab_runners/Action Unit Experiments/helpers/find_SEMAINE.m +++ b/matlab_runners/Action Unit Experiments/helpers/find_SEMAINE.m @@ -10,8 +10,10 @@ elseif(exist('D:\Datasets\FERA_2015\semaine\SEMAINE-Sessions/', 'file')) SEMAINE_dir = 'D:\Datasets\FERA_2015\semaine\SEMAINE-Sessions/'; elseif(exist('D:/fera_2015/semaine/SEMAINE-Sessions/', 'file')) SEMAINE_dir = 'D:/fera_2015/semaine/SEMAINE-Sessions/'; +elseif(exist('/multicomp/datasets/face_datasets/FERA_2015/Semaine/SEMAINE-Sessions/', 'file')) + SEMAINE_dir = '/multicomp/datasets/face_datasets/FERA_2015/Semaine/SEMAINE-Sessions/'; else - fprintf('DISFA location not found (or not defined)\n'); + fprintf('SEMAINE location not found (or not defined)\n'); end if(exist('SEMAINE_dir', 'var')) diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/base64decode.m b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/base64decode.m new file mode 100644 index 00000000..3d34f01f --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/base64decode.m @@ -0,0 +1,117 @@ +function y = base64decode(x, outfname, alg) +%BASE64DECODE Perform base64 decoding on a string. +% +% INPUT: +% x - block of data to be decoded. Can be a string or a numeric +% vector containing integers in the range 0-255. Any character +% not part of the 65-character base64 subset set is silently +% ignored. Characters occuring after a '=' padding character are +% never decoded. If the length of the string to decode (after +% ignoring non-base64 chars) is not a multiple of 4, then a +% warning is generated. +% +% outfname - if provided the binary date from decoded string will be +% saved into a file. Since Base64 coding is often used to embbed +% binary data in xml files, this option can be used to extract and +% save them. +% +% alg - Algorithm to use: can take values 'java' or 'matlab'. Optional +% variable defaulting to 'java' which is a little faster. If +% 'java' is chosen than core of the code is performed by a call to +% a java library. Optionally all operations can be performed using +% matleb code. +% +% OUTPUT: +% y - array of binary data returned as uint8 +% +% This function is used to decode strings from the Base64 encoding specified +% in RFC 2045 - MIME (Multipurpose Internet Mail Extensions). The Base64 +% encoding is designed to represent arbitrary sequences of octets in a form +% that need not be humanly readable. A 65-character subset ([A-Za-z0-9+/=]) +% of US-ASCII is used, enabling 6 bits to be represented per printable +% character. +% +% See also BASE64ENCODE. +% +% Written by Jarek Tuszynski, SAIC, jaroslaw.w.tuszynski_at_saic.com +% +% Matlab version based on 2004 code by Peter J. Acklam +% E-mail: pjacklam@online.no +% URL: http://home.online.no/~pjacklam +% http://home.online.no/~pjacklam/matlab/software/util/datautil/base64encode.m + +if nargin<3, alg='java'; end +if nargin<2, outfname=''; end + +%% if x happen to be a filename than read the file +if (numel(x)<256) + if (exist(x, 'file')==2) + fid = fopen(x,'rb'); + x = fread(fid, 'uint8'); + fclose(fid); + end +end +x = uint8(x(:)); % unify format + +%% Perform conversion +switch (alg) + case 'java' + base64 = org.apache.commons.codec.binary.Base64; + y = base64.decode(x); + y = mod(int16(y),256); % convert from int8 to uint8 + case 'matlab' + %% Perform the mapping + % A-Z -> 0 - 25 + % a-z -> 26 - 51 + % 0-9 -> 52 - 61 + % + - -> 62 '-' is URL_SAFE alternative + % / _ -> 63 '_' is URL_SAFE alternative + map = uint8(zeros(1,256)+65); + map(uint8(['A':'Z', 'a':'z', '0':'9', '+/=']))= 0:64; + map(uint8('-_'))= 62:63; % URL_SAFE alternatives + x = map(x); % mapping + + x(x>64)=[]; % remove non-base64 chars + if rem(numel(x), 4) + warning('Length of base64 data not a multiple of 4; padding input.'); + end + x(x==64)=[]; % remove padding characters + + %% add padding and reshape + nebytes = length(x); % number of encoded bytes + nchunks = ceil(nebytes/4); % number of chunks/groups + if rem(nebytes, 4)>0 + x(end+1 : 4*nchunks) = 0; % add padding + end + x = reshape(uint8(x), 4, nchunks); + y = repmat(uint8(0), 3, nchunks); % for the decoded data + + %% Rearrange every 4 bytes into 3 bytes + % 00aaaaaa 00bbbbbb 00cccccc 00dddddd + % to form + % aaaaaabb bbbbcccc ccdddddd + y(1,:) = bitshift(x(1,:), 2); % 6 highest bits of y(1,:) + y(1,:) = bitor(y(1,:), bitshift(x(2,:), -4)); % 2 lowest bits of y(1,:) + y(2,:) = bitshift(x(2,:), 4); % 4 highest bits of y(2,:) + y(2,:) = bitor(y(2,:), bitshift(x(3,:), -2)); % 4 lowest bits of y(2,:) + y(3,:) = bitshift(x(3,:), 6); % 2 highest bits of y(3,:) + y(3,:) = bitor(y(3,:), x(4,:)); % 6 lowest bits of y(3,:) + + %% remove extra padding + switch rem(nebytes, 4) + case 2 + y = y(1:end-2); + case 3 + y = y(1:end-1); + end +end + +%% reshape to a row vector and make it a character array +y = uint8(reshape(y, 1, numel(y))); + +%% save to file if needed +if ~isempty(outfname) + fid = fopen(outfname,'wb'); + fwrite(fid, y, 'uint8'); + fclose(fid); +end diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/base64encode.m b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/base64encode.m new file mode 100644 index 00000000..769c9a70 --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/base64encode.m @@ -0,0 +1,138 @@ +function y = base64encode(x, alg, isChunked, url_safe) +%BASE64ENCODE Perform base64 encoding on a string. +% INPUT: +% x - block of data to be encoded. Can be a string or a numeric +% vector containing integers in the range 0-255. +% alg - Algorithm to use: can take values 'java' or 'matlab'. Optional +% variable defaulting to 'java' which is a little faster. If +% 'java' is chosen than core of the code is performed by a call to +% a java library. Optionally all operations can be performed using +% matleb code. +% isChunked - encode output into 76 character blocks. The returned +% encoded string is broken into lines of no more than +% 76 characters each, and each line will end with EOL. Notice that +% if resulting string is saved as part of an xml file, those EOL's +% are often stripped by xmlwrite funtrion prior to saving. +% url_safe - use Modified Base64 for URL applications ('base64url' +% encoding) "Base64 alphabet" ([A-Za-z0-9-_=]). +% +% +% OUTPUT: +% y - character array using only "Base64 alphabet" characters +% +% This function may be used to encode strings into the Base64 encoding +% specified in RFC 2045 - MIME (Multipurpose Internet Mail Extensions). +% The Base64 encoding is designed to represent arbitrary sequences of +% octets in a form that need not be humanly readable. A 65-character +% subset ([A-Za-z0-9+/=]) of US-ASCII is used, enabling 6 bits to be +% represented per printable character. +% +% See also BASE64DECODE. +% +% Written by Jarek Tuszynski, SAIC, jaroslaw.w.tuszynski_at_saic.com +% +% Matlab version based on 2004 code by Peter J. Acklam +% E-mail: pjacklam@online.no +% URL: http://home.online.no/~pjacklam +% http://home.online.no/~pjacklam/matlab/software/util/datautil/base64encode.m + +if nargin<2, alg='java'; end +if nargin<3, isChunked=false; end +if ~islogical(isChunked) + if isnumeric(isChunked) + isChunked=(isChunked>0); + else + isChunked=false; + end +end +if nargin<4, url_safe=false; end +if ~islogical(url_safe) + if isnumeric(url_safe) + url_safe=(url_safe>0); + else + url_safe=false; + end +end + + +%% if x happen to be a filename than read the file +if (numel(x)<256) + if (exist(x, 'file')==2) + fid = fopen(x,'rb'); + x = fread(fid, 'uint8'); % read image file as a raw binary + fclose(fid); + end +end + +%% Perform conversion +switch (alg) + case 'java' + base64 = org.apache.commons.codec.binary.Base64; + y = base64.encodeBase64(x, isChunked); + if url_safe + y = strrep(y,'=','-'); + y = strrep(y,'/','_'); + end + + case 'matlab' + + %% add padding if necessary, to make the length of x a multiple of 3 + x = uint8(x(:)); + ndbytes = length(x); % number of decoded bytes + nchunks = ceil(ndbytes / 3); % number of chunks/groups + if rem(ndbytes, 3)>0 + x(end+1 : 3*nchunks) = 0; % add padding + end + x = reshape(x, [3, nchunks]); % reshape the data + y = repmat(uint8(0), 4, nchunks); % for the encoded data + + %% Split up every 3 bytes into 4 pieces + % aaaaaabb bbbbcccc ccdddddd + % to form + % 00aaaaaa 00bbbbbb 00cccccc 00dddddd + y(1,:) = bitshift(x(1,:), -2); % 6 highest bits of x(1,:) + y(2,:) = bitshift(bitand(x(1,:), 3), 4); % 2 lowest bits of x(1,:) + y(2,:) = bitor(y(2,:), bitshift(x(2,:), -4)); % 4 highest bits of x(2,:) + y(3,:) = bitshift(bitand(x(2,:), 15), 2); % 4 lowest bits of x(2,:) + y(3,:) = bitor(y(3,:), bitshift(x(3,:), -6)); % 2 highest bits of x(3,:) + y(4,:) = bitand(x(3,:), 63); % 6 lowest bits of x(3,:) + + %% Perform the mapping + % 0 - 25 -> A-Z + % 26 - 51 -> a-z + % 52 - 61 -> 0-9 + % 62 -> + + % 63 -> / + map = ['A':'Z', 'a':'z', '0':'9', '+/']; + if (url_safe), map(63:64)='-_'; end + y = map(y(:)+1); + + %% Add padding if necessary. + npbytes = 3 * nchunks - ndbytes; % number of padding bytes + if npbytes>0 + y(end-npbytes+1 : end) = '='; % '=' is used for padding + end + + %% break into lines with length LineLength + if (isChunked) + eol = sprintf('\n'); + nebytes = numel(y); + nlines = ceil(nebytes / 76); % number of lines + neolbytes = length(eol); % number of bytes in eol string + + % pad data so it becomes a multiple of 76 elements + y(nebytes + 1 : 76 * nlines) = 0; + y = reshape(y, 76, nlines); + + % insert eol strings + y(end + 1 : end + neolbytes, :) = eol(:, ones(1, nlines)); + + % remove padding, but keep the last eol string + m = nebytes + neolbytes * (nlines - 1); + n = (76+neolbytes)*nlines - neolbytes; + y(m+1 : n) = []; + end +end + +%% reshape to a row vector and make it a character array +y = char(reshape(y, 1, numel(y))); diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/gen_object_display.m b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/gen_object_display.m new file mode 100644 index 00000000..d2241dcc --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/gen_object_display.m @@ -0,0 +1,143 @@ +function gen_object_display( obj_struct,indent ) +% +% gen_object_display - general function to display an object's content +% +% format: gen_object_display( obj_struct,indent ) +% +% input: obj_struct - a copy of the object stored inside a structure +% indent - amount of "indent" when printing to the screen +% +% output: to the screen +% +% example: gen_object_display( struct( my_object_handle) ); +% gen_object_display( ny_structure ); +% +% Correction History: +% 2006-11-01 - Jarek Tuszynski - added support for struct arrays + +%% handle insufficient input +if ( nargin == 0 ) + help gen_object_display; + return; +elseif (nargin == 1) + indent = 1; +end + +%% check input for errors +% if ~isstruct( obj_struct ) +% fprintf( '\n\n\tMake sure that ''obj_struct'' is a struct type\n' ); +% return +% end + +% if (iscell( obj_struct )) +% for i =1:length(obj_struct) +% gen_object_display( obj_struct{i},indent + 2 ); +% end +% return +% end +if ~isstruct( obj_struct ) + space = sprintf( sprintf( '%%%ds',indent ),' ' ); + fprintf( ' %s', space); + disp(obj_struct); + return +end + +% find the longest name +field_list = fieldnames( obj_struct ); +max_strlen = 0; +for idx = 1:length( field_list ) + max_strlen = max( max_strlen,length(field_list{idx}) ); +end + +%% setup the display format (spacing) +space = sprintf( sprintf( '%%%ds',indent ),' ' ); +name_format = sprintf( ' %s%%%ds: ', space, max_strlen ); +name_format2= sprintf( ' %s%%%ds', space, max_strlen ); +max_displen = 110 - max_strlen - indent; + +%% display each field, if it is not too long +for iItem = 1:length( obj_struct ) % loop added by JT + for idx = 1:length( field_list ) + + % prepare field name to be displayed + name = sprintf( name_format,field_list{idx} ); + %temp = getfield( obj_struct,field_list{idx} ); % original by OG + temp = obj_struct(iItem).(field_list{idx}); % modification by JT + + % proceed according the variable's type + switch (1) + case islogical( temp ), % case added by JT + if isscalar(temp) + if (temp) + fprintf( '%strue\n',name ); + else + fprintf( '%sfalse\n',name ); + end + else + fprintf( '%s[%dx%d logical]\n',name,size(temp,1),size(temp,2) ); + end + case ischar( temp ), + if (length(temp) + fprintf( '[No method to display type]' ); + end + fprintf( '\n' ); + end + end + if (length(obj_struct)>1), fprintf('\n'); end % added by JT +end % added by JT \ No newline at end of file diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/test.html b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/test.html new file mode 100644 index 00000000..b837f39e --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/test.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + +
Apples44%
Bannanas23%
Oranges13%
Other10%
+
\ No newline at end of file diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script.html b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script.html new file mode 100644 index 00000000..3603ed6d --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script.html @@ -0,0 +1,2538 @@ + + + + + Tutorial for xml_io_tools Package

Tutorial for xml_io_tools Package

By Jarek Tuszynski

Package xml_io_tools can read XML files into MATLAB struct and writes MATLAB data types to XML files with help of simple interface to MATLAB's xmlwrite and xmlread functions.

Two function to simplify reading and writing XML files from MATLAB:

  • Function xml_read first calls MATLAB's xmlread function and than converts its output ('Document Object Model' tree of Java objects) to tree of MATLAB struct's. The output is in the format of nested structs and cells. In the output data structure field names are based on XML tags.
  • Function xml_write first convert input tree of MATLAB structs and cells and other types to tree of 'Document Object Model' nodes, and then writes resulting object to XML file using MATLAB's xmlwrite function. .

Contents

This package can:

  • Read most XML files, created inside and outside of MATLAB environment, and convert them to MATLAB data structures.
  • Write any MATLAB's struct tree to XML file
  • Handle XML attributes and special XML nodes like comments, processing instructions and CDATA sections
  • Supports base64 encoding and decoding to allow handling embeded binary data
  • Be studied, modified, customized, rewritten and used in other packages without any limitations. All code is included and documented. Software is distributed under BSD Licence (included).

This package does not:

  • Guarantee to recover the same Matlab objects that were saved. If you need to be able to recover carbon copy of the structure that was saved than you will have to use one of the packages that uses special set of tags saved as xml attributes that help to guide the parsing of XML code. This package does not use those tags.
  • Guarantee to work with older versions of MATLAB. Functions do not work with versions of MATLAB prior to 7.1 (26-Jul-2005).

Change History

  • 2006-11-06 - original version
  • 2006-11-26 - corrected xml_write to handle writing Matlab's column arrays to xml files. Bug discovered and diagnosed by Kalyan Dutta.
  • 2006-11-28 - made changes to handle special node types like: COMMENTS and CDATA sections
  • 2007-03-12 - Writing CDATA sections still did not worked. The problem was diagnosed and fixed by Alberto Amaro. The fix involved rewriting xmlwrite to use Apache Xerces java files directly instead of MATLAB's XMLUtils java class.
  • 2007-06-21 - Fixed problem reported by Anna Kelbert in Reviews about not writing attributes of ROOT node. Also: added support for Processing Instructions, added support for global text nodes: Processing Instructions and comments, allowed writing tag names with special characters
  • 2007-07-20 - Added tutorial script file. Extended support for global text nodes. Added more Preference fields.
  • 2008-01-23 - Fixed problem reported by Anna Krewet of converting dates in format '2007-01-01' to numbers. Improved and added warning messages. Added detection of old Matlab versions incompatible with the library. Expanded documentation.
  • 2008-06-23 - Fixed problem with writing 1D array reported by Mark Neil. Extended xml_read's Pref.Num2Str to 3 settings (never, smart and always) for better control. Added parameter Pref.KeepNS for keeping or ignoring namespace data when reading. Fixed a bug related to writing 2D cell arrays brought up by Andrej's Mosat review.
  • 2008-09-11 - Resubmitting last upload - zip file is still old
  • 2009-02-26 - Small changes. More error handling. More robust in case of large binary objects. Added support for Base64 encoding/decoding of binary objects (using functions by Peter J. Acklam).
  • 2009-06-26 - changes to xml_read: added CellItem parameter to allow better control of reading files with 'item' notation (see comment by Shlomi); changed try-catch statements so xml_read would work for mablab versions prior to 7.5 (see Thomas Pilutti comment)
  • 2009-12-03 - added PreserveSpace parameter for contolling empty string handling as suggested by Sebastiaan. Fix suggested by Michael Murphy. Fixed number recognition code as suggested by Yuan Ren.
  • 2010-05-04 - implemented fixes suggested by Dylan Reynolds from Airbus.
  • 2010-07-28 - implemented support for 2D arrays of cells and structs suggested by Rodney Behn from MIT Lincoln Laboratory. Also attempted large scale cleanup of xml_write function
  • 2010-08-18 - minor extension to allow better handling of logical scalars and arrays and function handles suggested by Andreas Richter and others
  • 2010-09-20 - allow reading and writing of sparse matrices. Improve reading of 1D boolean arrays.
  • 2010-11-05 - Fix problem with empty cells reported by Richard Cotton; fixed issues with reading boolean arrays reported by Zohar Bar-Yehuda; Improved speed of base64 coding and decoding by switching to java based code.

Licence

The package is distributed under BSD License

format compact; % viewing preference
+clear variables;
+type('license.txt')
+
+Copyright (c) 2007, Jaroslaw Tuszynski
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are 
+met:
+
+    * Redistributions of source code must retain the above copyright 
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright 
+      notice, this list of conditions and the following disclaimer in 
+      the documentation and/or other materials provided with the distribution
+      
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.
+
+

Write XML file based on a Struct using "xml_write"

Any MATLAB data struct can be saved to XML file.

MyTree=[];
+MyTree.MyNumber = 13;
+MyTree.MyString = 'Hello World';
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Read XML file producing a Struct using "xml_read"

[tree treeName] = xml_read ('test.xml');
+disp([treeName{1} ' ='])
+gen_object_display(tree)
+
MyTree =
+    MyNumber: [13]
+    MyString: 'Hello World'
+

"Pref.XmlEngine" flag in "xml_write"

Occasionaly some operations are performed better by Apache Xerces XML engine than default xmlread function. That is why xml_write provide an option for choosing the underlaying xml engine. Code below performs the same operation as the previous section but using Apache Xerces XML engine. Notice that in this case name of root element was passed as variable and not extracted from the variable name.

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+xml_write('test.xml', MyTree, 'TreeOfMine', Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<TreeOfMine>
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</TreeOfMine>
+
+

Writing Struct with different type MATLAB arrays

MyTree=[];
+MyTree.Empty   = [];                   % Empty variable
+MyTree.Num_1x1 = 13;                   % simple scalar
+MyTree.Vec_1x3 = [1 2 3];              % horizontal vector
+MyTree.Vec_4x1 = [1; 2; 3; 4];         % vertical vector
+MyTree.Mat_2x2 = [1, 2; 3, 4];         % 2D matrix
+MyTree.Cube_3D = reshape(1:8,[2 2 2]); % 3D array
+MyTree.String1 = '[2003 10 30]';       % number string with    [] brackets
+MyTree.String2 = ' 2003 10 30 ';       % number string without [] brackets
+MyTree.Logical_1x1 = false;            % single logical
+MyTree.Logical_2x2 = [false, true; true, false]; % 2D matrix of logicals
+MyTree.Logical_Str = 'False False	True True';
+MyTree.Int_2x2 = uint8([1 2;3 4]);     % 2D matrix of uint8 integers
+MyTree.Complex_1x1 = complex(1, 7);    % complex scalar
+MyTree.Complex_2x2 = complex([1 2;3 4],[2 2;7 7]); % 2D matrix of complex numbers
+MyTree.Sparse_9x9 = sparse(1:9,1:9,1); % sparse 9x9 matrix
+MyTree.Function = @sum;                % function handle
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <Empty/>
+   <Num_1x1>13</Num_1x1>
+   <Vec_1x3>[1 2 3]</Vec_1x3>
+   <Vec_4x1>[1;2;3;4]</Vec_4x1>
+   <Mat_2x2>[1 2;3 4]</Mat_2x2>
+   <Cube_3D>[1;2;3;4;5;6;7;8]</Cube_3D>
+   <String1>[2003 10 30]</String1>
+   <String2>2003 10 30</String2>
+   <Logical_1x1>[false]</Logical_1x1>
+   <Logical_2x2>[false true;true false]</Logical_2x2>
+   <Logical_Str>False False True True</Logical_Str>
+   <Int_2x2>[uint8([1 2;3 4])]</Int_2x2>
+   <Complex_1x1>1+i*7</Complex_1x1>
+   <Complex_2x2>[1+i*2 2+i*2;3+i*7 4+i*7]</Complex_2x2>
+   <Sparse_9x9>[sparse([1;2;3;4;5;6;7;8;9], [1;2;3;4;5;6;7;8;9], [1;1;1;1;1;1;1;1;1], 9, 9)]</Sparse_9x9>
+   <Function>[@sum]</Function>
+</MyTree>
+

Read Struct with MATLAB arrays

Notice that 'Cube_3D' did not preserve original dimentions

[tree treeName] = xml_read ('test.xml');
+disp([treeName{1} ' ='])
+gen_object_display(tree)
+
MyTree =
+          Empty: [0x0 double]
+        Num_1x1: [13]
+        Vec_1x3: [1  2  3]
+        Vec_4x1: [4x1 double]
+        Mat_2x2: [2x2 double]
+        Cube_3D: [8x1 double]
+        String1: [2003    10    30]
+        String2: [2003    10    30]
+    Logical_1x1: false
+    Logical_2x2: [2x2 logical]
+    Logical_Str: [1x4 logical]
+        Int_2x2: [2x2 double]
+    Complex_1x1: [1+7i]
+    Complex_2x2: [2x2 double]
+     Sparse_9x9: [9x9 double]
+       Function: [No method to display type]
+

"Pref.StructItem" flag in "xml_write" (controls 1D arrays of structs)

Create a simple structure with 1D array of struct's

MyTree = [];
+MyTree.a(1).b = 'jack';
+MyTree.a(2).b = 'john';
+gen_object_display(MyTree)
+
    a: [1x2 struct]
+       b: 'jack'
+
+       b: 'john'
+
+

Write XML with "StructItem = true" (default). Notice single 'a' section and multiple 'item' sub-sections. Those subsections are used to store array elements

wPref.StructItem = true;
+xml_write('test.xml', MyTree, 'MyTree',wPref);
+type('test.xml')
+fprintf('\nxml_read output:\n')
+gen_object_display(xml_read ('test.xml'))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>
+      <item>
+         <b>jack</b>
+      </item>
+      <item>
+         <b>john</b>
+      </item>
+   </a>
+</MyTree>
+
+xml_read output:
+    a: [2x1 struct]
+       b: 'jack'
+
+       b: 'john'
+
+

Write XML with "StructItem = false". Notice multiple 'a' sections

wPref.StructItem = false;
+xml_write('test.xml', MyTree, 'MyTree',wPref);
+type('test.xml')
+fprintf('\nxml_read output:\n')
+gen_object_display(xml_read ('test.xml'))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>
+      <b>jack</b>
+   </a>
+   <a>
+      <b>john</b>
+   </a>
+</MyTree>
+
+xml_read output:
+    a: [2x1 struct]
+       b: 'jack'
+
+       b: 'john'
+
+

Notice that xml_read function produced the same struct when reading both files

Potential problems with "StructItem = true":

wPref.StructItem = true;
+MyTree1 = []; MyTree1.a.b    = 'jack';
+MyTree2 = []; MyTree2.a(1).b = 'jack';
+MyTree3 = []; MyTree3.a(2).b = 'jack';
+xml_write('test.xml', MyTree1, [], wPref); type('test.xml');
+xml_write('test.xml', MyTree2, [], wPref); type('test.xml');
+xml_write('test.xml', MyTree3, [], wPref); type('test.xml');
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree1>
+   <a>
+      <b>jack</b>
+   </a>
+</MyTree1>
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree2>
+   <a>
+      <b>jack</b>
+   </a>
+</MyTree2>
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree3>
+   <a>
+      <item>
+         <b/>
+      </item>
+      <item>
+         <b>jack</b>
+      </item>
+   </a>
+</MyTree3>
+

Notice that MyTree1 and MyTree2 produce identical files with no 'items', while MyTree2 and MyTree3 produce very different file structures. It was pointed out to me that files produced from MyTree2 and MyTree3 can not belong to the same schema, which can be a problem. The solution is to use cells.

wPref.CellItem = true;
+wPref.NoCells  = true;
+MyTree2 = []; MyTree2.a{1}.b = 'jack';
+MyTree3 = []; MyTree3.a{2}.b = 'jack';
+xml_write('test.xml', MyTree2, [], wPref); type('test.xml');
+xml_write('test.xml', MyTree3, [], wPref); type('test.xml');
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree2>
+   <a>
+      <item>
+         <b>jack</b>
+      </item>
+   </a>
+</MyTree2>
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree3>
+   <a>
+      <item/>
+      <item>
+         <b>jack</b>
+      </item>
+   </a>
+</MyTree3>
+

"Pref.CellItem" flag in "xml_write" (controls 1D arrays of cells)

Create a simple structure with cell arrays

MyTree = [];
+MyTree.a = {'jack', 'john'};
+disp(MyTree)
+
    a: {'jack'  'john'}
+

Write XML with "CellItem = true" (default). Notice single 'a' section and multiple 'item' sections

Pref=[]; Pref.CellItem = true;
+xml_write('test.xml', MyTree, 'MyTree',Pref);
+type('test.xml')
+fprintf('\nxml_read output:\n');
+disp(xml_read ('test.xml'))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>
+      <item>jack</item>
+      <item>john</item>
+   </a>
+</MyTree>
+
+xml_read output:
+    a: {'jack'  'john'}
+

Write XML with "CellItem = false". Notice multiple 'a' sections

Pref=[]; Pref.CellItem = false;
+xml_write('test.xml', MyTree, 'MyTree',Pref);
+type('test.xml')
+fprintf('\nxml_read output:\n');
+disp(xml_read ('test.xml'))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>jack</a>
+   <a>john</a>
+</MyTree>
+
+xml_read output:
+    a: {'jack'  'john'}
+

Notice that xml_read function produced the same struct when reading both files

"Pref.NoCells" flag in "xml_read"

Create a cell/struct mixture object

MyTree = [];
+MyTree.a{1}.b = 'jack';
+MyTree.a{2}.b = [];
+MyTree.a{2}.c = 'john';
+gen_object_display(MyTree);
+
    a: [1x2 cell] = 
+       b: 'jack'
+
+       b: [0x0 double]
+       c: 'john'
+
+

Save it to xml file

Pref=[]; Pref.CellItem = false;
+xml_write('test.xml', MyTree, 'MyTree',Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>
+      <b>jack</b>
+   </a>
+   <a>
+      <b/>
+      <c>john</c>
+   </a>
+</MyTree>
+

Read above file with "Pref.NoCells=true" (default) - output is quite different then input By default program is trying to convert everything to struct's and arrays of structs. In case arrays of structs all the structs in array need to have the same fields, and if they are not than MATLAB creates empty fields.

Pref=[]; Pref.NoCells=true;
+gen_object_display(xml_read('test.xml', Pref))
+
    a: [2x1 struct]
+       b: 'jack'
+       c: [0x0 double]
+
+       b: [0x0 double]
+       c: 'john'
+
+

Read above file with "Pref.NoCells=false" - now input and output are the same Cell arrays of structs allow structs in array to have different fields.

Pref=[]; Pref.NoCells=false;
+gen_object_display(xml_read('test.xml', Pref))
+
    a: [1x2 cell] = 
+       b: 'jack'
+
+       b: [0x0 double]
+       c: 'john'
+
+

"Pref.ItemName" flag in "xml_write" (customize 1D arrays of structs and cells)

Create a cell/struct mixture object

MyTree = [];
+MyTree.a{1}.b = 'jack';
+MyTree.a{2}.c = 'john';
+gen_object_display(MyTree);
+
    a: [1x2 cell] = 
+       b: 'jack'
+
+       c: 'john'
+
+

Save it to xml file, using 'item' notation but with different name

Pref=[];
+Pref.CellItem = true;
+Pref.ItemName = 'MyItem';
+xml_write('test.xml', MyTree, 'MyTree',Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>
+      <MyItem>
+         <b>jack</b>
+      </MyItem>
+      <MyItem>
+         <c>john</c>
+      </MyItem>
+   </a>
+</MyTree>
+

"Pref.ItemName" flag in "xml_read"

Read above file with default settings ("Pref.ItemName = 'item'") The results do not match the original structure

Pref=[]; Pref.NoCells  = false;
+gen_object_display(xml_read('test.xml', Pref))
+
    a: [1x1 struct]
+       MyItem: [1x2 cell] = 
+               b: 'jack'
+
+               c: 'john'
+
+

Read above file with "Pref.ItemName = 'MyItem'" - now saved and read MATLAB structures are the same

Pref=[];
+Pref.ItemName = 'MyItem';
+Pref.NoCells  = false;
+gen_object_display(xml_read('test.xml', Pref))
+
    a: [1x2 cell] = 
+       b: 'jack'
+
+       c: 'john'
+
+

"Pref.CellItem" flag in "xml_read"

"Pref.ItemName" is used to create xml files with clearly marked arrays "Pref.CellItem" flag in "xml_read" ensures that they are always read as arrays by forcing output to stay in cell format. In cell format s{1} is different than s, while s(1) is indistinguishable from s.

Create a test file

MyTree = [];
+MyTree.a1{1}.b = 'jack'; % a1 - single struct
+MyTree.a2{1}.b = 'jack'; % a2 - cell array of structs with the same fields
+MyTree.a2{2}.b = 'john';
+MyTree.a3{1}.b = 'jack'; % a3 - cell array of structs with the different fields
+MyTree.a3{2}.c = 'john';
+Pref=[];
+Pref.CellItem = true;
+Pref.Debug = true;
+xml_write('test.xml', MyTree, 'MyTree',Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a1>
+      <item>
+         <b>jack</b>
+      </item>
+   </a1>
+   <a2>
+      <item>
+         <b>jack</b>
+      </item>
+      <item>
+         <b>john</b>
+      </item>
+   </a2>
+   <a3>
+      <item>
+         <b>jack</b>
+      </item>
+      <item>
+         <c>john</c>
+      </item>
+   </a3>
+</MyTree>
+

Read above file with "Pref.CellItem = true" (default) All outputs are in cell format

Pref=[];
+Pref.NoCells  = false;  % allow cell output
+Pref.CellItem = true;   % keep 'item' arrays as cells
+gen_object_display(xml_read('test.xml', Pref))
+
    a1: [1x1 cell] = 
+        b: 'jack'
+
+    a2: [1x1 cell] = 
+            [1x1 struct]    [1x1 struct]
+
+    a3: [1x1 cell] = 
+            [1x1 struct]    [1x1 struct]
+
+

Read above file with "Pref.CellItem = false" Outputs format is determined by content

Pref=[];
+Pref.NoCells  = false; % allow cell output
+Pref.CellItem = false; % allow 'item' arrays to beheave like other fields
+gen_object_display(xml_read('test.xml', Pref))
+
    a1: [1x1 struct]
+        b: 'jack'
+    a2: [2x1 struct]
+        b: 'jack'
+
+        b: 'john'
+
+    a3: [1x2 cell] = 
+        b: 'jack'
+
+        c: 'john'
+
+

Read above file with "Pref.CellItem = false" and "Pref.NoCells = true" All outputs are in struct format

Pref=[];
+Pref.NoCells  = true;  % don't allow cell output
+Pref.CellItem = false; % allow 'item' arrays to beheave like other fields
+gen_object_display(xml_read('test.xml', Pref))
+
    a1: [1x1 struct]
+        b: 'jack'
+    a2: [2x1 struct]
+        b: 'jack'
+
+        b: 'john'
+
+    a3: [2x1 struct]
+        b: 'jack'
+        c: [0x0 double]
+
+        b: [0x0 double]
+        c: 'john'
+
+

"Pref.CellTable" flag in "xml_write" (controls 2D arrays of cells)

Create a structure with 2D arrays of cells

MyTree = [];
+MyTree.M = {[1,2;3,4], 'M12'; struct('a','jack'), {11, 'N12'; 21, 'N22'}};
+gen_object_display(MyTree)
+
    M: [2x2 cell] = 
+           [2x2 double]    'M12'
+           [1x1 struct]    {2x2 cell}
+

Write XML with "CellTable = 'Html" (default). This option mimics use of HTML "tr" and "td" tags to encode 2D tables. Tag names can be changed using TableName parameter (see below)

wPref = [];
+wPref.CellTable = 'Html';
+xml_write('test.xml', MyTree, 'MyTree',wPref);
+type('test.xml')
+fprintf('\nxml_read output:\n')
+rPref=[]; rPref.NoCells=false;
+gen_object_display(xml_read('test.xml', rPref))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <M>
+      <tr>
+         <td>[1 2;3 4]</td>
+         <td>M12</td>
+      </tr>
+      <tr>
+         <td>
+            <a>jack</a>
+         </td>
+         <td>
+            <tr>
+               <td>11</td>
+               <td>N12</td>
+            </tr>
+            <tr>
+               <td>21</td>
+               <td>N22</td>
+            </tr>
+         </td>
+      </tr>
+   </M>
+</MyTree>
+
+xml_read output:
+    M: [2x2 cell] = 
+           [2x2 double]    'M12'
+           [1x1 struct]    {2x2 cell}
+

Write XML with "CellTable = 'Vector'". Converts 2D arrays to 1D array and item or regular notation. This option is mostly provided for backward compatibility since this was the behavior in prior verions of the code

wPref = [];
+wPref.CellTable = 'Vector';
+xml_write('test.xml', MyTree, 'MyTree',wPref);
+type('test.xml')
+fprintf('\nxml_read output:\n')
+rPref=[]; rPref.NoCells=false;
+gen_object_display(xml_read('test.xml', rPref))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <M>
+      <item>[1 2;3 4]</item>
+      <item>
+         <a>jack</a>
+      </item>
+      <item>M12</item>
+      <item>
+         <item>11</item>
+         <item>21</item>
+         <item>N12</item>
+         <item>N22</item>
+      </item>
+   </M>
+</MyTree>
+
+xml_read output:
+    M: [1x4 cell] = 
+            1     2
+     3     4
+
+       a: 'jack'
+
+       M12
+
+           [11]    [21]    'N12'    'N22'
+
+

Create a simpler structure without struct's

MyTree = [];
+MyTree.M = {[1,2;3,4], 'M12'; 'M21', {11, 'N12'; 21, 'N22'}};
+gen_object_display(MyTree)
+
    M: [2x2 cell] = 
+           [2x2 double]    'M12'
+           'M21'    {2x2 cell}
+

Write XML with "CellTable = 'Matlab". This option encodes tables consisting of numbers, strings and other cell arrays as MATLAB command string. Unlike 'Html' option it does not work if one of the cells is a struct

wPref = [];
+wPref.CellTable = 'Matlab';
+xml_write('test.xml', MyTree, 'MyTree',wPref);
+type('test.xml')
+fprintf('\nxml_read output:\n')
+rPref=[]; rPref.NoCells=false;
+gen_object_display(xml_read('test.xml', rPref))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <M>{[1 2;3 4],'M12';'M21',{11,'N12';21,'N22';};}</M>
+</MyTree>
+
+xml_read output:
+    M: [2x2 cell] = 
+           [2x2 double]    'M12'
+           'M21'    {2x2 cell}
+

Write 2D cell array in HTML format

MyTree = [];
+MyTree.table.ATTRIBUTE.border=1;
+MyTree.table.CONTENT = {'Apples', '44%'; 'Bannanas', '23%'; 'Oranges', '13%'; 'Other', '10%'};
+xml_write('html/test.html', MyTree);
+type('html/test.html')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <table border="1">
+      <tr>
+         <td>Apples</td>
+         <td>44%</td>
+      </tr>
+      <tr>
+         <td>Bannanas</td>
+         <td>23%</td>
+      </tr>
+      <tr>
+         <td>Oranges</td>
+         <td>13%</td>
+      </tr>
+      <tr>
+         <td>Other</td>
+         <td>10%</td>
+      </tr>
+   </table>
+</MyTree>
+

Click on test.html to opened this file with a web brouwser

"Pref.StructTable" flag in "xml_write" (controls 2D arrays of structs)

Create a simple structure with arrays of struct's

MyTree = [];
+MyTree.a(1,1).b = 'jack';
+MyTree.a(1,2).b = 'john';
+MyTree.a(2,1).b = 'jim';
+MyTree.a(2,2).b = 'jill';
+gen_object_display(MyTree)
+
    a: [2x2 struct]
+      a(1,1) =
+        b: 'jack'
+      a(1,2) =
+        b: 'john'
+      a(2,1) =
+        b: 'jim'
+      a(2,2) =
+        b: 'jill'
+

Write XML with "StructTable = 'Html" (default). This option mimics use of HTML "tr" and "td" tags to encode 2D tables. Tag names can be changed using TableName parameter (see below)

wPref = [];
+wPref.StructTable = 'Html';
+xml_write('test.xml', MyTree, 'MyTree',wPref);
+type('test.xml')
+fprintf('\nxml_read output:\n')
+gen_object_display(xml_read ('test.xml'))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>
+      <tr>
+         <td>
+            <b>jack</b>
+         </td>
+         <td>
+            <b>john</b>
+         </td>
+      </tr>
+      <tr>
+         <td>
+            <b>jim</b>
+         </td>
+         <td>
+            <b>jill</b>
+         </td>
+      </tr>
+   </a>
+</MyTree>
+
+xml_read output:
+    a: [2x2 struct]
+      a(1,1) =
+        b: 'jack'
+      a(1,2) =
+        b: 'john'
+      a(2,1) =
+        b: 'jim'
+      a(2,2) =
+        b: 'jill'
+

Write XML with "CellTable = 'Vector'". Converts 2D arrays to 1D array and item or regular notation. This option is mostly provided for backward compatibility since this was the behavior in prior verions of the code

wPref = [];
+wPref.StructTable = 'Vector';
+xml_write('test.xml', MyTree, 'MyTree',wPref);
+type('test.xml')
+fprintf('\nxml_read output:\n')
+gen_object_display(xml_read ('test.xml'))
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <a>
+      <item>
+         <b>jack</b>
+      </item>
+      <item>
+         <b>jim</b>
+      </item>
+      <item>
+         <b>john</b>
+      </item>
+      <item>
+         <b>jill</b>
+      </item>
+   </a>
+</MyTree>
+
+xml_read output:
+    a: [4x1 struct]
+       b: 'jack'
+
+       b: 'jim'
+
+       b: 'john'
+
+       b: 'jill'
+
+

"Pref.TableName" flag in "xml_write" (controls encoding tags used for 2D arrays)

Create a cell object

MyTree = [];
+MyTree.M = {[1,2;3,4], 'M12'; 21, {11, 'N12'; 21, 'N22'}};
+gen_object_display(MyTree);
+
    M: [2x2 cell] = 
+           [2x2 double]    'M12'
+           [21]    {2x2 cell}
+

Save it to xml file, using 'Html' notation but with different names for rows and cells

Pref=[]; Pref.TableName = {'row','cell'};
+xml_write('test.xml', MyTree, 'MyTree',Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <M>
+      <row>
+         <cell>[1 2;3 4]</cell>
+         <cell>M12</cell>
+      </row>
+      <row>
+         <cell>21</cell>
+         <cell>
+            <row>
+               <cell>11</cell>
+               <cell>N12</cell>
+            </row>
+            <row>
+               <cell>21</cell>
+               <cell>N22</cell>
+            </row>
+         </cell>
+      </row>
+   </M>
+</MyTree>
+

"Pref.TableName" flag in "xml_read"

Read above file with default settings ("Pref.TableName = {'tr','td'}") The results do not match the original structure

Pref=[]; Pref.NoCells  = false;
+gen_object_display(xml_read('test.xml', Pref))
+
    M: [1x1 struct]
+       row: [2x1 struct]
+            cell: [1x2 cell] = 
+                       1     2
+     3     4
+
+                  M12
+
+
+            cell: [1x2 cell] = 
+                      21
+
+                  row: [2x1 struct]
+                       cell: [1x2 cell] = 
+                                 11
+
+                             N12
+
+
+                       cell: [1x2 cell] = 
+                                 21
+
+                             N22
+
+
+
+
+

Read above file with "Pref.TableName = {'row','cell'}" - now saved and read MATLAB structures are the same

Pref=[];
+Pref.TableName = {'row','cell'};
+Pref.NoCells  = false;
+gen_object_display(xml_read('test.xml', Pref))
+
    M: [2x2 cell] = 
+           [2x2 double]    'M12'
+           [21]    {2x2 cell}
+

"Pref.Str2Num" flag in xml_read (control conversion to numbers while reading)

Create a cell/struct mixture object

MyTree = [];
+MyTree.str     = 'sphere';
+MyTree.num1    =  123;
+MyTree.num2    = '123';
+MyTree.num3    = '[Inf,NaN]';
+MyTree.calc    = '1+2+3+4';
+MyTree.func    = 'sin(pi)/2';
+MyTree.String1 = '[2003 10 30]';
+MyTree.String2 = '2003 10 30';   % array resembling date
+MyTree.ISO8601 = '2003-10-30';   % date in ISO 8601 format
+MyTree.US_date = '2003/10/30';   % US style date format
+MyTree.complex = '2003i-10e-30'; % complex number resembling a date
+gen_object_display(MyTree);
+
        str: 'sphere'
+       num1: [123]
+       num2: '123'
+       num3: '[Inf,NaN]'
+       calc: '1+2+3+4'
+       func: 'sin(pi)/2'
+    String1: '[2003 10 30]'
+    String2: '2003 10 30'
+    ISO8601: '2003-10-30'
+    US_date: '2003/10/30'
+    complex: '2003i-10e-30'
+

Save it to xml file

xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <str>sphere</str>
+   <num1>123</num1>
+   <num2>123</num2>
+   <num3>[Inf,NaN]</num3>
+   <calc>1+2+3+4</calc>
+   <func>sin(pi)/2</func>
+   <String1>[2003 10 30]</String1>
+   <String2>2003 10 30</String2>
+   <ISO8601>2003-10-30</ISO8601>
+   <US_date>2003/10/30</US_date>
+   <complex>2003i-10e-30</complex>
+</MyTree>
+

Read above file with default settings ("Pref.Str2Num = true" or "Pref.Str2Num = 'smart'"). Under this setting all strings that look like numbers are converted to numbers, except for strings that are recognized by MATLAB 'datenum' function as dates

gen_object_display(xml_read('test.xml'))
+
        str: 'sphere'
+       num1: [123]
+       num2: [123]
+       num3: [Inf  NaN]
+       calc: [10]
+       func: 'sin(pi)/2'
+    String1: [2003    10    30]
+    String2: [2003    10    30]
+    ISO8601: '2003-10-30'
+    US_date: '2003/10/30'
+    complex: [-1e-029+2003i]
+

Note that all the fields of 'MyTree' can be converted to numbers (even 'sphere') but by default the function is trying to 'judge' if a string should be converted to a number or not

MyCell = {'sphere','1+2+3+4','sin(pi)/2','2003 10 30','2003-10-30','2003/10/30','2003i-10e-30'};
+cellfun(@str2num, MyCell, 'UniformOutput', false)
+
ans = 
+  Columns 1 through 6
+    [21x21 double]    [10]    [6.1232e-017]    [1x3 double]    [1963]    [6.6767]
+  Column 7
+    [-1.0000e-029 +2.0030e+003i]
+

Read above file with "Pref.Str2Num = false" or "Pref.Str2Num = 'never'" to keep all the fields in string format

Pref=[]; Pref.Str2Num = false;
+gen_object_display(xml_read('test.xml', Pref))
+
        str: 'sphere'
+       num1: '123'
+       num2: '123'
+       num3: '[Inf,NaN]'
+       calc: '1+2+3+4'
+       func: 'sin(pi)/2'
+    String1: '[2003 10 30]'
+    String2: '2003 10 30'
+    ISO8601: '2003-10-30'
+    US_date: '2003/10/30'
+    complex: '2003i-10e-30'
+

Read above file with "Pref.Str2Num = always" to convert all strings that look like numbers to numbers note the likelly unintendet conversion of 'ISO8601'

Pref=[]; Pref.Str2Num   = 'always';
+gen_object_display(xml_read('test.xml', Pref))
+
        str: 'sphere'
+       num1: [123]
+       num2: [123]
+       num3: [Inf  NaN]
+       calc: [10]
+       func: 'sin(pi)/2'
+    String1: [2003    10    30]
+    String2: [2003    10    30]
+    ISO8601: [1963]
+    US_date: '2003/10/30'
+    complex: [-1e-029+2003i]
+

Notice that all three settings will produce the same output for "num1" and "num2" and there is no way to reproduce the original "MyTree" structure.

"Pref.PreserveSpace" flag in xml_write (control handling of strings with leading/trailing spaces)

Create a struct with strings

MyTree=[];
+MyTree.Empty     = '';
+MyTree.OneSpace  = ' ';
+MyTree.TwoSpaces = '  ';
+MyTree.String1   = ' Hello      World ';
+

Write XML with "PreserveSpace = false" (default).

Pref=[]; Pref.PreserveSpace = false; % (default setting)
+xml_write('test.xml', MyTree, [], Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <Empty/>
+   <OneSpace/>
+   <TwoSpaces/>
+   <String1>Hello World</String1>
+</MyTree>
+

Write XML with "PreserveSpace = true".

Pref=[]; Pref.PreserveSpace = true;
+xml_write('test.xml', MyTree, [], Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <Empty/>
+   <OneSpace> </OneSpace>
+   <TwoSpaces>  </TwoSpaces>
+   <String1> Hello      World </String1>
+</MyTree>
+

"Pref.PreserveSpace" flag in xml_read

Read file while using "PreserveSpace = false" (default).

Pref=[]; Pref.PreserveSpace = false; % (default setting)
+gen_object_display(xml_read('test.xml',Pref))
+
        Empty: [0x0 double]
+     OneSpace: [0x0 double]
+    TwoSpaces: [0x0 double]
+      String1: 'Hello      World'
+

Read file while using "PreserveSpace = true".

Pref=[]; Pref.PreserveSpace = true;
+gen_object_display(xml_read('test.xml',Pref))
+
        Empty: [0x0 double]
+     OneSpace: ' '
+    TwoSpaces: '  '
+      String1: ' Hello      World '
+

Write XML files with ATTRIBUTEs

In order to add node attributes a special ATTRIBUTE field is used. ATTRIBUTEs have to be of simple types like numbers or strings (not struct or cells). Attributes are easy to attach to structs nodes like MyTree below.

MyTree=[];
+MyTree.MyNumber = 13;
+MyTree.MyString = 'Hello World'; % simple case
+MyTree.ATTRIBUTE.Num = 2;
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree Num="2">
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

In case when one needs to attach attributes to nodes which are not structs (for example strings, numbers or calls) then special CONTENT field needs to be used to make the node a struct node.

MyTree=[];
+MyTree.MyNumber = 13;
+MyTree.MyString.CONTENT = 'Hello World'; % simple case
+MyTree.MyString.ATTRIBUTE.Num = 2;
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <MyNumber>13</MyNumber>
+   <MyString Num="2">Hello World</MyString>
+</MyTree>
+

"Pref.Str2Num" flag in file with ATTRIBUTEs

Create a cell/struct mixture object

MyTree = [];
+MyTree.X.ATTRIBUTE.str     = 'sphere';
+MyTree.X.ATTRIBUTE.num1    =  123;
+MyTree.X.ATTRIBUTE.num2    = '123';
+MyTree.X.ATTRIBUTE.num3    = '[Inf,NaN]';
+MyTree.X.ATTRIBUTE.calc    = '1+2+3+4';
+MyTree.X.ATTRIBUTE.func    = 'sin(pi)/2';
+MyTree.X.ATTRIBUTE.String1 = '[2003 10 30]';
+MyTree.X.ATTRIBUTE.String2 = '2003 10 30';   % array resembling date
+MyTree.X.ATTRIBUTE.ISO8601 = '2003-10-30';   % date in ISO 8601 format
+MyTree.X.ATTRIBUTE.US_date = '2003/10/30';   % US style date format
+MyTree.X.ATTRIBUTE.complex = '2003i-10e-30'; % complex number resembling a date
+gen_object_display(MyTree);
+
    X: [1x1 struct]
+       ATTRIBUTE: [1x1 struct]
+                      str: 'sphere'
+                     num1: [123]
+                     num2: '123'
+                     num3: '[Inf,NaN]'
+                     calc: '1+2+3+4'
+                     func: 'sin(pi)/2'
+                  String1: '[2003 10 30]'
+                  String2: '2003 10 30'
+                  ISO8601: '2003-10-30'
+                  US_date: '2003/10/30'
+                  complex: '2003i-10e-30'
+

Save it to xml file

xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <X ISO8601="2003-10-30" String1="[2003 10 30]" String2="2003 10 30" US_date="2003/10/30" calc="1+2+3+4" complex="2003i-10e-30" func="sin(pi)/2" num1="123" num2="123" num3="[Inf,NaN]" str="sphere"/>
+</MyTree>
+

Read above file with default settings ("Pref.Str2Num = true" or "Pref.Str2Num = 'smart'"). Under this setting all strings that look like numbers are converted to numbers, except for strings that are recognized by MATLAB 'datenum' function as dates

gen_object_display(xml_read('test.xml'))
+
    X: [1x1 struct]
+         CONTENT: [0x0 double]
+       ATTRIBUTE: [1x1 struct]
+                  ISO8601: '2003-10-30'
+                  String1: '[2003 10 30]'
+                  String2: '2003 10 30'
+                  US_date: '2003/10/30'
+                     calc: '1+2+3+4'
+                  complex: [-1e-029+2003i]
+                     func: 'sin(pi)/2'
+                     num1: [123]
+                     num2: [123]
+                     num3: '[Inf,NaN]'
+                      str: 'sphere'
+

Read above file with "Pref.Str2Num = false" or "Pref.Str2Num = 'never'" to keep all the fields in string format

Pref=[]; Pref.Str2Num = false;
+gen_object_display(xml_read('test.xml', Pref))
+
    X: [1x1 struct]
+         CONTENT: [0x0 double]
+       ATTRIBUTE: [1x1 struct]
+                  ISO8601: '2003-10-30'
+                  String1: '[2003 10 30]'
+                  String2: '2003 10 30'
+                  US_date: '2003/10/30'
+                     calc: '1+2+3+4'
+                  complex: '2003i-10e-30'
+                     func: 'sin(pi)/2'
+                     num1: '123'
+                     num2: '123'
+                     num3: '[Inf,NaN]'
+                      str: 'sphere'
+

Read above file with "Pref.Str2Num = always" to convert all strings that look like numbers to numbers

Pref=[]; Pref.Str2Num   = 'always';
+gen_object_display(xml_read('test.xml', Pref))
+
    X: [1x1 struct]
+         CONTENT: [0x0 double]
+       ATTRIBUTE: [1x1 struct]
+                  ISO8601: '2003-10-30'
+                  String1: '[2003 10 30]'
+                  String2: '2003 10 30'
+                  US_date: '2003/10/30'
+                     calc: '1+2+3+4'
+                  complex: [-1e-029+2003i]
+                     func: 'sin(pi)/2'
+                     num1: [123]
+                     num2: [123]
+                     num3: '[Inf,NaN]'
+                      str: 'sphere'
+

Notice that all three settings will produce the same output for "num1" and "num2" and there is no way to reproduce the original "MyTree" structure.

Write XML files with COMMENTs

Insertion of Comments is done with help of special COMMENT field. Note that MATLAB's xmlwrite is less readable due to lack of end-of-line characters around comment section.

MyTree=[];
+MyTree.COMMENT = 'This is a comment';
+MyTree.MyNumber = 13;
+MyTree.MyString.CONTENT = 'Hello World';
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree><!--This is a comment-->
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Same operation using Apache Xerces XML engine gives the same result

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+xml_write('test.xml', MyTree, 'MyTree', Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<MyTree>
+    <!--This is a comment-->
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTree>
+
+

Comments in XML top level (method #1) This method uses cell array

MyTree=[];
+MyTree.MyNumber = 13;
+MyTree.MyString = 'Hello World';
+xml_write('test.xml', MyTree, {'MyTree', [], 'This is a global comment'});
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?><!--This is a global comment-->
+<MyTree>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Same operation using Apache Xerces XML engine gives even nicer results.

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+xml_write('test.xml', MyTree, {'MyTree', [], 'This is a global comment'}, Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!--This is a global comment-->
+<MyTree>
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTree>
+
+

Comments in XML top level (method #2) This method adds an extra top layer to the struct 'tree' and sets "Pref.RootOnly = false", which informs the function about the extra layer. Notice that RootName is also saved as a part of the 'tree', and does not have to be passed in separately.

MyTree=[];
+MyTree.COMMENT = 'This is a global comment';
+MyTree.MyTest.MyNumber = 13;
+MyTree.MyTest.MyString = 'Hello World';
+Pref=[]; Pref.RootOnly = false;
+xml_write('test.xml', MyTree, [], Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?><!--This is a global comment-->
+<MyTest>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTest>
+

Same operation using Apache Xerces XML engine

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+Pref.RootOnly  = false;
+xml_write('test.xml', MyTree, [], Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!--This is a global comment-->
+<MyTest>
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTest>
+
+

Write XML files with PROCESSING_INSTRUCTIONs

Insertion of Processing Instructions is done through use of special PROCESSING_INSTRUCTION field, which stores the instruction string. The string has to be in 'target data' format separated by space.

MyTree=[];
+MyTree.PROCESSING_INSTRUCTION = 'xml-stylesheet type="a" href="foo"';
+MyTree.MyNumber = 13;
+MyTree.MyString = 'Hello World';
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree><?xml-stylesheet type="a" href="foo"?>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Same operation using Apache Xerces XML engine

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+xml_write('test.xml', MyTree, 'MyTree', Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<MyTree><?xml-stylesheet type="a" href="foo"?>
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTree>
+
+

PROCESSING_INSTRUCTIONs in XML top level (method #1) This method uses cell array

MyTree=[];
+MyTree.MyNumber = 13;
+MyTree.MyString = 'Hello World';
+xml_write('test.xml', MyTree, {'MyTree', 'xml-stylesheet type="a" href="foo"'});
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="a" href="foo"?>
+<MyTree>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Same operation using Apache Xerces XML engine

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+xml_write('test.xml', MyTree, {'MyTree', 'xml-stylesheet type="a" href="foo"'}, Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="a" href="foo"?>
+<MyTree>
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTree>
+
+

PROCESSING_INSTRUCTIONs in XML top level (method #2) This method adds an extra top layer to the struct 'tree' and sets pref.RootOnly=false, which informs the function about the extra layer. Notice that RootName is also saved as a part of the 'tree', and does not have to be passed in separately.

MyTree=[];
+MyTree.PROCESSING_INSTRUCTION =  'xml-stylesheet type="a" href="foo"';
+MyTree.MyTest.MyNumber = 13;
+MyTree.MyTest.MyString = 'Hello World';
+Pref=[]; Pref.RootOnly = false;
+xml_write('test.xml', MyTree, [], Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="a" href="foo"?>
+<MyTest>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTest>
+

Same operation using Apache Xerces XML engine

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+Pref.RootOnly  = false;
+xml_write('test.xml', MyTree, 'MyTree', Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<?xml-stylesheet type="a" href="foo"?>
+<MyTest>
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTest>
+
+

Write XML files with CDATA Sections

"In an XML document a CDATA (Character DATA) section is a section of element content that is marked for the parser to interpret as only character data, not markup." (from Wikipedia) To insert CDATA Sections one use special CDATA_SECTION field, which stores the instruction string. Note that MATLAB's xmlwrite created wrong xml code for CDATA section

MyTree=[];
+MyTree.CDATA_SECTION = '<A>txt</A>';
+MyTree.MyNumber = 13;
+MyTree.MyString = 'Hello World';
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>&lt;A&gt;txt&lt;/A&gt;<MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Same operation using Apache Xerces XML engine produces correct results

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+xml_write('test.xml', MyTree, 'MyTree', Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<MyTree><![CDATA[<A>txt</A>]]><MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTree>
+
+

Write XML files with special characters in TAG names

The input to xml_write requires that all tags one wants in XML document have to be encoded as field names of MATLAB's struct's. Matlab has a lot of restrictions on variable names. This section is about XML tags with names not allowed as MATLAB variables, or more specifically with characters allowed as xml tag names but not allowed as MATLAB variable names. Characters like that can be replaced by their hexadecimal representation just as it is done by genvarname function. Alternative way of writing the first example is:

MyTree=[];
+MyTree.('MyNumber') = 13;               % same as MyTree.MyNumber = 13;
+MyTree.MyString.CONTENT = 'Hello World';
+MyTree.MyString.ATTRIBUTE.('Num') = 2;  % same as MyTree.MyString.ATTRIBUTE.Num = 2;
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <MyNumber>13</MyNumber>
+   <MyString Num="2">Hello World</MyString>
+</MyTree>
+

This approach fails for some characters like dash '-', colon ':', and international characters.

MyTree=[];
+try
+  MyTree.('My-Number') = 13;
+  MyTree.MyString.CONTENT = 'Hello World';
+  MyTree.MyString.ATTRIBUTE.('Num_ö') = 2;
+catch  %#ok<CTCH>
+  err = lasterror; %#ok<LERR>
+  disp(err.message);
+end
+
Invalid field name: 'My-Number'.
+

It can be overcome by replacing offending characters with their hexadecimal representation. That can be done manually or with use of genvarname function. Note that MATLAB 'type' function does not show correctly 'ö' letter in xml file, but opening the file in editor shows that it is correct.

MyTree=[];
+MyTree.(genvarname('My-Number')) = 13;
+MyTree.MyString.CONTENT = 'Hello World';
+MyTree.MyString.ATTRIBUTE.Num_0xF6 = 2;
+gen_object_display(MyTree);
+xml_write('test.xml', MyTree);
+type('test.xml')
+
    My0x2DNumber: [13]
+        MyString: [1x1 struct]
+                    CONTENT: 'Hello World'
+                  ATTRIBUTE: [1x1 struct]
+                             Num_0xF6: [2]
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <My-Number>13</My-Number>
+   <MyString Num_ö="2">Hello World</MyString>
+</MyTree>
+

Also two of the characters '-' and ':' can be encoded by a special strings: '_DASH_' and '_COLON_' respectively

MyTree=[];
+MyTree.My_DASH_Number = 13;
+MyTree.MyString.CONTENT = 'Hello World';
+MyTree.MyString.ATTRIBUTE.Num0xF6 = 2;
+xml_write('test.xml', MyTree);
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <My-Number>13</My-Number>
+   <MyString Numö="2">Hello World</MyString>
+</MyTree>
+

Write XML files with Namespaces

No extra special fields are needed to define XML namespaces, only colon character written using '0x3A' or '_COLON_'. Below is an example of a namespace definition

MyTree=[];
+MyTree.f_COLON_child.ATTRIBUTE.xmlns_COLON_f = 'http://www.foo.com';
+MyTree.f_COLON_child.f_COLON_MyNumber = 13;
+MyTree.f_COLON_child.f_COLON_MyString = 'Hello World';
+xml_write('test.xml', MyTree, 'MyTree');
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <f:child xmlns:f="http://www.foo.com">
+      <f:MyNumber>13</f:MyNumber>
+      <f:MyString>Hello World</f:MyString>
+   </f:child>
+</MyTree>
+

Same operation using Apache Xerces XML engine

Pref=[]; Pref.XmlEngine = 'Xerces';  % use Xerces xml generator directly
+xml_write('test.xml', MyTree, 'f_COLON_MyTree', Pref);
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<f:MyTree>
+    <f:child xmlns:f="http://www.foo.com">
+        <f:MyNumber>13</f:MyNumber>
+        <f:MyString>Hello World</f:MyString>
+    </f:child>
+</f:MyTree>
+
+

"Pref.KeepNS" flag in "xml_read"

Thise option allow keeping or exclusion of namespaces in tag names. By default the namespace data is kept but it produces much longer field names in the output structure. Ignoring namespace will produce more readible output. Perform default read of file with namespace

tree = xml_read('test.xml');
+gen_object_display(tree);
+
    f_COLON_child: [1x1 struct]
+                   f_COLON_MyNumber: [13]
+                   f_COLON_MyString: 'Hello World'
+                          ATTRIBUTE: [1x1 struct]
+                                     xmlns_COLON_f: 'http://www.foo.com'
+

Now the same operation with KeepNS = false.

Pref=[]; Pref.KeepNS = false; % do not read attributes
+tree = xml_read('test.xml', Pref);
+gen_object_display(tree);
+
    child: [1x1 struct]
+            MyNumber: [13]
+            MyString: 'Hello World'
+           ATTRIBUTE: [1x1 struct]
+                      f: 'http://www.foo.com'
+

Read XML files with special node types

Display and read the file, then show the data structure. Note that MATLAB 'type' function shows 'ö' letter incorrectly as 'A¶' in xml file, but opening the file in editor shows that it is correct.

fprintf('Test xml file:\n');
+type('test_file.xml')
+
Test xml file:
+
+<?xml version="1.0" encoding="utf-8" ?> 
+<?xml-stylesheet type="text/css" href="foo.css"?>
+<!-- This is a Global Comment -->
+<aaa xmlns:xsi="http://www.foo.org">
+  <?ProcInst type="local processing instruction"?>
+  <!-- local comment 1 -->
+  bbb
+  <!-- local comment 2 -->
+  ccc
+  <matrix bad-name='fff'>
+    5e3+2*i, Inf
+    NaN,     pi
+  </matrix>
+  <ee_e> ee_e </ee_e>
+  <ff-f> ff-f </ff-f>
+  <ggög> ggög </ggög>
+  <![CDATA[
+    Here <ddd>xml</ddd> tags are treated as ...
+    ... text
+	]]>
+</aaa>
+
+
+

Read only the Root Element (default)

[tree GlobalTextNodes] = xml_read('test_file.xml');
+fprintf('Global Data (Root name, Global Processing Instructions and Global Comments):\n');
+disp(GlobalTextNodes')
+fprintf('\nStructure read from the file (uncludes COMMENT and CDATA sections):\n');
+gen_object_display(tree);
+
Global Data (Root name, Global Processing Instructions and Global Comments):
+    'aaa'
+    'xml-stylesheet type="text/css" href="foo.css"'
+    'This is a Global Comment'
+
+Structure read from the file (uncludes COMMENT and CDATA sections):
+    PROCESSING_INSTRUCTION: 'ProcInst type="local processing instruction"'
+                   COMMENT: [1x2 cell] = 
+                            local comment 1
+
+                            local comment 2
+
+                   CONTENT: [1x2 cell] = 
+                            bbb
+
+                            ccc
+
+                    matrix: [1x1 struct]
+                              CONTENT: [2x2 double]
+                            ATTRIBUTE: [1x1 struct]
+                                       bad_DASH_name: 'fff'
+                      ee_e: 'ee_e'
+                 ff_DASH_f: 'ff-f'
+                   gg0xF6g: 'ggög'
+             CDATA_SECTION: 'Here <ddd>xml</ddd> tags are treated as ...
+    ... text'
+                 ATTRIBUTE: [1x1 struct]
+                            xmlns_COLON_xsi: 'http://www.foo.org'
+

Read the whole tree including global Comments and Processing Instructions

Pref=[]; Pref.RootOnly = false;
+[tree GlobalTextNodes] = xml_read('test_file.xml', Pref);
+fprintf('Global Data (Root name, Global Processing Instructions and Global Comments):\n');
+disp(GlobalTextNodes')
+fprintf('\nStructure read from the file (uncludes COMMENT and CDATA sections):\n');
+gen_object_display(tree);
+
Global Data (Root name, Global Processing Instructions and Global Comments):
+    'aaa'
+    'xml-stylesheet type="text/css" href="foo.css"'
+    'This is a Global Comment'
+
+Structure read from the file (uncludes COMMENT and CDATA sections):
+    PROCESSING_INSTRUCTION: 'xml-stylesheet type="text/css" href="foo.css"'
+                   COMMENT: 'This is a Global Comment'
+                       aaa: [1x1 struct]
+                            PROCESSING_INSTRUCTION: 'ProcInst type="local processing instruction"'
+                                           COMMENT: [1x2 cell] = 
+                                                    local comment 1
+
+                                                    local comment 2
+
+                                           CONTENT: [1x2 cell] = 
+                                                    bbb
+
+                                                    ccc
+
+                                            matrix: [1x1 struct]
+                                                      CONTENT: [2x2 double]
+                                                    ATTRIBUTE: [1x1 struct]
+                                                               bad_DASH_name: 'fff'
+                                              ee_e: 'ee_e'
+                                         ff_DASH_f: 'ff-f'
+                                           gg0xF6g: 'ggög'
+                                     CDATA_SECTION: 'Here <ddd>xml</ddd> tags are treated as ...
+    ... text'
+                                         ATTRIBUTE: [1x1 struct]
+                                                    xmlns_COLON_xsi: 'http://www.foo.org'
+

"Pref.ReadAttr" flag in "xml_read" (control handling of nodes with attributes)

Those option allow exclusion of attributes

Pref=[]; Pref.ReadAttr = false; % do not read attributes
+tree = xml_read('test_file.xml', Pref);
+gen_object_display(tree);
+
    PROCESSING_INSTRUCTION: 'ProcInst type="local processing instruction"'
+                   COMMENT: [1x2 cell] = 
+                            local comment 1
+
+                            local comment 2
+
+                   CONTENT: [1x2 cell] = 
+                            bbb
+
+                            ccc
+
+                    matrix: [2x2 double]
+                      ee_e: 'ee_e'
+                 ff_DASH_f: 'ff-f'
+                   gg0xF6g: 'ggög'
+             CDATA_SECTION: 'Here <ddd>xml</ddd> tags are treated as ...
+    ... text'
+

"Pref.ReadSpec" flag in "xml_read"

Those option allow exclusion of special nodes, like comments, processing instructions, CData sections, etc.

Pref=[]; Pref.ReadSpec = false; % do not read special node types
+tree = xml_read('test_file.xml', Pref);
+gen_object_display(tree);
+
      CONTENT: [1x2 cell] = 
+               bbb
+
+               ccc
+
+       matrix: [1x1 struct]
+                 CONTENT: [2x2 double]
+               ATTRIBUTE: [1x1 struct]
+                          bad_DASH_name: 'fff'
+         ee_e: 'ee_e'
+    ff_DASH_f: 'ff-f'
+      gg0xF6g: 'ggög'
+    ATTRIBUTE: [1x1 struct]
+               xmlns_COLON_xsi: 'http://www.foo.org'
+

"Pref.RootOnly" flag in "xml_read"

As it was shown in previous examples RootOnly parameter can be used to capture global (top level) special nodes (like COMMENTs and PROCESSING_INSTRUCTIONs) which are ignored by default

Pref=[]; Pref.RootOnly = false; % do not read special node types
+tree = xml_read('test_file.xml', Pref);
+gen_object_display(tree);
+
    PROCESSING_INSTRUCTION: 'xml-stylesheet type="text/css" href="foo.css"'
+                   COMMENT: 'This is a Global Comment'
+                       aaa: [1x1 struct]
+                            PROCESSING_INSTRUCTION: 'ProcInst type="local processing instruction"'
+                                           COMMENT: [1x2 cell] = 
+                                                    local comment 1
+
+                                                    local comment 2
+
+                                           CONTENT: [1x2 cell] = 
+                                                    bbb
+
+                                                    ccc
+
+                                            matrix: [1x1 struct]
+                                                      CONTENT: [2x2 double]
+                                                    ATTRIBUTE: [1x1 struct]
+                                                               bad_DASH_name: 'fff'
+                                              ee_e: 'ee_e'
+                                         ff_DASH_f: 'ff-f'
+                                           gg0xF6g: 'ggög'
+                                     CDATA_SECTION: 'Here <ddd>xml</ddd> tags are treated as ...
+    ... text'
+                                         ATTRIBUTE: [1x1 struct]
+                                                    xmlns_COLON_xsi: 'http://www.foo.org'
+

"Pref.RootOnly" flag in "xml_write"

Writing previously read tree with default "Pref.RootOnly = true" gives wrong output file

Pref=[]; Pref.RootOnly = true; % do not read special node types
+xml_write('test.xml', tree, [], Pref);
+fprintf('Test xml file:\n');
+type('test.xml')
+
Test xml file:
+
+<?xml version="1.0" encoding="utf-8"?>
+<tree><?xml-stylesheet type="text/css" href="foo.css"?><!--This is a Global Comment-->
+   <aaa xmlns:xsi="http://www.foo.org"><?ProcInst type="local processing instruction"?><!--local comment 1--><!--local comment 2-->
+      <item>bbb</item>
+      <item>ccc</item>
+      <matrix bad-name="fff">[5000+i*2 Inf;NaN 3.14159265358979]</matrix>
+      <ee_e>ee_e</ee_e>
+      <ff-f>ff-f</ff-f>
+      <ggög>ggög</ggög>Here &lt;ddd&gt;xml&lt;/ddd&gt; tags are treated as ...
+    ... text</aaa>
+</tree>
+

Writing the same tree with "Pref.RootOnly = false" gives correct output

Pref=[]; Pref.RootOnly = false; % do not read special node types
+xml_write('test.xml', tree, [], Pref);
+fprintf('Test xml file:\n');
+type('test.xml')
+
Test xml file:
+
+<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/css" href="foo.css"?><!--This is a Global Comment-->
+<aaa xmlns:xsi="http://www.foo.org"><?ProcInst type="local processing instruction"?><!--local comment 1--><!--local comment 2-->
+   <item>bbb</item>
+   <item>ccc</item>
+   <matrix bad-name="fff">[5000+i*2 Inf;NaN 3.14159265358979]</matrix>
+   <ee_e>ee_e</ee_e>
+   <ff-f>ff-f</ff-f>
+   <ggög>ggög</ggög>Here &lt;ddd&gt;xml&lt;/ddd&gt; tags are treated as ...
+    ... text</aaa>
+

"Pref.NumLevels" flag in "xml_read"

This parameter allows user to skip parts of the tree in order to save time and memory. Usefull only in a rare case when a small portion of large XML file is needed.

Create test tile

MyTree = [];
+MyTree.Level1 = 1;
+MyTree.Level1_.Level2 = 2;
+MyTree.Level1_.Level2_.Level3 = 3;
+MyTree.Level1_.Level2_.Level3_.Level4 = 4;
+xml_write('test.xml', MyTree);
+fprintf('Test xml file:\n');
+type('test.xml')
+
Test xml file:
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <Level1>1</Level1>
+   <Level1_>
+      <Level2>2</Level2>
+      <Level2_>
+         <Level3>3</Level3>
+         <Level3_>
+            <Level4>4</Level4>
+         </Level3_>
+      </Level2_>
+   </Level1_>
+</MyTree>
+

Use Default ("Pref.NumLevels = infinity") setting

tree = xml_read('test.xml');
+gen_object_display(tree);
+
     Level1: [1]
+    Level1_: [1x1 struct]
+              Level2: [2]
+             Level2_: [1x1 struct]
+                       Level3: [3]
+                      Level3_: [1x1 struct]
+                               Level4: [4]
+

Limit the read to only 2 levels

Pref=[]; Pref.NumLevels = 2;
+tree = xml_read('test.xml', Pref);
+gen_object_display(tree);
+
     Level1: [1]
+    Level1_: [1x1 struct]
+              Level2: [2]
+             Level2_: [0x0 double]
+

Create DOM object based on a Struct using "xml_write"

Create Struct tree

MyTree=[];
+MyTree.MyNumber = 13;
+MyTree.MyString = 'Hello World';
+

Convert Struct to DOM object using xml_write

DOM = xml_write([], MyTree);
+xmlwrite('test.xml', DOM);   % Save DOM object using MATLAB function
+type('test.xml')
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Convert DOM object to Struct using "xml_read"

DOM = xmlread('test.xml');       % Read DOM object using MATLAB function
+[tree treeName] = xml_read(DOM); % Convert DOM object to Struct
+disp([treeName{1} ' ='])
+gen_object_display(tree)
+
MyTree =
+    MyNumber: [13]
+    MyString: 'Hello World'
+

Write XML file based on a DOM using "xml_write_xerces"

xmlwrite_xerces('test.xml', DOM); % Save DOM object using Xerces library
+type('test.xml')
+
+<?xml version="1.0" encoding="UTF-8"?>
+<MyTree>
+    <MyNumber>13</MyNumber>
+    <MyString>Hello World</MyString>
+</MyTree>
+
+

Write XML to string instead of a file

DOM = xml_write([], MyTree);
+str = xmlwrite(DOM);
+disp(str)
+
<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <MyNumber>13</MyNumber>
+   <MyString>Hello World</MyString>
+</MyTree>
+

Write XML file with embedded binary data encoded as Base64 (using java version)

fid = fopen('football.jpg', 'rb');
+raw1 = uint8(fread(fid, 'uint8'));                % read image file as a raw binary
+fclose(fid);
+
+MyTree=[];
+MyTree.Size = 13;
+MyTree.MyString = 'Hello World'; % simple case
+MyTree.MyImage.ATTRIBUTE.EncodingMIMEType = 'base64';
+MyTree.MyImage.CONTENT = base64encode(raw1,'java');% perform base64 encoding of the binary data
+xml_write('test.xml', MyTree);             % write xml file
+

Read XML file with embedded binary data encoded as Base64 (using java version)

tree = xml_read('test.xml', Pref);         % read xml file
+raw  = base64decode(tree.MyImage.CONTENT, '', 'java');   % convert xml image to raw binary
+fid = fopen('MyFootball.jpg', 'wb');
+fwrite(fid, raw, 'uint8');                 % dumb the raw binary to the hard disk
+fclose(fid);
+I = imread('MyFootball.jpg');              % read it as an image
+imshow(I);
+

Write XML file with embedded binary data encoded as Base64 (simpler version using only matlab code

Notice that process of writing to xml stripped all end-of-lie characters from base64 code.

isChunked = true; % break into chunks 76 characters long
+url_safe  = true; % 'base64url' encoding
+code = base64encode('license.txt', 'matlab', isChunked, url_safe);
+disp(code)
+MyTree=[];
+MyTree.Size = 13;
+MyTree.MyString = 'Hello World';
+MyTree.MyImage.ATTRIBUTE.EncodingMIMEType = 'base64';
+MyTree.MyImage.CONTENT = code;   % perform base64 encoding of the binary data
+xml_write('test.xml', MyTree);   % write xml file
+type('test.xml')
+
Q29weXJpZ2h0IChjKSAyMDA3LCBKYXJvc2xhdyBUdXN6eW5za2kKQWxsIHJpZ2h0cyByZXNlcnZl
+ZC4KClJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdp
+dGggb3Igd2l0aG91dCAKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQg
+dGhlIGZvbGxvd2luZyBjb25kaXRpb25zIGFyZSAKbWV0OgoKICAgICogUmVkaXN0cmlidXRpb25z
+IG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQgCiAgICAgIG5v
+dGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1l
+ci4KICAgICogUmVkaXN0cmlidXRpb25zIGluIGJpbmFyeSBmb3JtIG11c3QgcmVwcm9kdWNlIHRo
+ZSBhYm92ZSBjb3B5cmlnaHQgCiAgICAgIG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMg
+YW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiAKICAgICAgdGhlIGRvY3VtZW50YXRpb24g
+YW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24KICAg
+ICAgClRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgQlkgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFO
+RCBDT05UUklCVVRPUlMgIkFTIElTIiAKQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFO
+VElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSAKSU1QTElFRCBXQVJSQU5U
+SUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBP
+U0UgCkFSRSBESVNDTEFJTUVELiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQ09QWVJJR0hUIE9XTkVS
+IE9SIENPTlRSSUJVVE9SUyBCRSAKTElBQkxFIEZPUiBBTlkgRElSRUNULCBJTkRJUkVDVCwgSU5D
+SURFTlRBTCwgU1BFQ0lBTCwgRVhFTVBMQVJZLCBPUiAKQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJ
+TkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgClNVQlNUSVRVVEUg
+R09PRFMgT1IgU0VSVklDRVM7IExPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJ
+TkVTUyAKSU5URVJSVVBUSU9OKSBIT1dFVkVSIENBVVNFRCBBTkQgT04gQU5ZIFRIRU9SWSBPRiBM
+SUFCSUxJVFksIFdIRVRIRVIgSU4gCkNPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JU
+IChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpIApBUklTSU5HIElOIEFOWSBXQVkg
+T1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRSAK
+UE9TU0lCSUxJVFkgT0YgU1VDSCBEQU1BR0UuCg==
+
+
+<?xml version="1.0" encoding="utf-8"?>
+<MyTree>
+   <Size>13</Size>
+   <MyString>Hello World</MyString>
+   <MyImage EncodingMIMEType="base64">Q29weXJpZ2h0IChjKSAyMDA3LCBKYXJvc2xhdyBUdXN6eW5za2kKQWxsIHJpZ2h0cyByZXNlcnZl ZC4KClJlZGlzdHJpYnV0aW9uIGFuZCB1c2UgaW4gc291cmNlIGFuZCBiaW5hcnkgZm9ybXMsIHdp dGggb3Igd2l0aG91dCAKbW9kaWZpY2F0aW9uLCBhcmUgcGVybWl0dGVkIHByb3ZpZGVkIHRoYXQg dGhlIGZvbGxvd2luZyBjb25kaXRpb25zIGFyZSAKbWV0OgoKICAgICogUmVkaXN0cmlidXRpb25z IG9mIHNvdXJjZSBjb2RlIG11c3QgcmV0YWluIHRoZSBhYm92ZSBjb3B5cmlnaHQgCiAgICAgIG5v dGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMgYW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1l ci4KICAgICogUmVkaXN0cmlidXRpb25zIGluIGJpbmFyeSBmb3JtIG11c3QgcmVwcm9kdWNlIHRo ZSBhYm92ZSBjb3B5cmlnaHQgCiAgICAgIG5vdGljZSwgdGhpcyBsaXN0IG9mIGNvbmRpdGlvbnMg YW5kIHRoZSBmb2xsb3dpbmcgZGlzY2xhaW1lciBpbiAKICAgICAgdGhlIGRvY3VtZW50YXRpb24g YW5kL29yIG90aGVyIG1hdGVyaWFscyBwcm92aWRlZCB3aXRoIHRoZSBkaXN0cmlidXRpb24KICAg ICAgClRISVMgU09GVFdBUkUgSVMgUFJPVklERUQgQlkgVEhFIENPUFlSSUdIVCBIT0xERVJTIEFO RCBDT05UUklCVVRPUlMgIkFTIElTIiAKQU5EIEFOWSBFWFBSRVNTIE9SIElNUExJRUQgV0FSUkFO VElFUywgSU5DTFVESU5HLCBCVVQgTk9UIExJTUlURUQgVE8sIFRIRSAKSU1QTElFRCBXQVJSQU5U SUVTIE9GIE1FUkNIQU5UQUJJTElUWSBBTkQgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBP U0UgCkFSRSBESVNDTEFJTUVELiBJTiBOTyBFVkVOVCBTSEFMTCBUSEUgQ09QWVJJR0hUIE9XTkVS IE9SIENPTlRSSUJVVE9SUyBCRSAKTElBQkxFIEZPUiBBTlkgRElSRUNULCBJTkRJUkVDVCwgSU5D SURFTlRBTCwgU1BFQ0lBTCwgRVhFTVBMQVJZLCBPUiAKQ09OU0VRVUVOVElBTCBEQU1BR0VTIChJ TkNMVURJTkcsIEJVVCBOT1QgTElNSVRFRCBUTywgUFJPQ1VSRU1FTlQgT0YgClNVQlNUSVRVVEUg R09PRFMgT1IgU0VSVklDRVM7IExPU1MgT0YgVVNFLCBEQVRBLCBPUiBQUk9GSVRTOyBPUiBCVVNJ TkVTUyAKSU5URVJSVVBUSU9OKSBIT1dFVkVSIENBVVNFRCBBTkQgT04gQU5ZIFRIRU9SWSBPRiBM SUFCSUxJVFksIFdIRVRIRVIgSU4gCkNPTlRSQUNULCBTVFJJQ1QgTElBQklMSVRZLCBPUiBUT1JU IChJTkNMVURJTkcgTkVHTElHRU5DRSBPUiBPVEhFUldJU0UpIApBUklTSU5HIElOIEFOWSBXQVkg T1VUIE9GIFRIRSBVU0UgT0YgVEhJUyBTT0ZUV0FSRSwgRVZFTiBJRiBBRFZJU0VEIE9GIFRIRSAK UE9TU0lCSUxJVFkgT0YgU1VDSCBEQU1BR0UuCg==</MyImage>
+</MyTree>
+

Read XML file with embedded binary data encoded as Base64 (simpler version using only matlab code

tree = xml_read('test.xml', Pref);         % read xml file
+base64decode(tree.MyImage.CONTENT, 'license2.txt', 'matlab'); % save xml image as raw binary
+type('license2.txt')
+
+Copyright (c) 2007, Jaroslaw Tuszynski
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
+modification, are permitted provided that the following conditions are 
+met:
+
+    * Redistributions of source code must retain the above copyright 
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright 
+      notice, this list of conditions and the following disclaimer in 
+      the documentation and/or other materials provided with the distribution
+      
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
+POSSIBILITY OF SUCH DAMAGE.
+
+
\ No newline at end of file diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script.png b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script.png new file mode 100644 index 00000000..2f5d8c09 Binary files /dev/null and b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script.png differ diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script_01.png b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script_01.png new file mode 100644 index 00000000..d12694fa Binary files /dev/null and b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/html/xml_tutorial_script_01.png differ diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/license.txt b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/license.txt new file mode 100644 index 00000000..a560b7a8 --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/license.txt @@ -0,0 +1,24 @@ +Copyright (c) 2007, Jaroslaw Tuszynski +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/test_file.xml b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/test_file.xml new file mode 100644 index 00000000..f3c1109d --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/test_file.xml @@ -0,0 +1,22 @@ + + + + + + + bbb + + ccc + + 5e3+2*i, Inf + NaN, pi + + ee_e + ff-f + ggög + xml tags are treated as ... + ... text + ]]> + + diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_read.m b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_read.m new file mode 100644 index 00000000..03c590d6 --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_read.m @@ -0,0 +1,550 @@ +function [tree, RootName, DOMnode] = xml_read(xmlfile, Pref) +%XML_READ reads xml files and converts them into Matlab's struct tree. +% +% DESCRIPTION +% tree = xml_read(xmlfile) reads 'xmlfile' into data structure 'tree' +% +% tree = xml_read(xmlfile, Pref) reads 'xmlfile' into data structure 'tree' +% according to your preferences +% +% [tree, RootName, DOMnode] = xml_read(xmlfile) get additional information +% about XML file +% +% INPUT: +% xmlfile URL or filename of xml file to read +% Pref Preferences: +% Pref.ItemName - default 'item' - name of a special tag used to itemize +% cell arrays +% Pref.ReadAttr - default true - allow reading attributes +% Pref.ReadSpec - default true - allow reading special nodes +% Pref.Str2Num - default 'smart' - convert strings that look like numbers +% to numbers. Options: "always", "never", and "smart" +% Pref.KeepNS - default true - keep or strip namespace info +% Pref.NoCells - default true - force output to have no cell arrays +% Pref.Debug - default false - show mode specific error messages +% Pref.NumLevels- default infinity - how many recursive levels are +% allowed. Can be used to speed up the function by prunning the tree. +% Pref.RootOnly - default true - output variable 'tree' corresponds to +% xml file root element, otherwise it correspond to the whole file. +% Pref.CellItem - default 'true' - leave 'item' nodes in cell notation. +% OUTPUT: +% tree tree of structs and/or cell arrays corresponding to xml file +% RootName XML tag name used for root (top level) node. +% Optionally it can be a string cell array storing: Name of +% root node, document "Processing Instructions" data and +% document "comment" string +% DOMnode output of xmlread +% +% DETAILS: +% Function xml_read first calls MATLAB's xmlread function and than +% converts its output ('Document Object Model' tree of Java objects) +% to tree of MATLAB struct's. The output is in format of nested structs +% and cells. In the output data structure field names are based on +% XML tags, except in cases when tags produce illegal variable names. +% +% Several special xml node types result in special tags for fields of +% 'tree' nodes: +% - node.CONTENT - stores data section of the node if other fields are +% present. Usually data section is stored directly in 'node'. +% - node.ATTRIBUTE.name - stores node's attribute called 'name'. +% - node.COMMENT - stores node's comment section (string). For global +% comments see "RootName" output variable. +% - node.CDATA_SECTION - stores node's CDATA section (string). +% - node.PROCESSING_INSTRUCTIONS - stores "processing instruction" child +% node. For global "processing instructions" see "RootName" output variable. +% - other special node types like: document fragment nodes, document type +% nodes, entity nodes, notation nodes and processing instruction nodes +% will be treated like regular nodes +% +% EXAMPLES: +% MyTree=[]; +% MyTree.MyNumber = 13; +% MyTree.MyString = 'Hello World'; +% xml_write('test.xml', MyTree); +% [tree treeName] = xml_read ('test.xml'); +% disp(treeName) +% gen_object_display() +% % See also xml_examples.m +% +% See also: +% xml_write, xmlread, xmlwrite +% +% Written by Jarek Tuszynski, SAIC, jaroslaw.w.tuszynski_at_saic.com +% References: +% - Function inspired by Example 3 found in xmlread function. +% - Output data structures inspired by xml_toolbox structures. + +%% default preferences +DPref.TableName = {'tr','td'}; % name of a special tags used to itemize 2D cell arrays +DPref.ItemName = 'item'; % name of a special tag used to itemize 1D cell arrays +DPref.CellItem = false; % leave 'item' nodes in cell notation +DPref.ReadAttr = true; % allow reading attributes +DPref.ReadSpec = true; % allow reading special nodes: comments, CData, etc. +DPref.KeepNS = true; % Keep or strip namespace info +DPref.Str2Num = 'smart';% convert strings that look like numbers to numbers +DPref.NoCells = true; % force output to have no cell arrays +DPref.NumLevels = 1e10; % number of recurence levels +DPref.PreserveSpace = false; % Preserve or delete spaces at the beggining and the end of stings? +RootOnly = true; % return root node with no top level special nodes +Debug = false; % show specific errors (true) or general (false)? +tree = []; +RootName = []; + +%% Check Matlab Version +v = ver('MATLAB'); +version = str2double(regexp(v.Version, '\d.\d','match','once')); +if (version<7.1) + error('Your MATLAB version is too old. You need version 7.1 or newer.'); +end + +%% read user preferences +if (nargin>1) + if (isfield(Pref, 'TableName')), DPref.TableName = Pref.TableName; end + if (isfield(Pref, 'ItemName' )), DPref.ItemName = Pref.ItemName; end + if (isfield(Pref, 'CellItem' )), DPref.CellItem = Pref.CellItem; end + if (isfield(Pref, 'Str2Num' )), DPref.Str2Num = Pref.Str2Num ; end + if (isfield(Pref, 'NoCells' )), DPref.NoCells = Pref.NoCells ; end + if (isfield(Pref, 'NumLevels')), DPref.NumLevels = Pref.NumLevels; end + if (isfield(Pref, 'ReadAttr' )), DPref.ReadAttr = Pref.ReadAttr; end + if (isfield(Pref, 'ReadSpec' )), DPref.ReadSpec = Pref.ReadSpec; end + if (isfield(Pref, 'KeepNS' )), DPref.KeepNS = Pref.KeepNS; end + if (isfield(Pref, 'RootOnly' )), RootOnly = Pref.RootOnly; end + if (isfield(Pref, 'Debug' )), Debug = Pref.Debug ; end + if (isfield(Pref, 'PreserveSpace')), DPref.PreserveSpace = Pref.PreserveSpace; end +end +if ischar(DPref.Str2Num), % convert from character description to numbers + DPref.Str2Num = find(strcmpi(DPref.Str2Num, {'never', 'smart', 'always'}))-1; + if isempty(DPref.Str2Num), DPref.Str2Num=1; end % 1-smart by default +end + +%% read xml file using Matlab function +if isa(xmlfile, 'org.apache.xerces.dom.DeferredDocumentImpl'); + % if xmlfile is a DOMnode than skip the call to xmlread + try + try + DOMnode = xmlfile; + catch ME + error('Invalid DOM node: \n%s.', getReport(ME)); + end + catch %#ok catch for mablab versions prior to 7.5 + error('Invalid DOM node. \n'); + end +else % we assume xmlfile is a filename + if (Debug) % in debuging mode crashes are allowed + DOMnode = xmlread(xmlfile); + else % in normal mode crashes are not allowed + try + try + DOMnode = xmlread(xmlfile); + catch ME + error('Failed to read XML file %s: \n%s',xmlfile, getReport(ME)); + end + catch %#ok catch for mablab versions prior to 7.5 + error('Failed to read XML file %s\n',xmlfile); + end + end +end +Node = DOMnode.getFirstChild; + +%% Find the Root node. Also store data from Global Comment and Processing +% Instruction nodes, if any. +GlobalTextNodes = cell(1,3); +GlobalProcInst = []; +GlobalComment = []; +GlobalDocType = []; +while (~isempty(Node)) + if (Node.getNodeType==Node.ELEMENT_NODE) + RootNode=Node; + elseif (Node.getNodeType==Node.PROCESSING_INSTRUCTION_NODE) + data = strtrim(char(Node.getData)); + target = strtrim(char(Node.getTarget)); + GlobalProcInst = [target, ' ', data]; + GlobalTextNodes{2} = GlobalProcInst; + elseif (Node.getNodeType==Node.COMMENT_NODE) + GlobalComment = strtrim(char(Node.getData)); + GlobalTextNodes{3} = GlobalComment; + % elseif (Node.getNodeType==Node.DOCUMENT_TYPE_NODE) + % GlobalTextNodes{4} = GlobalDocType; + end + Node = Node.getNextSibling; +end + +%% parse xml file through calls to recursive DOMnode2struct function +if (Debug) % in debuging mode crashes are allowed + [tree RootName] = DOMnode2struct(RootNode, DPref, 1); +else % in normal mode crashes are not allowed + try + try + [tree RootName] = DOMnode2struct(RootNode, DPref, 1); + catch ME + error('Unable to parse XML file %s: \n %s.',xmlfile, getReport(ME)); + end + catch %#ok catch for mablab versions prior to 7.5 + error('Unable to parse XML file %s.',xmlfile); + end +end + +%% If there were any Global Text nodes than return them +if (~RootOnly) + if (~isempty(GlobalProcInst) && DPref.ReadSpec) + t.PROCESSING_INSTRUCTION = GlobalProcInst; + end + if (~isempty(GlobalComment) && DPref.ReadSpec) + t.COMMENT = GlobalComment; + end + if (~isempty(GlobalDocType) && DPref.ReadSpec) + t.DOCUMENT_TYPE = GlobalDocType; + end + t.(RootName) = tree; + tree=t; +end +if (~isempty(GlobalTextNodes)) + GlobalTextNodes{1} = RootName; + RootName = GlobalTextNodes; +end + + +%% ======================================================================= +% === DOMnode2struct Function =========================================== +% ======================================================================= +function [s TagName LeafNode] = DOMnode2struct(node, Pref, level) + +%% === Step 1: Get node name and check if it is a leaf node ============== +[TagName LeafNode] = NodeName(node, Pref.KeepNS); +s = []; % initialize output structure + +%% === Step 2: Process Leaf Nodes (nodes with no children) =============== +if (LeafNode) + if (LeafNode>1 && ~Pref.ReadSpec), LeafNode=-1; end % tags only so ignore special nodes + if (LeafNode>0) % supported leaf node types + try + try % use try-catch: errors here are often due to VERY large fields (like images) that overflow java memory + s = char(node.getData); + if (isempty(s)), s = ' '; end % make it a string + % for some reason current xmlread 'creates' a lot of empty text + % fields with first chatacter=10 - those will be deleted. + if (~Pref.PreserveSpace || s(1)==10) + if (isspace(s(1)) || isspace(s(end))), s = strtrim(s); end % trim speces is any + end + if (LeafNode==1), s=str2var(s, Pref.Str2Num, 0); end % convert to number(s) if needed + catch ME % catch for mablab versions 7.5 and higher + warning('xml_io_tools:read:LeafRead', ... + 'This leaf node could not be read and was ignored. '); + getReport(ME) + end + catch %#ok catch for mablab versions prior to 7.5 + warning('xml_io_tools:read:LeafRead', ... + 'This leaf node could not be read and was ignored. '); + end + end + if (LeafNode==3) % ProcessingInstructions need special treatment + target = strtrim(char(node.getTarget)); + s = [target, ' ', s]; + end + return % We are done the rest of the function deals with nodes with children +end +if (level>Pref.NumLevels+1), return; end % if Pref.NumLevels is reached than we are done + +%% === Step 3: Process nodes with children =============================== +if (node.hasChildNodes) % children present + Child = node.getChildNodes; % create array of children nodes + nChild = Child.getLength; % number of children + + % --- pass 1: how many children with each name ----------------------- + f = []; + for iChild = 1:nChild % read in each child + [cname cLeaf] = NodeName(Child.item(iChild-1), Pref.KeepNS); + if (cLeaf<0), continue; end % unsupported leaf node types + if (~isfield(f,cname)), + f.(cname)=0; % initialize first time I see this name + end + f.(cname) = f.(cname)+1; % add to the counter + end % end for iChild + % text_nodes become CONTENT & for some reason current xmlread 'creates' a + % lot of empty text fields so f.CONTENT value should not be trusted + if (isfield(f,'CONTENT') && f.CONTENT>2), f.CONTENT=2; end + + % --- pass 2: store all the children as struct of cell arrays ---------- + for iChild = 1:nChild % read in each child + [c cname cLeaf] = DOMnode2struct(Child.item(iChild-1), Pref, level+1); + if (cLeaf && isempty(c)) % if empty leaf node than skip + continue; % usually empty text node or one of unhandled node types + elseif (nChild==1 && cLeaf==1) + s=c; % shortcut for a common case + else % if normal node + if (level>Pref.NumLevels), continue; end + n = f.(cname); % how many of them in the array so far? + if (~isfield(s,cname)) % encountered this name for the first time + if (n==1) % if there will be only one of them ... + s.(cname) = c; % than save it in format it came in + else % if there will be many of them ... + s.(cname) = cell(1,n); + s.(cname){1} = c; % than save as cell array + end + f.(cname) = 1; % initialize the counter + else % already have seen this name + s.(cname){n+1} = c; % add to the array + f.(cname) = n+1; % add to the array counter + end + end + end % for iChild +end % end if (node.hasChildNodes) + +%% === Step 4: Post-process struct's created for nodes with children ===== +if (isstruct(s)) + fields = fieldnames(s); + nField = length(fields); + + % Detect structure that looks like Html table and store it in cell Matrix + if (nField==1 && strcmpi(fields{1},Pref.TableName{1})) + tr = s.(Pref.TableName{1}); + fields2 = fieldnames(tr{1}); + if (length(fields2)==1 && strcmpi(fields2{1},Pref.TableName{2})) + % This seems to be a special structure such that for + % Pref.TableName = {'tr','td'} 's' corresponds to + % M11 M12 + % M12 M22 + % Recognize it as encoding for 2D struct + nr = length(tr); + for r = 1:nr + row = tr{r}.(Pref.TableName{2}); + Table(r,1:length(row)) = row; %#ok + end + s = Table; + end + end + + % --- Post-processing: convert 'struct of cell-arrays' to 'array of structs' + % Example: let say s has 3 fields s.a, s.b & s.c and each field is an + % cell-array with more than one cell-element and all 3 have the same length. + % Then change it to array of structs, each with single cell. + % This way element s.a{1} will be now accessed through s(1).a + vec = zeros(size(fields)); + for i=1:nField, vec(i) = f.(fields{i}); end + if (numel(vec)>1 && vec(1)>1 && var(vec)==0) % convert from struct of + s = cell2struct(struct2cell(s), fields, 1); % arrays to array of struct + end % if anyone knows better way to do above conversion please let me know. + +end + +%% === Step 5: Process nodes with attributes ============================= +if (node.hasAttributes && Pref.ReadAttr) + if (~isstruct(s)), % make into struct if is not already + ss.CONTENT=s; + s=ss; + end + Attr = node.getAttributes; % list of all attributes + for iAttr = 1:Attr.getLength % for each attribute + name = char(Attr.item(iAttr-1).getName); % attribute name + name = str2varName(name, Pref.KeepNS); % fix name if needed + value = char(Attr.item(iAttr-1).getValue); % attribute value + value = str2var(value, Pref.Str2Num, 1); % convert to number if possible + s.ATTRIBUTE.(name) = value; % save again + end % end iAttr loop +end % done with attributes +if (~isstruct(s)), return; end %The rest of the code deals with struct's + +%% === Post-processing: fields of "s" +% convert 'cell-array of structs' to 'arrays of structs' +fields = fieldnames(s); % get field names +nField = length(fields); +for iItem=1:length(s) % for each struct in the array - usually one + for iField=1:length(fields) + field = fields{iField}; % get field name + % if this is an 'item' field and user want to leave those as cells + % than skip this one + if (strcmpi(field, Pref.ItemName) && Pref.CellItem), continue; end + x = s(iItem).(field); + if (iscell(x) && all(cellfun(@isstruct,x(:))) && numel(x)>1) % it's cell-array of structs + % numel(x)>1 check is to keep 1 cell-arrays created when Pref.CellItem=1 + try % this operation fails sometimes + % example: change s(1).a{1}.b='jack'; s(1).a{2}.b='john'; to + % more convinient s(1).a(1).b='jack'; s(1).a(2).b='john'; + s(iItem).(field) = [x{:}]'; %#ok % converted to arrays of structs + catch %#ok + % above operation will fail if s(1).a{1} and s(1).a{2} have + % different fields. If desired, function forceCell2Struct can force + % them to the same field structure by adding empty fields. + if (Pref.NoCells) + s(iItem).(field) = forceCell2Struct(x); %#ok + end + end % end catch + end + end +end + +%% === Step 4: Post-process struct's created for nodes with children ===== + +% --- Post-processing: remove special 'item' tags --------------------- +% many xml writes (including xml_write) use a special keyword to mark +% arrays of nodes (see xml_write for examples). The code below converts +% s.item to s.CONTENT +ItemContent = false; +if (isfield(s,Pref.ItemName)) + s.CONTENT = s.(Pref.ItemName); + s = rmfield(s,Pref.ItemName); + ItemContent = Pref.CellItem; % if CellItem than keep s.CONTENT as cells +end + +% --- Post-processing: clean up CONTENT tags --------------------- +% if s.CONTENT is a cell-array with empty elements at the end than trim +% the length of this cell-array. Also if s.CONTENT is the only field than +% remove .CONTENT part and store it as s. +if (isfield(s,'CONTENT')) + if (iscell(s.CONTENT) && isvector(s.CONTENT)) + x = s.CONTENT; + for i=numel(x):-1:1, if ~isempty(x{i}), break; end; end + if (i==1 && ~ItemContent) + s.CONTENT = x{1}; % delete cell structure + else + s.CONTENT = x(1:i); % delete empty cells + end + end + if (nField==1) + if (ItemContent) + ss = s.CONTENT; % only child: remove a level but ensure output is a cell-array + s=[]; s{1}=ss; + else + s = s.CONTENT; % only child: remove a level + end + end +end + + + +%% ======================================================================= +% === forceCell2Struct Function ========================================= +% ======================================================================= +function s = forceCell2Struct(x) +% Convert cell-array of structs, where not all of structs have the same +% fields, to a single array of structs + +%% Convert 1D cell array of structs to 2D cell array, where each row +% represents item in original array and each column corresponds to a unique +% field name. Array "AllFields" store fieldnames for each column +AllFields = fieldnames(x{1}); % get field names of the first struct +CellMat = cell(length(x), length(AllFields)); +for iItem=1:length(x) + fields = fieldnames(x{iItem}); % get field names of the next struct + for iField=1:length(fields) % inspect all fieldnames and find those + field = fields{iField}; % get field name + col = find(strcmp(field,AllFields),1); + if isempty(col) % no column for such fieldname yet + AllFields = [AllFields; field]; %#ok + col = length(AllFields); % create a new column for it + end + CellMat{iItem,col} = x{iItem}.(field); % store rearanged data + end +end +%% Convert 2D cell array to array of structs +s = cell2struct(CellMat, AllFields, 2); + +%% ======================================================================= +% === str2var Function ================================================== +% ======================================================================= +function val=str2var(str, option, attribute) +% Can this string 'str' be converted to a number? if so than do it. +val = str; +len = numel(str); +if (len==0 || option==0), return; end % Str2Num="never" of empty string -> do not do enything +if (len>10000 && option==1), return; end % Str2Num="smart" and string is very long -> probably base64 encoded binary +digits = '(Inf)|(NaN)|(pi)|[\t\n\d\+\-\*\.ei EI\[\]\;\,]'; +s = regexprep(str, digits, ''); % remove all the digits and other allowed characters +if (~all(~isempty(s))) % if nothing left than this is probably a number + if (~isempty(strfind(str, ' '))), option=2; end %if str has white-spaces assume by default that it is not a date string + if (~isempty(strfind(str, '['))), option=2; end % same with brackets + str(strfind(str, '\n')) = ';';% parse data tables into 2D arrays, if any + if (option==1) % the 'smart' option + try % try to convert to a date, like 2007-12-05 + datenum(str); % if successful than leave it as string + catch %#ok % if this is not a date than ... + option=2; % ... try converting to a number + end + end + if (option==2) + if (attribute) + num = str2double(str); % try converting to a single number using sscanf function + if isnan(num), return; end % So, it wasn't really a number after all + else + num = str2num(str); %#ok % try converting to a single number or array using eval function + end + if(isnumeric(num) && numel(num)>0), val=num; end % if convertion to a single was succesful than save + end +elseif ((str(1)=='[' && str(end)==']') || (str(1)=='{' && str(end)=='}')) % this looks like a (cell) array encoded as a string + try + val = eval(str); + catch %#ok + val = str; + end +elseif (~attribute) % see if it is a boolean array with no [] brackets + str1 = lower(str); + str1 = strrep(str1, 'false', '0'); + str1 = strrep(str1, 'true' , '1'); + s = regexprep(str1, '[01 \;\,]', ''); % remove all 0/1, spaces, commas and semicolons + if (~all(~isempty(s))) % if nothing left than this is probably a boolean array + num = str2num(str1); %#ok + if(isnumeric(num) && numel(num)>0), val = (num>0); end % if convertion was succesful than save as logical + end +end + + +%% ======================================================================= +% === str2varName Function ============================================== +% ======================================================================= +function str = str2varName(str, KeepNS) +% convert a sting to a valid matlab variable name +if(KeepNS) + str = regexprep(str,':','_COLON_', 'once', 'ignorecase'); +else + k = strfind(str,':'); + if (~isempty(k)) + str = str(k+1:end); + end +end +str = regexprep(str,'-','_DASH_' ,'once', 'ignorecase'); +if (~isvarname(str)) && (~iskeyword(str)) + str = genvarname(str); +end + +%% ======================================================================= +% === NodeName Function ================================================= +% ======================================================================= +function [Name LeafNode] = NodeName(node, KeepNS) +% get node name and make sure it is a valid variable name in Matlab. +% also get node type: +% LeafNode=0 - normal element node, +% LeafNode=1 - text node +% LeafNode=2 - supported non-text leaf node, +% LeafNode=3 - supported processing instructions leaf node, +% LeafNode=-1 - unsupported non-text leaf node +switch (node.getNodeType) + case node.ELEMENT_NODE + Name = char(node.getNodeName);% capture name of the node + Name = str2varName(Name, KeepNS); % if Name is not a good variable name - fix it + LeafNode = 0; + case node.TEXT_NODE + Name = 'CONTENT'; + LeafNode = 1; + case node.COMMENT_NODE + Name = 'COMMENT'; + LeafNode = 2; + case node.CDATA_SECTION_NODE + Name = 'CDATA_SECTION'; + LeafNode = 2; + case node.DOCUMENT_TYPE_NODE + Name = 'DOCUMENT_TYPE'; + LeafNode = 2; + case node.PROCESSING_INSTRUCTION_NODE + Name = 'PROCESSING_INSTRUCTION'; + LeafNode = 3; + otherwise + NodeType = {'ELEMENT','ATTRIBUTE','TEXT','CDATA_SECTION', ... + 'ENTITY_REFERENCE', 'ENTITY', 'PROCESSING_INSTRUCTION', 'COMMENT',... + 'DOCUMENT', 'DOCUMENT_TYPE', 'DOCUMENT_FRAGMENT', 'NOTATION'}; + Name = char(node.getNodeName);% capture name of the node + warning('xml_io_tools:read:unkNode', ... + 'Unknown node type encountered: %s_NODE (%s)', NodeType{node.getNodeType}, Name); + LeafNode = -1; +end + + diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_tutorial_script.m b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_tutorial_script.m new file mode 100644 index 00000000..bc2af096 --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_tutorial_script.m @@ -0,0 +1,914 @@ +%% Tutorial for xml_io_tools Package +% *By Jarek Tuszynski* +% +% Package xml_io_tools can read XML files into MATLAB struct and writes +% MATLAB data types to XML files with help of simple interface to +% MATLAB's xmlwrite and xmlread functions. +% +% Two function to simplify reading and writing XML files from MATLAB: +% +% * Function xml_read first calls MATLAB's xmlread function and than +% converts its output ('Document Object Model' tree of Java objects) +% to tree of MATLAB struct's. The output is in the format of nested +% structs and cells. In the output data structure field names are based on +% XML tags. +% +% * Function xml_write first convert input tree of MATLAB structs and cells +% and other types to tree of 'Document Object Model' nodes, and then writes +% resulting object to XML file using MATLAB's xmlwrite function. . +% +%% This package can: +% * Read most XML files, created inside and outside of MATLAB environment, +% and convert them to MATLAB data structures. +% * Write any MATLAB's struct tree to XML file +% * Handle XML attributes and special XML nodes like comments, processing +% instructions and CDATA sections +% * Supports base64 encoding and decoding to allow handling embeded binary +% data +% * Be studied, modified, customized, rewritten and used in other packages +% without any limitations. All code is included and documented. Software +% is distributed under BSD Licence (included). +% +%% This package does not: +% * Guarantee to recover the same Matlab objects that were saved. If you +% need to be able to recover carbon copy of the structure that was saved +% than you will have to use one of the packages that uses special set of +% tags saved as xml attributes that help to guide the parsing of XML code. +% This package does not use those tags. +% * Guarantee to work with older versions of MATLAB. Functions do not work +% with versions of MATLAB prior to 7.1 (26-Jul-2005). +% +%% Change History +% * 2006-11-06 - original version +% * 2006-11-26 - corrected xml_write to handle writing Matlab's column +% arrays to xml files. Bug discovered and diagnosed by Kalyan Dutta. +% * 2006-11-28 - made changes to handle special node types like: +% COMMENTS and CDATA sections +% * 2007-03-12 - Writing CDATA sections still did not worked. The problem +% was diagnosed and fixed by Alberto Amaro. The fix involved rewriting +% xmlwrite to use Apache Xerces java files directly instead of MATLAB's +% XMLUtils java class. +% * 2007-06-21 - Fixed problem reported by Anna Kelbert in Reviews about +% not writing attributes of ROOT node. Also: added support for Processing +% Instructions, added support for global text nodes: Processing +% Instructions and comments, allowed writing tag names with special +% characters +% * 2007-07-20 - Added tutorial script file. Extended support for global +% text nodes. Added more Preference fields. +% * 2008-01-23 - Fixed problem reported by Anna Krewet of converting dates +% in format '2007-01-01' to numbers. Improved and added warning messages. +% Added detection of old Matlab versions incompatible with the library. +% Expanded documentation. +% * 2008-06-23 - Fixed problem with writing 1D array reported by Mark Neil. +% Extended xml_read's Pref.Num2Str to 3 settings (never, smart and always) +% for better control. Added parameter Pref.KeepNS for keeping or ignoring +% namespace data when reading. Fixed a bug related to writing 2D cell +% arrays brought up by Andrej's Mosat review. +% * 2008-09-11 - Resubmitting last upload - zip file is still old +% * 2009-02-26 - Small changes. More error handling. More robust in case of +% large binary objects. Added support for Base64 encoding/decoding of +% binary objects (using functions by Peter J. Acklam). +% * 2009-06-26 - changes to xml_read: added CellItem parameter to allow +% better control of reading files with 'item' notation (see comment by +% Shlomi); changed try-catch statements so xml_read would work for mablab +% versions prior to 7.5 (see Thomas Pilutti comment) +% * 2009-12-03 - added PreserveSpace parameter for contolling empty string +% handling as suggested by Sebastiaan. Fix suggested by Michael Murphy. +% Fixed number recognition code as suggested by Yuan Ren. +% * 2010-05-04 - implemented fixes suggested by Dylan Reynolds from Airbus. +% * 2010-07-28 - implemented support for 2D arrays of cells and structs +% suggested by Rodney Behn from MIT Lincoln Laboratory. Also attempted +% large scale cleanup of xml_write function +% * 2010-08-18 - minor extension to allow better handling of logical +% scalars and arrays and function handles suggested by Andreas Richter +% and others +% * 2010-09-20 - allow reading and writing of sparse matrices. Improve +% reading of 1D boolean arrays. +% * 2010-11-05 - Fix problem with empty cells reported by Richard Cotton; +% fixed issues with reading boolean arrays reported by Zohar Bar-Yehuda; +% Improved speed of base64 coding and decoding by switching to java based +% code. +%% Licence +% The package is distributed under BSD License +format compact; % viewing preference +clear variables; +type('license.txt') + +%% Write XML file based on a Struct using "xml_write" +% Any MATLAB data struct can be saved to XML file. +MyTree=[]; +MyTree.MyNumber = 13; +MyTree.MyString = 'Hello World'; +xml_write('test.xml', MyTree); +type('test.xml') + +%% Read XML file producing a Struct using "xml_read" +[tree treeName] = xml_read ('test.xml'); +disp([treeName{1} ' =']) +gen_object_display(tree) + +%% "Pref.XmlEngine" flag in "xml_write" +% Occasionaly some operations are performed better by Apache Xerces XML +% engine than default xmlread function. That is why xml_write provide an +% option for choosing the underlaying xml engine. Code below performs the +% same operation as the previous section but using Apache Xerces XML engine. +% Notice that in this case name of root element +% was passed as variable and not extracted from the variable name. +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +xml_write('test.xml', MyTree, 'TreeOfMine', Pref); +type('test.xml') + +%% Writing Struct with different type MATLAB arrays +MyTree=[]; +MyTree.Empty = []; % Empty variable +MyTree.Num_1x1 = 13; % simple scalar +MyTree.Vec_1x3 = [1 2 3]; % horizontal vector +MyTree.Vec_4x1 = [1; 2; 3; 4]; % vertical vector +MyTree.Mat_2x2 = [1, 2; 3, 4]; % 2D matrix +MyTree.Cube_3D = reshape(1:8,[2 2 2]); % 3D array +MyTree.String1 = '[2003 10 30]'; % number string with [] brackets +MyTree.String2 = ' 2003 10 30 '; % number string without [] brackets +MyTree.Logical_1x1 = false; % single logical +MyTree.Logical_2x2 = [false, true; true, false]; % 2D matrix of logicals +MyTree.Logical_Str = 'False False True True'; +MyTree.Int_2x2 = uint8([1 2;3 4]); % 2D matrix of uint8 integers +MyTree.Complex_1x1 = complex(1, 7); % complex scalar +MyTree.Complex_2x2 = complex([1 2;3 4],[2 2;7 7]); % 2D matrix of complex numbers +MyTree.Sparse_9x9 = sparse(1:9,1:9,1); % sparse 9x9 matrix +MyTree.Function = @sum; % function handle +xml_write('test.xml', MyTree); +type('test.xml') + +%% Read Struct with MATLAB arrays +% Notice that 'Cube_3D' did not preserve original dimentions +[tree treeName] = xml_read ('test.xml'); +disp([treeName{1} ' =']) +gen_object_display(tree) + +%% "Pref.StructItem" flag in "xml_write" (controls 1D arrays of structs) +% *Create a simple structure with 1D array of struct's* +MyTree = []; +MyTree.a(1).b = 'jack'; +MyTree.a(2).b = 'john'; +gen_object_display(MyTree) +%% +% *Write XML with "StructItem = true" (default). Notice single 'a' +% section and multiple 'item' sub-sections. Those subsections are used +% to store array elements* +wPref.StructItem = true; +xml_write('test.xml', MyTree, 'MyTree',wPref); +type('test.xml') +fprintf('\nxml_read output:\n') +gen_object_display(xml_read ('test.xml')) +%% +% *Write XML with "StructItem = false". Notice multiple 'a' sections* +wPref.StructItem = false; +xml_write('test.xml', MyTree, 'MyTree',wPref); +type('test.xml') +fprintf('\nxml_read output:\n') +gen_object_display(xml_read ('test.xml')) +%% +% *Notice that xml_read function produced the same struct when reading both files* +%% +% *Potential problems with "StructItem = true":* +wPref.StructItem = true; +MyTree1 = []; MyTree1.a.b = 'jack'; +MyTree2 = []; MyTree2.a(1).b = 'jack'; +MyTree3 = []; MyTree3.a(2).b = 'jack'; +xml_write('test.xml', MyTree1, [], wPref); type('test.xml'); +xml_write('test.xml', MyTree2, [], wPref); type('test.xml'); +xml_write('test.xml', MyTree3, [], wPref); type('test.xml'); +%% +% *Notice that MyTree1 and MyTree2 produce identical files with no 'items', +% while MyTree2 and MyTree3 produce very different file structures. It was +% pointed out to me that files produced from MyTree2 and MyTree3 can not +% belong to the same schema, which can be a problem. The solution is to use +% cells.* +wPref.CellItem = true; +wPref.NoCells = true; +MyTree2 = []; MyTree2.a{1}.b = 'jack'; +MyTree3 = []; MyTree3.a{2}.b = 'jack'; +xml_write('test.xml', MyTree2, [], wPref); type('test.xml'); +xml_write('test.xml', MyTree3, [], wPref); type('test.xml'); + + +%% "Pref.CellItem" flag in "xml_write" (controls 1D arrays of cells) +% *Create a simple structure with cell arrays* +MyTree = []; +MyTree.a = {'jack', 'john'}; +disp(MyTree) +%% +% *Write XML with "CellItem = true" (default). Notice single 'a' +% section and multiple 'item' sections* +Pref=[]; Pref.CellItem = true; +xml_write('test.xml', MyTree, 'MyTree',Pref); +type('test.xml') +fprintf('\nxml_read output:\n'); +disp(xml_read ('test.xml')) +%% +% *Write XML with "CellItem = false". Notice multiple 'a' sections* +Pref=[]; Pref.CellItem = false; +xml_write('test.xml', MyTree, 'MyTree',Pref); +type('test.xml') +fprintf('\nxml_read output:\n'); +disp(xml_read ('test.xml')) +%% +% *Notice that xml_read function produced the same struct when reading both files* + +%% "Pref.NoCells" flag in "xml_read" +% *Create a cell/struct mixture object* +MyTree = []; +MyTree.a{1}.b = 'jack'; +MyTree.a{2}.b = []; +MyTree.a{2}.c = 'john'; +gen_object_display(MyTree); +%% +% *Save it to xml file* +Pref=[]; Pref.CellItem = false; +xml_write('test.xml', MyTree, 'MyTree',Pref); +type('test.xml') +%% +% *Read above file with "Pref.NoCells=true" (default) - output is quite different then input* +% By default program is trying to convert everything to struct's and arrays +% of structs. In case arrays of structs all the structs in array need to have the +% same fields, and if they are not than MATLAB creates empty fields. +Pref=[]; Pref.NoCells=true; +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Read above file with "Pref.NoCells=false" - now input and output are the same* +% Cell arrays of structs allow structs in array to have different fields. +Pref=[]; Pref.NoCells=false; +gen_object_display(xml_read('test.xml', Pref)) + +%% "Pref.ItemName" flag in "xml_write" (customize 1D arrays of structs and cells) +% *Create a cell/struct mixture object* +MyTree = []; +MyTree.a{1}.b = 'jack'; +MyTree.a{2}.c = 'john'; +gen_object_display(MyTree); +%% +% *Save it to xml file, using 'item' notation but with different name* +Pref=[]; +Pref.CellItem = true; +Pref.ItemName = 'MyItem'; +xml_write('test.xml', MyTree, 'MyTree',Pref); +type('test.xml') + +%% "Pref.ItemName" flag in "xml_read" +% *Read above file with default settings ("Pref.ItemName = 'item'")* +% The results do not match the original structure +Pref=[]; Pref.NoCells = false; +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Read above file with "Pref.ItemName = 'MyItem'" - now saved and read +% MATLAB structures are the same* +Pref=[]; +Pref.ItemName = 'MyItem'; +Pref.NoCells = false; +gen_object_display(xml_read('test.xml', Pref)) + +%% "Pref.CellItem" flag in "xml_read" +% "Pref.ItemName" is used to create xml files with clearly marked arrays +% "Pref.CellItem" flag in "xml_read" ensures that they are always read as +% arrays by forcing output to stay in cell format. In cell format s{1} is +% different than s, while s(1) is indistinguishable from s. +%% +% *Create a test file* +MyTree = []; +MyTree.a1{1}.b = 'jack'; % a1 - single struct +MyTree.a2{1}.b = 'jack'; % a2 - cell array of structs with the same fields +MyTree.a2{2}.b = 'john'; +MyTree.a3{1}.b = 'jack'; % a3 - cell array of structs with the different fields +MyTree.a3{2}.c = 'john'; +Pref=[]; +Pref.CellItem = true; +Pref.Debug = true; +xml_write('test.xml', MyTree, 'MyTree',Pref); +type('test.xml') +%% +% *Read above file with "Pref.CellItem = true" (default)* +% All outputs are in cell format +Pref=[]; +Pref.NoCells = false; % allow cell output +Pref.CellItem = true; % keep 'item' arrays as cells +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Read above file with "Pref.CellItem = false"* +% Outputs format is determined by content +Pref=[]; +Pref.NoCells = false; % allow cell output +Pref.CellItem = false; % allow 'item' arrays to beheave like other fields +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Read above file with "Pref.CellItem = false" and "Pref.NoCells = true"* +% All outputs are in struct format +Pref=[]; +Pref.NoCells = true; % don't allow cell output +Pref.CellItem = false; % allow 'item' arrays to beheave like other fields +gen_object_display(xml_read('test.xml', Pref)) + +%% "Pref.CellTable" flag in "xml_write" (controls 2D arrays of cells) +% *Create a structure with 2D arrays of cells* +MyTree = []; +MyTree.M = {[1,2;3,4], 'M12'; struct('a','jack'), {11, 'N12'; 21, 'N22'}}; +gen_object_display(MyTree) +%% +% *Write XML with "CellTable = 'Html" (default). This option mimics use of +% HTML "tr" and "td" tags to encode 2D tables. Tag names can +% be changed using TableName parameter (see below)* +wPref = []; +wPref.CellTable = 'Html'; +xml_write('test.xml', MyTree, 'MyTree',wPref); +type('test.xml') +fprintf('\nxml_read output:\n') +rPref=[]; rPref.NoCells=false; +gen_object_display(xml_read('test.xml', rPref)) +%% +% *Write XML with "CellTable = 'Vector'".* +% Converts 2D arrays to 1D array and item or regular notation. This option +% is mostly provided for backward compatibility since this was the +% behavior in prior verions of the code +wPref = []; +wPref.CellTable = 'Vector'; +xml_write('test.xml', MyTree, 'MyTree',wPref); +type('test.xml') +fprintf('\nxml_read output:\n') +rPref=[]; rPref.NoCells=false; +gen_object_display(xml_read('test.xml', rPref)) +%% +% *Create a simpler structure without struct's* +MyTree = []; +MyTree.M = {[1,2;3,4], 'M12'; 'M21', {11, 'N12'; 21, 'N22'}}; +gen_object_display(MyTree) +%% +% *Write XML with "CellTable = 'Matlab". This option encodes tables +% consisting of numbers, strings and other cell arrays as MATLAB command +% string. Unlike 'Html' option it does not work if one of the cells is +% a struct* +wPref = []; +wPref.CellTable = 'Matlab'; +xml_write('test.xml', MyTree, 'MyTree',wPref); +type('test.xml') +fprintf('\nxml_read output:\n') +rPref=[]; rPref.NoCells=false; +gen_object_display(xml_read('test.xml', rPref)) + +%% Write 2D cell array in HTML format +MyTree = []; +MyTree.table.ATTRIBUTE.border=1; +MyTree.table.CONTENT = {'Apples', '44%'; 'Bannanas', '23%'; 'Oranges', '13%'; 'Other', '10%'}; +xml_write('html/test.html', MyTree); +type('html/test.html') +%% +% Click on to opened this file with a web brouwser + +%% "Pref.StructTable" flag in "xml_write" (controls 2D arrays of structs) +% *Create a simple structure with arrays of struct's* +MyTree = []; +MyTree.a(1,1).b = 'jack'; +MyTree.a(1,2).b = 'john'; +MyTree.a(2,1).b = 'jim'; +MyTree.a(2,2).b = 'jill'; +gen_object_display(MyTree) +%% +% *Write XML with "StructTable = 'Html" (default). This option mimics use of +% HTML "tr" and "td" tags to encode 2D tables. Tag names can +% be changed using TableName parameter (see below)* +wPref = []; +wPref.StructTable = 'Html'; +xml_write('test.xml', MyTree, 'MyTree',wPref); +type('test.xml') +fprintf('\nxml_read output:\n') +gen_object_display(xml_read ('test.xml')) +%% +% *Write XML with "CellTable = 'Vector'".* +% Converts 2D arrays to 1D array and item or regular notation. This option +% is mostly provided for backward compatibility since this was the +% behavior in prior verions of the code +wPref = []; +wPref.StructTable = 'Vector'; +xml_write('test.xml', MyTree, 'MyTree',wPref); +type('test.xml') +fprintf('\nxml_read output:\n') +gen_object_display(xml_read ('test.xml')) + +%% "Pref.TableName" flag in "xml_write" (controls encoding tags used for 2D arrays) +% *Create a cell object* +MyTree = []; +MyTree.M = {[1,2;3,4], 'M12'; 21, {11, 'N12'; 21, 'N22'}}; +gen_object_display(MyTree); +%% +% *Save it to xml file, using 'Html' notation but with different names for +% rows and cells* +Pref=[]; Pref.TableName = {'row','cell'}; +xml_write('test.xml', MyTree, 'MyTree',Pref); +type('test.xml') + +%% "Pref.TableName" flag in "xml_read" +% *Read above file with default settings ("Pref.TableName = {'tr','td'}")* +% The results do not match the original structure +Pref=[]; Pref.NoCells = false; +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Read above file with "Pref.TableName = {'row','cell'}" - now saved and read +% MATLAB structures are the same* +Pref=[]; +Pref.TableName = {'row','cell'}; +Pref.NoCells = false; +gen_object_display(xml_read('test.xml', Pref)) + +%% "Pref.Str2Num" flag in xml_read (control conversion to numbers while reading) +% *Create a cell/struct mixture object* +MyTree = []; +MyTree.str = 'sphere'; +MyTree.num1 = 123; +MyTree.num2 = '123'; +MyTree.num3 = '[Inf,NaN]'; +MyTree.calc = '1+2+3+4'; +MyTree.func = 'sin(pi)/2'; +MyTree.String1 = '[2003 10 30]'; +MyTree.String2 = '2003 10 30'; % array resembling date +MyTree.ISO8601 = '2003-10-30'; % date in ISO 8601 format +MyTree.US_date = '2003/10/30'; % US style date format +MyTree.complex = '2003i-10e-30'; % complex number resembling a date +gen_object_display(MyTree); +%% +% *Save it to xml file* +xml_write('test.xml', MyTree); +type('test.xml') +%% +% *Read above file with default settings* +% ("Pref.Str2Num = true" or "Pref.Str2Num = 'smart'"). Under this setting all +% strings that look like numbers are converted to numbers, except for +% strings that are recognized by MATLAB 'datenum' function as dates +gen_object_display(xml_read('test.xml')) +%% +% *Note that all the fields of 'MyTree' can be converted to numbers (even +% 'sphere') but by default the function is trying to 'judge' if a string +% should be converted to a number or not* +MyCell = {'sphere','1+2+3+4','sin(pi)/2','2003 10 30','2003-10-30','2003/10/30','2003i-10e-30'}; +cellfun(@str2num, MyCell, 'UniformOutput', false) +%% +% *Read above file with "Pref.Str2Num = false" or "Pref.Str2Num = 'never'" +% to keep all the fields in string format* +Pref=[]; Pref.Str2Num = false; +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Read above file with "Pref.Str2Num = always" +% to convert all strings that look like numbers to numbers* note the likelly +% unintendet conversion of 'ISO8601' +Pref=[]; Pref.Str2Num = 'always'; +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Notice that all three settings will produce the same output for "num1" and +% "num2" and there is no way to reproduce the original "MyTree" structure.* + +%% "Pref.PreserveSpace" flag in xml_write (control handling of strings with leading/trailing spaces) +% *Create a struct with strings* +MyTree=[]; +MyTree.Empty = ''; +MyTree.OneSpace = ' '; +MyTree.TwoSpaces = ' '; +MyTree.String1 = ' Hello World '; +%% +% *Write XML with "PreserveSpace = false" (default).* +Pref=[]; Pref.PreserveSpace = false; % (default setting) +xml_write('test.xml', MyTree, [], Pref); +type('test.xml') +%% +% *Write XML with "PreserveSpace = true".* +Pref=[]; Pref.PreserveSpace = true; +xml_write('test.xml', MyTree, [], Pref); +type('test.xml') + +%% "Pref.PreserveSpace" flag in xml_read +% *Read file while using "PreserveSpace = false" (default).* +Pref=[]; Pref.PreserveSpace = false; % (default setting) +gen_object_display(xml_read('test.xml',Pref)) +%% +% *Read file while using "PreserveSpace = true".* +Pref=[]; Pref.PreserveSpace = true; +gen_object_display(xml_read('test.xml',Pref)) + + +%% Write XML files with ATTRIBUTEs +% In order to add node attributes a special ATTRIBUTE field is used. +% ATTRIBUTEs have to be of simple types like numbers or strings (not +% struct or cells). Attributes are easy to attach to structs nodes like +% MyTree below. +MyTree=[]; +MyTree.MyNumber = 13; +MyTree.MyString = 'Hello World'; % simple case +MyTree.ATTRIBUTE.Num = 2; +xml_write('test.xml', MyTree); +type('test.xml') + +%% +% In case when one needs to attach attributes to nodes which are not +% structs (for example strings, numbers or calls) then special CONTENT +% field needs to be used to make the node a struct node. +MyTree=[]; +MyTree.MyNumber = 13; +MyTree.MyString.CONTENT = 'Hello World'; % simple case +MyTree.MyString.ATTRIBUTE.Num = 2; +xml_write('test.xml', MyTree); +type('test.xml') + +%% "Pref.Str2Num" flag in file with ATTRIBUTEs +% *Create a cell/struct mixture object* +MyTree = []; +MyTree.X.ATTRIBUTE.str = 'sphere'; +MyTree.X.ATTRIBUTE.num1 = 123; +MyTree.X.ATTRIBUTE.num2 = '123'; +MyTree.X.ATTRIBUTE.num3 = '[Inf,NaN]'; +MyTree.X.ATTRIBUTE.calc = '1+2+3+4'; +MyTree.X.ATTRIBUTE.func = 'sin(pi)/2'; +MyTree.X.ATTRIBUTE.String1 = '[2003 10 30]'; +MyTree.X.ATTRIBUTE.String2 = '2003 10 30'; % array resembling date +MyTree.X.ATTRIBUTE.ISO8601 = '2003-10-30'; % date in ISO 8601 format +MyTree.X.ATTRIBUTE.US_date = '2003/10/30'; % US style date format +MyTree.X.ATTRIBUTE.complex = '2003i-10e-30'; % complex number resembling a date +gen_object_display(MyTree); +%% +% *Save it to xml file* +xml_write('test.xml', MyTree); +type('test.xml') +%% +% *Read above file with default settings* +% ("Pref.Str2Num = true" or "Pref.Str2Num = 'smart'"). Under this setting all +% strings that look like numbers are converted to numbers, except for +% strings that are recognized by MATLAB 'datenum' function as dates +gen_object_display(xml_read('test.xml')) + +%% +% *Read above file with "Pref.Str2Num = false" or "Pref.Str2Num = 'never'" +% to keep all the fields in string format* +Pref=[]; Pref.Str2Num = false; +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Read above file with "Pref.Str2Num = always" +% to convert all strings that look like numbers to numbers* +Pref=[]; Pref.Str2Num = 'always'; +gen_object_display(xml_read('test.xml', Pref)) +%% +% *Notice that all three settings will produce the same output for "num1" and +% "num2" and there is no way to reproduce the original "MyTree" structure.* + + +%% Write XML files with COMMENTs +% Insertion of Comments is done with help of special COMMENT field. +% Note that MATLAB's xmlwrite is less readable due to lack of end-of-line +% characters around comment section. +MyTree=[]; +MyTree.COMMENT = 'This is a comment'; +MyTree.MyNumber = 13; +MyTree.MyString.CONTENT = 'Hello World'; +xml_write('test.xml', MyTree); +type('test.xml') + +%% +% *Same operation using Apache Xerces XML engine* +% gives the same result +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +xml_write('test.xml', MyTree, 'MyTree', Pref); +type('test.xml') + +%% +% *Comments in XML top level (method #1)* +% This method uses cell array +MyTree=[]; +MyTree.MyNumber = 13; +MyTree.MyString = 'Hello World'; +xml_write('test.xml', MyTree, {'MyTree', [], 'This is a global comment'}); +type('test.xml') +%% +% *Same operation using Apache Xerces XML engine* +% gives even nicer results. +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +xml_write('test.xml', MyTree, {'MyTree', [], 'This is a global comment'}, Pref); +type('test.xml') + +%% +% *Comments in XML top level (method #2)* +% This method adds an extra top layer to the struct 'tree' and sets +% "Pref.RootOnly = false", which informs the function about the extra +% layer. Notice that RootName is also saved as a part of +% the 'tree', and does not have to be passed in separately. +MyTree=[]; +MyTree.COMMENT = 'This is a global comment'; +MyTree.MyTest.MyNumber = 13; +MyTree.MyTest.MyString = 'Hello World'; +Pref=[]; Pref.RootOnly = false; +xml_write('test.xml', MyTree, [], Pref); +type('test.xml') +%% +% *Same operation using Apache Xerces XML engine* +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +Pref.RootOnly = false; +xml_write('test.xml', MyTree, [], Pref); +type('test.xml') + +%% Write XML files with PROCESSING_INSTRUCTIONs +% Insertion of Processing Instructions is done through use of special +% PROCESSING_INSTRUCTION field, which stores the instruction string. The +% string has to be in 'target data' format separated by space. +MyTree=[]; +MyTree.PROCESSING_INSTRUCTION = 'xml-stylesheet type="a" href="foo"'; +MyTree.MyNumber = 13; +MyTree.MyString = 'Hello World'; +xml_write('test.xml', MyTree); +type('test.xml') + +%% +% *Same operation using Apache Xerces XML engine* +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +xml_write('test.xml', MyTree, 'MyTree', Pref); +type('test.xml') + +%% +% *PROCESSING_INSTRUCTIONs in XML top level (method #1)* +% This method uses cell array +MyTree=[]; +MyTree.MyNumber = 13; +MyTree.MyString = 'Hello World'; +xml_write('test.xml', MyTree, {'MyTree', 'xml-stylesheet type="a" href="foo"'}); +type('test.xml') +%% +% *Same operation using Apache Xerces XML engine* +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +xml_write('test.xml', MyTree, {'MyTree', 'xml-stylesheet type="a" href="foo"'}, Pref); +type('test.xml') + +%% +% *PROCESSING_INSTRUCTIONs in XML top level (method #2)* +% This method adds an extra top layer to the struct 'tree' and sets +% pref.RootOnly=false, which informs the function about the extra +% layer. Notice that RootName is also saved as a part of +% the 'tree', and does not have to be passed in separately. +MyTree=[]; +MyTree.PROCESSING_INSTRUCTION = 'xml-stylesheet type="a" href="foo"'; +MyTree.MyTest.MyNumber = 13; +MyTree.MyTest.MyString = 'Hello World'; +Pref=[]; Pref.RootOnly = false; +xml_write('test.xml', MyTree, [], Pref); +type('test.xml') +%% +% *Same operation using Apache Xerces XML engine* +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +Pref.RootOnly = false; +xml_write('test.xml', MyTree, 'MyTree', Pref); +type('test.xml') + +%% Write XML files with CDATA Sections +% "In an XML document a CDATA (Character DATA) section is a section of +% element content that is marked for the parser to interpret as only +% character data, not markup." (from Wikipedia) +% To insert CDATA Sections one use special CDATA_SECTION field, +% which stores the instruction string. Note that MATLAB's xmlwrite created +% wrong xml code for CDATA section +MyTree=[]; +MyTree.CDATA_SECTION = 'txt'; +MyTree.MyNumber = 13; +MyTree.MyString = 'Hello World'; +xml_write('test.xml', MyTree); +type('test.xml') +%% +% *Same operation using Apache Xerces XML engine produces correct results* +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +xml_write('test.xml', MyTree, 'MyTree', Pref); +type('test.xml') + +%% Write XML files with special characters in TAG names +% The input to xml_write requires that all tags one wants in XML document +% have to be encoded as field names of MATLAB's struct's. Matlab has a lot +% of restrictions on variable names. This section is about XML tags with +% names not allowed as MATLAB variables, or more specifically with +% characters allowed as xml tag names but not allowed as MATLAB variable +% names. Characters like that can be replaced by their hexadecimal +% representation just as it is done by genvarname function. Alternative way +% of writing the first example is: +MyTree=[]; +MyTree.('MyNumber') = 13; % same as MyTree.MyNumber = 13; +MyTree.MyString.CONTENT = 'Hello World'; +MyTree.MyString.ATTRIBUTE.('Num') = 2; % same as MyTree.MyString.ATTRIBUTE.Num = 2; +xml_write('test.xml', MyTree); +type('test.xml') + +%% +% *This approach fails for some characters like dash '-', colon ':', and +% international characters.* +MyTree=[]; +try + MyTree.('My-Number') = 13; + MyTree.MyString.CONTENT = 'Hello World'; + MyTree.MyString.ATTRIBUTE.('Num_ö') = 2; +catch %#ok + err = lasterror; %#ok + disp(err.message); +end + +%% +% It can be overcome by replacing offending characters with their +% hexadecimal representation. That can be done manually or with use of +% genvarname function. Note that MATLAB 'type' function does not show +% correctly 'ö' letter in xml file, but opening the file in editor shows +% that it is correct. +MyTree=[]; +MyTree.(genvarname('My-Number')) = 13; +MyTree.MyString.CONTENT = 'Hello World'; +MyTree.MyString.ATTRIBUTE.Num_0xF6 = 2; +gen_object_display(MyTree); +xml_write('test.xml', MyTree); +type('test.xml') + +%% +% *Also two of the characters '-' and ':' can be encoded by a special strings: +% '_DASH_' and '_COLON_' respectively* +MyTree=[]; +MyTree.My_DASH_Number = 13; +MyTree.MyString.CONTENT = 'Hello World'; +MyTree.MyString.ATTRIBUTE.Num0xF6 = 2; +xml_write('test.xml', MyTree); +type('test.xml') + +%% Write XML files with Namespaces +% No extra special fields are needed to define XML namespaces, only colon +% character written using '0x3A' or '_COLON_'. Below is an +% example of a namespace definition +MyTree=[]; +MyTree.f_COLON_child.ATTRIBUTE.xmlns_COLON_f = 'http://www.foo.com'; +MyTree.f_COLON_child.f_COLON_MyNumber = 13; +MyTree.f_COLON_child.f_COLON_MyString = 'Hello World'; +xml_write('test.xml', MyTree, 'MyTree'); +type('test.xml') +%% +% *Same operation using Apache Xerces XML engine* +Pref=[]; Pref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +xml_write('test.xml', MyTree, 'f_COLON_MyTree', Pref); +type('test.xml') + +%% "Pref.KeepNS" flag in "xml_read" +% Thise option allow keeping or exclusion of namespaces in tag names. +% By default the namespace data is kept but it produces much longer field +% names in the output structure. Ignoring namespace will produce more +% readible output. +% Perform default read of file with namespace +tree = xml_read('test.xml'); +gen_object_display(tree); + +%% +% Now the same operation with KeepNS = false. +Pref=[]; Pref.KeepNS = false; % do not read attributes +tree = xml_read('test.xml', Pref); +gen_object_display(tree); + +%% Read XML files with special node types +% Display and read the file, then show the data structure. Note that +% MATLAB 'type' function shows 'ö' letter incorrectly as 'A¶' in xml file, +% but opening the file in editor shows that it is correct. +fprintf('Test xml file:\n'); +type('test_file.xml') +%% +% Read only the Root Element (default) +[tree GlobalTextNodes] = xml_read('test_file.xml'); +fprintf('Global Data (Root name, Global Processing Instructions and Global Comments):\n'); +disp(GlobalTextNodes') +fprintf('\nStructure read from the file (uncludes COMMENT and CDATA sections):\n'); +gen_object_display(tree); +%% +% Read the whole tree including global Comments and Processing Instructions +Pref=[]; Pref.RootOnly = false; +[tree GlobalTextNodes] = xml_read('test_file.xml', Pref); +fprintf('Global Data (Root name, Global Processing Instructions and Global Comments):\n'); +disp(GlobalTextNodes') +fprintf('\nStructure read from the file (uncludes COMMENT and CDATA sections):\n'); +gen_object_display(tree); + +%% "Pref.ReadAttr" flag in "xml_read" (control handling of nodes with attributes) +% Those option allow exclusion of attributes +Pref=[]; Pref.ReadAttr = false; % do not read attributes +tree = xml_read('test_file.xml', Pref); +gen_object_display(tree); + +%% "Pref.ReadSpec" flag in "xml_read" +% Those option allow exclusion of special nodes, like +% comments, processing instructions, CData sections, etc. +Pref=[]; Pref.ReadSpec = false; % do not read special node types +tree = xml_read('test_file.xml', Pref); +gen_object_display(tree); + +%% "Pref.RootOnly" flag in "xml_read" +% As it was shown in previous examples RootOnly parameter can be used to +% capture global (top level) special nodes (like COMMENTs and +% PROCESSING_INSTRUCTIONs) which are ignored by default +Pref=[]; Pref.RootOnly = false; % do not read special node types +tree = xml_read('test_file.xml', Pref); +gen_object_display(tree); + +%% "Pref.RootOnly" flag in "xml_write" +% Writing previously read tree with default "Pref.RootOnly = true" gives +% wrong output file +Pref=[]; Pref.RootOnly = true; % do not read special node types +xml_write('test.xml', tree, [], Pref); +fprintf('Test xml file:\n'); +type('test.xml') +%% +% Writing the same tree with "Pref.RootOnly = false" gives correct output +Pref=[]; Pref.RootOnly = false; % do not read special node types +xml_write('test.xml', tree, [], Pref); +fprintf('Test xml file:\n'); +type('test.xml') + +%% "Pref.NumLevels" flag in "xml_read" +% This parameter allows user to skip parts of the tree in order to save +% time and memory. Usefull only in a rare case when a small portion of +% large XML file is needed. +% +% Create test tile +MyTree = []; +MyTree.Level1 = 1; +MyTree.Level1_.Level2 = 2; +MyTree.Level1_.Level2_.Level3 = 3; +MyTree.Level1_.Level2_.Level3_.Level4 = 4; +xml_write('test.xml', MyTree); +fprintf('Test xml file:\n'); +type('test.xml') +%% +% *Use Default ("Pref.NumLevels = infinity") setting* +tree = xml_read('test.xml'); +gen_object_display(tree); +%% +% *Limit the read to only 2 levels* +Pref=[]; Pref.NumLevels = 2; +tree = xml_read('test.xml', Pref); +gen_object_display(tree); + + + + +%% Create DOM object based on a Struct using "xml_write" +% *Create Struct tree* +MyTree=[]; +MyTree.MyNumber = 13; +MyTree.MyString = 'Hello World'; +%% +% *Convert Struct to DOM object using xml_write* +DOM = xml_write([], MyTree); +xmlwrite('test.xml', DOM); % Save DOM object using MATLAB function +type('test.xml') + +%% Convert DOM object to Struct using "xml_read" +DOM = xmlread('test.xml'); % Read DOM object using MATLAB function +[tree treeName] = xml_read(DOM); % Convert DOM object to Struct +disp([treeName{1} ' =']) +gen_object_display(tree) + +%% Write XML file based on a DOM using "xml_write_xerces" +xmlwrite_xerces('test.xml', DOM); % Save DOM object using Xerces library +type('test.xml') + +%% Write XML to string instead of a file +DOM = xml_write([], MyTree); +str = xmlwrite(DOM); +disp(str) + +%% Write XML file with embedded binary data encoded as Base64 (using java version) +fid = fopen('football.jpg', 'rb'); +raw1 = uint8(fread(fid, 'uint8')); % read image file as a raw binary +fclose(fid); + +MyTree=[]; +MyTree.Size = 13; +MyTree.MyString = 'Hello World'; % simple case +MyTree.MyImage.ATTRIBUTE.EncodingMIMEType = 'base64'; +MyTree.MyImage.CONTENT = base64encode(raw1,'java');% perform base64 encoding of the binary data +xml_write('test.xml', MyTree); % write xml file + +%% Read XML file with embedded binary data encoded as Base64 (using java version) +tree = xml_read('test.xml', Pref); % read xml file +raw = base64decode(tree.MyImage.CONTENT, '', 'java'); % convert xml image to raw binary +fid = fopen('MyFootball.jpg', 'wb'); +fwrite(fid, raw, 'uint8'); % dumb the raw binary to the hard disk +fclose(fid); +I = imread('MyFootball.jpg'); % read it as an image +imshow(I); + +%% Write XML file with embedded binary data encoded as Base64 (simpler version using only matlab code +% Notice that process of writing to xml stripped all end-of-lie characters +% from base64 code. +isChunked = true; % break into chunks 76 characters long +url_safe = true; % 'base64url' encoding +code = base64encode('license.txt', 'matlab', isChunked, url_safe); +disp(code) +MyTree=[]; +MyTree.Size = 13; +MyTree.MyString = 'Hello World'; +MyTree.MyImage.ATTRIBUTE.EncodingMIMEType = 'base64'; +MyTree.MyImage.CONTENT = code; % perform base64 encoding of the binary data +xml_write('test.xml', MyTree); % write xml file +type('test.xml') + +%% Read XML file with embedded binary data encoded as Base64 (simpler version using only matlab code +tree = xml_read('test.xml', Pref); % read xml file +base64decode(tree.MyImage.CONTENT, 'license2.txt', 'matlab'); % save xml image as raw binary +type('license2.txt') \ No newline at end of file diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_write.m b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_write.m new file mode 100644 index 00000000..8ed29d52 --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xml_write.m @@ -0,0 +1,447 @@ +function DOMnode = xml_write(filename, tree, RootName, Pref) +%XML_WRITE Writes Matlab data structures to XML file +% +% DESCRIPTION +% xml_write( filename, tree) Converts Matlab data structure 'tree' containing +% cells, structs, numbers and strings to Document Object Model (DOM) node +% tree, then saves it to XML file 'filename' using Matlab's xmlwrite +% function. Optionally one can also use alternative version of xmlwrite +% function which directly calls JAVA functions for XML writing without +% MATLAB middleware. This function is provided as a patch to existing +% bugs in xmlwrite (in R2006b). +% +% xml_write(filename, tree, RootName, Pref) allows you to specify +% additional preferences about file format +% +% DOMnode = xml_write([], tree) same as above except that DOM node is +% not saved to the file but returned. +% +% INPUT +% filename file name +% tree Matlab structure tree to store in xml file. +% RootName String with XML tag name used for root (top level) node +% Optionally it can be a string cell array storing: Name of +% root node, document "Processing Instructions" data and +% document "comment" string +% Pref Other preferences: +% Pref.ItemName - default 'item' - name of a special tag used to +% itemize cell or struct arrays +% Pref.XmlEngine - let you choose the XML engine. Currently default is +% 'Xerces', which is using directly the apache xerces java file. +% Other option is 'Matlab' which uses MATLAB's xmlwrite and its +% XMLUtils java file. Both options create identical results except in +% case of CDATA sections where xmlwrite fails. +% Pref.CellItem - default 'true' - allow cell arrays to use 'item' +% notation. See below. +% Pref.RootOnly - default true - output variable 'tree' corresponds to +% xml file root element, otherwise it correspond to the whole file. +% Pref.StructItem - default 'true' - allow arrays of structs to use +% 'item' notation. For example "Pref.StructItem = true" gives: +% +% +% ... <\item> +% ... <\item> +% <\b> +% <\a> +% while "Pref.StructItem = false" gives: +% +% ... <\b> +% ... <\b> +% <\a> +% +% +% Several special xml node types can be created if special tags are used +% for field names of 'tree' nodes: +% - node.CONTENT - stores data section of the node if other fields +% (usually ATTRIBUTE are present. Usually data section is stored +% directly in 'node'. +% - node.ATTRIBUTE.name - stores node's attribute called 'name'. +% - node.COMMENT - create comment child node from the string. For global +% comments see "RootName" input variable. +% - node.PROCESSING_INSTRUCTIONS - create "processing instruction" child +% node from the string. For global "processing instructions" see +% "RootName" input variable. +% - node.CDATA_SECTION - stores node's CDATA section (string). Only works +% if Pref.XmlEngine='Xerces'. For more info, see comments of F_xmlwrite. +% - other special node types like: document fragment nodes, document type +% nodes, entity nodes and notation nodes are not being handled by +% 'xml_write' at the moment. +% +% OUTPUT +% DOMnode Document Object Model (DOM) node tree in the format +% required as input to xmlwrite. (optional) +% +% EXAMPLES: +% MyTree=[]; +% MyTree.MyNumber = 13; +% MyTree.MyString = 'Hello World'; +% xml_write('test.xml', MyTree); +% type('test.xml') +% %See also xml_tutorial.m +% +% See also +% xml_read, xmlread, xmlwrite +% +% Written by Jarek Tuszynski, SAIC, jaroslaw.w.tuszynski_at_saic.com + +%% Check Matlab Version +v = ver('MATLAB'); +v = str2double(regexp(v.Version, '\d.\d','match','once')); +if (v<7) + error('Your MATLAB version is too old. You need version 7.0 or newer.'); +end + +%% default preferences +DPref.TableName = {'tr','td'}; % name of a special tags used to itemize 2D cell arrays +DPref.ItemName = 'item'; % name of a special tag used to itemize 1D cell arrays +DPref.StructItem = true; % allow arrays of structs to use 'item' notation +DPref.CellItem = true; % allow cell arrays to use 'item' notation +DPref.StructTable= 'Html'; +DPref.CellTable = 'Html'; +DPref.XmlEngine = 'Matlab'; % use matlab provided XMLUtils +%DPref.XmlEngine = 'Xerces'; % use Xerces xml generator directly +DPref.PreserveSpace = false; % Preserve or delete spaces at the beggining and the end of stings? +RootOnly = true; % Input is root node only +GlobalProcInst = []; +GlobalComment = []; +GlobalDocType = []; + +%% read user preferences +if (nargin>3) + if (isfield(Pref, 'TableName' )), DPref.TableName = Pref.TableName; end + if (isfield(Pref, 'ItemName' )), DPref.ItemName = Pref.ItemName; end + if (isfield(Pref, 'StructItem')), DPref.StructItem = Pref.StructItem; end + if (isfield(Pref, 'CellItem' )), DPref.CellItem = Pref.CellItem; end + if (isfield(Pref, 'CellTable')), DPref.CellTable = Pref.CellTable; end + if (isfield(Pref, 'StructTable')), DPref.StructTable= Pref.StructTable; end + if (isfield(Pref, 'XmlEngine' )), DPref.XmlEngine = Pref.XmlEngine; end + if (isfield(Pref, 'RootOnly' )), RootOnly = Pref.RootOnly; end + if (isfield(Pref, 'PreserveSpace')), DPref.PreserveSpace = Pref.PreserveSpace; end +end +if (nargin<3 || isempty(RootName)), RootName=inputname(2); end +if (isempty(RootName)), RootName='ROOT'; end +if (iscell(RootName)) % RootName also stores global text node data + rName = RootName; + RootName = char(rName{1}); + if (length(rName)>1), GlobalProcInst = char(rName{2}); end + if (length(rName)>2), GlobalComment = char(rName{3}); end + if (length(rName)>3), GlobalDocType = char(rName{4}); end +end +if(~RootOnly && isstruct(tree)) % if struct than deal with each field separatly + fields = fieldnames(tree); + for i=1:length(fields) + field = fields{i}; + x = tree(1).(field); + if (strcmp(field, 'COMMENT')) + GlobalComment = x; + elseif (strcmp(field, 'PROCESSING_INSTRUCTION')) + GlobalProcInst = x; + elseif (strcmp(field, 'DOCUMENT_TYPE')) + GlobalDocType = x; + else + RootName = field; + t = x; + end + end + tree = t; +end + +%% Initialize jave object that will store xml data structure +RootName = varName2str(RootName); +if (~isempty(GlobalDocType)) + % n = strfind(GlobalDocType, ' '); + % if (~isempty(n)) + % dtype = com.mathworks.xml.XMLUtils.createDocumentType(GlobalDocType); + % end + % DOMnode = com.mathworks.xml.XMLUtils.createDocument(RootName, dtype); + warning('xml_io_tools:write:docType', ... + 'DOCUMENT_TYPE node was encountered which is not supported yet. Ignoring.'); +end +DOMnode = com.mathworks.xml.XMLUtils.createDocument(RootName); + + +%% Use recursive function to convert matlab data structure to XML +root = DOMnode.getDocumentElement; +struct2DOMnode(DOMnode, root, tree, DPref.ItemName, DPref); + +%% Remove the only child of the root node +root = DOMnode.getDocumentElement; +Child = root.getChildNodes; % create array of children nodes +nChild = Child.getLength; % number of children +if (nChild==1) + node = root.removeChild(root.getFirstChild); + while(node.hasChildNodes) + root.appendChild(node.removeChild(node.getFirstChild)); + end + while(node.hasAttributes) % copy all attributes + root.setAttributeNode(node.removeAttributeNode(node.getAttributes.item(0))); + end +end + +%% Save exotic Global nodes +if (~isempty(GlobalComment)) + DOMnode.insertBefore(DOMnode.createComment(GlobalComment), DOMnode.getFirstChild()); +end +if (~isempty(GlobalProcInst)) + n = strfind(GlobalProcInst, ' '); + if (~isempty(n)) + proc = DOMnode.createProcessingInstruction(GlobalProcInst(1:(n(1)-1)),... + GlobalProcInst((n(1)+1):end)); + DOMnode.insertBefore(proc, DOMnode.getFirstChild()); + end +end +% Not supported yet as the code below does not work +% if (~isempty(GlobalDocType)) +% n = strfind(GlobalDocType, ' '); +% if (~isempty(n)) +% dtype = DOMnode.createDocumentType(GlobalDocType); +% DOMnode.insertBefore(dtype, DOMnode.getFirstChild()); +% end +% end + +%% save java DOM tree to XML file +if (~isempty(filename)) + if (strcmpi(DPref.XmlEngine, 'Xerces')) + xmlwrite_xerces(filename, DOMnode); + else + xmlwrite(filename, DOMnode); + end +end + + +%% ======================================================================= +% === struct2DOMnode Function =========================================== +% ======================================================================= +function [] = struct2DOMnode(xml, parent, s, TagName, Pref) +% struct2DOMnode is a recursive function that converts matlab's structs to +% DOM nodes. +% INPUTS: +% xml - jave object that will store xml data structure +% parent - parent DOM Element +% s - Matlab data structure to save +% TagName - name to be used in xml tags describing 's' +% Pref - preferenced +% OUTPUT: +% parent - modified 'parent' + +% perform some conversions +if (ischar(s) && min(size(s))>1) % if 2D array of characters + s=cellstr(s); % than convert to cell array +end +% if (strcmp(TagName, 'CONTENT')) +% while (iscell(s) && length(s)==1), s = s{1}; end % unwrap cell arrays of length 1 +% end +TagName = varName2str(TagName); + +%% == node is a 2D cell array == +% convert to some other format prior to further processing +nDim = nnz(size(s)>1); % is it a scalar, vector, 2D array, 3D cube, etc? +if (iscell(s) && nDim==2 && strcmpi(Pref.CellTable, 'Matlab')) + s = var2str(s, Pref.PreserveSpace); +end +if (nDim==2 && (iscell (s) && strcmpi(Pref.CellTable, 'Vector')) || ... + (isstruct(s) && strcmpi(Pref.StructTable, 'Vector'))) + s = s(:); +end +if (nDim>2), s = s(:); end % can not handle this case well +nItem = numel(s); +nDim = nnz(size(s)>1); % is it a scalar, vector, 2D array, 3D cube, etc? + +%% == node is a cell == +if (iscell(s)) % if this is a cell or cell array + if ((nDim==2 && strcmpi(Pref.CellTable,'Html')) || (nDim< 2 && Pref.CellItem)) + % if 2D array of cells than can use HTML-like notation or if 1D array + % than can use item notation + if (strcmp(TagName, 'CONTENT')) % CONTENT nodes already have ... + array2DOMnode(xml, parent, s, Pref.ItemName, Pref ); % recursive call + else + node = xml.createElement(TagName); % ... + array2DOMnode(xml, node, s, Pref.ItemName, Pref ); % recursive call + parent.appendChild(node); + end + else % use ...<\TagName> ...<\TagName> notation + array2DOMnode(xml, parent, s, TagName, Pref ); % recursive call + end +%% == node is a struct == +elseif (isstruct(s)) % if struct than deal with each field separatly + if ((nDim==2 && strcmpi(Pref.StructTable,'Html')) || (nItem>1 && Pref.StructItem)) + % if 2D array of structs than can use HTML-like notation or + % if 1D array of structs than can use 'items' notation + node = xml.createElement(TagName); + array2DOMnode(xml, node, s, Pref.ItemName, Pref ); % recursive call + parent.appendChild(node); + elseif (nItem>1) % use ...<\TagName> ...<\TagName> notation + array2DOMnode(xml, parent, s, TagName, Pref ); % recursive call + else % otherwise save each struct separatelly + fields = fieldnames(s); + node = xml.createElement(TagName); + for i=1:length(fields) % add field by field to the node + field = fields{i}; + x = s.(field); + switch field + case {'COMMENT', 'CDATA_SECTION', 'PROCESSING_INSTRUCTION'} + if iscellstr(x) % cell array of strings -> add them one by one + array2DOMnode(xml, node, x(:), field, Pref ); % recursive call will modify 'node' + elseif ischar(x) % single string -> add it + struct2DOMnode(xml, node, x, field, Pref ); % recursive call will modify 'node' + else % not a string - Ignore + warning('xml_io_tools:write:badSpecialNode', ... + ['Struct field named ',field,' encountered which was not a string. Ignoring.']); + end + case 'ATTRIBUTE' % set attributes of the node + if (isempty(x)), continue; end + if (isstruct(x)) + attName = fieldnames(x); % get names of all the attributes + for k=1:length(attName) % attach them to the node + att = xml.createAttribute(varName2str(attName(k))); + att.setValue(var2str(x.(attName{k}),Pref.PreserveSpace)); + node.setAttributeNode(att); + end + else + warning('xml_io_tools:write:badAttribute', ... + 'Struct field named ATTRIBUTE encountered which was not a struct. Ignoring.'); + end + otherwise % set children of the node + struct2DOMnode(xml, node, x, field, Pref ); % recursive call will modify 'node' + end + end % end for i=1:nFields + parent.appendChild(node); + end +%% == node is a leaf node == +else % if not a struct and not a cell than it is a leaf node + switch TagName % different processing depending on desired type of the node + case 'COMMENT' % create comment node + com = xml.createComment(s); + parent.appendChild(com); + case 'CDATA_SECTION' % create CDATA Section + cdt = xml.createCDATASection(s); + parent.appendChild(cdt); + case 'PROCESSING_INSTRUCTION' % set attributes of the node + OK = false; + if (ischar(s)) + n = strfind(s, ' '); + if (~isempty(n)) + proc = xml.createProcessingInstruction(s(1:(n(1)-1)),s((n(1)+1):end)); + parent.insertBefore(proc, parent.getFirstChild()); + OK = true; + end + end + if (~OK) + warning('xml_io_tools:write:badProcInst', ... + ['Struct field named PROCESSING_INSTRUCTION need to be',... + ' a string, for example: xml-stylesheet type="text/css" ', ... + 'href="myStyleSheet.css". Ignoring.']); + end + case 'CONTENT' % this is text part of already existing node + txt = xml.createTextNode(var2str(s, Pref.PreserveSpace)); % convert to text + parent.appendChild(txt); + otherwise % I guess it is a regular text leaf node + txt = xml.createTextNode(var2str(s, Pref.PreserveSpace)); + node = xml.createElement(TagName); + node.appendChild(txt); + parent.appendChild(node); + end +end % of struct2DOMnode function + +%% ======================================================================= +% === array2DOMnode Function ============================================ +% ======================================================================= +function [] = array2DOMnode(xml, parent, s, TagName, Pref) +% Deal with 1D and 2D arrays of cell or struct. Will modify 'parent'. +nDim = nnz(size(s)>1); % is it a scalar, vector, 2D array, 3D cube, etc? +switch nDim + case 2 % 2D array + for r=1:size(s,1) + subnode = xml.createElement(Pref.TableName{1}); + for c=1:size(s,2) + v = s(r,c); + if iscell(v), v = v{1}; end + struct2DOMnode(xml, subnode, v, Pref.TableName{2}, Pref ); % recursive call + end + parent.appendChild(subnode); + end + case 1 %1D array + for iItem=1:numel(s) + v = s(iItem); + if iscell(v), v = v{1}; end + struct2DOMnode(xml, parent, v, TagName, Pref ); % recursive call + end + case 0 % scalar -> this case should never be called + if ~isempty(s) + if iscell(s), s = s{1}; end + struct2DOMnode(xml, parent, s, TagName, Pref ); + end +end + +%% ======================================================================= +% === var2str Function ================================================== +% ======================================================================= +function str = var2str(object, PreserveSpace) +% convert matlab variables to a string +switch (1) + case isempty(object) + str = ''; + case (isnumeric(object) || islogical(object)) + if ndims(object)>2, object=object(:); end % can't handle arrays with dimention > 2 + str=mat2str(object); % convert matrix to a string + % mark logical scalars with [] (logical arrays already have them) so the xml_read + % recognizes them as MATLAB objects instead of strings. Same with sparse + % matrices + if ((islogical(object) && isscalar(object)) || issparse(object)), + str = ['[' str ']']; + end + if (isinteger(object)), + str = ['[', class(object), '(', str ')]']; + end + case iscell(object) + if ndims(object)>2, object=object(:); end % can't handle cell arrays with dimention > 2 + [nr nc] = size(object); + obj2 = object; + for i=1:length(object(:)) + str = var2str(object{i}, PreserveSpace); + if (ischar(object{i})), object{i} = ['''' object{i} '''']; else object{i}=str; end + obj2{i} = [object{i} ',']; + end + for r = 1:nr, obj2{r,nc} = [object{r,nc} ';']; end + obj2 = obj2.'; + str = ['{' obj2{:} '}']; + case isstruct(object) + str=''; + warning('xml_io_tools:write:var2str', ... + 'Struct was encountered where string was expected. Ignoring.'); + case isa(object, 'function_handle') + str = ['[@' char(object) ']']; + case ischar(object) + str = object; + otherwise + str = char(object); +end + +%% string clean-up +str=str(:); str=str.'; % make sure this is a row vector of char's +if (~isempty(str)) + str(str<32|str==127)=' '; % convert no-printable characters to spaces + if (~PreserveSpace) + str = strtrim(str); % remove spaces from begining and the end + str = regexprep(str,'\s+',' '); % remove multiple spaces + end +end + +%% ======================================================================= +% === var2Namestr Function ============================================== +% ======================================================================= +function str = varName2str(str) +% convert matlab variable names to a sting +str = char(str); +p = strfind(str,'0x'); +if (~isempty(p)) + for i=1:length(p) + before = str( p(i)+(0:3) ); % string to replace + after = char(hex2dec(before(3:4))); % string to replace with + str = regexprep(str,before,after, 'once', 'ignorecase'); + p=p-3; % since 4 characters were replaced with one - compensate + end +end +str = regexprep(str,'_COLON_',':', 'once', 'ignorecase'); +str = regexprep(str,'_DASH_' ,'-', 'once', 'ignorecase'); + diff --git a/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xmlwrite_xerces.m b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xmlwrite_xerces.m new file mode 100644 index 00000000..56dd30f3 --- /dev/null +++ b/matlab_runners/Action Unit Experiments/helpers/xml_io_tools_2010_11_05/xmlwrite_xerces.m @@ -0,0 +1,109 @@ +function varargout=xmlwrite_xerces(varargin) +%XMLWRITE_XERCES Serialize an XML Document Object Model node using Xerces parser. +% xmlwrite_xerces(FILENAME,DOMNODE) serializes the DOMNODE to file FILENAME. +% +% The function xmlwrite_xerces is very similar the Matlab function xmlwrite +% but works directly with the XERCES java classes (written by Apache XML +% Project) instead of the XMLUtils class created by Mathworks. Xerces files +% are provided in standard MATLAB instalation and live in root\java\jarext +% directory. +% +% Written by A.Amaro (02-22-2007) and generously donated to xml_io_tools. +% This function is needed as a work-around for a bug in XMLUtils library +% which can not write CDATA SECTION nodes correctly. Also Xerces and +% XMLUtils libraries handle namespaces differently. +% +% Examples: +% % See xmlwrite examples this function have almost identical behavior. +% +% Advanced use: +% FILENAME can also be a URN, java.io.OutputStream or java.io.Writer object +% SOURCE can also be a SAX InputSource, JAXP Source, InputStream, or +% Reader object + +returnString = false; +if length(varargin)==1 + returnString = true; + result = java.io.StringWriter; + source = varargin{1}; +else + result = varargin{1}; + if ischar(result) + % Using the XERCES classes directly, is not needed to modify the + % filename string. So I have commented this next line + % result = F_xmlstringinput(result,false); + end + + source = varargin{2}; + if ischar(source) + source = F_xmlstringinput(source,true); + end +end + +% SERIALIZATION OF THE DOM DOCUMENT USING XERCES CLASSES DIRECTLY + +% 1) create the output format according to the document definitions +% and type +objOutputFormat = org.apache.xml.serialize.OutputFormat(source); +set(objOutputFormat,'Indenting','on'); + +% 2) create the output stream. In this case: an XML file +objFile = java.io.File(result); +objOutputStream = java.io.FileOutputStream(objFile); + +% 3) Create the Xerces Serializer object +objSerializer= org.apache.xml.serialize.XMLSerializer(objOutputStream,objOutputFormat); + +% 4) Serialize to the XML files +javaMethod('serialize',objSerializer,source); + +% 5) IMPORTANT! Delete the objects to liberate the XML file created +objOutputStream.close; + +if returnString + varargout{1}=char(result.toString); +end + +%% ======================================================================== + function out = F_xmlstringinput(xString,isFullSearch,varargin) +% The function F_xmlstringinput is a copy of the private function: +% 'xmlstringinput' that the original xmlwrite function uses. + +if isempty(xString) + error('Filename is empty'); +elseif ~isempty(findstr(xString,'://')) + %xString is already a URL, most likely prefaced by file:// or http:// + out = xString; + return; +end + +xPath=fileparts(xString); +if isempty(xPath) + if nargin<2 || isFullSearch + out = which(xString); + if isempty(out) + error('xml:FileNotFound','File %s not found',xString); + end + else + out = fullfile(pwd,xString); + end +else + out = xString; + if (nargin<2 || isFullSearch) && ~exist(xString,'file') + %search to see if xString exists when isFullSearch + error('xml:FileNotFound','File %s not found',xString); + end +end + +%Return as a URN +if strncmp(out,'\\',2) + % SAXON UNC filepaths need to look like file:///\\\server-name\ + out = ['file:///\',out]; +elseif strncmp(out,'/',1) + % SAXON UNIX filepaths need to look like file:///root/dir/dir + out = ['file://',out]; +else + % DOS filepaths need to look like file:///d:/foo/bar + out = ['file:///',strrep(out,'\','/')]; +end + diff --git a/matlab_runners/Action Unit Experiments/run_AU_prediction_DISFA.m b/matlab_runners/Action Unit Experiments/run_AU_prediction_DISFA.m index 20b0c7dc..2506ee3e 100644 --- a/matlab_runners/Action Unit Experiments/run_AU_prediction_DISFA.m +++ b/matlab_runners/Action Unit Experiments/run_AU_prediction_DISFA.m @@ -1,6 +1,17 @@ clear -DISFA_dir = 'D:/Datasets/DISFA/Videos_LeftCamera/'; -executable = '"../../x64/Release/FeatureExtraction.exe"'; + +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end + +if(exist('D:/Datasets/DISFA/Videos_LeftCamera/', 'file')) + DISFA_dir = 'D:/Datasets/DISFA/Videos_LeftCamera/'; +else + DISFA_dir = '/multicomp/datasets/face_datasets/DISFA/Videos_LeftCamera/'; +end + videos = dir([DISFA_dir, '*.avi']); @@ -22,7 +33,11 @@ parfor v = 1:numel(videos) output_file = [output name '_au.txt']; command = [executable ' -f "' vid_file '" -of "' output_file '" -q -no2Dfp -no3Dfp -noMparams -noPose -noGaze']; - dos(command); + if(isunix) + unix(command, '-echo'); + else + dos(command); + end end @@ -31,8 +46,7 @@ end % Note that DISFA was used in training, this is not meant for experimental % results but rather to show how to do AU prediction and how to interpret % the results - -Label_dir = 'D:/Datasets/DISFA/ActionUnit_Labels/'; +Label_dir = [DISFA_dir, '/../ActionUnit_Labels/']; prediction_dir = 'out_DISFA/'; label_folders = dir([Label_dir, 'SN*']); diff --git a/matlab_runners/Action Unit Experiments/run_AU_prediction_FERA2011.m b/matlab_runners/Action Unit Experiments/run_AU_prediction_FERA2011.m index f2d2f28a..4daf563e 100644 --- a/matlab_runners/Action Unit Experiments/run_AU_prediction_FERA2011.m +++ b/matlab_runners/Action Unit Experiments/run_AU_prediction_FERA2011.m @@ -1,6 +1,7 @@ clear -fera_loc = 'D:\Datasets\fera\'; +addpath(genpath('helpers/')); +find_FERA2011; out_loc = './out_fera/'; @@ -9,41 +10,38 @@ if(~exist(out_loc, 'dir')) end %% -executable = '"../../x64/Release/FeatureExtraction.exe"'; +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end -fera_dirs = dir([fera_loc, 'au_train*']); +fera_dirs = dir([FERA2011_dir, 'train*']); -for f1=1:numel(fera_dirs) +parfor f1=1:numel(fera_dirs) - fera_dirs_level_2 = dir([fera_loc, fera_dirs(f1).name]); - fera_dirs_level_2 = fera_dirs_level_2(3:end); - - parfor f2=1:numel(fera_dirs_level_2) + vid_files = dir([FERA2011_dir, fera_dirs(f1).name, '/*.avi']); - vid_files = dir([fera_loc, fera_dirs(f1).name, '/', fera_dirs_level_2(f2).name, '/*.avi']); - - for v=1:numel(vid_files) + for v=1:numel(vid_files) - command = [executable ' -asvid -q -no2Dfp -no3Dfp -noMparams -noPose -noGaze -au_static ']; + command = [executable ' -asvid -q -no2Dfp -no3Dfp -noMparams -noPose -noGaze -au_static ']; - curr_vid = [fera_loc, fera_dirs(f1).name, '/', fera_dirs_level_2(f2).name, '/', vid_files(v).name]; + curr_vid = [FERA2011_dir, fera_dirs(f1).name, '/', vid_files(v).name]; - [~,name,~] = fileparts(curr_vid); - output_file = [out_loc name '.au.txt']; - - command = cat(2, command, [' -f "' curr_vid '" -of "' output_file '"']); + [~,name,~] = fileparts(curr_vid); + output_file = [out_loc name '.au.txt']; + command = cat(2, command, [' -f "' curr_vid '" -of "' output_file '"']); + if(isunix) + unix(command, '-echo'); + else dos(command); end end end %% -addpath('./helpers/'); - -find_FERA2011; - [ labels_gt, valid_ids, filenames] = extract_FERA2011_labels(FERA2011_dir, all_recs, all_aus); labels_gt = cat(1, labels_gt{:}); diff --git a/matlab_runners/Action Unit Experiments/run_AU_prediction_SEMAINE.m b/matlab_runners/Action Unit Experiments/run_AU_prediction_SEMAINE.m index 4947b075..ef862fef 100644 --- a/matlab_runners/Action Unit Experiments/run_AU_prediction_SEMAINE.m +++ b/matlab_runners/Action Unit Experiments/run_AU_prediction_SEMAINE.m @@ -9,7 +9,11 @@ if(~exist(out_loc, 'dir')) mkdir(out_loc); end -executable = '"../../x64/Release/FeatureExtraction.exe"'; +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end %% parfor f1=1:numel(devel_recs) @@ -27,8 +31,13 @@ parfor f1=1:numel(devel_recs) name = f1_dir; output_aus = [out_loc name '.au.txt']; - command = cat(2, command, [' -f "' curr_vid '" -of "' output_aus]); - dos(command); + command = cat(2, command, [' -f "' curr_vid '" -of "' output_aus, '"']); + + if(isunix) + unix(command, '-echo'); + else + dos(command); + end end end diff --git a/matlab_runners/Demos/feature_extraction_demo_img_seq.m b/matlab_runners/Demos/feature_extraction_demo_img_seq.m index aff1b730..9d723443 100644 --- a/matlab_runners/Demos/feature_extraction_demo_img_seq.m +++ b/matlab_runners/Demos/feature_extraction_demo_img_seq.m @@ -1,4 +1,10 @@ -executable = '"../../x64/Release/FeatureExtraction.exe"'; +clear + +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end output = './output_features_seq/'; @@ -29,11 +35,15 @@ for i=1:numel(in_dirs) command = cat(2, command, ['-asvid -fdir "' in_dirs{i} '" -of "' outputFile '" ']); - command = cat(2, command, [' -simalign "' outputDir_aligned '" -hogalign "' outputHOG_aligned '"']); + command = cat(2, command, [' -simalign "' outputDir_aligned '" -simsize 200 -hogalign "' outputHOG_aligned '"']); end -dos(command); +if(isunix) + unix(command); +else + dos(command); +end %% Demonstrating reading the output files diff --git a/matlab_runners/Demos/feature_extraction_demo_vid.m b/matlab_runners/Demos/feature_extraction_demo_vid.m index 8c8b5d4c..e062d54c 100644 --- a/matlab_runners/Demos/feature_extraction_demo_vid.m +++ b/matlab_runners/Demos/feature_extraction_demo_vid.m @@ -1,4 +1,10 @@ -executable = '"../../x64/Release/FeatureExtraction.exe"'; +clear + +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end output = './output_features_vid/'; @@ -40,7 +46,11 @@ for i=1:numel(in_files) end -dos(command); +if(isunix) + unix(command); +else + dos(command); +end %% Demonstrating reading the output files diff --git a/matlab_runners/Demos/gaze_extraction_demo_vid.m b/matlab_runners/Demos/gaze_extraction_demo_vid.m index 373424af..dddfb8db 100644 --- a/matlab_runners/Demos/gaze_extraction_demo_vid.m +++ b/matlab_runners/Demos/gaze_extraction_demo_vid.m @@ -1,4 +1,10 @@ -exe = '"../../x64/Release/FeatureExtraction.exe"'; +clear + +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end output = './output_features_vid/'; @@ -10,7 +16,7 @@ in_files = dir('../../videos/2015-10-15-15-14.avi'); % some parameters verbose = true; -command = exe; +command = executable; command = cat(2, command, ' -verbose -no2Dfp -no3Dfp -noMparams -noPose -noAUs '); % add all videos to single argument list (so as not to load the model anew @@ -31,7 +37,11 @@ for i=1:numel(in_files) end -dos(command); +if(isunix) + unix(command); +else + dos(command); +end %% Demonstrating reading the output files filename = [output name]; diff --git a/matlab_runners/Demos/run_demo_images.m b/matlab_runners/Demos/run_demo_images.m index 54e517ff..36931503 100644 --- a/matlab_runners/Demos/run_demo_images.m +++ b/matlab_runners/Demos/run_demo_images.m @@ -1,6 +1,9 @@ clear - -executable = '"../../x64/Release/FaceLandmarkImg.exe"'; +if(isunix) + executable = '"../../build/bin/FaceLandmarkImg"'; +else + executable = '"../../x64/Release/FaceLandmarkImg.exe"'; +end in_dir = '../../videos/'; out_dir = './demo_img/'; @@ -38,4 +41,8 @@ command = cat(2, command, [' -mloc "', model, '"']); % Comment to skip this functionality command = cat(2, command, ' -wild '); -dos(command); \ No newline at end of file +if(isunix) + unix(command); +else + dos(command); +end \ No newline at end of file diff --git a/matlab_runners/Demos/run_demo_video_multi.m b/matlab_runners/Demos/run_demo_video_multi.m index d5519257..f36f59aa 100644 --- a/matlab_runners/Demos/run_demo_video_multi.m +++ b/matlab_runners/Demos/run_demo_video_multi.m @@ -1,4 +1,10 @@ -executable = '"../../x64/Release/FaceLandmarkVidMulti.exe"'; +clear; + +if(isunix) + executable = '"../../build/bin/FaceLandmarkVidMulti"'; +else + executable = '"../../x64/Release/FaceLandmarkVidMulti.exe"'; +end output = './demo_vid/'; @@ -38,4 +44,8 @@ for i=1:numel(in_files) end -dos(command); \ No newline at end of file +if(isunix) + unix(command); +else + dos(command); +end \ No newline at end of file diff --git a/matlab_runners/Demos/run_demo_videos.m b/matlab_runners/Demos/run_demo_videos.m index 3961e12b..5013eaec 100644 --- a/matlab_runners/Demos/run_demo_videos.m +++ b/matlab_runners/Demos/run_demo_videos.m @@ -1,4 +1,10 @@ -executable = '"../../x64/Release/FaceLandmarkVid.exe"'; +clear + +if(isunix) + executable = '"../../build/bin/FaceLandmarkVid"'; +else + executable = '"../../x64/Release/FaceLandmarkVid.exe"'; +end output = './demo_vid/'; @@ -39,4 +45,8 @@ for i=1:numel(in_files) end -dos(command); \ No newline at end of file +if(isunix) + unix(command); +else + dos(command); +end \ No newline at end of file diff --git a/matlab_runners/Feature Point Experiments/Run_CLM_fitting_on_images.m b/matlab_runners/Feature Point Experiments/Run_OF_on_images.m similarity index 90% rename from matlab_runners/Feature Point Experiments/Run_CLM_fitting_on_images.m rename to matlab_runners/Feature Point Experiments/Run_OF_on_images.m index 131a15de..3cca6f50 100644 --- a/matlab_runners/Feature Point Experiments/Run_CLM_fitting_on_images.m +++ b/matlab_runners/Feature Point Experiments/Run_OF_on_images.m @@ -1,6 +1,4 @@ -function [ err_outline, err_no_outline ] = Run_CLM_fitting_on_images(output_loc, database_root, varargin) -%RUN_CLM_FITTING_ON_IMAGES Summary of this function goes here -% Detailed explanation goes here +function [ err_outline, err_no_outline ] = Run_OF_on_images(output_loc, database_root, varargin) dataset_dirs = {}; @@ -27,8 +25,12 @@ else verbose = false; end -command = '"../../x64/Release/FaceLandmarkImg.exe" '; - +if(isunix) + command = '"../../build/bin/FaceLandmarkImg"'; +else + command = '"../../x64/Release/FaceLandmarkImg.exe"'; +end + if(any(strcmp(varargin, 'model'))) model = varargin{find(strcmp(varargin, 'model')) + 1}; else @@ -61,7 +63,11 @@ parfor i=1:numel(dataset_dirs) command_c = cat(2, command_c, ' -wild '); - dos(command_c); + if(isunix) + unix(command_c, '-echo'); + else + dos(command_c); + end end toc diff --git a/matlab_runners/Feature Point Experiments/results/fps_yt.mat b/matlab_runners/Feature Point Experiments/results/fps_yt.mat index af63eceb..1815902d 100644 Binary files a/matlab_runners/Feature Point Experiments/results/fps_yt.mat and b/matlab_runners/Feature Point Experiments/results/fps_yt.mat differ diff --git a/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf b/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf index 15a9235b..5414ccfe 100644 Binary files a/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf and b/matlab_runners/Feature Point Experiments/results/in-the-wild-res-no-outline.pdf differ diff --git a/matlab_runners/Feature Point Experiments/results/landmark_detections.mat b/matlab_runners/Feature Point Experiments/results/landmark_detections.mat index 2e08f3fa..765cd012 100644 Binary files a/matlab_runners/Feature Point Experiments/results/landmark_detections.mat and b/matlab_runners/Feature Point Experiments/results/landmark_detections.mat differ diff --git a/matlab_runners/Feature Point Experiments/run_clm_feature_point_tests_wild.m b/matlab_runners/Feature Point Experiments/run_OpenFace_feature_point_tests_300W.m similarity index 71% rename from matlab_runners/Feature Point Experiments/run_clm_feature_point_tests_wild.m rename to matlab_runners/Feature Point Experiments/run_OpenFace_feature_point_tests_300W.m index 7dd5f999..ae819473 100644 --- a/matlab_runners/Feature Point Experiments/run_clm_feature_point_tests_wild.m +++ b/matlab_runners/Feature Point Experiments/run_OpenFace_feature_point_tests_300W.m @@ -5,25 +5,19 @@ curr_dir = cd('.'); % Replace this with your downloaded 300-W train data if(exist([getenv('USERPROFILE') '/Dropbox/AAM/test data/'], 'file')) database_root = [getenv('USERPROFILE') '/Dropbox/AAM/test data/']; +elseif(exist('D:/Dropbox/Dropbox/AAM/test data/', 'file')) + database_root = 'D:/Dropbox/Dropbox/AAM/test data/'; else - database_root = 'C:\300W/'; + database_root = '/multicomp/datasets/300-W/'; end -%% Run using DPN generak model -out_clnf = [curr_dir '/out_dclm/']; -if(~exist(out_clnf, 'file')) - mkdir(out_clnf); -end - -[err_dclm, err_no_out_dclm] = Run_CLM_fitting_on_images(out_clnf, database_root, 'use_afw', 'use_lfpw', 'use_ibug', 'use_helen', 'verbose', 'model', 'model/main_dclm_general.txt', 'multi_view', 1); - %% Run using CLNF in the wild model out_clnf = [curr_dir '/out_wild_clnf_wild/']; if(~exist(out_clnf, 'file')) mkdir(out_clnf); end -[err_clnf_wild, err_no_out_clnf_wild] = Run_CLM_fitting_on_images(out_clnf, database_root, 'use_afw', 'use_lfpw', 'use_ibug', 'use_helen', 'verbose', 'model', 'model/main_clnf_wild.txt', 'multi_view', 1); +[err_clnf_wild, err_no_out_clnf_wild] = Run_OF_on_images(out_clnf, database_root, 'use_afw', 'use_lfpw', 'use_ibug', 'use_helen', 'verbose', 'model', 'model/main_clnf_wild.txt', 'multi_view', 1); %% Run using SVR model out_svr = [curr_dir '/out_wild_svr_wild/']; @@ -31,26 +25,44 @@ if(~exist(out_svr, 'file')) mkdir(out_svr); end -[err_svr_wild, err_no_out_svr_wild] = Run_CLM_fitting_on_images(out_svr, database_root, 'use_afw', 'use_lfpw', 'use_ibug', 'use_helen', 'verbose', 'model', 'model/main_clm_wild.txt', 'multi_view', 1); +[err_svr_wild, err_no_out_svr_wild] = Run_OF_on_images(out_svr, database_root, 'use_afw', 'use_lfpw', 'use_ibug', 'use_helen', 'verbose', 'model', 'model/main_clm_wild.txt', 'multi_view', 1); + +%% Run using general CLNF model +out_clnf = [curr_dir '/out_wild_clnf/']; +if(~exist(out_clnf, 'file')) + mkdir(out_clnf); +end + +[err_clnf, err_no_out_clnf] = Run_OF_on_images(out_clnf, database_root, 'use_afw', 'use_lfpw', 'use_ibug', 'use_helen', 'verbose', 'model', 'model/main_clnf_general.txt', 'multi_view', 1); + +%% Run using SVR model +out_svr = [curr_dir '/out_wild_svr/']; +if(~exist(out_svr, 'file')) + mkdir(out_svr); +end + +[err_svr, err_no_out_svr] = Run_OF_on_images(out_svr, database_root, 'use_afw', 'use_lfpw', 'use_ibug', 'use_helen', 'verbose', 'model', 'model/main_clm_general.txt', 'multi_view', 1); %% save('results/landmark_detections.mat'); f = fopen('results/landmark_detections.txt', 'w'); fprintf(f, 'Type, mean, median\n'); -fprintf(f, 'err dclm: %f, %f\n', mean(err_dclm), median(err_dclm)); - +fprintf(f, 'err clnf: %f, %f\n', mean(err_clnf), median(err_clnf)); fprintf(f, 'err clnf wild: %f, %f\n', mean(err_clnf_wild), median(err_clnf_wild)); +fprintf(f, 'err svr: %f, %f\n', mean(err_svr), median(err_svr)); fprintf(f, 'err svr wild: %f, %f\n', mean(err_svr_wild), median(err_svr_wild)); +fprintf(f, 'err clnf no out: %f, %f\n', mean(err_no_out_clnf), median(err_no_out_clnf)); fprintf(f, 'err clnf wild no out: %f, %f\n', mean(err_no_out_clnf_wild), median(err_no_out_clnf_wild)); +fprintf(f, 'err svr no out: %f, %f\n', mean(err_no_out_svr), median(err_no_out_svr)); fprintf(f, 'err svr wild no out: %f, %f\n', mean(err_no_out_svr_wild), median(err_no_out_svr_wild)); fclose(f); -%% Draw the corresponding error graphs comparing DCLM, CLNF and SVR +%% Draw the corresponding error graphs comparing CLNF and SVR % set up the canvas line_width = 6; @@ -95,18 +107,6 @@ intraface_error = compute_error( labels - 0.5, shapes); plot(error_x, error_y, 'g--','DisplayName', 'SDM', 'LineWidth',line_width); hold on; -% load DCLM errors -load('out_dclm/res.mat'); -labels = labels([1:60,62:64,66:end],:, detected_cpp); -shapes = shapes([1:60,62:64,66:end],:, detected_cpp); -labels = labels(18:end,:,:); -shapes = shapes(18:end,:,:); - -clnf_error_cpp = compute_error( labels, shapes); -[error_x, error_y] = cummErrorCurve(clnf_error_cpp); -plot(error_x, error_y, 'r','DisplayName', 'DCLM', 'LineWidth',line_width); -hold on; - % load clnf errors load('out_wild_clnf_wild/res.mat'); labels = labels([1:60,62:64,66:end],:, detected_cpp); @@ -116,7 +116,7 @@ shapes = shapes(18:end,:,:); clnf_error_cpp = compute_error( labels, shapes); [error_x, error_y] = cummErrorCurve(clnf_error_cpp); -plot(error_x, error_y, 'DisplayName', 'CLM+CLNF', 'LineWidth',line_width); +plot(error_x, error_y, 'r','DisplayName', 'CLM+CLNF', 'LineWidth',line_width); hold on; % load svr errors @@ -149,8 +149,8 @@ plot(error_x, error_y, 'kx','DisplayName', 'DRMF', 'LineWidth',line_width); hold on; % Make it look nice and print to a pdf -set(gca,'xtick',[0:0.025:0.1]) -xlim([0,0.1]); +set(gca,'xtick',[0:0.05:0.15]) +xlim([0,0.15]); xlabel('Size normalised shape RMS error','FontName','Helvetica'); ylabel('Proportion of images','FontName','Helvetica'); grid on diff --git a/matlab_runners/Feature Point Experiments/run_yt_dataset.m b/matlab_runners/Feature Point Experiments/run_yt_dataset.m index aeca5b8c..5c38e45c 100644 --- a/matlab_runners/Feature Point Experiments/run_yt_dataset.m +++ b/matlab_runners/Feature Point Experiments/run_yt_dataset.m @@ -1,6 +1,10 @@ clear -executable = '"../../x64/Release/FeatureExtraction.exe"'; +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end output = 'yt_features/'; @@ -10,11 +14,13 @@ end if(exist([getenv('USERPROFILE') '/Dropbox/AAM/test data/'], 'file')) database_root = [getenv('USERPROFILE') '/Dropbox/AAM/test data/']; -else +elseif(exist('D:/Dropbox/Dropbox/AAM/test data/', 'file')) database_root = 'D:/Dropbox/Dropbox/AAM/test data/'; +else + database_root = '/multicomp/datasets/'; end -database_root = [database_root, '/ytceleb_annotations_CVPR2014/']; +database_root = [database_root, '/ytceleb/']; in_vids = dir([database_root '/*.avi']); @@ -33,7 +39,11 @@ for i=1:numel(in_vids) command = cat(2, command, [' -f "' in_file_name '" -of "' outputFile_fp '"']); end -dos(command); +if(isunix) + unix(command, '-echo') +else + dos(command); +end %% output = 'yt_features_clm/'; @@ -59,7 +69,11 @@ for i=1:numel(in_vids) command = cat(2, command, [' -f "' in_file_name '" -of "' outputFile_fp '"']); end -dos(command); +if(isunix) + unix(command, '-echo') +else + dos(command); +end %% evaluating yt datasets d_loc = 'yt_features/'; d_loc_clm = 'yt_features_clm/'; diff --git a/matlab_runners/Full_test_suite.m b/matlab_runners/Full_test_suite.m index 29cbbe69..a44a4955 100644 --- a/matlab_runners/Full_test_suite.m +++ b/matlab_runners/Full_test_suite.m @@ -1,6 +1,6 @@ % This is sort of the unit test for the whole module (needs datasets) -% Will take over an hour to run all - +% Will take several hours to run all +clear tic %% Head pose cd('Head Pose Experiments'); @@ -12,7 +12,7 @@ cd('../'); %% Features cd('Feature Point Experiments'); -run_clm_feature_point_tests_wild; +run_OpenFace_feature_point_tests_300W; assert(median(err_clnf) < 0.041); assert(median(err_clnf_wild) < 0.041); run_yt_dataset; diff --git a/matlab_runners/Gaze Experiments/extract_mpii_gaze_test.m b/matlab_runners/Gaze Experiments/extract_mpii_gaze_test.m index fd0fa7d8..2d15c51e 100644 --- a/matlab_runners/Gaze Experiments/extract_mpii_gaze_test.m +++ b/matlab_runners/Gaze Experiments/extract_mpii_gaze_test.m @@ -7,9 +7,12 @@ if(exist([getenv('USERPROFILE') '/Dropbox/AAM/eye_clm/mpii_data/'], 'file')) database_root = [getenv('USERPROFILE') '/Dropbox/AAM/eye_clm/mpii_data/']; elseif(exist('D:\Dropbox/Dropbox/AAM/eye_clm/mpii_data/', 'file')) database_root = 'D:\Dropbox/Dropbox/AAM/eye_clm/mpii_data/'; +elseif(exist('/multicomp/datasets/mpii_gaze/mpii_data/', 'file')) + database_root = '/multicomp/datasets/mpii_gaze/mpii_data/'; else fprintf('MPII gaze dataset not found\n'); end + output_loc = './gaze_estimates_MPII/'; if(~exist(output_loc, 'dir')) mkdir(output_loc); @@ -18,7 +21,13 @@ end output = './mpii_out/'; %% Perform actual gaze predictions -command = sprintf('"../../x64/Release/FaceLandmarkImg.exe" -fx 1028 -fy 1028 -gaze '); +if(isunix) + executable = '"../../build/bin/FaceLandmarkImg"'; +else + executable = '"../../x64/Release/FaceLandmarkImg.exe"'; +end + +command = sprintf('%s -fx 1028 -fy 1028 -gaze ', executable); p_dirs = dir([database_root, 'p*']); parfor p=1:numel(p_dirs) @@ -30,7 +39,11 @@ parfor p=1:numel(p_dirs) command_c = cat(2, command, input_loc, out_img_loc, out_p_loc); command_c = cat(2, command_c, ' -wild'); - dos(command_c); + if(isunix) + unix(command_c, '-echo'); + else + dos(command_c); + end; end %% diff --git a/matlab_runners/Gaze Experiments/mpii_1500_errs.mat b/matlab_runners/Gaze Experiments/mpii_1500_errs.mat new file mode 100644 index 00000000..e006f70e Binary files /dev/null and b/matlab_runners/Gaze Experiments/mpii_1500_errs.mat differ diff --git a/matlab_runners/Head Pose Experiments/results/Pose_OF.mat b/matlab_runners/Head Pose Experiments/results/Pose_OF.mat index 68e460cf..1ee2f9dc 100644 Binary files a/matlab_runners/Head Pose Experiments/results/Pose_OF.mat and b/matlab_runners/Head Pose Experiments/results/Pose_OF.mat differ diff --git a/matlab_runners/Head Pose Experiments/results/Pose_OF.txt b/matlab_runners/Head Pose Experiments/results/Pose_OF.txt index 9e98decc..1dd76e84 100644 --- a/matlab_runners/Head Pose Experiments/results/Pose_OF.txt +++ b/matlab_runners/Head Pose Experiments/results/Pose_OF.txt @@ -1,4 +1,4 @@ Dataset and model, pitch, yaw, roll, mean, median biwi error: 7.955, 5.583, 4.402, 5.980, 2.624 bu error: 2.762, 4.103, 2.568, 3.145, 2.118 -ict error: 3.619, 3.606, 3.625, 3.617, 2.027 +ict error: 3.620, 3.608, 3.626, 3.618, 2.028 diff --git a/matlab_runners/Head Pose Experiments/run_biwi_experiment.m b/matlab_runners/Head Pose Experiments/run_biwi_experiment.m index bcb705f9..cabc6bae 100644 --- a/matlab_runners/Head Pose Experiments/run_biwi_experiment.m +++ b/matlab_runners/Head Pose Experiments/run_biwi_experiment.m @@ -1,30 +1,25 @@ -function [fps, resDir] = run_biwi_experiment(rootDir, biwiDir, outputDir, verbose, depth, version, varargin) +function [output_dir] = run_biwi_experiment(rootDir, biwiDir, verbose, depth, varargin) % Biwi dataset experiment -executable = '"../../x64/Release/FeatureExtraction.exe"'; +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end -output = 'Tracker_'; +output_dir = 'experiments/biwi_out'; dbSeqDir = dir([rootDir biwiDir]); - -% listing the output based on the current revision -output = [output 'r' num2str(version)]; if(depth) - output = cat(2, output, '_depth'); + output_dir = cat(2, output_dir, '_depth'); end -outputDir = cat(2, outputDir, ['/' output '/']); - -if(~exist([outputDir], 'dir')) - mkdir([outputDir]); -end +output_dir = cat(2, output_dir, '/'); offset = 0; r = 1 + offset; - -tic; numTogether = 25; @@ -34,7 +29,7 @@ for i=3 + offset:numTogether:numel(dbSeqDir) command = executable; - command = cat(2, command, [' -root ' '"' rootDir '"']); + command = cat(2, command, [' -inroot ' '"' rootDir '"']); % deal with edge cases if(numTogether + i > numel(dbSeqDir)) @@ -44,7 +39,7 @@ for i=3 + offset:numTogether:numel(dbSeqDir) for n=0:numTogether-1 inputFile = [biwiDir dbSeqDir(i+n).name '/colour.avi']; - outputFile = [outputDir dbSeqDir(i+n).name '.txt']; + outputFile = [output_dir dbSeqDir(i+n).name '.txt']; command = cat(2, command, [' -f "' inputFile '" -of "' outputFile '"']); @@ -54,7 +49,7 @@ for i=3 + offset:numTogether:numel(dbSeqDir) end if(verbose) - outputVideo = [outputDir dbSeqDir(i).name '.avi']; + outputVideo = [output_dir dbSeqDir(i).name '.avi']; command = cat(2, command, [' -ov "' outputVideo '"']); end end @@ -65,9 +60,9 @@ for i=3 + offset:numTogether:numel(dbSeqDir) end r = r+1; - dos(command); + if(isunix) + unix(command, '-echo') + else + dos(command); + end end - -timeTaken = toc; -fps = 15678 / timeTaken; -resDir = outputDir; \ No newline at end of file diff --git a/matlab_runners/Head Pose Experiments/run_bu_experiment.m b/matlab_runners/Head Pose Experiments/run_bu_experiment.m index 04e244b1..fa5c5c3b 100644 --- a/matlab_runners/Head Pose Experiments/run_bu_experiment.m +++ b/matlab_runners/Head Pose Experiments/run_bu_experiment.m @@ -1,27 +1,21 @@ -function [fps, resDir] = run_bu_experiment(bu_dir, verbose, version, varargin) +function [output_dir] = run_bu_experiment(bu_dir, verbose, varargin) - executable = '"../../x64/Release/FeatureExtraction.exe"'; - - output = 'Tracker_'; - - % listing the output based on the current revision - output = [output 'r' num2str(version)]; - - output = cat(2, output, '/'); - - if(~exist([bu_dir output], 'dir')) - mkdir([bu_dir output]); + if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; + else + executable = '"../../x64/Release/FeatureExtraction.exe"'; end + output_dir = 'experiments/bu_out/'; + buFiles = dir([bu_dir '*.avi']); numTogether = 25; - tic; for i=1:numTogether:numel(buFiles) command = executable; - command = cat(2, command, [' -root ' '"' bu_dir '/"']); + command = cat(2, command, [' -inroot ' '"' bu_dir '/"']); % BU dataset orientation is in terms of camera plane, instruct the % tracker to output it in that format @@ -37,12 +31,12 @@ function [fps, resDir] = run_bu_experiment(bu_dir, verbose, version, varargin) [~, name, ~] = fileparts(inputFile); % where to output results - outputFile = [output name '.txt']; + outputFile = [output_dir name '.txt']; command = cat(2, command, [' -f "' inputFile '" -of "' outputFile '"']); if(verbose) - outputVideo = ['"' output name '.avi' '"']; + outputVideo = ['"' output_dir name '.avi' '"']; command = cat(2, command, [' -ov ' outputVideo]); end end @@ -53,13 +47,11 @@ function [fps, resDir] = run_bu_experiment(bu_dir, verbose, version, varargin) command = cat(2, command, [' -mloc "', varargin{find(strcmp('model', varargin))+1}, '"']); end - dos(command); + if(isunix) + unix(command, '-echo') + else + dos(command); + end end - - timeTaken = toc; - fps = 9000 / timeTaken; - - % tell the caller where the output was written - resDir = [bu_dir output]; - + end \ No newline at end of file diff --git a/matlab_runners/Head Pose Experiments/run_head_pose_tests_OpenFace.m b/matlab_runners/Head Pose Experiments/run_head_pose_tests_OpenFace.m index 4ff57b01..d8a7c0ed 100644 --- a/matlab_runners/Head Pose Experiments/run_head_pose_tests_OpenFace.m +++ b/matlab_runners/Head Pose Experiments/run_head_pose_tests_OpenFace.m @@ -8,38 +8,34 @@ if exist('D:/Datasets/HeadPose', 'file') database_root = 'D:/Datasets/HeadPose/'; elseif(exist([getenv('USERPROFILE') '/Dropbox/AAM/test data/'], 'file')) database_root = [getenv('USERPROFILE') '/Dropbox/AAM/test data/']; -else +elseif(exist([getenv('USERPROFILE') 'F:/Dropbox/Dropbox/AAM/test data/'], 'file')) database_root = 'F:/Dropbox/Dropbox/AAM/test data/'; +else + database_root = '/multicomp/datasets/head_pose_dbs/'; end buDir = [database_root, '/bu/uniform-light/']; % The fast and accurate clnf %% -v = 3; -[fps_bu_OF, resFolderBU_OF] = run_bu_experiment(buDir, false, v, 'model', 'model/main_clnf_general.txt'); +[resFolderBU_OF] = run_bu_experiment(buDir, false, 'model', 'model/main_clnf_general.txt'); [bu_error_OF, pred_hp_bu, gt_hp_bu, all_errors_bu_OF, rels_bu] = calcBUerror(resFolderBU_OF, buDir); %% % Run the Biwi test biwi_dir = '/biwi pose/'; -biwi_results_root = '/biwi pose results/'; -% Intensity -v = 4; -[fps_biwi_OF, res_folder_OF] = run_biwi_experiment(database_root, biwi_dir, biwi_results_root, false, false, v, 'model', 'model/main_clnf_general.txt'); +[res_folder_biwi_OF] = run_biwi_experiment(database_root, biwi_dir, false, false, 'model', 'model/main_clnf_general.txt'); % Calculate the resulting errors -[biwi_error_OF, pred_hp_biwi, gt_hp_biwi, ~, all_errors_biwi_OF, rels_biwi] = calcBiwiError([database_root res_folder_OF], [database_root biwi_dir]); +[biwi_error_OF, pred_hp_biwi, gt_hp_biwi, ~, all_errors_biwi_OF, rels_biwi] = calcBiwiError(res_folder_biwi_OF, [database_root biwi_dir]); %% Run the ICT test -ict_dir = ['ict/']; -ict_results_root = ['ict results/']; +ict_dir = ['/ict/']; -v = 4; % Intensity -[fps_ict_OF, res_folder_ict_OF] = run_ict_experiment(database_root, ict_dir, ict_results_root, false, false, v, 'model', 'model/main_clnf_general.txt'); +[res_folder_ict_OF] = run_ict_experiment(database_root, ict_dir, false, false, 'model', 'model/main_clnf_general.txt'); % Calculate the resulting errors -[ict_error_OF, pred_hp_ict, gt_hp_ict, ~, all_errors_ict_OF, rel_ict] = calcIctError([database_root res_folder_ict_OF], [database_root ict_dir]); +[ict_error_OF, pred_hp_ict, gt_hp_ict, ~, all_errors_ict_OF, rel_ict] = calcIctError(res_folder_ict_OF, [database_root ict_dir]); %% Save the results filename = 'results/Pose_OF'; diff --git a/matlab_runners/Head Pose Experiments/run_ict_experiment.m b/matlab_runners/Head Pose Experiments/run_ict_experiment.m index 0e2d9d43..3634a31e 100644 --- a/matlab_runners/Head Pose Experiments/run_ict_experiment.m +++ b/matlab_runners/Head Pose Experiments/run_ict_experiment.m @@ -1,27 +1,22 @@ -function [fps, resDir] = run_ict_experiment(rootDir, ictDir, outputRoot, verbose, depth, version, varargin) +function [output_dir] = run_ict_experiment(rootDir, ictDir, verbose, depth, varargin) %EVALUATEICTDATABASE Summary of this function goes here % Detailed explanation goes here -executable = '"../../x64/Release/FeatureExtraction.exe"'; +if(isunix) + executable = '"../../build/bin/FeatureExtraction"'; +else + executable = '"../../x64/Release/FeatureExtraction.exe"'; +end -output = 'Tracker_'; +output_dir = 'experiments/ict_out'; dbSeqDir = dir([rootDir ictDir]); - -% listing the output based on the current revision -output = [output 'r' num2str(version)]; if(depth) - output = cat(2, output, '_depth'); + output_dir = cat(2, output_dir, '_depth'); end -outputDir = cat(2, outputRoot, ['/' output '/']); - -if(~exist([rootDir outputDir], 'dir')) - mkdir([rootDir outputDir]); -end - -tic; +output_dir = cat(2, output_dir, '/'); numTogether = 10; @@ -29,7 +24,7 @@ for i=3:numTogether:numel(dbSeqDir) command = [executable ' -fx 535 -fy 536 -cx 327 -cy 241 -no2Dfp -no3Dfp -noMparams -noAUs -noGaze ']; - command = cat(2, command, [' -root ' '"' rootDir '/"']); + command = cat(2, command, [' -inroot ' '"' rootDir '/"']); % deal with edge cases if(numTogether + i > numel(dbSeqDir)) @@ -39,9 +34,9 @@ for i=3:numTogether:numel(dbSeqDir) for n=0:numTogether-1 inputFile = [ictDir dbSeqDir(i+n).name '/colour undist.avi']; - outputFile = [outputDir dbSeqDir(i+n).name '.txt']; + outputFile = [output_dir dbSeqDir(i+n).name '.txt']; - command = cat(2, command, [' -f "' inputFile '" -op "' outputFile '" ']); + command = cat(2, command, [' -f "' inputFile '" -of "' outputFile '" ']); if(depth) dDir = [ictDir dbSeqDir(i+n).name '/depthAligned/']; @@ -49,7 +44,7 @@ for i=3:numTogether:numel(dbSeqDir) end if(verbose) - outputVideo = [outputDir dbSeqDir(i+n).name '.avi']; + outputVideo = [output_dir dbSeqDir(i+n).name '.avi']; command = cat(2, command, [' -ov "' outputVideo '"']); end end @@ -58,12 +53,13 @@ for i=3:numTogether:numel(dbSeqDir) command = cat(2, command, [' -mloc "', varargin{find(strcmp('model', varargin))+1}, '"']); end - dos(command); + if(isunix) + unix(command, '-echo') + else + dos(command); + end end -timeTaken = toc; -fps = 10661 / timeTaken; -resDir = outputDir; end diff --git a/matlab_runners/Readme.txt b/matlab_runners/Readme.txt index a724b14c..aaf745c8 100644 --- a/matlab_runners/Readme.txt +++ b/matlab_runners/Readme.txt @@ -1,6 +1,6 @@ --------------------------------------- OpenFace Matlab runners ----------------------------------------------------------------------------- -These are provided for recreation of some of the experiments described in the publications and to demonstrate the command line interface by calling the C++ executables from matlab. This is intended to test the Windows version of the code. +These are provided for recreation of some of the experiments described in the publications and to demonstrate the command line interface by calling the C++ executables from Matlab. ======================== Demos ================================== diff --git a/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_afw.mat b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_afw.mat new file mode 100644 index 00000000..5fb35fbe Binary files /dev/null and b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_afw.mat differ diff --git a/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_helen_testset.mat b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_helen_testset.mat new file mode 100644 index 00000000..de7a22b4 Binary files /dev/null and b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_helen_testset.mat differ diff --git a/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_helen_trainset.mat b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_helen_trainset.mat new file mode 100644 index 00000000..f5e31210 Binary files /dev/null and b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_helen_trainset.mat differ diff --git a/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_ibug.mat b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_ibug.mat new file mode 100644 index 00000000..84ff0a0d Binary files /dev/null and b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_ibug.mat differ diff --git a/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_lfpw_testset.mat b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_lfpw_testset.mat new file mode 100644 index 00000000..c7fa32fb Binary files /dev/null and b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_lfpw_testset.mat differ diff --git a/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_lfpw_trainset.mat b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_lfpw_trainset.mat new file mode 100644 index 00000000..66fc591e Binary files /dev/null and b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_lfpw_trainset.mat differ diff --git a/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_xm2vts.mat b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_xm2vts.mat new file mode 100644 index 00000000..b1e3021f Binary files /dev/null and b/matlab_version/bounding_box_mapping/Bounding Boxes/bounding_boxes_xm2vts.mat differ