/*! * \file CyclopsFeature.h * \date 2019/11/12 * * \author Lin, Chi * Contact: lin.chi@hzleaper.com * * * \note */ #ifndef __CyclopsFeature_h_ #define __CyclopsFeature_h_ #include "CyclopsParam.h" #include "CyclopsEnums.h" #include "CyclopsGrid.h" /*! \brief External properties that would affect feature's computation. It's stored inside sample instance's user data */ enum CyclopsFeatureExtProp { CFExtPropStart = 9000, /*! Used by SizeFeature, instead of image/bounding rect size of sample instance, 2 float */ CFExtPropSize, /*! Used by PPFeature, instead of max-value normalization, 1 double */ CFExtPropNormValue, CFExtPropEnd = 10000, }; class SampleInstance; /*! \brief Base class for feature extractor */ class CyclopsFeature { public: /*! Parameter configurations */ CyclopsParams mParams; /*! Default auto-tune search grid */ CyclopsGrids mDefaultGrids; virtual ~CyclopsFeature() {} typedef std::shared_ptr Ptr; /*! Feature's type */ FeatureType getType() const { return mType; } /*! Enable or disable this feature */ void setEnabled(bool val = true) { mDisable = !val; } /*! Check if this feature is enabled or not */ bool isEnabled() const { return !mDisable; } /*! Change weight of this feature */ void setWeight(double w) { mWeight = w; mParams.setDirtyBit(true); } /*! Get weight of this feature */ double getWeight() const { return mWeight; } /*! Serialized to file or in-memory string */ void serialize(FileStorage& fs) const { mParams.serialize(fs); fs << "name" << mName; fs << "disable" << mDisable; fs << "weight" << mWeight; fs << "feature" << "{:"; serializeFeature(fs); fs << "}"; } /*! Deserialize from file or in-memory string */ void deserialize(const FileNode& node) { mParams.deserialize(node); node["name"] >> mName; node["disable"] >> mDisable; node["weight"] >> mWeight; deserializeFeature(node["feature"]); } /*! @overload */ Mat compute(const SampleInstance* pinst) { Mat f; compute(pinst, f); return f; } /*! Compute feature vector for provided image * @param pinst pointer to sample instance for computation * @param f output feature vector * @return size of feature vector, 0 for fail */ int compute(const SampleInstance* pinst, Mat& f) { if (mDisable) return 0; syncParam(); int ret = doCompute(pinst, f); if (!f.empty() && mWeight != 1) f *= mWeight; return ret; } /*! Get size of feature vector for given normalized image size */ int getSize(const Size& sampleSize) { if (mDisable) return 0; syncParam(); return doGetSize(sampleSize); } /*! Set parameter configuration for definition to real implementation */ void syncParam() { if (mParams.isDirty()) { doSyncParam(); mParams.setDirtyBit(false); } } /*! Backup parameters */ virtual void backupAll() { mParams.backupAll(); } /*! Restore parameter from last backup */ virtual void restoreAll() { mParams.restoreAll(); } /*! Whether any parameter is modified */ virtual bool isDirty() { return mParams.isDirty(); } /*! Do training of given samples */ virtual bool train(const std::vector& trainSamples) { return true; } /*! Whether we need to re-train this feature */ virtual bool needTrain() { return false; } /*! Whether we need to fix some bad parameter configuration * @param sampleSize uniformed sample size * @param doFix true if we should also do the real fix * @return true if fix is needed */ virtual bool needFix(const Size& sampleSize, bool doFix) { return !mParams.isValid(sampleSize, doFix).empty(); } /*! Clear trained cache */ virtual void clear() {} /*! Get feature's name */ const std::string& getName() const { return mName; } protected: virtual void doSyncParam() {} virtual int doCompute(const SampleInstance* pinst, Mat& f) { return 0; } virtual int doGetSize(const Size& sampleSize) const { return 0; } virtual void serializeFeature(FileStorage& fs) const {} virtual void deserializeFeature(const FileNode& node) {} protected: FeatureType mType; std::string mName; bool mDisable = false; double mWeight = 1.; }; /*! \brief Base class for Bag-of-words feature */ class BOWFeature : public CyclopsFeature { public: virtual ~BOWFeature() {} typedef std::shared_ptr Ptr; enum Param { /*! Count of vocabulary */ VocCount = 0, }; virtual bool isDirty(); virtual bool train(const std::vector& trainSamples); virtual bool needTrain() { return true; } virtual void clear(); protected: explicit BOWFeature(); virtual int doCompute(const SampleInstance* pinst, Mat& f); virtual int doGetSize(const Size& sampleSize) const; virtual void serializeFeature(FileStorage& fs) const; virtual void deserializeFeature(const FileNode& node); virtual int computeRaw(const Mat& img, Mat& f) { return 0; } virtual void serializeRaw(FileStorage& fs) const {} virtual void deserializeRaw(const FileNode& node) {} protected: cv::Ptr mExt; }; /*! \brief Image feature: ORB (oriented BRIEF) */ class ORBFeature : public BOWFeature { public: virtual ~ORBFeature() {} typedef std::shared_ptr Ptr; /*! Create an ORB feature with default parameters(recommended for 128x128 sample size) */ static ORBFeature::Ptr create(); enum Param { /*! Size of patch, 4 ~ 256 */ PatchSize = VocCount + 1, /*! Scale factor for pyramid, 1 ~ 2 */ ScaleFactor, /*! Level of pyramid (scale down) */ DownLevel, /* UpLevel, remove scale up level, as there's some bug in image pyramid generation code in opencv. add it back some day if they fix the issue https://github.com/opencv/opencv/issues/16197 */ /*! The number of points that produce each element of ORB, 2, 3, or 4 */ RandomPairCount, }; }; /*! \brief Image feature: AKAZE */ class AKAZEFeature : public BOWFeature { public: ~AKAZEFeature() {} typedef std::shared_ptr Ptr; /*! Create an AKAZE feature with default parameters(recommended for 128x128 sample size) */ static AKAZEFeature::Ptr create(); enum Param { /*! Whether this feature is rotation-invariant, true or false */ RotationInvariant = VocCount + 1, /*! Threshold to accept key point, > 0, usualy a small value */ Threshold, /*! Maximum octave evolution */ Octave, /*! Number of sub-levels per scale level (octave) */ SubLevel, /*! Diffusivity type, see DiffusivityType */ DiffusivityType }; enum DiffusivityType { G1 = 0, G2, Weickert, Charbonnier }; }; /*! \brief Image feature: HOG */ class HOGFeature : public CyclopsFeature { public: virtual ~HOGFeature() {} typedef std::shared_ptr Ptr; /*! Create a HOG feature with default parameters(recommended for 128x128 sample size) */ static HOGFeature::Ptr create(); enum Param { /*! Size of a cell for histogram, 2x2 ~ 128x128, should be 2^x */ CellSize = 0, /*! Bins of histogram, should be factor of 360(or 180 depending on SignedGradient) */ BinSize, /*! Count of cells in a block, block size = cell in block * cell size */ CellInBlock, /*! Count of cells in a block stride, block stride = cell in block stride * cell size */ CellInBlockStride, /*! Count of block in stride, win size = block size + block stride * block stride step */ BlockStrideStep, /*! use signed gradient or not, directions are analyzed in 180 or 360 */ SignedGradient, }; }; /*! \brief Image feature: Pixel-by-pixel */ class PPFeature : public CyclopsFeature { public: virtual ~PPFeature() {} typedef std::shared_ptr Ptr; /*! Create a pixel-by-pixel feature with default parameters(recommended for 128x128 sample size) */ static PPFeature::Ptr create(); enum Param { /*! Uniformed image size */ UniSize = 0, /*! Pixel value type, see PixelValue */ PixelValue, /*! Projection type , see ProjectionType */ Projection, }; enum PixelValueType { /*! Gray value in 0 ~ 1 */ GrayValue = 0, /*! Normalized gray value with maximum scaling to 0 ~ 1 */ GrayNormValue, /*! Binary value after auto-thresholding, bigger report 1, otherwise report 0. If roi is available, use roi's mask as source. If roi is using soft mask, report floating value between 0 ~ 1. */ BinaryValue, /*! Fraction of pixels in the foreground after auto-thresholding, bigger report foreground If roi is available, use roi's mask as source */ FractionValue, }; enum ProjectionType { /*! No projection */ None = 0, /*! Horizontal projection, report average of each row */ Horinzontal, /*! Vertical projection, report average of each column */ Vertical, /*! Horizontal and vertical projection, report average of each row and column */ HorinzontalAndVertical, }; }; /*! \brief Image feature: percentage of foreground pixels */ class FGFFeature : public CyclopsFeature { public: virtual ~FGFFeature() {} typedef std::shared_ptr Ptr; /*! Create a foreground fraction feature with default parameters */ static FGFFeature::Ptr create(); }; /*! \brief Base class for geometric features based on contours defined in sample's detect roi. Usually no parameter. */ class GeomFeature : public CyclopsFeature { public: virtual ~GeomFeature() {} typedef std::shared_ptr Ptr; protected: virtual int doGetSize(const Size& sampleSize) const; }; /*! \brief Geometric feature: How the sample looks like a circle: contour area / (pi * r ^ 2)\n For multiple contours, report the smallest value */ class CircularityFeature : public GeomFeature { public: virtual ~CircularityFeature() {} typedef std::shared_ptr Ptr; /*! Create a circularity feature with no parameters */ static CircularityFeature::Ptr create(); }; /*! \brief Geometric feature: How the sample looks like a convex hull: contour area / bounding hull area\n For multiple contours, report the smallest value */ class ConvexityFeature : public GeomFeature { public: virtual ~ConvexityFeature() {} typedef std::shared_ptr Ptr; /*! Create a convexity feature with no parameters */ static ConvexityFeature::Ptr create(); }; /*! \brief Geometric feature: difference between the contour and its convex hull: sum(distance of defect) A convex contour report 0, for multiple contours, report the biggest value */ class ConvDefectsFeature : public GeomFeature { public: virtual ~ConvDefectsFeature() {} typedef std::shared_ptr Ptr; /*! Create a convexity defects feature with no parameters */ static ConvDefectsFeature::Ptr create(); }; /*! \brief Geometric feature: Sin value of orientation of the bonding convex hull of the sample, not rotation-invariant. */ class OrientationFeature : public GeomFeature { public: virtual ~OrientationFeature() {} typedef std::shared_ptr Ptr; /*! Create a orientation feature with no parameters */ static OrientationFeature::Ptr create(); }; /*! \brief Geometric feature: Ratio of convex hull's min axes and max axes, whether the sample is long or like a square. */ class InertiaFeature : public GeomFeature { public: virtual ~InertiaFeature() {} typedef std::shared_ptr Ptr; /*! Create a inertia feature with no parameters */ static InertiaFeature::Ptr create(); }; /*! \brief Base class for features count components the sample's contour has. */ class CompCountFeature : public GeomFeature { public: virtual ~CompCountFeature() {} typedef std::shared_ptr Ptr; virtual bool train(const std::vector& trainSamples); virtual bool needTrain() { return true; } protected: virtual void serializeFeature(FileStorage& fs) const; virtual void deserializeFeature(const FileNode& node); float mNormCounts; }; /*! \brief Geometric feature: How many contours the sample has.\n Number of connected contour components, aka, count of adding sub-rois in sample's detect roi */ class ContourCountFeature : public CompCountFeature { public: virtual ~ContourCountFeature() {} typedef std::shared_ptr Ptr; /*! Create a contour count feature with no parameters */ static ContourCountFeature::Ptr create(); }; /*! \brief Geometric feature: How many holes the sample has.\n Number of holes, aka, count of subtracting sub-rois in sample's detect roi */ class HoleCountFeature : public CompCountFeature { public: virtual ~HoleCountFeature() {} typedef std::shared_ptr Ptr; /*! Create a hole count feature with no parameters */ static HoleCountFeature::Ptr create(); }; /*! \brief Base class for size features based on sample's original and uniformed image size. Usually no parameter. */ class SizeFeature : public CyclopsFeature { public: virtual ~SizeFeature() {} typedef std::shared_ptr Ptr; protected: virtual int doGetSize(const Size& sampleSize) const; Size2f getSampleInstSize(const SampleInstance* pinst); }; /*! \brief Size feature: Aspect ratio of sample image: width / height */ class AspectRatioFeature : public SizeFeature { public: virtual ~AspectRatioFeature() {} typedef std::shared_ptr Ptr; /*! Create a aspect ratio feature with no parameters */ static AspectRatioFeature::Ptr create(); }; /*! \brief Base class for features report the abstract size of sample image */ class AbstractSizeFeature : public SizeFeature { public: virtual ~AbstractSizeFeature() {} typedef std::shared_ptr Ptr; virtual bool train(const std::vector& trainSamples); virtual bool needTrain() { return true; } protected: virtual void serializeFeature(FileStorage& fs) const; virtual void deserializeFeature(const FileNode& node); float mNormSize; }; /*! \brief Size feature: Width of sample image, not scale-invariant */ class WidthFeature : public AbstractSizeFeature { public: virtual ~WidthFeature() {} typedef std::shared_ptr Ptr; /*! Create a width feature with no parameters */ static WidthFeature::Ptr create(); }; /*! \brief Size feature: Height of sample image, not scale-invariant */ class HeightFeature : public AbstractSizeFeature { public: virtual ~HeightFeature() {} typedef std::shared_ptr Ptr; /*! Create a height feature with no parameters */ static HeightFeature::Ptr create(); }; /*! \brief Size feature: Difference in size between the sample image and uniformed image's sizes: min(uniformed width / width, uniformed height / height) */ class ZoomFactorFeature : public SizeFeature { public: virtual ~ZoomFactorFeature() {} typedef std::shared_ptr Ptr; /*! Create a zoom factor feature with no parameters */ static ZoomFactorFeature::Ptr create(); }; #endif // CyclopsFeature_h_