namespace cv
{
using Scalar = double;
struct Mat
{};
struct Point2i
{};
inline void swap(Mat& a, Mat& b)
{}
}
// basic_types.hpp
#include <cstdint>
namespace cvip
{
// Aliases for most used types from std
//
using uint_t = std::uint32_t;
using upix_t = std::uint8_t;
using wpix_t = std::uint16_t;
// Aliases for most common types in cv
using vscalar = double;
using mscalar = cv::Scalar;
using matrix = cv::Mat;
using point = cv::Point2i;
// Algoritms/Scanning execution model
//
enum class execution_model : int
{
parallel, sequential
};
}
// i_operator.hpp
#include <memory>
#include <type_traits>
namespace cvip
{
namespace core
{
// Interface for image operator classes
//
class i_operator
{
protected:
// operator chain node type
//
using opnode_t = std::shared_ptr<i_operator>;
// apply operator on matrix
//
// rhs : A modifiable reference to a smart-reference image container.
// On input, it holds a reference to the original (right hand
// side) operand. On output, it holds a reference to an image
// with the same depth and size of the input image; this image
// will be used as temporary processing buffer in the chain of
// operators in an expression. Must be a non empty image.
//
// dst : A modifiable reference to a smart-reference image container.
// On input, assumed to be an empty matrix for the first operator
// in the chain of operators in an expression; for remaing calls,
// a reference to the buffer returned by the first operator in
// rhs. On output, the result of a successful operation.
//
virtual void apply(matrix& dst, matrix& src, bool const first) = 0;
// clone this operator
//
virtual opnode_t clone() const = 0;
friend matrix operator*(i_operator& lhs_op, matrix const& rhs_im);
friend class operator_expression;
};
// Tool for checking i_operator implementation
//
template<typename T>
class is_operator
{
public:
static auto constexpr value = std::is_base_of<i_operator, T>::value;
};
// Interface for operator predicates
//
class i_operator_predicate
{
public:
//virtual bool supported(matrix const& mat) const noexcept = 0;
virtual void do_apply(matrix& dst, matrix& src, bool const first) = 0;
template<typename ...Args>
void reset(Args&& ...arg);
};
// Tool for checking i_operator_predicate implementation
//
template<typename P>
class is_operator_predicate
{
private:
struct has_member { char z[1]; };
struct no_member { char z[2]; };
//template<typename T> static has_member has_supported_member(decltype(&T::supported));
template<typename T> static has_member has_do_apply_member(decltype(&T::do_apply));
//template<typename T> static no_member has_supported_member(...);
template<typename T> static no_member has_do_apply_member(...);
static auto constexpr yes = sizeof(has_member);
//static auto constexpr has_supported = sizeof(has_supported_member<P>(nullptr));
static auto constexpr has_do_apply = sizeof(has_do_apply_member<P>(nullptr));
static auto constexpr is_predicate = std::is_base_of<i_operator_predicate, P>::value;
public:
static auto constexpr value = is_predicate
or /*has_supported == yes and*/ has_do_apply == yes;
};
}
}
// operator.hpp
namespace cvip
{
namespace core
{
// Generic abstract base class for image matrix operators, partially
// implements i_operator providing cloning facility via CRT Pattern.
//
template<typename ConcreteOperator>
class base_operator : public i_operator
{
public:
base_operator() noexcept = default;
base_operator(base_operator const& src) noexcept = default;
base_operator(base_operator&& src) noexcept = default;
virtual ~base_operator() noexcept = default;
base_operator& operator=(base_operator const& src) noexcept = default;
base_operator& operator=(base_operator&& src) noexcept = default;
protected:
using operator_t = ConcreteOperator;
virtual opnode_t clone() const override;
};
// Predicate based image operator
//
template<typename Predicate>
class basic_operator : public base_operator< basic_operator<Predicate> >
{
public:
template<typename ...Args>
basic_operator(Args&& ...arg);
template<typename ...Args>
basic_operator& operator()(Args&& ...arg);
protected:
virtual void apply(matrix& dst, matrix& src, bool const first) override;
private:
using predicate_t = Predicate;
static_assert(is_operator_predicate<predicate_t>::value,
"predicate_t does not implement i_operator_predicate");
private:
predicate_t m_operation = { };
};
}
}
// operator.inl
#include <memory>
#include <utility>
namespace cvip
{
namespace core
{
// base_operator<ConcreteOperator>
//
template<typename ConcreteOperator>
inline typename base_operator<ConcreteOperator>::opnode_t base_operator<ConcreteOperator>::clone() const
{
static_assert(is_operator<operator_t>::value, "operator_t is not an i_operator");
return std::make_shared<operator_t>(*static_cast<operator_t const*>(this));
}
// basic_operator<Predicate>
//
template<typename Predicate> template<typename ...Args>
basic_operator<Predicate>::basic_operator(Args&& ...arg) :
m_operation{ std::forward<Args>(arg)... }
{
// NOOP
}
template<typename Predicate> template<typename ...Args>
inline basic_operator<Predicate>& cvip::core::basic_operator<Predicate>::operator()(Args&& ...arg)
{
return (m_operation.reset(std::forward<Args>(arg)...), *this);
}
template<typename Predicate>
inline void basic_operator<Predicate>::apply(matrix& dst, matrix& src, bool const first)
{
m_operation.do_apply(dst, src, first);
}
// image operator operations
//
inline matrix operator*(i_operator&& lhs_op, matrix const& rhs_im)
{
return lhs_op * rhs_im;
}
}
}
// operator.cpp
namespace cvip
{
namespace core
{
matrix operator*(i_operator& lhs_op, matrix const& rhs_im)
{
auto src = matrix{ rhs_im };
auto dst = matrix{ };
lhs_op.apply(dst, src, true);
return dst;
}
}
}
// expression.hpp
#include <list>
#include <memory>
namespace cvip
{
// Operator expressions
//
// The built-int multiplication operator (operator*) is used to indicate
// operations among image operators and image matrices.
//
// Arithmetic operation among operators and matrices is done from right to
// left, nevertheless, operation among operators are gathered from left to
// right within an operator expression.
//
// Applying an operator, P, on a matrix, M1, produces a new matrix, M2:
//
// P * M1 -> M2.
//
// Applying an operator, P2, on another operator, P1, produces an operator
// expression that gather both operators for later application in the
// appropriate order:
//
// P2 * P1 -> EX(P1, P2).
//
// Applying an operator expression on a matrix, M1, produces a new matrix,
// M2, after applying the encapsulated operators in the correct order:
//
// EX(P1, P2) * M1 -> M2.
//
// Applying an operator expression on an operator, P3, produces a new
// operator expression concatenating it with the existing operators in the
// proper order:
//
// EX(P1, P2) * P3 -> EX(P3, P1, P2).
//
// Applying an operator expression on another operator expression, produces
// a new operator expression concatenating the existing operators in the
// proper order:
//
// EX(P1, P2) * EX(P3, P4) -> EX(P3, P4, P1, P2).
//
// The commutative property does not hold for operators and operator
// expressions:
//
// P2 * P1 -> EX(P1, P2),
// P1 * P2 -> EX(P2, P1),
//
// EX(P1, P2) * P3 -> EX(P3, P1, P2),
// P3 * EX(P1, P2) -> EX(P1, P2, P3),
//
// EX(P1, P2) * EX(P3, P4) -> EX(P3, P4, P1, P2),
// EX(P3, P4) * EX(P1, P2) -> EX(P1, P2, P3, P4).
//
// Associative property among operators and operator expressions is just
// syntactic sugar and is implemented for semantic purposes, e.g. for
// grouping or emphasise related operations. The following operations are
// the same:
//
// (P1 * P2) * P3 * M === P1 * P2 * P3 * M,
//
// however, the next operations are equivalent, i.e. they produce the same
// result but the intermediate products are not the same:
//
// (P1 * P2) * P3 * M == P1 * (P2 * P3) * M,
//
// but
//
// (P1 * P2) * P3 * M !== P1 * (P2 * P3) * M.
//
// Listing the intermediate results, we have:
//
// Example 1.
//
// 1) P3 * P2 * P1 * M1 ... or (P3 * P2) * P1 * M1
// 2) EX(P2, P3) * P1 * M1
// 3) EX(P1, P2, P3) * M1 ... operators are applied in the order
// 4) M2 ... P1, P2, and P3
//
// Example 2.
//
// 1) P3 * (P2 * P1) * M1
// 2) P3 * EX(P1, P2) * M1
// 3) EX(P1, P2, P3) * M1 ... operators are applied in the order
// 4) M2 ... P1, P2, and P3
//
// Example 3.
//
// 1) P4 * P3 * (P2 * P1) * M1
// 2) P4 * P3 * EX(P1, P2) * M1
// 3) EX(P3, P4) * EX(P1, P2) * M1
// 4) EX(P1, P2, P3, P4) * M1
// 5) M2
//
// Example 4.
//
// 1) (P4 * P3) * (P2 * P1) * M1
// 2) EX(P3, P4) * (P2 * P1) * M1
// 3) EX(P3, P4) * EX(P1, P2) * M1
// 4) EX(P1, P2, P3, P4) * M1
// 5) M2
//
// Provided the operators are equivalent, Example 1 and Example 2, and
// Example 3 and Example 4, respectively, produce the same result.
//
// Applying association to operators or operator expressions and matrices,
// force the application of the gathered operators to the image and
// produces a new intermediate image before continuing with the remaning
// operators:
//
// Example 5.
//
// 1) P3 * P2 * (P1 * M1)
// 2) P3 * P2 * M2
// 3) EX(P2, P3) * M2
// 4) M3
//
// Even though, if the operators are equivalent, Example 1 and Example 5,
// produce the same result.
//
namespace core
{
//class operator_expression;
//operator_expression operator*(i_operator const& lhs_op, i_operator const& rhs_op);
//operator_expression operator*(operator_expression&& lhs_ex, i_operator const& rhs_op);
//operator_expression operator*(i_operator const& lhs_op, operator_expression&& rhs_ex);
//operator_expression operator*(operator_expression&& lhs_ex, operator_expression&& rhs_ex);
//matrix operator*(operator_expression& lhs_ex, matrix const& rhs_im);
// Operator expression
//
// Allows operator semantics for image matrix operations. Operations
// among image operators and image matrices are right associative,
// while operations among operators are left associative.
//
class operator_expression
{
public:
operator_expression() = delete;
operator_expression(operator_expression const& src) noexcept = default;
operator_expression(operator_expression&& src) noexcept = default;
~operator_expression() noexcept = default;
operator_expression& operator=(operator_expression const& src) noexcept = default;
operator_expression& operator=(operator_expression&& src) noexcept = default;
private:
operator_expression(i_operator const& lhs_op, i_operator const& rhs_op);
private:
matrix apply(matrix const& rhs_im);
void emplace_back(operator_expression&& lhs_ex);
void push_back(i_operator const& lhs_op);
void push_front(i_operator const& rhs_op);
private:
friend operator_expression operator*(i_operator const& lhs_op, i_operator const& rhs_op);
friend operator_expression operator*(operator_expression&& lhs_ex, i_operator const& rhs_op);
friend operator_expression operator*(i_operator const& lhs_op, operator_expression&& rhs_ex);
friend operator_expression operator*(operator_expression&& lhs_ex, operator_expression&& rhs_ex);
friend matrix operator*(operator_expression& lhs_ex, matrix const& rhs_im);
private:
using opnode_t = i_operator::opnode_t;
using opchain_t = std::list<opnode_t>;
using exdata_t = std::shared_ptr<opchain_t>;
private:
exdata_t construct_data(i_operator const& lhs_op, i_operator const& rhs_op);
private:
exdata_t m_data = { };
};
}
}
// expression.inl
#include <utility>
namespace cvip
{
namespace core
{
// operator_expression
//
inline void operator_expression::emplace_back(operator_expression&& lhs_ex)
{
m_data->splice(m_data->end(), std::move(*lhs_ex.m_data));
}
inline void operator_expression::push_back(i_operator const& lhs_op)
{
m_data->emplace_back(lhs_op.clone());
}
inline void operator_expression::push_front(i_operator const& rhs_op)
{
m_data->emplace_front(rhs_op.clone());
}
// operator_expression operator* (op * op)
//
inline operator_expression operator*(i_operator const& lhs_op, i_operator const& rhs_op)
{
return { lhs_op, rhs_op };
}
// operator_expression operator* (ex * op, op * ex)
//
inline operator_expression operator*(operator_expression&& lhs_ex, i_operator const& rhs_op)
{
return (lhs_ex.push_front(rhs_op), std::move(lhs_ex));
}
inline operator_expression operator*(i_operator const& lhs_op, operator_expression&& rhs_ex)
{
return (rhs_ex.push_back(lhs_op), std::move(rhs_ex));
}
inline operator_expression operator*(operator_expression const& lhs_ex, i_operator const& rhs_op)
{
return operator_expression{ lhs_ex } * rhs_op;
}
inline operator_expression operator*(i_operator const& lhs_op, operator_expression const& rhs_ex)
{
return lhs_op * operator_expression{ rhs_ex };
}
// operator_expression operator* (ex * ex)
//
inline operator_expression operator*(operator_expression&& lhs_ex, operator_expression&& rhs_ex)
{
return (rhs_ex.emplace_back(std::move(lhs_ex)), std::move(rhs_ex));
}
inline operator_expression operator*(operator_expression&& lhs_ex, operator_expression const& rhs_ex)
{
return std::move(lhs_ex) * operator_expression{ rhs_ex };
}
inline operator_expression operator*(operator_expression const& lhs_ex, operator_expression&& rhs_ex)
{
return operator_expression{ lhs_ex } * std::move(rhs_ex);
}
inline operator_expression operator*(operator_expression const& lhs_ex, operator_expression const& rhs_ex)
{
return operator_expression{ lhs_ex } * operator_expression{ rhs_ex };
}
inline matrix operator*(operator_expression& lhs_ex, matrix const& rhs_im)
{
return lhs_ex.apply(rhs_im);
}
inline matrix operator*(operator_expression&& lhs_ex, matrix const& rhs_im)
{
return lhs_ex * rhs_im;
}
}
}
// expression.cpp
#include <initializer_list>
#include <cassert>
namespace cvip
{
namespace core
{
operator_expression::operator_expression(i_operator const& lhs_op, i_operator const& rhs_op) :
m_data{ construct_data(lhs_op, rhs_op) }
{
assert(m_data.use_count() == 1);
}
matrix operator_expression::apply(matrix const& rhs_im)
{
auto src = matrix{ rhs_im };
auto dst = matrix{ };
auto first = true;
for (auto& op : *m_data)
{
op->apply(dst, src, first);
cv::swap(dst, src);
first = false;
}
// REMARK: The result is in src due to the swap
// at the end of each iteration!
return src;
}
inline operator_expression::exdata_t operator_expression::construct_data(i_operator const& lhs_op,
i_operator const& rhs_op)
{
return std::make_shared<opchain_t>(opchain_t{ rhs_op.clone(), lhs_op.clone() });
}
}
}
// test entry-point
#include <iostream>
using namespace std;
int main()
{
std::cout << "Hello world!";
return 0;
}
// struct i_op
// using opnode_t = i_operator::opnode_t;
// using opchain_t = std::list<opnode_t>;
// using exdata_t = std::shared_ptr<opchain_t>;
//