diff --git a/cmake/OpenCVFindMatlab.cmake b/cmake/OpenCVFindMatlab.cmake index 2a3fd173f9..552e173d84 100644 --- a/cmake/OpenCVFindMatlab.cmake +++ b/cmake/OpenCVFindMatlab.cmake @@ -24,13 +24,26 @@ # # cmake -DMATLAB_ROOT_DIR='/PATH/TO/ROOT_DIR' .. - +# ----- set_library_presuffix ----- +function(set_libarch_prefix_suffix) + if (UNIX AND NOT APPLE) + set(CMAKE_FIND_LIBRARY_PREFIXES "lib" PARENT_SCOPE) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".so" ".a" PARENT_SCOPE) + elseif (APPLE) + set(CMAKE_FIND_LIBRARY_PREFIXES "lib" PARENT_SCOPE) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".a" PARENT_SCOPE) + elseif (WIN32) + set(CMAKE_FIND_LIBRARY_PREFIXES "lib" PARENT_SCOPE) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib" ".dll" PARENT_SCOPE) + endif() +endfunction() # ----- locate_matlab_root ----- # # Attempt to find the path to the Matlab installation. If successful, sets # the absolute path in the variable MATLAB_ROOT_DIR function(locate_matlab_root) + # --- LINUX --- if (UNIX AND NOT APPLE) # possible root locations, in order of likelihood @@ -64,18 +77,47 @@ function(locate_matlab_root) # --- WINDOWS --- elseif (WIN32) - # query the registry + # search the path to see if Matlab exists there + # fingers crossed it is, otherwise we have to start hunting through the registry :/ + string(REGEX REPLACE ".*[;=](.*MATLAB[^;]*)\\\\bin.*" "\\1" MATLAB_ROOT_DIR_ "$ENV{PATH}") + if (MATLAB_ROOT_DIR_) + set(MATLAB_ROOT_DIR ${MATLAB_ROOT_DIR_} PARENT_SCOPE) + return() + endif() + + + # determine the Matlab version + set(REG_EXTENSION_ "SOFTWARE\\Mathworks\\MATLAB") set(REG_ROOTS_ "HKEY_LOCAL_MACHINE" "HKEY_CURRENT_USER") - foreach(REG_ROOT_ REG_ROOTS_) - execute_process(COMMAND reg query ${REG_ROOT_}\\SOFTWARE\\MathWorks\\MATLAB /f * /k OUTPUT_VARIABLE VERSIONS_) - if (VERSIONS_) + foreach(REG_ROOT_ ${REG_ROOTS_}) + execute_process(COMMAND reg query "${REG_ROOT_}\\${REG_EXTENSION_}" + OUTPUT_VARIABLE QUERY_RESPONSE_ + ) + if (QUERY_RESPONSE) + endif() + endforeach() + set(QUERY_PATH_ ${REG_ROOT_}\\SOFTWARE) + set(QUERY_PATH_REGEX_ "${REG_ROOT_}\\\\SOFTWARE\\\\Mathworks\\\\MATLAB\\\\([\\.0-9]+)") + message(${QUERY_PATH_}) + execute_process(COMMAND reg query ${QUERY_PATH_} OUTPUT_VARIABLE QUERY_RESPONSE_ + ERROR_VARIABLE ERROR_VAR_) + message("Error: ${ERROR_VAR_}") + message("Response: ${QUERY_RESPONSE_}") + if (QUERY_RESPONSE_) + string(REGEX MATCHALL ${QUERY_PATH_REGEX_} QUERY_MATCHES_ ${QUERY_RESPONSE_}) + foreach(QUERY_MATCH_ ${QUERY_MATCHES_}) + string(REGEX REPLACE ${QUERY_PATH_REGEX_} "\\1" QUERY_MATCH_ ${QUERY_MATCH_}) + list(APPEND VERSIONS_ ${QUERY_MATCH_}) + endforeach() + message(${VERSIONS_}) # sort in order from highest to lowest list(SORT VERSIONS_) list(REVERSE VERSIONS_) list(GET VERSIONS_ 0 VERSION_) - get_filename_component(MATLAB_ROOT_DIR_ [${REG_ROOT_}\\SOFTWARE\\MathWorks\\MATLAB\\${VERSION_};Install_Dir] ABSOLUTE PATH) + get_filename_component(MATLAB_ROOT_DIR_ [HKEY_LOCAL_MACHINE\\SOFTWARE\\Mathworks\\MATLAB] ABSOLUTE CACHE) + message(${MATLAB_ROOT_DIR_}) if (MATLAB_ROOT_DIR_) - break() + #break() endif() endif() endforeach() @@ -105,14 +147,22 @@ function(locate_matlab_components MATLAB_ROOT_DIR) if (NOT MATLAB_MEXEXT_) return() endif() - string(STRIP ${MATLAB_MEXEXT_} MATLAB_MEXEXT_) - string(REPLACE "mex" "" MATLAB_ARCH_ ${MATLAB_MEXEXT_}) + + # map the mexext to an architecture extension + set(ARCHITECTURES_ "maci64" "maci" "glnxa64" "glnx64" "sol64" "sola64" "win32" "win64" ) + foreach(ARCHITECTURE_ ${ARCHITECTURES_}) + if(EXISTS ${MATLAB_ROOT_DIR}/bin/${ARCHITECTURE_}) + set(MATLAB_ARCH_ ${ARCHITECTURE_}) + break() + endif() + endforeach() # get the path to the libraries set(MATLAB_LIBRARY_DIR_ ${MATLAB_ROOT_DIR}/bin/${MATLAB_ARCH_}) - + # get the libraries + set_libarch_prefix_suffix() find_library(MATLAB_LIB_MX_ mx PATHS ${MATLAB_LIBRARY_DIR_} NO_DEFAULT_PATH) find_library(MATLAB_LIB_MEX_ mex PATHS ${MATLAB_LIBRARY_DIR_} NO_DEFAULT_PATH) find_library(MATLAB_LIB_MAT_ mat PATHS ${MATLAB_LIBRARY_DIR_} NO_DEFAULT_PATH) diff --git a/modules/matlab/CMakeLists.txt b/modules/matlab/CMakeLists.txt index ae873a73f1..0465b036de 100644 --- a/modules/matlab/CMakeLists.txt +++ b/modules/matlab/CMakeLists.txt @@ -67,6 +67,8 @@ endif() if (BUILD_TESTS) add_subdirectory(test) endif() +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +add_subdirectory(io) # ---------------------------------------------------------------------------- # Configure time components @@ -84,6 +86,9 @@ foreach(opencv_module ${MATLAB_DEPS}) endif() endforeach() +# add extra headers by hand +list(APPEND opencv_hdrs "${OPENCV_MODULE_opencv_core_LOCATION}/include/opencv2/core/base.hpp") + # Configure checks # Check to see whether the generator and the mex compiler are working. # The checks currently test: diff --git a/modules/matlab/generator/gen_matlab.py b/modules/matlab/generator/gen_matlab.py index 34b306cb61..b74fdc9374 100644 --- a/modules/matlab/generator/gen_matlab.py +++ b/modules/matlab/generator/gen_matlab.py @@ -9,8 +9,12 @@ class MatlabWrapperGenerator(object): ns = {} for file in input_files: # get the file name - name = os.path.splitext(os.path.basename(file))[0] - ns[name] = parser.parse(file) + name = re.findall('include/opencv2/([^./]+)', file)[0] + #name = os.path.splitext(os.path.basename(file))[0] + try: + ns[name] = ns[name] + parser.parse(file) + except KeyError: + ns[name] = parser.parse(file) # cleanify the parser output parse_tree = ParseTree() diff --git a/modules/matlab/io/MatlabIO.cpp b/modules/matlab/io/MatlabIO.cpp index 3f61d940e3..0f4c936d29 100644 --- a/modules/matlab/io/MatlabIO.cpp +++ b/modules/matlab/io/MatlabIO.cpp @@ -1,11 +1,116 @@ #include #include +using namespace std; +using namespace cv; const char* day[] = { "Sun", "Mon", "Tue", "Wed", "Thurs", "Fri", "Sat" }; const char* month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; -const char* arch = "${MEX_ARCH}" +const char* arch = "${MEX_ARCH}"; -std::string formatCurrentTime() { +// ---------------------------------------------------------------------------- +// BASIC OPERATIONS +// ---------------------------------------------------------------------------- +string MatlabIO::filename(void) const { return filename_; } +bool open(const string& filename, ios_base::openmode mode); + filename_ = filename; + stream_.open(filename, mode); +} + +bool isOpen() const { return stream_.valid(); } +void close() { stream_.close(); } + +std::ifstream::pos_type filesize() { + std::ifstream::pos_type current = stream_.tellg(); + stream_.seekg(0, std::ifstream::end); + std::ifstream::pos_type end = stream_.tellg(); + stream_.seekg(current, std::ifstream::beg); + return end; +} + +void pushStreamPosition() { + stream_pos_ = stream_.tellg(); +} + +void popStreamPosition() { + stream_.seekg(stream_pos_, std::ifstream::beg); +} + +void setStreamPosition(std::ifstream::pos_type position) { + stream_.seekg(position, std::ifstream::beg); +} + +// ---------------------------------------------------------------------------- +// HEADERS +// ---------------------------------------------------------------------------- + + +void getFileHeader() { + // store the current stream position + pushStreamPosition(); + setStreamPosition(0); + stream_.read(const_cast(header_.data()), sizeof(char)*HEADER_LENGTH); + stream_.read(const_cast(subsys_.data()), sizeof(char)*SUBSYS_LENGTH); + stream_.read((char *)&version_, sizeof(int16_t)); + stream_.read(const_cast(endian_.data()), sizeof(char)*ENDIAN_LENGTH); + + // get the actual version + if (version_ == 0x0100) version_ = Matlab::IO::Version5; + if (version_ == 0x0200) version_ = Matlab::IO::Version73; + + // get the endianness + if (endian_.compare("IM") == 0) byte_swap_ = false; + if (endian_.compare("MI") == 0) byte_swap_ = true; + + // restore the current stream position + popStreamPosition(); +} + +// ---------------------------------------------------------------------------- +// INDEXING OPERATIONS +// ---------------------------------------------------------------------------- +void MatlabIO::indexNode(const MappingIndex& current) { + +} + + +void MatlabIO::indexNode(const SequenceIndex& current) { + +} + + +void MatlabIO::index() { + // if there is no open file, do nothing + if (!isOpen()) return; + + // read the global header + getFileHeader(); + + // change the endianness if need be + + // manually index the top-level node + MappingIndex root(filename_, vector(), stream_.tellg(), filesize(), 0, 0, stream_); + indexNode(root); + index_ = root; +} + + +// ---------------------------------------------------------------------------- +// FORMATTING / PRINTING +// ---------------------------------------------------------------------------- +template +string delimitedStringFromIterable(const Iterable& obj, const string& delimiter=string(" ")) { + string cache = ""; + ostringstream oss; + for (Iterable::iterator it = obj.begin(); it != obj.end(); ++it) { + // flush the cache and insert the next element + oss << cache << *it; + cache = delimiter; + } + return oss.str(); +} + + +string formatCurrentTime() { ostringstream oss; time_t rawtime; struct tm* timeinfo; @@ -24,9 +129,42 @@ std::string formatCurrentTime() { return oss.str(); } -void MatlabIO::whos() { - std::cout << "-------------------- whos --------------------" << std::endl; - std::cout << "Filename: " << filename() << std::endl; - std::cout << "File size: " << filesize() << "MB" << std::endl << std::endl; - std::cout << "Name size bytes type" << std::endl; - std::cout << "----------------------------------------------" << std::endl; + +void MatlabIO::printRootIndex() const { + cout << "--------------- top level file index ------------------" << endl; + cout << "Filename: " << filename() << endl; + cout << "File size: " << filesize() << "MB" << endl << endl; + cout << "Name size bytes type" << endl; + for (Map::iterator it = index_.mapping.begin(); it != index_.mapping.end(); ++it) { + cout << it->name << " "; + cout << delimitedStringFromIterable(it->size, "x") << " "; + cout << it->end - it->begin << " "; + cout << endl; + } + cout << "-------------------------------------------------------" << endl; +} + + +void printIndex(const Index& index, const int indentation) { + cout << string(2*indentation - 1, ' ') << "|" << endl; + cout << string(2*indentation - 1, ' ') << "|-- "; + cout << index.name << " (" << delimitedStringFromIterable(index.size, "x") << ")" << endl; + if (index.leaf) return; + if (index.associative) { + for (Map::iterator it = index.mapping.begin(); it != index.mapping.end(); ++it) { + printIndex(it->second, indentation+1); + } + } else { + for (vector::iterator it = index.sequence.begin(); it != index.sequence.end(); ++it) { + printIndex(it->second, indentation+1); + } + } +} + + +void MatlabIO::printFullIndex() const { + int indentation = 0; + cout << "----------------- full file index ---------------------" << endl; + printIndex(index_, indentation); + cout << "-------------------------------------------------------" << endl; +} diff --git a/modules/matlab/io/MatlabIO.hpp b/modules/matlab/io/MatlabIO.hpp index 9723bfc86a..1a452d3654 100644 --- a/modules/matlab/io/MatlabIO.hpp +++ b/modules/matlab/io/MatlabIO.hpp @@ -1,19 +1,39 @@ #ifndef MATLAB_IO_HPP_ #define MATLAB_IO_HPP_ +#include +#include +#include #include +#include +#include "primitives.hpp" +#include "bridge.hpp" +#include "mxarray.hpp" #include "map.hpp" namespace Matlab { namespace IO { - static const int VERSION_5 = 5; - static const int VERSION_73 = 73; + class RandomAccessRead {}; + class SequentialWrite {}; + static const int Version5 = 5; + static const int Version73 = 73; } -class Index { -private: +// predeclarations +class IONode; +class IONodeIterator; +class MatlabIO; + + +// ---------------------------------------------------------------------------- +// FILE AS A DATA STRUCTURE +// ---------------------------------------------------------------------------- +class IONode { +protected: //! the name of the field (if associative container) std::string name_; + //! the size of the field + std::vector size_; //! beginning of the data field in the file size_t begin_; //! address after the last data field @@ -26,36 +46,126 @@ private: bool compressed_; //! are the descendents associative (mappings) bool associative_; - //! the descendents of this node - union { - //! valid if the container is a sequence (list) - std::vector sequence_; - //! valid if the container is a mapping (associative) - Map mapping_; - }; + //! is this a leaf node containing data, or an interior node + bool leaf_; + //! the data stream from which the file was indexed + cv::Ptr stream_; + //! valid if the container is a sequence (list) + std::vector sequence_; + //! valid if the container is a mapping (associative) + Map mapping_; + IONode(const std::string& name, const std::Vector& size, size_t begin, size_t end, + int stored_type, int type, bool compressed, bool associative, bool leaf, istream& stream) : + name_(name), size_(size), begin_(begin), end_(end), stored_type_(stored_type), type_(type), + compressed_(compressed), associative_(associative), leaf_(leaf), stream_(stream) {} +public: + std::string name() const { return name_; } + std::vector size() const { return size_; } + size_t begin() const { return begin_; } + size_t end() const { return end_; } + int stored_type() const { return stored_type_; } + int type() const { return type_; } + bool compressed() const { return compressed_; } + bool associative() const { return associative_; } + bool leaf() const { return leaf_; } + IONode() : begin_(0), end_(0), stored_type_(0), type_(0), leaf_(true) {} + +#if __cplusplus >= 201103L + // conversion operators + template void operator=(const T& obj) { static_assert(0, "Unimplemented specialization for given type"); } + template operator T() { static_assert(0, "Unimplemented specialization for given type"); } +#else + // conversion operators + template void operator=(const T& obj) { T::unimplemented_specialization; } + template operator T() { T::unimplemented_specialization; } +#endif + + void swap(const IONode& other) { + using std::swap; + swap(name_, other.name_); + swap(size_, other.size_); + swap(begin_, other.begin_); + swap(end_, other.end_); + swap(stored_type_, other.stored_type_); + swap(type_, other.type_); + swap(compressed_, other.compressed_); + swap(associative_, other.associative_); + swap(leaf_, other.leaf_); + swap(stream_, other.stream_); + swap(sequence_, other.sequence_); + swap(mapping_, other.mapping_); + } +}; + +class SequenceIONode : public IONode { +public: + std::vector& sequence() { return sequence_; } + SequenceIONode(const std::string& name, const std::Vector& size, size_t begin, size_t end, + int stored_type, int type, const std::istream& stream) : + IONode(name, size, begin, end, stored_type, type, false, false, false, stream) {} +}; + +class MappingIONode : public IONode { +public: + Map& mapping() { return mapping_; } + MappingIONode(const std::string& name, const std::Vector& size, size_t begin, size_t end, + int stored_type, int type, const std::istream& stream) : + IONode(name, size, begin, end, stored_type, type, false, true, false, stream) {} +}; +class LeafIONode : public IONode { + LeafIONode(const std::string& name, const std::Vector& size, size_t begin, size_t end, + int stored_type, int type, const std::istream& stream) : + IONode(name, size, begin, end, stored_type, type, false, false, true, stream) {} }; -class MatlabIONode { +class CompressedIONode : public IONode { +private: + std::istringstream uncompressed_stream_; + std::vector data_; +public: + CompressedIONode(const std::string& name, const std::Vector& size, size_t begin, size_t end, + int stored_type, int type, const std::stream& stream) : + IONode(name, size, begin, end, stored_type, type, true, false, false, stream) {} +}; + +class Header : public IONode { + Header(const std::string& name, const std::Vector& size, size_t begin, size_t end, + int stored_type, int type, const std::stream& stream) : + IONode(name, size, begin, end, stored_type, type, true, false, false, stream) {} + + + + +// ---------------------------------------------------------------------------- +// FILE NODE +// ---------------------------------------------------------------------------- +class IONodeIterator : public std::iterator { }; + + +// ---------------------------------------------------------------------------- +// MATLABIO +// ---------------------------------------------------------------------------- class MatlabIO { private: // member variables static const int HEADER_LENGTH = 116; static const int SUBSYS_LENGTH = 8; static const int ENDIAN_LENGTH = 2; - char header_[HEADER_LENGTH+1]; - char subsys_[SUBSYS_LENGTH+1]; - char endian_[ENDIAN_LENGTH+1]; + std::string header_; + std::string subsys_; + std::string endian_; int version_; bool byte_swap_; std::string filename_; // uses a custom stream buffer for fast memory-mapped access and endian swapping std::fstream stream_; + std::ifstream::pos_type stream_pos_; //! the main file index. The top-level index must be associative - Index index_; + IONode index_; // internal methods void getFileHeader(); @@ -64,18 +174,31 @@ private: void getHeader(); void setHeader(); + CompressedIONode uncompress(const IONode& node); + public: // construct/destruct - MatlabIO() {} + MatlabIO() : header_(HEADER_LENGTH+1, '\0'), subsys_(SUBSYS_LENGTH+1, '\0'), + endian_(ENDIAN_LENGTH+1, '\0'), byte_swap(false), stream_pos_(0) {} ~MatlabIO {} // global read and write routines std::string filename(void); - bool open(const std::string& filename, const std::string& mode); + bool open(const std::string& filename, std::ios_base::openmode mode); + bool isOpen() const; + void close(); + void clear(); // index the contents of the file void index(); // print all of the top-level variables in the file -} + void printRootIndex() const; + void printFullIndex() const; + + // FileNode operations + IONode root() const; + IONode operator[](const String& nodename) const; +}; + #endif diff --git a/modules/matlab/test/OpenCVTest.m b/modules/matlab/test/OpenCVTest.m index 0f01b68db0..d1ab228f4d 100644 --- a/modules/matlab/test/OpenCVTest.m +++ b/modules/matlab/test/OpenCVTest.m @@ -64,6 +64,33 @@ classdef OpenCVTest < matlab.unittest.TestCase testcase.verifyLessThan(norm(mt1 - mt2), 1e-8, 'Too much precision lost in tranposition'); end + % multiple return + function multipleReturn(testcase) + A = randn(10); + A = A'*A; + [V1, D1] = eig(A); D1 = diag(D1); + [~, D2, V2] = cv.eigen(A); + testcase.verifyLessThan(norm(V1 - V2), 1e-6, 'Too much precision lost in eigenvectors'); + testcase.verifyLessThan(norm(D1 - D2), 1e-6, 'Too much precision lost in eigenvalues'); + end + + % complex output from SVD + function complexOutputSVD(testcase) + A = randn(10); + [V1, D1] = eig(A); + [~, D2, V2] = cv.eigen(A); + testcase.verifyTrue(~isreal(V2) && size(V2,3) == 1, 'Output should be complex'); + testcase.verifyLessThan(norm(V1 - V2), 1e-6, 'Too much precision lost in eigenvectors'); + end + + % complex output from Fourier Transform + function complexOutputFFT(testcase) + A = randn(10); + F1 = fft2(A); + F2 = cv.dft(A, cv.DFT_COMPLEX_OUTPUT); + testcase.verifyTrue(~isreal(F2) && size(F2,3) == 1, 'Output should be complex'); + testcase.verifyLessThan(norm(F1 - F2), 1e-6, 'Too much precision lost in eigenvectors'); + end % ------------------------------------------------------------------------- % TYPE CASTS