diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 471a7a1..d2e9551 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@v2 - + - name: Setup Ubuntu if: matrix.os == 'ubuntu-20.04' run: | @@ -52,8 +52,8 @@ jobs: # access regardless of the host operating system shell: bash working-directory: ${{runner.workspace}}/build - # Note the current convention is to use the -S and -B options here to specify source - # and build directories, but this is only available with CMake 3.13 and higher. + # Note the current convention is to use the -S and -B options here to specify source + # and build directories, but this is only available with CMake 3.13 and higher. # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DRT_BUILD_TESTS=ON @@ -66,6 +66,6 @@ jobs: - name: Test working-directory: ${{runner.workspace}}/build shell: bash - # Execute tests defined by the CMake configuration. + # Execute tests defined by the CMake configuration. # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest -V -C $BUILD_TYPE diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index 8762e2e..b6e3604 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -66,6 +66,12 @@ target_link_libraries(rt_retexture_mesh Boost::program_options ) +add_executable(rt_pherc118_pipeline src/PHerc118Pezzo4Pipeline.cpp) +target_link_libraries(rt_pherc118_pipeline + rt::core + rt::graph + smgl::smgl +) if(VC_FOUND) add_executable(rt_dewarp src/TextureDewarp.cpp) diff --git a/apps/src/PHerc118Pezzo4Pipeline.cpp b/apps/src/PHerc118Pezzo4Pipeline.cpp new file mode 100644 index 0000000..ce4e32d --- /dev/null +++ b/apps/src/PHerc118Pezzo4Pipeline.cpp @@ -0,0 +1,276 @@ +#include +#include + +#include "rt/filesystem.hpp" +#include "rt/graph/Nodes.hpp" + +using namespace rt; +using namespace rt::graph; +using namespace smgl; + +namespace fs = rt::filesystem; + +smgl::GraphStyle style; + +struct SubgraphOutput { + OutputPort* inputImage{nullptr}; + OutputPort* outputImage{nullptr}; + OutputPort* transform{nullptr}; +}; + +// Warning: SubgraphOutput is only valid for the lifetime of the passed Graph +static SubgraphOutput AddImageRegSubgraph( + Graph& graph, + const fs::path& movingPath, + OutputPort& fixedImage, + const fs::path& ldmsPath = "", + bool runBspline = true, + bool runDeformable = true, + bool genResample = false) +{ + // Setup the output struct + SubgraphOutput result; + + // Read image + auto readIm = graph.insertNode(); + readIm->path = movingPath; + result.inputImage = &readIm->image; + style.setRankSource(readIm); + + // Detect/Load landmarks + Node::Pointer ldmNode; + if (ldmsPath.empty()) { + auto detLdm = graph.insertNode(); + detLdm->fixedImage = fixedImage; + detLdm->movingImage = readIm->image; + ldmNode = detLdm; + } else { + auto loadLdm = graph.insertNode(); + loadLdm->path = ldmsPath; + ldmNode = loadLdm; + } + + // Calculate affine transform + auto affLdm = graph.insertNode(); + affLdm->fixedLandmarks = ldmNode->getOutputPort("fixedLandmarks"); + affLdm->movingLandmarks = ldmNode->getOutputPort("movingLandmarks"); + result.transform = &affLdm->transform; + + if (runBspline) { + // Update the landmark positions + auto updateLdm = graph.insertNode(); + updateLdm->transform = affLdm->transform; + updateLdm->landmarksIn = ldmNode->getOutputPort("movingLandmarks"); + + // Calculate bspline warping + auto bspLdm = graph.insertNode(); + bspLdm->fixedImage = fixedImage; + bspLdm->fixedLandmarks = ldmNode->getOutputPort("fixedLandmarks"); + bspLdm->movingLandmarks = updateLdm->landmarksOut; + + // Combine the landmark transforms + auto ldmTfm = graph.insertNode(); + ldmTfm->first = affLdm->transform; + ldmTfm->second = bspLdm->transform; + result.transform = &ldmTfm->result; + } + + // Run deformable as requested + if (runDeformable) { + // Resample the input for deformable + auto resIm1 = graph.insertNode(); + resIm1->fixedImage = fixedImage; + resIm1->movingImage = readIm->image; + resIm1->transform = *result.transform; + + // Run deformable + auto defReg = graph.insertNode(); + defReg->fixedImage = fixedImage; + defReg->movingImage = resIm1->resampledImage; + defReg->iterations = 25; + + // Final transform + auto allTfms = graph.insertNode(); + allTfms->first = *result.transform; + allTfms->second = defReg->transform; + result.transform = &allTfms->result; + } + + // Resample the output as requested + if (genResample) { + auto resIm2 = graph.insertNode(); + resIm2->fixedImage = fixedImage; + resIm2->movingImage = readIm->image; + resIm2->transform = *result.transform; + result.outputImage = &resIm2->resampledImage; + } + + return result; +} + +static void WriteRetextureMeshSubgraph( + Graph& graph, + const fs::path& path, + OutputPort& mesh, + OutputPort& uvMap, + OutputPort& texture) +{ + auto writer = graph.insertNode(); + writer->path = path; + writer->mesh = mesh; + writer->uvMap = uvMap; + writer->image = texture; + style.setRankSink(writer); +} + +int main() +{ + // Register node types for serialization + RegisterAllNodeTypes(); + + std::string project = "PHerc118-Pezzo4"; + bool useCache = true; + + // Setup graph + Graph graph; + graph.setEnableCache(useCache); + graph.setCacheFile("results/" + project + ".json"); + graph.setCacheType(CacheType::Subdirectory); + + // Clean cache for repeated runs + fs::remove_all(graph.cacheDir()); + + // Setup output dir + fs::path outDir = "results/" + project; + fs::create_directories(outDir); + + // Load mesh + auto meshRead = graph.insertNode(); + meshRead->path = "2017/PHerc118-Pezzo4-Artec4.obj"; + style.setRankSource(meshRead); + + // Reorder texture + auto reorder = graph.insertNode(); + reorder->meshIn = meshRead->mesh; + reorder->uvMapIn = meshRead->uvMap; + reorder->imageIn = meshRead->image; + reorder->sampleRate = 0.1; + + // Reg 1998 + auto result1998 = AddImageRegSubgraph( + graph, "1998/bodley_grclassb1_Pezzo04.tif", reorder->imageOut, + "1998/1998-to-2017-landmarks.ldm"); + auto write1998tfm = graph.insertNode(); + write1998tfm->path = "PHerc118-1998-to-Mesh2017.tfm"; + write1998tfm->transform = *result1998.transform; + + // Update the UV map to point to 1998 space + auto transformUV1998 = graph.insertNode(); + transformUV1998->uvMapIn = reorder->uvMapOut; + transformUV1998->fixedImage = reorder->imageOut; + transformUV1998->movingImage = *result1998.inputImage; + transformUV1998->transform = *result1998.transform; + + // Reg 2005 -> 1998 + auto result2005 = AddImageRegSubgraph( + graph, "2005/Bod05b-PHerc118c04-0950-40b.tif", *result1998.inputImage, + "2005/2005-to-1998-landmarks.ldm"); + auto write2005tfm = graph.insertNode(); + write2005tfm->path = "PHerc118-2005-to-1998.tfm"; + write2005tfm->transform = *result2005.transform; + + // Update the UV map to point to 2005 space + auto tfm2005ToReorder = graph.insertNode(); + tfm2005ToReorder->first = *result2005.transform; + tfm2005ToReorder->second = *result1998.transform; + auto write2005tfm2 = graph.insertNode(); + write2005tfm2->path = "PHerc118-2005-to-Mesh2017.tfm"; + write2005tfm2->transform = tfm2005ToReorder->result; + style.setRankSink(write1998tfm, write2005tfm, write2005tfm2); + + auto transformUV2005 = graph.insertNode(); + transformUV2005->uvMapIn = reorder->uvMapOut; + transformUV2005->fixedImage = reorder->imageOut; + transformUV2005->movingImage = *result2005.inputImage; + transformUV2005->transform = tfm2005ToReorder->result; + + // Load 2017 IR/RGB (preregistered to 1998) + auto readIR2017 = graph.insertNode(); + readIR2017->path = "2017/PHerc118-Pezzo4-IR950.png"; + auto readRGB2017 = graph.insertNode(); + readRGB2017->path = "2017/PHerc118-Pezzo4-RGB2020_8bpc.png"; + style.setRankSource(readIR2017, readRGB2017); + + // Register disegni to 1998 + fs::path disegniDir = "Disegni/Napolitani-individual"; + std::size_t cnt{0}; + for (const auto& e : fs::directory_iterator(disegniDir)) { + if (cnt == 2) { + break; + } + const auto& imgPath = e.path(); + if (not fs::is_regular_file(imgPath) or imgPath.extension() != ".png") { + continue; + } + + auto ldmPath = imgPath; + ldmPath.replace_extension("ldm"); + if (not fs::exists(ldmPath)) { + continue; + } + + auto r = AddImageRegSubgraph( + graph, imgPath, *result2005.inputImage, ldmPath, true, false, true); + auto writeDisegnitfm = graph.insertNode(); + writeDisegnitfm->path = imgPath.stem().string() + "-to-Mesh2017.tfm"; + writeDisegnitfm->transform = *r.transform; + + fs::create_directories(outDir / "Disegni-overlays-2005"); + auto outFile = imgPath.filename().replace_extension("tif"); + auto outputPath = outDir / "Disegni-overlays-2005" / outFile; + auto writer = graph.insertNode(); + writer->path = outputPath; + writer->image = *r.outputImage; + style.setRankSink(writeDisegnitfm, writer); + + cnt++; + } + + // Write meshes + std::string prefix = "PHerc118-Pezzo04-"; + WriteRetextureMeshSubgraph( + graph, outDir / (prefix + "1998.obj"), meshRead->mesh, + transformUV1998->uvMapOut, *result1998.inputImage); + WriteRetextureMeshSubgraph( + graph, outDir / (prefix + "2005.obj"), meshRead->mesh, + transformUV2005->uvMapOut, *result2005.inputImage); + WriteRetextureMeshSubgraph( + graph, outDir / (prefix + "2017-IR950.obj"), meshRead->mesh, + transformUV1998->uvMapOut, readIR2017->image); + WriteRetextureMeshSubgraph( + graph, outDir / (prefix + "2017-RGB.obj"), meshRead->mesh, + transformUV1998->uvMapOut, readRGB2017->image); + + // Setup style + style.defaultStyle().inputPorts.bgcolor = "#87BF73"; + style.defaultStyle().inputPorts.color = "#87BF73"; + style.defaultStyle().label.bgcolor = "#1897D4"; + style.defaultStyle().label.color = "#1897D4"; + style.defaultStyle().outputPorts.bgcolor = "#4C9D2F"; + style.defaultStyle().outputPorts.color = "#4C9D2F"; + style.defaultStyle().font.color = "white"; + + NodeStyle doStyle; + doStyle.label.bgcolor = "#0033A0"; + doStyle.label.color = "#0033A0"; + style.setClassStyle(doStyle); + style.setClassStyle(doStyle); + style.setClassStyle(doStyle); + style.setClassStyle(doStyle); + style.setClassStyle(doStyle); + + // Run the graph + smgl::WriteDotFile("results/" + project + "-graph.gv", graph, style); + graph.update(); + smgl::WriteDotFile("results/" + project + "-graph.gv", graph, style); +} \ No newline at end of file diff --git a/cmake/BuildSmeagol.cmake b/cmake/BuildSmeagol.cmake index 3db509c..ed29511 100644 --- a/cmake/BuildSmeagol.cmake +++ b/cmake/BuildSmeagol.cmake @@ -1,7 +1,7 @@ FetchContent_Declare( smeagol GIT_REPOSITORY https://gitlab.com/educelab/smeagol.git - GIT_TAG v0.8 + GIT_TAG dev CMAKE_CACHE_ARGS -DSMGL_BUILD_JSON:BOOL=ON -DSMGL_USE_BOOSTFS:BOOL=${RT_USE_BOOSTFS} diff --git a/cmake/FindFilesystem.cmake b/cmake/FindFilesystem.cmake index e71dd37..8e40e0a 100644 --- a/cmake/FindFilesystem.cmake +++ b/cmake/FindFilesystem.cmake @@ -110,9 +110,6 @@ cmake_push_check_state() set(CMAKE_REQUIRED_QUIET ${Filesystem_FIND_QUIETLY}) -# All of our tests required C++17 or later -set(CMAKE_CXX_STANDARD 17) - # Normalize and check the component list we were given set(want_components ${Filesystem_FIND_COMPONENTS}) if(Filesystem_FIND_COMPONENTS STREQUAL "") @@ -185,6 +182,7 @@ if(CXX_FILESYSTEM_HAVE_FS) ]] code @ONLY) # Try to compile a simple filesystem program without any linker flags + set(CMAKE_REQUIRED_FLAGS -std=c++17) check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED) set(can_link ${CXX_FILESYSTEM_NO_LINK_NEEDED})