Я изучаю цифровую обработку изображений самостоятельно и был бы очень признателен, если бы кто-то мог прокомментировать, следует ли применять polymorphism
для этого случая или если есть лучший дизайн класса.
По сути, 2D-фильтр / ядро может быть либо: non-separable
, либо separable
. Важной операцией ядра является convolution
, и способ ее вычисления зависит от типа фильтра.
template < typename T >
class CKernel2D{
public:
//....
virtual CMatrix<T> myConvolution(const CMatrix<T> & input) = 0;
//....
};
template < typename T >
class CNonSeparableKernel : public CKernel2D<T> {
public:
//....
CMatrix<T> myConvolution(const CMatrix<T> & input );
void initNonSeparableFilter1( double, int );
//....
private:
CMatrix<T> m_Kernel;
};
template < typename T >
class CSeparableKernel2D : public CKernel2D<T>{
public:
//....
CMatrix<T> myConvolution(const CMatrix<T> & input );
void initSeparableFilter1( double, double );
//....
private:
std::vector<T> m_KernelX;
std::vector<T> m_KernelY;
};
Обратите внимание, что даже у класса CSeparableKernel2D
может быть два закрытых члена: CKernel1D<T> m_X, m_Y
. Класс CKernel1D<T>
может иметь свой собственный метод myConvolution
, т.е. myConvolutionRows
, myConvolutionCols
.
Также, как правило, мы хотели бы применить набор filters
(отделимый / неразделимый) к данному изображению, то есть сверить входное изображение с данным filter
. Поэтому, в зависимости от filter type
, должен вызываться соответствующий метод myConvolution
.
(1) Какой должен быть самый чистый способ сделать что-либо? как?
CNonSeparableKernel<float> myNonSepFilter1;
myNonSepFilter1.initNonSeparableFilter1(3.0, 1);
CNonSeparableKernel<float> mySepFilter1;
mySepFilter1.initSeparableFilter1(0.5, 0.5);
std::vector<CKernel2D<float> > m_vFilterbank;
m_vFilterbank.push_back(myNonSepFilter1); // Would like to assign a non-sep filter.
m_vFilterbank.push_back(mySepFilter1); // Would like to assign a sep filter.
Мне казалось, что единственный способ сделать это - использовать полиморфизм, верно?
CKernel2D<float> * pKernel2d = NULL;
pKernel2d = &mySepFilter1; m_vFilterbank.push_back(*pKernel2d);
pKernel2d = &myNonSepFilter1; m_vFilterbank.push_back(*pKernel2d);
(2) Теперь, предположив, что наша filterbank
уже заполнена ядрами обоих типов, чтобы применить свертку к входному изображению, достаточно сделать:
outputSeparable1 = m_vFilterbank.at(0).myConvolution(input);
outputNonSeparable1 = m_vFilterbank.at(1).myConvolution(input);
(3) А теперь представьте, что я хотел бы иметь функцию друга convolution
со следующим прототипом:
friend CMatrix<T> convolution(const CKernel2D<T> &, const CImage<T> &);
еще раз, я бы хотел, чтобы вызывался правильный метод myConvolution
в зависимости от типа kernel
. Как я мог добиться такой операции? Я читаю по поводу Virtual Friend Function Idiom
, как вы думаете, имеет ли смысл применять эту идиому для такого случая?
Все комментарии и предложения действительно приветствуются ;-) Мне бы очень хотелось услышать, что вы думаете об этом дизайне? Есть ли лучший способ разработать эти функции?