source/render/GenerateCameraOverlaps.cpp (123 lines of code) (raw):

/** * Copyright 2004-present Facebook. All Rights Reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #include <gflags/gflags.h> #include <glog/logging.h> #include <folly/Format.h> #include "source/util/Camera.h" #include "source/util/ImageUtil.h" #include "source/util/SystemUtil.h" #include "source/util/ThreadPool.h" using namespace fb360_dep; using namespace fb360_dep::image_util; const std::string kUsageMessage = R"( - Generates a series of images of the rig cameras projected into destination cameras over a series of fixed depths. - Example: ./GenerateCameraOverlaps \ --frame=000000 \ --output=/path/to/output \ --rig=/path/to/rigs/rig.json \ --color=/path/to/video/color A typical extension of this is creating a video over the series of depth generated, i.e.: ffmpeg -framerate 10 -pattern_type glob \ -i '/path/to/output/overlaps/cam0/*.png' -c:v libx264 -pix_fmt yuv420p \ -vf "scale=trunc(iw/2)*2:trunc(ih/2)*2" /path/to/output/overlaps/cam0.mp4 -y )"; DEFINE_string(cameras, "", "cameras to render (comma-separated)"); DEFINE_string(color, "", "path to input color images (required)"); DEFINE_string(frame, "000000", "frame to process (lexical)"); DEFINE_uint64(max_depth_m, 10, "max depth in cm"); DEFINE_uint64(min_depth_m, 1, "min depth in cm"); DEFINE_uint64(num_depths, 50, "num depths"); DEFINE_string(output, "", "path to output directory (required)"); DEFINE_string(rig, "", "path to camera rig .json (required)"); DEFINE_double(scale, 0.5, "image scale factor"); using PixelType = cv::Vec4f; using Image = cv::Mat_<PixelType>; Image projectSrcsToDst( const Camera& camDst, const Camera::Rig& rigSrc, const std::vector<Image>& imagesSrc, const float disparity) { Image colorDst(camDst.resolution.y(), camDst.resolution.x(), cv::Scalar(0)); for (int y = 0; y < colorDst.rows; ++y) { for (int x = 0; x < colorDst.cols; ++x) { Camera::Vector2 dstPixel = {x + 0.5, y + 0.5}; if (camDst.isOutsideImageCircle(dstPixel)) { colorDst(y, x) = 0.0f; continue; } const Camera::Vector3 world = camDst.rig(dstPixel, 1.0f / disparity); int count = 0; PixelType sum = cv_util::createBGRA<PixelType>(0, 0, 0, 0); for (int iSrc = 0; iSrc < int(rigSrc.size()); ++iSrc) { Camera::Vector2 srcPixel; if (rigSrc[iSrc].sees(world, srcPixel)) { sum += cv_util::getPixelBilinear(imagesSrc[iSrc], srcPixel.x(), srcPixel.y()); ++count; } } colorDst(y, x) = 1.0f / count * sum; } } return colorDst; } void dumpOverlaps( const Camera::Rig& rigSrc, const Camera::Rig& rigDst, const std::vector<Image>& imagesSrc, const int numDisps, const float minDisparity, const float maxDisparity, const filesystem::path& outputDir) { ThreadPool threadPool; for (const Camera& camDst : rigDst) { filesystem::create_directories(outputDir / camDst.id); } // Loop through disparities for (int d = 0; d < numDisps; ++d) { LOG(INFO) << folly::sformat("Depth {} of {}...", (d + 1), numDisps); threadPool.spawn([&, d] { const float disparity = probeDisparity(d, numDisps, minDisparity, maxDisparity); for (const Camera& camDst : rigDst) { const Image colorDst = projectSrcsToDst(camDst, rigSrc, imagesSrc, disparity); // Add text to image showing current depth const float depth = 1.0f / disparity; const float depthCm = depth * 100; const std::string depthStr = std::to_string(int(depthCm)); const cv::Point2f textPos( (80.0f / 100.0f) * colorDst.cols, (6.0f / 100.0f) * colorDst.rows); const int textFont = cv::FONT_HERSHEY_PLAIN; const double textScale = 2; const cv::Scalar textColor(0, 1, 0, 1); // green cv::putText(colorDst, depthStr + " cm", textPos, textFont, textScale, textColor); // Pad filename with zeros so they are saved in lexicographical order const std::string filename = folly::sformat("{}/{}/{:05}_cm.png", outputDir.string(), camDst.id, int(depthCm)); cv_util::imwriteExceptionOnFail(filename, 255.0f * colorDst); } }); } threadPool.join(); } int main(int argc, char* argv[]) { system_util::initDep(argc, argv, kUsageMessage); CHECK_NE(FLAGS_color, ""); CHECK_NE(FLAGS_rig, ""); CHECK_NE(FLAGS_output, ""); Camera::Rig rigSrc = Camera::loadRig(FLAGS_rig); for (Camera& src : rigSrc) { src = src.rescale(src.resolution * FLAGS_scale); } const Camera::Rig rigDst = filterDestinations(rigSrc, FLAGS_cameras); CHECK_GT(rigDst.size(), 0) << "no destinations!"; LOG(INFO) << "Loading images..."; const std::vector<Image> imagesSrc = loadScaledImages<PixelType>(FLAGS_color, rigSrc, FLAGS_frame, FLAGS_scale); CHECK_EQ(imagesSrc.size(), rigSrc.size()); const filesystem::path overlapsDir = filesystem::path(FLAGS_output) / "overlaps"; dumpOverlaps( rigSrc, rigDst, imagesSrc, FLAGS_num_depths, 1.0f / FLAGS_min_depth_m, 1.0f / FLAGS_max_depth_m, overlapsDir); return EXIT_SUCCESS; }