Skip to content
Open
28 changes: 18 additions & 10 deletions src/command-line/execution/executors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ namespace found {
CalibrationPipelineExecutor::CalibrationPipelineExecutor(CalibrationOptions &&options,
std::unique_ptr<CalibrationAlgorithm> calibrationAlgorithm)
: options_(std::move(options)) {
this->calibrationAlgorithm = std::move(calibrationAlgorithm);
this->pipeline_.Complete(*this->calibrationAlgorithm);
this->pipeline_.Complete(std::move(calibrationAlgorithm));
}

void CalibrationPipelineExecutor::ExecutePipeline() {
Expand Down Expand Up @@ -45,12 +44,22 @@ DistancePipelineExecutor::DistancePipelineExecutor(DistanceOptions &&options,
std::unique_ptr<DistanceDeterminationAlgorithm> distanceAlgorithm,
std::unique_ptr<VectorGenerationAlgorithm> vectorizationAlgorithm)
: options_(std::move(options)) {
this->edgeDetectionAlgorithm = std::move(edgeDetectionAlgorithm);
this->distanceAlgorithm = std::move(distanceAlgorithm);
this->vectorizationAlgorithm = std::move(vectorizationAlgorithm);
this->pipeline_.AddStage(*this->edgeDetectionAlgorithm)
.AddStage(*this->distanceAlgorithm)
.Complete(*this->vectorizationAlgorithm);
this->pipeline_.AddStage(std::move(edgeDetectionAlgorithm))
.AddStage(std::move(distanceAlgorithm))
.Complete(std::move(vectorizationAlgorithm));
}


DistancePipelineExecutor::DistancePipelineExecutor(DistanceOptions &&options,
std::unique_ptr<EdgeDetectionAlgorithm> edgeDetectionAlgorithm,
std::unique_ptr<EdgeFilteringAlgorithms> filters,
std::unique_ptr<DistanceDeterminationAlgorithm> distanceAlgorithm,
std::unique_ptr<VectorGenerationAlgorithm> vectorizationAlgorithm)
: options_(std::move(options)) {
this->pipeline_.AddStage(std::move(edgeDetectionAlgorithm))
.AddStage(std::move(filters))
.AddStage(std::move(distanceAlgorithm))
.Complete(std::move(vectorizationAlgorithm));
}

void DistancePipelineExecutor::ExecutePipeline() {
Expand Down Expand Up @@ -93,8 +102,7 @@ void DistancePipelineExecutor::OutputResults() {
OrbitPipelineExecutor::OrbitPipelineExecutor(OrbitOptions &&options,
std::unique_ptr<OrbitPropagationAlgorithm> orbitPropagationAlgorithm)
: options_(std::move(options)) {
this->orbitPropagationAlgorithm = std::move(orbitPropagationAlgorithm);
this->pipeline_.Complete(*this->orbitPropagationAlgorithm);
this->pipeline_.Complete(std::move(orbitPropagationAlgorithm));
}

void OrbitPipelineExecutor::ExecutePipeline() {
Expand Down
50 changes: 30 additions & 20 deletions src/command-line/execution/executors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "distance/edge.hpp"
#include "distance/distance.hpp"
#include "distance/vectorize.hpp"
#include "distance/edge-filters.hpp"

#include "orbit/orbit.hpp"

Expand Down Expand Up @@ -59,8 +60,6 @@ class CalibrationPipelineExecutor : public PipelineExecutor {
const CalibrationOptions options_;
/// The Calibration pipeline
CalibrationPipeline pipeline_;
/// The Calibration Algorithm used
std::unique_ptr<CalibrationAlgorithm> calibrationAlgorithm;
};

/**
Expand All @@ -75,19 +74,38 @@ class DistancePipelineExecutor : public PipelineExecutor {
~DistancePipelineExecutor();

/**
* Constructs a DistancePipelineExecutor
*
* @param options The options to create the pipeline
* @param edgeDetectionAlgorithm The edge detection algorithm to use
* @param distanceAlgorithm The distance determination algorithm to use
* @param vectorizationAlgorithm The vectorization algorithm to use
*
* @pre options.image.image must be point to heap allocated memory.
* This is guarenteed as long as strtoimage is used to create the image,
* and it throws an error if the image is not valid.
* Constructs a DistancePipelineExecutor (no edge-filters)
*
* @param options The DistanceOptions to configure the pipeline (moved into the executor)
* @param edgeDetectionAlgorithm The EdgeDetectionAlgorithm used by the pipeline (moved into the executor)
* @param distanceAlgorithm The DistanceDeterminationAlgorithm used by the pipeline (moved into the executor)
* @param vectorizationAlgorithm The VectorGenerationAlgorithm used by the pipeline (moved into the executor)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did the preconditions go?

*
* @pre edgeDetectionAlgorithm, distanceAlgorithm, and vectorizationAlgorithm are non-null and already
* configured to operate on (Image -> Points -> PositionVector) in that order.
* @pre Each provided stage is already "ready" (e.g., pipelines passed in were Completed) before transfer.
*/
explicit DistancePipelineExecutor(DistanceOptions &&options,
std::unique_ptr<EdgeDetectionAlgorithm> edgeDetectionAlgorithm,
std::unique_ptr<DistanceDeterminationAlgorithm> distanceAlgorithm,
std::unique_ptr<VectorGenerationAlgorithm> vectorizationAlgorithm);

/**
* Constructs a DistancePipelineExecutor with an edge-filtering pipeline
*
* @param options The DistanceOptions to configure the pipeline (moved into the executor)
* @param edgeDetectionAlgorithm The EdgeDetectionAlgorithm used by the pipeline (moved into the executor)
* @param filters A pipeline of edge filtering stages; ownership is transferred to the executor
* @param distanceAlgorithm The DistanceDeterminationAlgorithm used by the pipeline (moved into the executor)
* @param vectorizationAlgorithm The VectorGenerationAlgorithm used by the pipeline (moved into the executor)
*
* @pre edgeDetectionAlgorithm, filters, distanceAlgorithm, and vectorizationAlgorithm are non-null.
* @pre filters has been completed (ready) prior to being passed in so it can run as a stage.
* @pre Stage input/output types align with the Distance pipeline: Image -> Points -> Points -> PositionVector.
*/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same note, where are my preconditions

explicit DistancePipelineExecutor(DistanceOptions &&options,
std::unique_ptr<EdgeDetectionAlgorithm> edgeDetectionAlgorithm,
std::unique_ptr<EdgeFilteringAlgorithms> filters,
std::unique_ptr<DistanceDeterminationAlgorithm> distanceAlgorithm,
std::unique_ptr<VectorGenerationAlgorithm> vectorizationAlgorithm);

Expand All @@ -99,12 +117,6 @@ class DistancePipelineExecutor : public PipelineExecutor {
const DistanceOptions options_;
/// The Distance pipeline being used
DistancePipeline pipeline_;
/// The Edge Detection Algorithm used
std::unique_ptr<EdgeDetectionAlgorithm> edgeDetectionAlgorithm;
/// The Distance Determination Algorithm being used
std::unique_ptr<DistanceDeterminationAlgorithm> distanceAlgorithm;
/// The Vectorization/Rotation Algorithm being used
std::unique_ptr<VectorGenerationAlgorithm> vectorizationAlgorithm;
};

/**
Expand All @@ -130,8 +142,6 @@ class OrbitPipelineExecutor : public PipelineExecutor {
const OrbitOptions options_;
/// The Orbit pipeline
OrbitPipeline pipeline_;
/// The Orbit Propagation Algorithm being used
std::unique_ptr<OrbitPropagationAlgorithm> orbitPropagationAlgorithm;
};

} // namespace found
Expand Down
3 changes: 3 additions & 0 deletions src/command-line/parsing/options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ FOUND_CLI_OPTION("output-file" , std::string , outputFile
#define ISDDA "ISDDA" // The IterativeSphericalDistanceDeterminationAlgorithm (ISDDA)

/// Distance Flags
// TODO: Remove enable-noop-edge-filter when the first edge filtering algorithm is implemented.
#define DISTANCE \
FOUND_CLI_OPTION("image" , found::Image , image , {} , found::strtoimage(optarg) , kNoDefaultArgument, REQ_ASSIGN, "The image to process (JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC)") \
FOUND_CLI_OPTION("calibration-data" , found::DataFile , calibrationData , {defaultDFHead} , found::strtodf(optarg) , kNoDefaultArgument, REQ_ASSIGN, "The calibration data (*.found)" ) \
Expand All @@ -65,6 +66,8 @@ FOUND_CLI_OPTION("isdda-discrim-ratio" , decimal , ISDDADiscimRati
FOUND_CLI_OPTION("isdda-pdf-order" , int , ISDDAPdfOrd , 2 , atoi(optarg) , kNoDefaultArgument, REQ_ASSIGN, "The Probability Density Function Order for ISSDA (even int)" ) \
FOUND_CLI_OPTION("isdda-radius-loss-order" , int , ISDDARadLossOrd , 4 , atoi(optarg) , kNoDefaultArgument, REQ_ASSIGN, "The Radius Loss Order ISSDA (even int)" ) \
FOUND_CLI_OPTION("output-file" , std::string , outputFile , "" , optarg , kNoDefaultArgument, REQ_ASSIGN, "The output file (*.found)" ) \
FOUND_CLI_OPTION("enable-noop-edge-filter" , bool , enableNoOpEdgeFilter, false , found::strtobool(optarg) , true , OPT_ASSIGN, "Enable the NoOp edge filter (test/demo only)" ) \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a note to remove this upon implementation of the first edge filtering algorithm. Since we do not use NoOp Algorithms, we should not encourage this pattern.




// Orbit Flags
Expand Down
88 changes: 53 additions & 35 deletions src/common/pipeline/pipelines.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
#include <optional>
#include <stdexcept>
#include <cassert>
#include <memory>
#include <type_traits>
#include <utility>

#include "common/pipeline/stages.hpp"

Expand Down Expand Up @@ -62,6 +65,8 @@ class Pipeline : public FunctionStage<Input, Output> {
protected:
/// The stages of this
Action *stages[N];
/// Ownership storage for the stages
std::unique_ptr<Action> ownedStages[N];
/// The number of stages
size_t size = 0;
/// Whether we're complete
Expand All @@ -70,39 +75,44 @@ class Pipeline : public FunctionStage<Input, Output> {
std::optional<Output> finalProduct;

/**
* Adds a stage to this pipeline
*
* @param stage The stage to add to the pipeline
*
* @throw std::invalid_argument iff this is already completed
*
* Adds a stage to this pipeline, taking ownership of the provided stage.
*
* @param stage The stage to add to the pipeline (ownership transferred via unique_ptr)
*
* @throw std::invalid_argument iff this pipeline has already been completed
*
* @pre This method is called when the number of registered stages is
* less than N - 1
*
* less than N - 1
*
* @post The pipeline stores the stage internally, extending its lifetime to match
* the pipeline's lifetime.
*/
inline void AddStageHelper(Action &stage) {
inline void AddStageHelper(std::unique_ptr<Action> &&stage) {
assert(this->size < N);
if (this->ready) throw std::invalid_argument("Pipeline is already ready");
this->stages[size++] = &stage;
this->stages[size] = stage.get();
this->ownedStages[size++] = std::move(stage);
}

/**
* Completes a pipeline with a stage
*
* @param stage The stage to add
*
* @throw std::invalid_argument iff this is already completed
*
* Completes a pipeline with a final stage, taking ownership of it.
*
* @param stage The final stage to add (ownership transferred via unique_ptr)
*
* @throw std::invalid_argument iff this pipeline has already been completed
*
* @pre This method is called when the number of
* registered stages is less than N
*
* registered stages is less than N
* @pre The pipeline is not ready yet (this->ready == false)
*
* @post The pipeline is ready (this->ready == true)
*
* @post The pipeline is ready (this->ready == true) and retains the stage
* for the remainder of its lifetime.
*/
inline void CompleteHelper(Action &stage) {
inline void CompleteHelper(std::unique_ptr<Action> &&stage) {
assert(this->size < N);
if (this->ready) throw std::invalid_argument("Pipeline is already ready");
this->stages[size++] = &stage;
this->stages[size] = stage.get();
this->ownedStages[size++] = std::move(stage);
this->ready = true;
}

Expand Down Expand Up @@ -162,20 +172,25 @@ class SequentialPipeline : public Pipeline<Input, Output, N> {
* @pre This method is called when the number of registered stages is
* less than N - 1
*/
template<typename I, typename O> SequentialPipeline &AddStage(FunctionStage<I, O> &stage) {
template<typename StageType>
SequentialPipeline &AddStage(std::unique_ptr<StageType> stage) {
static_assert(std::is_base_of<FunctionStage<typename StageType::InputType,
typename StageType::OutputType>, StageType>::value,
"Stage must derive from FunctionStage");
StageType *stagePtr = stage.get();
if (this->size == 0) {
if (!std::is_same<Input, I>::value) {
if (!std::is_same<Input, typename StageType::InputType>::value) {
throw std::invalid_argument("The initial input type is not correct");
}
this->firstResource = reinterpret_cast<Input *>(&stage.GetResource());
this->firstResource = reinterpret_cast<Input *>(&stagePtr->GetResource());
} else {
// Chain here, and blindly trust the user
*this->lastProduct = static_cast<void *>(&stage.GetResource());
*this->lastProduct = static_cast<void *>(&stagePtr->GetResource());
}
// Add to our list
Pipeline<Input, Output, N>::AddStageHelper(stage);
Pipeline<Input, Output, N>::AddStageHelper(std::move(stage));
// Now, reset the lastProduct to be of this stage
this->lastProduct = reinterpret_cast<void **>(&stage.GetProduct());
this->lastProduct = reinterpret_cast<void **>(&stagePtr->GetProduct());
// Return the pipeline for chaining
return *this;
}
Expand All @@ -195,10 +210,13 @@ class SequentialPipeline : public Pipeline<Input, Output, N> {
* @pre This method is called when the number of registered stages is less
* than N
*/
template<typename I> SequentialPipeline &Complete(FunctionStage<I, Output> &stage) {
template<typename StageType>
SequentialPipeline &Complete(std::unique_ptr<StageType> stage) {
static_assert(std::is_same<Output, typename StageType::OutputType>::value,
"Final stage output must match pipeline output");
assert(this->size < N);
if (this->ready) throw std::invalid_argument("Pipeline is already ready");
this->AddStage(stage);
this->AddStage(std::move(stage));
this->ready = true;
return *this;
}
Expand Down Expand Up @@ -269,9 +287,9 @@ class ModifyingPipeline : public Pipeline<T, T, N> {
*
* @return this, with the added stage
*/
ModifyingPipeline &AddStage(ModifyingStage<T> &stage) {
ModifyingPipeline &AddStage(std::unique_ptr<ModifyingStage<T>> stage) {
assert(this->size < N - 1);
Pipeline<T, T, N>::AddStageHelper(stage);
Pipeline<T, T, N>::AddStageHelper(std::move(stage));
return *this;
}

Expand All @@ -285,9 +303,9 @@ class ModifyingPipeline : public Pipeline<T, T, N> {
* @pre This method is called when the number of
* registered stages is less than N
*/
ModifyingPipeline &Complete(ModifyingStage<T> &stage) {
ModifyingPipeline &Complete(std::unique_ptr<ModifyingStage<T>> stage) {
assert(this->size < N);
Pipeline<T, T, N>::CompleteHelper(stage);
Pipeline<T, T, N>::CompleteHelper(std::move(stage));
return *this;
}

Expand All @@ -312,7 +330,7 @@ class ModifyingPipeline : public Pipeline<T, T, N> {
}
*this->product = input;
for (size_t i = 0; i < this->size; i++) {
dynamic_cast<ModifyingStage<T> *>(this->stages[i])->SetResource(*this->product);
dynamic_cast<ModifyingStage<T> *>(this->stages[i])->SetResource(*this->product); // GCOVR_EXCL_LINE
}
Pipeline<T, T, N>::DoActionHelper();
return *this->product;
Expand Down
10 changes: 10 additions & 0 deletions src/common/pipeline/stages.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ using raw_type = std::remove_cv_t<std::remove_reference_t<T>>;
*/
class Action {
public:
virtual ~Action() = default;
/**
* Performs some action
*/
Expand Down Expand Up @@ -55,6 +56,15 @@ class Stage : public Action {
template<typename Input, typename Output>
class FunctionStage : public Stage<const raw_type<Input> &, raw_type<Output>> {
public:
/**
* Canonical, non-reference input type for this stage.
*/
using InputType = raw_type<Input>;
/**
* Canonical, non-reference output type for this stage.
*/
using OutputType = raw_type<Output>;

/**
* Constructs a new Stage
*/
Expand Down
2 changes: 1 addition & 1 deletion src/common/style.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ typedef std::pair<std::function<Vec3(int)>,std::function<Vec3(int)>> KinematicPr

/// Number of (maximum) stages for each pipeline
constexpr size_t calibration_size = 1;
constexpr size_t distance_size = 3;
constexpr size_t distance_size = 4;
constexpr size_t orbit_size = 2;

/// Pipeline for Calibration
Expand Down
Loading