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.
742 lines
20 KiB
C++
742 lines
20 KiB
C++
/*!
|
|
* \file BlobInstance.h
|
|
* \date 2019/08/30
|
|
*
|
|
* \author Lin, Chi
|
|
* Contact: lin.chi@hzleaper.com
|
|
*
|
|
*
|
|
* \note
|
|
*/
|
|
|
|
#ifndef __BlobInstance_h_
|
|
#define __BlobInstance_h_
|
|
|
|
#include "CVUtils.h"
|
|
|
|
class BlobDetector;
|
|
/*! \brief Store geometric and optical properties for one single blob
|
|
*
|
|
* Most properties are lazy calculated, which means it's calculated on the first time its get function is called
|
|
*/
|
|
struct BlobInstance
|
|
{
|
|
BlobInstance(int _boundSize = 1) : boundSize(_boundSize) {}
|
|
BlobInstance(const vector<Point>& _contour, int _boundSize = 1) : contour(_contour), boundSize(_boundSize) {}
|
|
BlobInstance(vector<Point>&& _contour, int _boundSize = 1) : boundSize(_boundSize) {
|
|
contour = std::move(_contour);
|
|
}
|
|
|
|
/*! Set hole contours, calculate properties such as area more precisely considering holes */
|
|
inline void setHoleContour(const vector<vector<Point> >& holes) { holeContour = holes; }
|
|
/** @overload */
|
|
inline void setHoleContour(vector<vector<Point> >&& holes) { holeContour = std::move(holes); }
|
|
/*! Set source image for optical properties calculation, and also generate local mask */
|
|
inline void setSourceImg(const Mat& img);
|
|
/*! Set source soft mask for soft-thresholding */
|
|
inline void setSourceSoftMask(const Mat& softMask);
|
|
/*! Update confidence by factor */
|
|
inline void updateConfidence(float factor) { confidence *= factor; }
|
|
/*! Set transform matrix, if this blob is created inside ROI */
|
|
inline void setTransMat(const Mat& mat) {
|
|
transMat = mat;
|
|
// transform matrix updated, clean up cache
|
|
contour_transed.clear(); holeContour_transed.clear(); rr_transed.size = Size(0, 0);
|
|
}
|
|
|
|
/*! Get source image */
|
|
inline const Mat& getSourceImg();
|
|
/*! Get source image in gray scale */
|
|
inline const Mat& getGrayImg();
|
|
/*! Get mask of contour and holes */
|
|
inline const Mat& getMask();
|
|
|
|
/*! Get bounded mask of contour and holes */
|
|
inline const Mat& getMaskBounded();
|
|
/*! Get bounded source image in gray scale */
|
|
inline const Mat& getSourceImgBounded();
|
|
|
|
/*! Get blob contour, transform matrix is applied if there's one */
|
|
inline const vector<Point2f>& getContour();
|
|
/*! Get blob contour in integer, transform matrix is applied if there's one */
|
|
inline vector<Point> getContour2i();
|
|
/*! Get blob's hole contours, transform matrix is applied if there's one */
|
|
inline const vector<vector<Point2f> >& getHoleContour();
|
|
/*! Get confidence property */
|
|
inline float getConfidence() { return confidence; }
|
|
/*! Get area, holes are excluded */
|
|
inline float getArea();
|
|
/*! Get perimeter of blob's contour */
|
|
inline float getPerimeter();
|
|
/*! Get width of blob's rotated bounding rect */
|
|
inline float getWidth();
|
|
/*! Get height of blob's rotated bounding rect */
|
|
inline float getHeight();
|
|
/*! Get angle of blob's rotated bounding rect */
|
|
inline float getAngle();
|
|
enum AngleMode {
|
|
/*! Default, point to longer axis*/
|
|
Default = 0,
|
|
/*! Always return 0 */
|
|
Ignore,
|
|
/*! Always return -90 ~ 90 */
|
|
AlwaysUp
|
|
};
|
|
/** @overload */
|
|
inline float getAngle(AngleMode mode);
|
|
/** @overload */
|
|
inline float getAngle(int mode) { return getAngle(static_cast<AngleMode>(mode)); }
|
|
/*! Get circularity, closer to 1, much more likely it is a circle */
|
|
inline float getCircularity();
|
|
/*! Get convexity, closer to 1, much more likely it is a convex hull */
|
|
inline float getConvexity();
|
|
/*! Get inertia, closer to 1, much more likely it has the same length of minor and major axes, like a square */
|
|
inline float getInertia();
|
|
/*! Get center of gravity of blob */
|
|
inline Point2f getCenter();
|
|
/*! Get rotated bounding rect */
|
|
inline RotatedRect getBoundingRR();
|
|
/*! Get valley point of blob, which is the center of biggest flat field */
|
|
inline Point2f getValley();
|
|
|
|
/*! Get average luminance of blob */
|
|
inline float getLuminanceAverage();
|
|
/*! Get standard deviation luminance of blob */
|
|
inline float getLuminanceDeviation();
|
|
/*! Get variance luminance of blob */
|
|
inline float getLuminanceVariance();
|
|
/*! Get minimum luminance of blob */
|
|
inline float getLuminanceMin();
|
|
/*! Get maximum luminance of blob */
|
|
inline float getLuminanceMax();
|
|
/*! Get average of top n% brightest pixels in blob */
|
|
inline float getLuminanceTopNAvg(float topN);
|
|
/*! Get average of top n% darkest pixels in blob */
|
|
inline float getLuminanceBottomNAvg(float bottomN);
|
|
/*! Get majority luminance of blob */
|
|
inline float getLuminanceMajor();
|
|
|
|
/*! Get average contrast of blob */
|
|
inline float getContrastAverage();
|
|
/*! Get standard deviation contrast of blob */
|
|
inline float getContrastDeviation();
|
|
/*! Get variance contrast of blob */
|
|
inline float getContrastVariance();
|
|
/*! Get minimum contrast of blob */
|
|
inline float getContrastMin();
|
|
/*! Get maximum contrast of blob */
|
|
inline float getContrastMax();
|
|
/*! Get average of top n% pixels with biggest contrast in blob */
|
|
inline float getContrastTopNAvg(float topN);
|
|
/*! Get average of top n% pixels with smallest contrast in blob */
|
|
inline float getContrastBottomNAvg(float bottomN);
|
|
/*! Get majority contrast of blob */
|
|
inline float getContrastMajor();
|
|
|
|
/*! Get average color of blob, for greyscale image, it's the same as luminance */
|
|
inline Scalar getColorAverage();
|
|
/*! Get standard deviation color of blob, for greyscale image, it's the same as luminance */
|
|
inline Scalar getColorDeviation();
|
|
/*! Get variance color of blob, for greyscale image, it's the same as luminance */
|
|
inline Scalar getColorVariance();
|
|
|
|
/*! Get sharpness of blob, for color image, it's computed in greyscale */
|
|
inline float getSharpness();
|
|
|
|
protected:
|
|
inline void calcMoms();
|
|
inline float getContourArea();
|
|
inline const Rect& getBoundingRect();
|
|
inline bool hasProps(int val);
|
|
inline void setProps(int val, bool on = true);
|
|
inline void applyTrans(const vector<Point>& pnts, vector<Point2f>& transed);
|
|
inline Point2f applyTrans(const Point2f& p);
|
|
inline bool hasImage() { return !sourceImg.empty(); }
|
|
inline const Mat& getContrastImg();
|
|
inline std::vector<uchar>& getGrayPixels();
|
|
inline std::vector<uchar>& getContrastPixels();
|
|
|
|
protected:
|
|
vector<Point> contour;
|
|
vector<vector<Point> > holeContour; // no hole contour if detector told us to
|
|
float confidence = 1.;
|
|
|
|
int boundSize; // bounded size
|
|
|
|
Mat sourceImg; // source image for advanced properties calculation
|
|
Mat sourceImgBounded; // n pixel bounded, shared memory with sourceImg
|
|
Mat sourceSoftMask; // source soft mask
|
|
Mat sourceSoftMaskBounded; // n pixel bounded, shared memory with sourceSoftMask
|
|
|
|
Mat sourceMask; // source mask for advanced properties calculation
|
|
Mat sourceMaskBounded; // n pixel bounded, shared memory with sourceMask
|
|
|
|
Mat sourceImgGray; // optional
|
|
Mat sourceImgContrast; // optional
|
|
std::vector<uchar> grayPixels; // optional
|
|
std::vector<uchar> contrastPixels; // optional
|
|
|
|
Mat transMat; // matrix for perform inverted transform in roi
|
|
vector<Point2f> contour_transed;
|
|
vector<vector<Point2f> > holeContour_transed;
|
|
RotatedRect rr_transed;
|
|
|
|
enum BlobProp {
|
|
Reserved = 0,
|
|
Area = 0x00000001, // pixel-count-area
|
|
AreaContour = 0x00000002, // green-formula-area of outer contour
|
|
Perimeter = 0x00000004,
|
|
RR = 0x00000008, // rotated bounding rect, width, height, angle
|
|
Circularity = 0x00000010,
|
|
Convexity = 0x00000020,
|
|
Mom = 0x00000040, // inertia, center
|
|
BR = 0x00000080, // bounding rect
|
|
Valley = 0x00000100,
|
|
LuminanceMinMax = 0x00000200,
|
|
LuminanceAvgDev = 0x00000400,
|
|
ContrastMinMax = 0x00000800,
|
|
ContrastAvgDev = 0x00001000,
|
|
Color = 0x00004000,
|
|
Shapness = 0x00008000,
|
|
LuminanceMajor = 0x00010000,
|
|
ContrastMajor = 0x00020000,
|
|
};
|
|
int props = BlobProp::Reserved; // at most, 32 kinds of properties
|
|
|
|
float area;
|
|
float perimeter;
|
|
RotatedRect rr;
|
|
float circularity;
|
|
float convexity;
|
|
float inertia;
|
|
Point2f center;
|
|
float cArea;
|
|
Rect r;
|
|
Point2f valley;
|
|
|
|
float lumMin;
|
|
float lumMax;
|
|
float lumAvg;
|
|
float lumDev;
|
|
float lumMajor;
|
|
|
|
float contrastMin;
|
|
float contrastMax;
|
|
float contrastAvg;
|
|
float contrastDev;
|
|
float contrastMajor;
|
|
|
|
float sharp;
|
|
|
|
Scalar colorAvg;
|
|
Scalar colorDev;
|
|
};
|
|
|
|
float BlobInstance::getArea()
|
|
{
|
|
if (!hasProps(BlobProp::Area)) {
|
|
area = sum(getMask())[0] / 255;
|
|
setProps(BlobProp::Area);
|
|
}
|
|
return area;
|
|
}
|
|
|
|
float BlobInstance::getPerimeter()
|
|
{
|
|
if (!hasProps(BlobProp::Perimeter)) {
|
|
perimeter = arcLength(contour, true);
|
|
setProps(BlobProp::Perimeter);
|
|
}
|
|
return perimeter;
|
|
}
|
|
|
|
float BlobInstance::getWidth()
|
|
{
|
|
return getBoundingRR().size.width;
|
|
}
|
|
|
|
float BlobInstance::getHeight()
|
|
{
|
|
return getBoundingRR().size.height;
|
|
}
|
|
|
|
float BlobInstance::getAngle()
|
|
{
|
|
return getBoundingRR().angle;
|
|
}
|
|
|
|
float BlobInstance::getAngle(AngleMode mode)
|
|
{
|
|
if (mode == AngleMode::Ignore) return 0;
|
|
float a = getAngle();
|
|
if (mode == AngleMode::AlwaysUp) {
|
|
if (a < -45) return a; // -90 ~ -45
|
|
else if (a > 45) return a - 180; // 45 ~ 90
|
|
else return a - 90; // -45 ~ 45
|
|
}
|
|
return a; // default, point to longer axis
|
|
}
|
|
|
|
float BlobInstance::getCircularity()
|
|
{
|
|
if (!hasProps(BlobProp::Circularity)) {
|
|
circularity = 4 * CV_PI * getContourArea() / (getPerimeter() * getPerimeter());
|
|
setProps(BlobProp::Circularity);
|
|
}
|
|
return circularity;
|
|
}
|
|
|
|
float BlobInstance::getConvexity()
|
|
{
|
|
if (!hasProps(BlobProp::Convexity)) {
|
|
convexity = getContourArea() / getConvexHullArea(contour);
|
|
setProps(BlobProp::Convexity);
|
|
}
|
|
return convexity;
|
|
}
|
|
|
|
float BlobInstance::getInertia()
|
|
{
|
|
calcMoms();
|
|
return inertia;
|
|
}
|
|
|
|
Point2f BlobInstance::getCenter()
|
|
{
|
|
calcMoms();
|
|
return applyTrans(center);
|
|
}
|
|
|
|
RotatedRect BlobInstance::getBoundingRR()
|
|
{
|
|
if (!hasProps(BlobProp::RR)) {
|
|
rr = minAreaRect2(contour);
|
|
setProps(BlobProp::RR);
|
|
}
|
|
|
|
if (rr_transed.size.empty()) {
|
|
rr_transed = rr;
|
|
if (!transMat.empty()) {
|
|
transRotateRect23(rr_transed, (double*)transMat.data);
|
|
}
|
|
}
|
|
|
|
return rr_transed;
|
|
}
|
|
|
|
const Rect& BlobInstance::getBoundingRect()
|
|
{
|
|
if (!hasProps(BlobProp::BR)) {
|
|
r = boundingRect(contour);
|
|
setProps(BlobProp::BR);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
bool BlobInstance::hasProps(int val)
|
|
{
|
|
return (props & val) != 0;
|
|
}
|
|
|
|
void BlobInstance::setProps(int val, bool on)
|
|
{
|
|
on ? (props |= val) : (props &= (~val));
|
|
}
|
|
|
|
void BlobInstance::applyTrans(const vector<Point>& pnts, vector<Point2f>& transed)
|
|
{
|
|
bool doTrans = !transMat.empty();
|
|
double* p_mat = (double*)transMat.data;
|
|
int pcount = pnts.size();
|
|
transed.clear();
|
|
transed.reserve(pcount);
|
|
for (int i = 0; i < pcount; ++i) {
|
|
Point2f p = pnts[i];
|
|
if (doTrans) {
|
|
transPoint23<double, float>(p.x, p.y, p_mat);
|
|
}
|
|
transed.push_back(std::move(p));
|
|
}
|
|
}
|
|
|
|
Point2f BlobInstance::applyTrans(const Point2f& p)
|
|
{
|
|
if (transMat.empty()) return p;
|
|
Point2f p2 = p;
|
|
transPoint23<double, float>(p2.x, p2.y, (double*)(transMat.data));
|
|
return p2;
|
|
}
|
|
|
|
const Mat& BlobInstance::getSourceImg()
|
|
{
|
|
return sourceImg;
|
|
}
|
|
|
|
const Mat& BlobInstance::getGrayImg()
|
|
{
|
|
if (sourceImgGray.empty()) {
|
|
ensureGrayImg(sourceImg, sourceImgGray);
|
|
}
|
|
return sourceImgGray;
|
|
}
|
|
|
|
const Mat& BlobInstance::getContrastImg()
|
|
{
|
|
if (sourceImgContrast.empty()) {
|
|
gradiant(getGrayImg(), sourceImgContrast, 3, 3);
|
|
}
|
|
return sourceImgContrast;
|
|
}
|
|
|
|
const Mat& BlobInstance::getMask()
|
|
{
|
|
if (sourceMask.empty()) {
|
|
// generate local mask
|
|
sourceMaskBounded = Mat::zeros(sourceImg.rows + boundSize * 2,
|
|
sourceImg.cols + boundSize * 2, CV_8U);
|
|
sourceMask = Mat(sourceMaskBounded, Rect(boundSize, boundSize, sourceImg.cols, sourceImg.rows));
|
|
createMaskByContour(sourceMask, contour, holeContour, getBoundingRect().tl(), true);
|
|
if (!sourceSoftMask.empty()) {
|
|
sourceMask &= sourceSoftMask;
|
|
}
|
|
}
|
|
return sourceMask;
|
|
}
|
|
|
|
const Mat& BlobInstance::getMaskBounded()
|
|
{
|
|
if (sourceMaskBounded.empty()) {
|
|
getMask(); // delegate to generate mask
|
|
}
|
|
return sourceMaskBounded;
|
|
}
|
|
|
|
const Mat& BlobInstance::getSourceImgBounded()
|
|
{
|
|
return sourceImgBounded;
|
|
}
|
|
|
|
std::vector<uchar>& BlobInstance::getGrayPixels()
|
|
{
|
|
if (grayPixels.empty()) {
|
|
grayPixels = cvtMat2Vec<uchar>(getGrayImg(), getMask());
|
|
}
|
|
return grayPixels;
|
|
}
|
|
|
|
std::vector<uchar>& BlobInstance::getContrastPixels()
|
|
{
|
|
if (contrastPixels.empty()) {
|
|
contrastPixels = cvtMat2Vec<uchar>(getContrastImg(), getMask());
|
|
}
|
|
return contrastPixels;
|
|
}
|
|
|
|
Point2f BlobInstance::getValley()
|
|
{
|
|
if (!hasProps(BlobProp::Valley)) {
|
|
Point tl(getBoundingRect().x - boundSize, getBoundingRect().y - boundSize);
|
|
valley = detectValley(getMaskBounded());
|
|
valley.x += (getBoundingRect().x - boundSize); // valley position is detected on bounded mask.
|
|
valley.y += (getBoundingRect().y - boundSize);
|
|
setProps(BlobProp::Valley);
|
|
}
|
|
return applyTrans(valley);
|
|
}
|
|
|
|
float BlobInstance::getLuminanceAverage()
|
|
{
|
|
if (!hasProps(BlobProp::LuminanceAvgDev)) {
|
|
if (!hasImage()) return 0;
|
|
getColorAverage(); // delegate to average color calculation
|
|
if (sourceImg.channels() == 1) {
|
|
lumAvg = colorAvg[0]; lumDev = colorDev[0];
|
|
}
|
|
else {
|
|
lumAvg = (colorAvg[0] + colorAvg[1] + colorAvg[2]) / 3;
|
|
lumDev = (colorDev[0] + colorDev[1] + colorDev[2]) / 3;
|
|
}
|
|
setProps(BlobProp::LuminanceAvgDev);
|
|
}
|
|
return lumAvg;
|
|
}
|
|
|
|
float BlobInstance::getLuminanceDeviation()
|
|
{
|
|
if (!hasProps(BlobProp::LuminanceAvgDev)) {
|
|
getLuminanceAverage(); // delegate to average luminance calculation
|
|
}
|
|
return lumDev;
|
|
}
|
|
|
|
float BlobInstance::getLuminanceVariance()
|
|
{
|
|
float v = getLuminanceDeviation();
|
|
return v * v;
|
|
}
|
|
|
|
float BlobInstance::getLuminanceMin()
|
|
{
|
|
if (!hasProps(BlobProp::LuminanceMinMax)) {
|
|
if (!hasImage()) return 0;
|
|
double minVal, maxVal;
|
|
minMaxLoc(getGrayImg(), &minVal, &maxVal, nullptr, nullptr, getMask());
|
|
lumMin = minVal;
|
|
lumMax = maxVal;
|
|
setProps(BlobProp::LuminanceMinMax);
|
|
}
|
|
return lumMin;
|
|
}
|
|
|
|
float BlobInstance::getLuminanceMax()
|
|
{
|
|
if (!hasProps(BlobProp::LuminanceMinMax)) {
|
|
getLuminanceMin(); // delegate to minimum luminance calculation
|
|
}
|
|
return lumMax;
|
|
}
|
|
|
|
float BlobInstance::getLuminanceTopNAvg(float topN)
|
|
{
|
|
vector<uchar>& grayPixels = getGrayPixels();
|
|
int topNCount = grayPixels.size() * topN / 100;
|
|
partial_sort(grayPixels.begin(), grayPixels.begin() + topNCount, grayPixels.end(),
|
|
[](const uchar& v1, const uchar& v2) { return v1 > v2; });
|
|
Mat topNMat(1, topNCount, CV_8U, grayPixels.data());
|
|
return mean(topNMat)[0];
|
|
}
|
|
|
|
float BlobInstance::getLuminanceBottomNAvg(float bottomN)
|
|
{
|
|
vector<uchar>& grayPixels = getGrayPixels();
|
|
int bottomNCount = grayPixels.size() * bottomN / 100;
|
|
partial_sort(grayPixels.begin(), grayPixels.begin() + bottomNCount, grayPixels.end(),
|
|
[](const uchar& v1, const uchar& v2) { return v1 < v2; });
|
|
Mat bottomNMat(1, bottomN, CV_8U, grayPixels.data());
|
|
return mean(bottomNMat)[0];
|
|
}
|
|
|
|
float BlobInstance::getLuminanceMajor()
|
|
{
|
|
if (!hasProps(BlobProp::LuminanceMajor)) {
|
|
if (!hasImage()) return 0;
|
|
lumMajor = getMajor(getGrayImg(), getMask());
|
|
setProps(BlobProp::LuminanceMajor);
|
|
}
|
|
return lumMajor;
|
|
}
|
|
|
|
float BlobInstance::getContrastAverage()
|
|
{
|
|
if (!hasProps(BlobProp::ContrastAvgDev)) {
|
|
if (!hasImage()) return 0;
|
|
Scalar meanVal, devVal;
|
|
meanStdDev(getContrastImg(), meanVal, devVal, getMask());
|
|
contrastAvg = meanVal[0];
|
|
contrastDev = devVal[0];
|
|
setProps(BlobProp::ContrastAvgDev);
|
|
}
|
|
return contrastAvg;
|
|
}
|
|
|
|
float BlobInstance::getContrastDeviation()
|
|
{
|
|
if (!hasProps(BlobProp::ContrastAvgDev)) {
|
|
getContrastAverage(); // delegate to average contrast calculation
|
|
}
|
|
return contrastDev;
|
|
}
|
|
|
|
float BlobInstance::getContrastVariance()
|
|
{
|
|
float v = getContrastDeviation();
|
|
return v * v;
|
|
}
|
|
|
|
float BlobInstance::getContrastMin()
|
|
{
|
|
if (!hasProps(BlobProp::ContrastMinMax)) {
|
|
if (!hasImage()) return 0;
|
|
double minVal, maxVal;
|
|
minMaxLoc(getContrastImg(), &minVal, &maxVal, nullptr, nullptr, getMask());
|
|
contrastMin = minVal;
|
|
contrastMax = maxVal;
|
|
setProps(BlobProp::ContrastMinMax);
|
|
}
|
|
return contrastMin;
|
|
}
|
|
|
|
float BlobInstance::getContrastMax()
|
|
{
|
|
if (!hasProps(BlobProp::ContrastMinMax)) {
|
|
getContrastMin(); // delegate to minimum contrast calculation
|
|
}
|
|
return contrastMax;
|
|
}
|
|
|
|
float BlobInstance::getContrastTopNAvg(float topN)
|
|
{
|
|
vector<uchar>& contrastPixels = getContrastPixels();
|
|
int topNCount = contrastPixels.size() * topN / 100;
|
|
partial_sort(contrastPixels.begin(), contrastPixels.begin() + topNCount, contrastPixels.end(),
|
|
[](const uchar& v1, const uchar& v2) { return v1 > v2; });
|
|
Mat topNMat(1, topNCount, CV_8U, contrastPixels.data());
|
|
return mean(topNMat)[0];
|
|
}
|
|
|
|
float BlobInstance::getContrastBottomNAvg(float bottomN)
|
|
{
|
|
vector<uchar>& contrastPixels = getContrastPixels();
|
|
int bottomNCount = contrastPixels.size() * bottomN / 100;
|
|
partial_sort(contrastPixels.begin(), contrastPixels.begin() + bottomNCount, contrastPixels.end(),
|
|
[](const uchar& v1, const uchar& v2) { return v1 < v2; });
|
|
Mat bottomNMat(1, bottomN, CV_8U, contrastPixels.data());
|
|
return mean(bottomNMat)[0];
|
|
}
|
|
|
|
float BlobInstance::getContrastMajor()
|
|
{
|
|
if (!hasProps(BlobProp::ContrastMajor)) {
|
|
if (!hasImage()) return 0;
|
|
contrastMajor = getMajor(getContrastImg(), getMask());
|
|
setProps(BlobProp::ContrastMajor);
|
|
}
|
|
return contrastMajor;
|
|
}
|
|
|
|
float BlobInstance::getSharpness()
|
|
{
|
|
if (!hasProps(BlobProp::Shapness)) {
|
|
if (!hasImage()) return 0;
|
|
sharp = sharpness(getGrayImg(), &getMask(), nullptr);
|
|
setProps(BlobProp::Shapness);
|
|
}
|
|
return sharp;
|
|
}
|
|
|
|
Scalar BlobInstance::getColorAverage()
|
|
{
|
|
if (!hasProps(BlobProp::Color)) {
|
|
if (!hasImage()) return Scalar();
|
|
meanStdDev(sourceImg, colorAvg, colorDev, getMask());
|
|
setProps(BlobProp::Color);
|
|
}
|
|
return colorAvg;
|
|
}
|
|
|
|
Scalar BlobInstance::getColorDeviation()
|
|
{
|
|
if (!hasProps(BlobProp::Color)) {
|
|
getColorAverage(); // delegate to average color calculation
|
|
}
|
|
return colorDev;
|
|
}
|
|
|
|
Scalar BlobInstance::getColorVariance()
|
|
{
|
|
Scalar v = getColorDeviation();
|
|
return Scalar(v[0] * v[0], v[1] * v[1], v[2] * v[2]);
|
|
}
|
|
|
|
void BlobInstance::calcMoms()
|
|
{
|
|
if (!hasProps(BlobProp::Mom)) {
|
|
Moments moms = moments(contour);
|
|
inertia = GeomUtils::getInertia(moms);
|
|
if (!sourceSoftMask.empty()) {
|
|
Mat m = getMask();
|
|
center = findMaskCenter(m);
|
|
Rect r = getBoundingRect();
|
|
center.x += r.x;
|
|
center.y += r.y;
|
|
}
|
|
else if (moms.m00 == 0)
|
|
center = getBoundingRR().center;
|
|
else
|
|
center = Point2f(moms.m10 / moms.m00, moms.m01 / moms.m00);
|
|
setProps(BlobProp::Mom);
|
|
}
|
|
}
|
|
|
|
float BlobInstance::getContourArea()
|
|
{
|
|
if (!hasProps(BlobProp::AreaContour)) {
|
|
cArea = contourArea(contour);
|
|
setProps(BlobProp::AreaContour);
|
|
}
|
|
return cArea;
|
|
}
|
|
|
|
void BlobInstance::setSourceImg(const Mat& img)
|
|
{
|
|
const Rect& r = getBoundingRect();
|
|
Rect rBounded = scaleRect(r, boundSize);
|
|
if (rBounded.x >= 0 && rBounded.y >= 0 &&
|
|
rBounded.x + rBounded.width < img.cols && rBounded.y + rBounded.height < img.rows) {
|
|
sourceImgBounded = Mat(img, rBounded);
|
|
sourceImg = Mat(img, r);
|
|
}
|
|
else {
|
|
sourceImgBounded = Mat::zeros(rBounded.size(), CV_8U);
|
|
copyMakeBorder(Mat(img, r), sourceImgBounded, boundSize, boundSize, boundSize, boundSize, BORDER_REPLICATE);
|
|
sourceImg = Mat(sourceImgBounded, Rect(boundSize, boundSize, r.width, r.height));
|
|
}
|
|
}
|
|
|
|
void BlobInstance::setSourceSoftMask(const Mat& softMask)
|
|
{
|
|
sourceSoftMask = Mat(softMask, getBoundingRect());
|
|
const Rect& r = getBoundingRect();
|
|
Rect rBounded = scaleRect(r, boundSize);
|
|
if (rBounded.x >= 0 && rBounded.y >= 0 &&
|
|
rBounded.x + rBounded.width < softMask.cols && rBounded.y + rBounded.height < softMask.rows) {
|
|
sourceSoftMaskBounded = Mat(softMask, rBounded);
|
|
sourceSoftMask = Mat(softMask, r);
|
|
}
|
|
else {
|
|
sourceSoftMaskBounded = Mat::zeros(rBounded.size(), CV_8U);
|
|
sourceSoftMask = Mat(sourceSoftMaskBounded, Rect(boundSize, boundSize, r.width, r.height));
|
|
Mat(softMask, r).copyTo(sourceSoftMask);
|
|
}
|
|
}
|
|
|
|
const vector<Point2f>& BlobInstance::getContour()
|
|
{
|
|
if (!contour_transed.empty()) {
|
|
return contour_transed;
|
|
}
|
|
|
|
applyTrans(contour, contour_transed);
|
|
|
|
return contour_transed;
|
|
}
|
|
|
|
vector<Point> BlobInstance::getContour2i()
|
|
{
|
|
if (transMat.empty()) {
|
|
return contour;
|
|
}
|
|
else {
|
|
const vector<Point2f>& ret = getContour();
|
|
return saturate_cast_points<int, float>(ret);
|
|
}
|
|
}
|
|
|
|
const vector<vector<cv::Point2f> >& BlobInstance::getHoleContour()
|
|
{
|
|
if (!holeContour_transed.empty()) {
|
|
return holeContour_transed;
|
|
}
|
|
|
|
int holeCount = holeContour.size();
|
|
for (int i = 0; i < holeCount; ++i) {
|
|
vector<Point2f> hole2;
|
|
applyTrans(holeContour[i], hole2);
|
|
holeContour_transed.push_back(std::move(hole2));
|
|
}
|
|
|
|
return holeContour_transed;
|
|
}
|
|
|
|
#endif // BlobInstance_h_
|