IsometricPatternMatcher/IsometricPattern.cpp (251 lines of code) (raw):

// Copyright (c) Facebook, Inc. and its affiliates. // // This source code is licensed under the MIT license found in the // LICENSE file in the root directory of this source tree. #include <IsometricPatternMatcher/Image.h> #include <IsometricPatternMatcher/IsometricPattern.h> #include <glog/logging.h> #include <pangolin/pangolin.h> #include <fstream> #include <random> namespace surreal_opensource { Eigen::MatrixXi IsometricGridDot::makeIsometricPattern(uint32_t seed) { Eigen::MatrixXi M(storageMapRows_, storageMapRows_); std::mt19937 rng(seed); std::uniform_int_distribution<uint32_t> uintDist1(0, 1); for (int r = 0; r < M.rows(); ++r) { int row = r - numberLayer_; for (int q = 0; q < M.cols(); ++q) { int col = q - numberLayer_ + (r - numberLayer_ - ((r - numberLayer_) & 1)) / 2; M(r, q) = (r + q < numberLayer_ || r + q > 3 * numberLayer_ || // upper and lower triangles of the matrix abs(row) > gridRowsCols_[0] / 2 || abs(col) > gridRowsCols_[1] / 2) // outside the specified rows and cols of the grid ? 2 : uintDist1(rng); } // upper and lower triangles of the matrix should be Null, incidated by 2 } return M; } std::array<Eigen::MatrixXi, 6> IsometricGridDot::makeIsometricPatternGroup( Eigen::MatrixXi pattern0) { std::array<Eigen::MatrixXi, 6> outputPatternGroup; outputPatternGroup[0] = pattern0; for (int i = 1; i < 6; ++i) { outputPatternGroup[i] = Rotate60Right(outputPatternGroup[i - 1]); } return outputPatternGroup; } Eigen::MatrixXi IsometricGridDot::Rotate60Right( Eigen::MatrixXi& inputPatternGrid) const { Eigen::MatrixXi outputPatternGrid = inputPatternGrid; const size_t numLayer = (inputPatternGrid.rows() - 1) / 2; for (int r = 0; r < inputPatternGrid.rows(); ++r) { for (int q = 0; q < inputPatternGrid.cols(); ++q) { if (r + q >= numLayer && r + q <= 3 * numLayer) outputPatternGrid(r, q) = inputPatternGrid(2 * numLayer - q, r + q - numLayer); } // upper and lower triangles of the matrix should be Null, incidated by 2 } return outputPatternGrid; } Eigen::Vector3i IsometricGridDot::rotateRight60ForDot( const Eigen::Vector3i& coord) { Eigen::Vector3i rotateCoord; rotateCoord.resize(3, 1); rotateCoord << -coord(2), -coord(0), -coord(1); // [x, y, z]->[-z, -x, -y] return rotateCoord; } Eigen::Vector3i IsometricGridDot::rotateLeft60ForDot( const Eigen::Vector3i& coord) { Eigen::Vector3i rotateCoord; rotateCoord.resize(3, 1); rotateCoord << -coord(1), -coord(2), -coord(0); // [x, y, z]->[-y, -z, -x] return rotateCoord; } void IsometricGridDot::Init() { // get patternPtsCodes and patternPts according to patternGroup_[0] patternPts_.resize(3, storageMapRows_ * storageMapRows_); patternPtsCodes_.resize(storageMapRows_ * storageMapRows_); double centerX = (gridRowsCols_[1] - 1) / 2 * horizontalSpacing_; // in meter double centerY = (gridRowsCols_[0] - 1) / 2 * verticalSpacing_; int centerIndx = numberLayer_; for (int r = 0; r < patternGroup_[0].rows(); ++r) { double y = centerY + (r - centerIndx) * verticalSpacing_; for (int c = 0; c < patternGroup_[0].cols(); ++c) { patternPtsCodes_[r * storageMapRows_ + c] = patternGroup_[0](r, c); double x = centerX + ((c - centerIndx) + (r - centerIndx) / 2.0) * horizontalSpacing_; patternPts_.col(r * storageMapRows_ + c) = Eigen::Vector3d(x, y, 0); } } } IsometricGridDot::IsometricGridDot(double verticalSpacing, double horizontalSpacing, double dotRadius, size_t numberLayer, const Eigen::Vector2i& gridRowsCols, uint32_t seed) : verticalSpacing_(verticalSpacing), horizontalSpacing_(horizontalSpacing), dotRadius_(dotRadius), numberLayer_(numberLayer), gridRowsCols_(gridRowsCols), storageMapRows_(numberLayer * 2 + 1) { // Create binary pattern (and rotated pattern) from seed patternGroup_ = makeIsometricPatternGroup(makeIsometricPattern(seed)); Init(); } IsometricGridDot::IsometricGridDot(const std::string& gridFile) { std::string ext = pangolin::FileLowercaseExtention(gridFile); if (ext == ".svg") LoadFromSVG(gridFile); else CHECK(false) << fmt::format( "Unsupported Grid FileType. Needs to be svg (grid file: {})", gridFile); } void IsometricGridDot::LoadFromSVG(const std::string& gridFile) { std::fstream svgFile; svgFile.open(gridFile, std::fstream::in); CHECK(svgFile.is_open()) << fmt::format( "Unable to open Target SVG file, '{}'", gridFile); std::string binaryPattern; // look for surreal parameters line std::string line; while (!svgFile.eof()) { getline(svgFile, line); if (IsXmlParamsString(line) == false) continue; bool success = ParseXmlParamsString(line, binaryPattern, numberLayer_, gridRowsCols_, horizontalSpacing_, verticalSpacing_, dotRadius_); CHECK(success) << fmt::format("Unable to parse grid file {}", gridFile.c_str()); break; } CHECK(!svgFile.eof()) << "Grid file {} does not contain target parameters.", gridFile.c_str(); storageMapRows_ = numberLayer_ * 2 + 1; Eigen::MatrixXi pattern0(1, storageMapRows_ * storageMapRows_); for (int i = 0; i < binaryPattern.length(); i++) { pattern0(0, i) = binaryPattern[i] - '0'; } pattern0.resize(storageMapRows_, storageMapRows_); patternGroup_ = makeIsometricPatternGroup(pattern0.adjoint()); Init(); } bool IsometricGridDot::ParseXmlParamsString( const std::string& s, std::string& binaryPattern, size_t& numberLayer, Eigen::Vector2i& gridRowsCols, double& horizontalSpacing, double& verticalSpacing, double& dotRadius) { if (!ParseOption(s, "-vertical-spacing ", verticalSpacing)) { LOG(ERROR) << "Xml comment in svg pattern file does not contain " "-vertical-spacing field."; return false; } if (!ParseOption(s, "-horizontal-spacing ", horizontalSpacing)) { LOG(ERROR) << "Xml comment in svg pattern file does not contain " "-horizontal-spacing field."; return false; } if (!ParseOption(s, "-layer-number ", numberLayer)) { LOG(ERROR) << "Xml comment in svg pattern file does not contain " "-layer-number field."; return false; } if (!ParseOption(s, "-rows ", gridRowsCols[0])) { LOG(ERROR) << "Xml comment in svg pattern file does not contain -rows field."; return false; } if (!ParseOption(s, "-cols ", gridRowsCols[1])) { LOG(ERROR) << "Xml comment in svg pattern file does not contain -cols field."; return false; } if (!ParseOption(s, "-grid-dotRadius ", dotRadius)) { LOG(ERROR) << "Xml comment in svg pattern file does not contain " "-grid-dotRadius field."; return false; } if (!ParseOption(s, "-grid-pattern ", binaryPattern)) { LOG(ERROR) << "Xml comment in svg pattern file does not contain " "-grid-pattern field."; return false; } if (std::ceil((numberLayer * 2 + 1) * (numberLayer * 2 + 1)) != binaryPattern.length()) { LOG(ERROR) << "Pattern string length does not agree with the specified grid size."; return false; } return true; } bool IsometricGridDot::IsXmlParamsString(std::string s) { return (s.find(ISOMETRIC_PATTERN_TRAILING_STRING) != std::string::npos); } template <typename T> bool IsometricGridDot::ParseOption(const std::string& s, const std::string& key, T& val) { size_t pos = s.find(key); if (pos == std::string::npos) { return false; } else { std::stringstream ss(s.substr(pos + key.length())); ss >> val; return true; } } void IsometricGridDot::SaveSVG( const std::string& filename, const std::string& color0, const std::string& color1, const std::string& bgcolor, int patternGroupIndex) // patternGroupIndex only for testing const { std::ofstream f(filename.c_str()); const Eigen::MatrixXi& M = patternGroup_[patternGroupIndex]; const double offsetInMm = horizontalSpacing_ * 1000; // meters to millimeters f << "<?xml version=\"1.0\" standalone=\"no\"?>" << std::endl << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" " "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << std::endl; f << ISOMETRIC_PATTERN_TRAILING_STRING << " -vertical-spacing " << verticalSpacing_ << " -horizontal-spacing " << horizontalSpacing_ << " -layer-number " << numberLayer_ << " -rows " << gridRowsCols_[0] << " -cols " << gridRowsCols_[1] << " -grid-dotRadius " << dotRadius_; f << " -grid-pattern "; for (int r = 0; r < M.rows(); ++r) { for (int q = 0; q < M.cols(); ++q) f << M(r, q); } f << std::endl << " -matrix " << std::endl << M; f << " -->" << std::endl; // units in mm const double canvasWidth = ((gridRowsCols_[1] - 1) * horizontalSpacing_ * 1000) + offsetInMm * 2.; const double canvasHeight = ((gridRowsCols_[0] - 1) * verticalSpacing_ * 1000) + offsetInMm * 2.; f << fmt::format(R"#(<svg width="{}mm" height="{}mm">)#", canvasWidth, canvasHeight); f << std::endl; f << fmt::format( R"#(<rect width="{}mm" height="{}mm" style="fill:{}" z-index="-1"/>)#", canvasWidth, canvasHeight, bgcolor); f << std::endl; for (int r = 0; r < M.rows(); ++r) { for (int q = 0; q < M.cols(); ++q) { if (M(r, q) == 0 || M(r, q) == 1) { std::string color = (M(r, q) == 1) ? color1 : color0; f << fmt::format( R"#(<circle cx="{}mm" cy="{}mm" r="{}mm" fill="{}" stroke-width="0"/>)#", patternPts_(0, r * M.cols() + q) * 1000 + offsetInMm, patternPts_(1, r * M.cols() + q) * 1000 + offsetInMm, dotRadius_ * 1000, // meters to millimeters color); f << std::endl; } } } f << "</svg>" << std::endl; } void IsometricGridDot::SaveSVGPairs(const std::string& svgForCode1, const std::string& svgForCode0, const std::string& dotcolor, const std::string& bgcolor, int patternGroupIndex) const { // svg pattern for code 1 SaveSVG(svgForCode1, bgcolor, dotcolor, bgcolor, patternGroupIndex); // svg pattern for code 0 SaveSVG(svgForCode0, dotcolor, bgcolor, bgcolor, patternGroupIndex); } } // namespace surreal_opensource