/*! * \file DetectRoi.h * \date 2018/03/23 * * \author Lin, Chi * Contact: lin.chi@hzleaper.com * * * \note */ #ifndef __DetectRoi_h_ #define __DetectRoi_h_ #include "CVUtils.h" #include "StdUtils.h" #include "CyclopsEnums.h" #include "CyclopsModules.h" /*! \brief Define a complex roi for detection with a collection of shapes. * * Define a complex roi, which could be a union of many shapes, and more over, subtraction of some others. * The output is generally a transformed image within the specified area, and a mask telling which pixels should be excluded. * * Example: * \code{.cpp} * DetectRoi droi; * droi.add({{200, 500}, {500, 500}, {500, 200}}, ShapedRoiType::Polygon); // add a triangle * Mat mask; // result mask, could be empty if no pixel should be excluded * Mat roiImg = droi.apply(img, &mask); // result image, used for detecton later * \endcode * */ class DetectRoi : public ICyclopsModuleInstance { /*! \fn getAngle * Get value of main direction of roi, see also setAngle() */ DECLARE_PARAMETER_GET(float, Angle) /*! \fn getScale * Get value of scale factor of the entire roi, see also setScale() */ DECLARE_PARAMETER_GET(float, Scale) /*! \fn setInterpolationFlags * Define interpolation algorithm used to apply warp affine, default to INTER_AREA, * refer to cv::InterpolationFlags for detail explanation of flags. see also getInterpolationFlags() * \fn getInterpolationFlags * Get value of interpolation algorithm used to apply warp affine, see also setInterpolationFlags() */ DECLARE_PARAMETER(int, InterpolationFlags) /*! \fn setBorderMode * Define pixel extrapolation method (used to build the borders), default to BORDER_CONSTANT (means use constant value), * see also getBorderMode() * \fn getBorderMode * Get value of pixel extrapolatiuon method (used to build the borders), see also setBorderMode() */ DECLARE_PARAMETER(int, BorderMode) /*! \fn getReserveSize * Get value of a small size of reserved area around the roi, see also setReserveSize() */ DECLARE_PARAMETER_GET(int, ReserveSize) /*! \fn setFractionalBit * Define number of fractional bits in the point coordinates, used to draw masks in floating value. * Points are calculated as Point2f(x * 2 ^ -shift, y * 2 ^ -shift). * So for example, for shift = 2, we'll have 0.25-pixel precision. * default to 0, see also getFractionalBit() * \fn getFractionalBit * Get value of number of fractional bits in the point coordinates, see also setFractionalBit() */ DECLARE_PARAMETER(int, FractionalBit) /*! \fn setMaskOnly * A mask-only roi will not do image crop or rotation, apply() returns original image and generated mask * default to false, see also getMaskOnly() * \fn getMaskOnly * Get whether this is a mask-only roi, see also setMaskOnly() */ DECLARE_PARAMETER(bool, MaskOnly) public: DetectRoi() : mAngle(0), mScale(1.f), mDirtyBit(false), mInterpolationFlags(INTER_AREA), mBorderMode(BORDER_CONSTANT), mReserveSize(0), mFractionalBit(0), mMaskOnly(false) {} virtual ~DetectRoi() {} //! Smart pointer to hold an instance of CircleDetector typedef std::shared_ptr Ptr; DECL_GET_INSTANCE(DetectRoi::Ptr) /*! \fn serializeToMemory * Serialize information of this detect roi into a in-memory string, see also deserializeFromMemory() * @param str used to take the output serialization result * @return true for succeed, false for fail * \fn serializeToFile * Serialize information of this detect roi into a text file, see also deserializeFromFile() * @param filename file name (full path) where we will write the data * @return true for succeed, false for fail * \fn deserializeFromMemory * Deserialize the detect roi from in-memory string, see also serializeToMemory() * @param str in-memory string * @return true for succeed, false for fail * \fn deserializeFromFile * Deserialize the detect roi from a text file, see also serializeToFile() * @param filename file name (full path) where we will read the data * @return true for succeed, false for fail */ DECL_SERIALIZE_FUNCS /*! reset all sub-rois added before */ virtual void reset(); /*! check if this is an empty roi before apply */ virtual bool empty() const; /*! Define main direction of roi, default to 0, see also getAngle(), We'll rotate the image to the defined angle.*/ virtual void setAngle(float a); /*! @overload, set main direction angle via vector */ virtual void setAngle(const Point2f& dir); /*! Define scale factor of the entire roi, 0 to 2, default to 1 means no scale, see also getScale() */ virtual void setScale(float s); /*! Define a small size of reserved area around the roi, default to 0, see also getReserveSize() */ virtual void setReserveSize(int s); /*! Add a new sub-roi to include more pixels, see also sub() * @param roiVertexes roi vertexes.\n * for Rectangle and Polygon, it should be the corner vertexes;\n * for Circle, it should be the center and a point on the circle;\n * for Annulus, it should be the center, one point on the inner circle and one point on the outer circle;\n * for Mask, it should be the position of mask' top-left + corner of all white pixels' bounding convex hull, see also genBoundingHullForMask(). * @param pMask custom mask for Mask sub-roi * @param type sub-roi type */ virtual void add(const Point2fVec& roiVertexes, ShapedRoiType type = ShapedRoiType::Rectangle, const Mat* pMask = nullptr); /*! Substract a new sub-roi to exclude more pixels, see also add() * @param roiVertexes roi vertexes.\n * for Rectangle and Polygon, it should be the corner vertexes;\n * for Circle, it should be the center and a point on the circle;\n * for Annulus, it should be the center, one point on the inner circle and one point on the outer circle;\n * for Mask, it should be the position of mask' top-left + corner of all white pixels' bounding convex hull, see also genBoundingHullForMask(). * @param pMask custom mask for Mask sub-roi * @param type sub-roi type */ virtual void sub(const Point2fVec& roiVertexes, ShapedRoiType type = ShapedRoiType::Rectangle, const Mat* pMask = nullptr); /*! Apply the roi to provided image, with no rotation * @param img input image * @param opMask output mask, set to nullptr if you don't need the mask.\n * Note it may be empty if no pixel should be excluded, say the roi is just a rectangle. * @return result image within the roi */ virtual Mat apply(const Mat& img, Mat* opMask = nullptr); /*! Apply the roi to provided image, rotated to the provided direction * @param img input image * @param angle main direction * @param opMask output mask, set to nullptr if you don't need the mask.\n * Note it may be empty if no pixel should be excluded, say the roi is just a rectangle. * @return result image within the roi */ DEPRECATED virtual Mat apply(const Mat& img, float angle, Mat* opMask = nullptr); /*! Apply the roi to provided image, rotated to the provided direction * @param img input image * @param dir main direction * @param opMask output mask, set to nullptr if you don't need the mask.\n * Note it may be empty if no pixel should be excluded, say the roi is just a rectangle. * @return result image within the roi */ DEPRECATED virtual Mat apply(const Mat& img, Point2f& dir, Mat* opMask = nullptr); /*! Total count of sub-roi used to include more pixels */ inline int addRoiCount() const { return mAddRois.size(); } /*! Total count of sub-roi used to exclude more pixels */ inline int subRoiCount() const { return mSubRois.size(); } /*! Get vertexes of sub-roi used to include more pixels, with given index */ inline const Point2fVec& getAddRoiVertexes(int idx) const { _ASSERTE(idx >= 0 && idx < addRoiCount()); return mAddRois[idx]->getVertexes(); } /*! Get vertexes of sub-roi used to exclude more pixels, with given index */ inline const Point2fVec& getSubRoiVertexes(int idx) const { _ASSERTE(idx >= 0 && idx < subRoiCount()); return mSubRois[idx]->getVertexes(); } inline Mat getInvTransMat() const { return mInvRotMat.clone(); } /*! Transform a collection of lines from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param lines input and output lines */ virtual void invTransform(vector& lines) const; /*! Transform a single line from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param line input and output line */ virtual void invTransform(Vec4f& line) const; /*! Transform a single point from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param pnt input and output point */ template void invTransform(Point_& pnt) const { invTransform(pnt.x, pnt.y); } /*! Transform a (x, y) position from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param x input and output x position * @param y input and output y position */ virtual void invTransform(float& x, float& y) const; /*! Transform a (x, y) position from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param x input and output x position * @param y input and output y position */ virtual void invTransform(double& x, double& y) const; /*! Transform a (x, y) position from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param x input and output x position * @param y input and output y position */ virtual void invTransform(int& x, int& y) const; /*! Transform a collection of points from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param pnts input and output point */ template void invTransform(vector>& pnts) const { if (empty()) return; // do nothing if this is an empty roi std::for_each(pnts.begin(), pnts.end(), [&](Point_& p) { invTransform(p); }); } /*! Transform a rotated rectangle from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param line input and output line */ virtual void invTransform(RotatedRect& rr) const; /*! Transform an angle from coordinates of roi, back to the coordinates of original image.\n * It should be done after you apply this roi to a image. * @param angle input and output angle */ virtual void invTransform(float& angle) const; /*! Transform a single point from coordinates of original image, to coordinates of roi\n * It should be done after you apply this roi to a image. * @param pnt input and output point */ template void transform(Point_& pnt) const { transform(pnt.x, pnt.y); } /*! Transform a (x, y) position from coordinates of original image, to coordinates of roi\n * It should be done after you apply this roi to a image. * @param x input and output x position * @param y input and output y position */ virtual void transform(float& x, float& y) const; /*! Transform a (x, y) position from coordinates of original image, to coordinates of roi\n * It should be done after you apply this roi to a image. * @param x input and output x position * @param y input and output y position */ virtual void transform(double& x, double& y) const; /*! Transform a (x, y) position from coordinates of original image, to coordinates of roi\n * It should be done after you apply this roi to a image. * @param x input and output x position * @param y input and output y position */ virtual void transform(int& x, int& y) const; /*! Transform a collection of points from coordinates of original image, to coordinates of roi\n * It should be done after you apply this roi to a image. * @param pnts input and output point */ template void transform(vector>& pnts) const { if (empty()) return; // do nothing if this is an empty roi std::for_each(pnts.begin(), pnts.end(), [&](Point_& p) { transform(p); }); } /*! \deprecated Apply the roi to provided image, with no rotation */ DEPRECATED Mat applyNoRotate(const Mat& img, Mat* opMask = nullptr); /*! Get bounding box of the roi */ virtual Rect getBoundingRect(); /*! Get new size of transformed image */ virtual Size getNewSize(); /*! Get bounding convex hull of the roi */ const Point2fVec& getBoundingHull() const { return mRoiVertexes; } /*! Get bounding rotated rect of the roi */ const RotatedRect& getBoundingRR() const { return mRoiRR; } /*! return a copy of roi that is moved by offset, relative to current position, P' = P + offset */ virtual DetectRoi translate(const Point2f& offset); /*! return a copy of roi that is scaled by factor, P' = P * s */ virtual DetectRoi scale(float xfactor, float yfactor); /*! return a copy of roi that a transform matrix is applied(either moved, scaled or rotated) */ virtual DetectRoi convert(const Mat& transMat); /*! Generate a mask. See also add()\n * 255 represent a valid pixel, 0 represent a excluded pixel * @param force force mask generation. If not set, the function will return an empty mask if roi is a straight rectangle * @param imgSize size of original image, used for mask-only roi * @return the mask */ virtual Mat genMask(bool force = false, const Size& imgSize = Size()); /*! Generate a mask with provided sub-roi. See also add()\n * 255 represent a valid pixel, 0 represent a excluded pixel * @param roiVertexes roi vertexes. * @param type sub-roi type * @return the mask */ static inline Mat genMask(const Point2fVec& roiVertexes, ShapedRoiType type = ShapedRoiType::Rectangle) { DetectRoi droi; droi.add(roiVertexes, type); return droi.genMask(); } /*! Generate bounding convex hull of a mask image */ static inline Point2fVec genBoundingHullForMask(const Mat& mask, const Point2f& tl) { Mat locateMat; findNonZero(mask, locateMat); vector hull; convexHull(locateMat, hull); Point2fVec ret; ret.reserve(hull.size() + 1); ret.push_back(tl); for (const Point& p : hull) { ret.push_back(Point2f(p.x + tl.x, p.y + tl.y)); } if (hull.size() == 1) { // one-dot, duplicated 4 points at the same position for (int i = 0; i < 3; i++) ret.push_back(ret.back()); } else if (hull.size() == 2) { // one-line, make a rect ret.push_back(ret[2]); ret.push_back(ret[1]); } return std::move(ret); } #if (CV_MAJOR_VERSION >= 3) /*! Draw out the mask with provided transformed image for displacement. * @param roiImg transformed image generated by DetectRoi * @return the result image with alpha channel */ virtual Mat highlightRoi(const Mat& roiImg); #endif /* decide if this is a rect roi with NO ROTATION */ virtual bool isComplexRoi(); private: struct ShapedRoiBase { ShapedRoiType type; ShapedRoiBase(ShapedRoiType _type = ShapedRoiType::Reserved) : type(_type) {} virtual bool fromVertexes(const Point2fVec& roiVertexes) = 0; virtual Point2fVec toVertexes() = 0; virtual const Point2fVec& getVertexes() const = 0; virtual Point2fVec mergeRoi(const Point2fVec& boundingHull) = 0; virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit) = 0; bool isValid() { return type != ShapedRoiType::Reserved; } typedef std::shared_ptr Ptr; }; struct RoiRectangle : public ShapedRoiBase { Point2fVec vertexes; RoiRectangle() : ShapedRoiBase(ShapedRoiType::Rectangle) { vertexes.resize(4); } virtual bool fromVertexes(const Point2fVec& roiVertexes); virtual Point2fVec toVertexes(); virtual const Point2fVec& getVertexes() const; virtual Point2fVec mergeRoi(const Point2fVec& boundingHull); virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit); }; struct RoiPolygon : public ShapedRoiBase { Point2fVec vertexes; RoiPolygon() : ShapedRoiBase(ShapedRoiType::Polygon) {} virtual bool fromVertexes(const Point2fVec& roiVertexes); virtual Point2fVec toVertexes(); virtual const Point2fVec& getVertexes() const; virtual Point2fVec mergeRoi(const Point2fVec& boundingHull); virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit); }; struct RoiCircle : public ShapedRoiBase { Point2f center; float radii; RoiCircle() : ShapedRoiBase(ShapedRoiType::Circle) {} virtual bool fromVertexes(const Point2fVec& roiVertexes); virtual Point2fVec toVertexes(); virtual const Point2fVec& getVertexes() const; virtual Point2fVec mergeRoi(const Point2fVec& boundingHull); virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit); void PaintCircle(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, float r, int fracBit); }; struct RoiAnnulus : public RoiCircle { float innerRadii; RoiAnnulus() : RoiCircle() { type = ShapedRoiType::Annulus; } virtual bool fromVertexes(const Point2fVec& roiVertexes); virtual Point2fVec toVertexes(); virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit); }; struct RoiMask : public RoiPolygon { Point2f pos; Mat customMask; RoiMask() : RoiPolygon() { type = ShapedRoiType::Mask; } virtual bool fromVertexes(const Point2fVec& roiVertexes); virtual Point2fVec toVertexes(); virtual const Point2fVec& getVertexes() const; virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit); }; struct RoiAnnulusSector : public RoiAnnulus { // point ordered by clock-wise Point2f p1, p2, midPnt, p3, p4; float startAngle, endAngle; RoiAnnulusSector() : RoiAnnulus() { type = ShapedRoiType::AnnulusSector; } void paintArc(Mat& mask, const Point& tl, const Scalar& c, float r, int fracBit); virtual bool fromVertexes(const Point2fVec& roiVertexes); virtual Point2fVec mergeRoi(const Point2fVec& boundingHull); virtual Point2fVec toVertexes(); virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit); }; struct RoiEllipse : public ShapedRoiBase { RoiEllipse() : ShapedRoiBase() { type = ShapedRoiType::Ellipse; } Point2f center, widthPnt, heightPnt; float width, height; virtual bool fromVertexes(const Point2fVec& roiVertexes); virtual Point2fVec mergeRoi(const Point2fVec& boundingHull); virtual Point2fVec toVertexes(); virtual void PaintMask(Mat& mask, const Point& tl, const Mat& rotMat, const Scalar& c, int fracBit); float genRotatedAngle(); virtual const Point2fVec& getVertexes() const; }; static ShapedRoiBase::Ptr genRoiFromType(ShapedRoiType type); bool prepare(const Size& imgSize); bool isPrepared() const; bool serialize(FileStorage& fs); bool deserialize(const FileNode& fs); void copyProps(DetectRoi& other); private: vector mAddRois; vector mSubRois; Point2fVec mRoiVertexes; // vertexes of bounding convex hull RotatedRect mRoiRR; // rotated bounding rect of mRoiVertexes Rect mBoundingRect; Size mNewSize; Mat mRotMat; Mat mInvRotMat; bool mDirtyBit; // dirty bit, true if rotation matrix is out-of-date. }; #endif // DetectRoi_h_