You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

490 lines
19 KiB
C

/*!
* \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<DetectRoi> 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<Vec4f>& 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<typename T>
void invTransform(Point_<T>& 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<typename T>
void invTransform(vector<Point_<T>>& pnts) const {
if (empty()) return; // do nothing if this is an empty roi
std::for_each(pnts.begin(), pnts.end(), [&](Point_<T>& p) {
invTransform<T>(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<typename T>
void transform(Point_<T>& 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<typename T>
void transform(vector<Point_<T>>& pnts) const {
if (empty()) return; // do nothing if this is an empty roi
std::for_each(pnts.begin(), pnts.end(), [&](Point_<T>& p) {
transform<T>(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<Point> 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<ShapedRoiBase> 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<ShapedRoiBase::Ptr> mAddRois;
vector<ShapedRoiBase::Ptr> 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_