276 lines
6.5 KiB
C++
276 lines
6.5 KiB
C++
#include "BlobContour.h"
|
|
|
|
BlobContour::BlobContour()
|
|
{
|
|
m_startPoint.x = 0;
|
|
m_startPoint.y = 0;
|
|
m_area = -1;
|
|
m_perimeter = -1;
|
|
m_moments.m00 = -1;
|
|
m_parent = NULL;
|
|
}
|
|
|
|
BlobContour::BlobContour(const cv::Point& startPoint, const cv::Size& imageRes) : m_contour(1)
|
|
{
|
|
|
|
m_startPoint.x = startPoint.x;
|
|
m_startPoint.y = startPoint.y;
|
|
m_area = -1;
|
|
m_perimeter = -1;
|
|
m_moments.m00 = -1;
|
|
m_parent = NULL;
|
|
//Empirical calculations
|
|
if (imageRes.width == -1 || imageRes.width*imageRes.height > 62500) {
|
|
m_contour[0].reserve(600);
|
|
}
|
|
else {
|
|
//I reserve a portion of the image's area
|
|
m_contour[0].reserve(imageRes.height*imageRes.width/4);
|
|
}
|
|
}
|
|
|
|
//! Copy constructor
|
|
BlobContour::BlobContour(BlobContour* source)
|
|
{
|
|
if (source != NULL) {
|
|
*this = *source;
|
|
}
|
|
}
|
|
|
|
BlobContour::BlobContour(const BlobContour& source)
|
|
{
|
|
m_area = source.m_area;
|
|
m_contour = source.m_contour;
|
|
m_contourPoints = source.m_contourPoints;
|
|
m_moments = source.m_moments;
|
|
m_perimeter = source.m_perimeter;
|
|
m_startPoint = source.m_startPoint;
|
|
m_parent = NULL;
|
|
}
|
|
|
|
BlobContour::~BlobContour()
|
|
{
|
|
}
|
|
|
|
//! Copy operator
|
|
BlobContour& BlobContour::operator=(const BlobContour& source)
|
|
{
|
|
if (this != &source) {
|
|
m_startPoint = source.m_startPoint;
|
|
m_area = source.m_area;
|
|
m_perimeter = source.m_perimeter;
|
|
m_moments = source.m_moments;
|
|
m_contour = source.m_contour;
|
|
m_contourPoints = source.m_contourPoints;
|
|
}
|
|
m_parent = NULL;
|
|
return *this;
|
|
}
|
|
|
|
/**
|
|
- FUNCIÓ: addChainCode
|
|
- FUNCIONALITAT: Add chain code to contour
|
|
- PARÀMETRES:
|
|
-
|
|
- RESULTAT:
|
|
-
|
|
- RESTRICCIONS:
|
|
-
|
|
- AUTOR: rborras
|
|
- DATA DE CREACIÓ: 2008/05/06
|
|
- MODIFICACIÓ: Data. Autor. Descripció.
|
|
*/
|
|
void BlobContour::addChainCode(ChainCode chaincode)
|
|
{
|
|
m_contour[0].push_back(chaincode);
|
|
}
|
|
|
|
//! Clears chain code contour and points
|
|
void BlobContour::reset()
|
|
{
|
|
m_contour.clear();
|
|
m_contourPoints.clear();
|
|
}
|
|
|
|
/**
|
|
- FUNCIÓ: getPerimeter
|
|
- FUNCIONALITAT: Get perimeter from chain code. Diagonals sum sqrt(2) and horizontal and vertical codes 1
|
|
- PARÀMETRES:
|
|
-
|
|
- RESULTAT:
|
|
-
|
|
- RESTRICCIONS:
|
|
-
|
|
- AUTOR: rborras
|
|
- DATA DE CREACIÓ: 2008/04/30
|
|
- MODIFICACIÓ: Data. Autor. Descripció.
|
|
- NOTA: Algorithm derived from "Methods to estimate area and perimeters of blob-like objects: A comparison", L.Yang
|
|
*/
|
|
double BlobContour::getPerimeter()
|
|
{
|
|
// is calculated?
|
|
if (m_perimeter != -1) {
|
|
return m_perimeter;
|
|
}
|
|
|
|
if (isEmpty()) {
|
|
return 0;
|
|
}
|
|
|
|
m_perimeter = cv::arcLength(getContourPoints(), true);
|
|
return m_perimeter;
|
|
}
|
|
|
|
/**
|
|
- FUNCIÓ: getArea
|
|
- FUNCIONALITAT: Computes area from chain code
|
|
- PARÀMETRES:
|
|
-
|
|
- RESULTAT:
|
|
- May give negative areas for clock wise contours
|
|
- RESTRICCIONS:
|
|
-
|
|
- AUTOR: rborras
|
|
- DATA DE CREACIÓ: 2008/04/30
|
|
- MODIFICACIÓ: Data. Autor. Descripció.
|
|
- NOTA: Algorithm derived from "Properties of contour codes", G.R. Wilson
|
|
*/
|
|
double BlobContour::getArea()
|
|
{
|
|
// is calculated?
|
|
if (m_area != -1) {
|
|
return m_area;
|
|
}
|
|
|
|
if (isEmpty()) {
|
|
return 0;
|
|
}
|
|
m_area = std::fabs(contourArea(getContourPoints(), false));
|
|
|
|
return m_area;
|
|
}
|
|
|
|
//! Get contour moment (p,q up to MAX_CALCULATED_MOMENTS)
|
|
double BlobContour::getMoment(int p, int q)
|
|
{
|
|
// is a valid moment?
|
|
if (p < 0 || q < 0 || p > MAX_MOMENTS_ORDER || q > MAX_MOMENTS_ORDER) {
|
|
return -1;
|
|
}
|
|
|
|
if (isEmpty()) {
|
|
return 0;
|
|
}
|
|
|
|
// it is calculated?
|
|
if (m_moments.m00 == -1) {
|
|
//cvMoments(getContourPoints(), &m_moments);
|
|
m_moments = cvMoments(moments(getContourPoints(), true));
|
|
}
|
|
|
|
return cvGetSpatialMoment(&m_moments, p, q);
|
|
|
|
}
|
|
|
|
const PointList BlobContour::EMPTY_LIST = PointList();
|
|
|
|
//! Calculate contour points from crack codes
|
|
const PointList& BlobContour::getContourPoints()
|
|
{
|
|
if (m_contour.size() == 0) {
|
|
return EMPTY_LIST;
|
|
}
|
|
if (m_contourPoints.size() != 0) {
|
|
return m_contourPoints[0];
|
|
}
|
|
m_contourPoints.push_back(PointList());
|
|
m_contourPoints[0].reserve(m_contour[0].size() + 1);
|
|
m_contourPoints[0].push_back(m_startPoint);
|
|
ChainCodeList::iterator it,en;
|
|
it = m_contour[0].begin();
|
|
en = m_contour[0].end();
|
|
cv::Point pt = m_contourPoints[0][m_contourPoints.size() - 1];
|
|
for (; it != en; ++it) {
|
|
pt = chainCode2Point(pt, *it);
|
|
m_contourPoints[0].push_back(pt);
|
|
}
|
|
return m_contourPoints[0];
|
|
}
|
|
|
|
Contours& BlobContour::getContours()
|
|
{
|
|
getContourPoints();
|
|
return m_contourPoints;
|
|
}
|
|
|
|
void BlobContour::shiftBlobContour(int x, int y)
|
|
{
|
|
m_startPoint.x += x;
|
|
m_startPoint.y += y;
|
|
|
|
for (unsigned int j = 0; j < m_contourPoints.size(); ++j) {
|
|
for (unsigned int i = 0; i < m_contourPoints[j].size(); ++i) {
|
|
m_contourPoints[j][i] += cv::Point(x, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
ChainCode points2ChainCode(const cv::Point& p1, const cv::Point& p2)
|
|
{
|
|
// /* Luca Nardelli & Saverio Murgia
|
|
// Freeman Chain Code:
|
|
// 321 Values indicate the chain code used to identify next pixel location.
|
|
// 4-0 If I join 2 blobs I can't just append the 2nd blob chain codes, since they will still start
|
|
// 567 from the 1st blob start point
|
|
// */
|
|
cv::Point diff = cv::Point(p2.x-p1.x, p2.y-p1.y);
|
|
if (diff.x == 1 && diff.y == 0) {
|
|
return 0;
|
|
}
|
|
else if (diff.x == 1 && diff.y == -1) {
|
|
return 1;
|
|
}
|
|
else if (diff.x == 0 && diff.y == -1) {
|
|
return 2;
|
|
}
|
|
else if (diff.x == -1 && diff.y == -1) {
|
|
return 3;
|
|
}
|
|
else if (diff.x == -1 && diff.y == 0) {
|
|
return 4;
|
|
}
|
|
else if (diff.x == -1 && diff.y == 1) {
|
|
return 5;
|
|
}
|
|
else if (diff.x == 0 && diff.y == 1) {
|
|
return 6;
|
|
}
|
|
else if (diff.x == 1 && diff.y == 1) {
|
|
return 7;
|
|
}
|
|
else {
|
|
return 200;
|
|
}
|
|
}
|
|
|
|
cv::Point chainCode2Point(const cv::Point& origin, ChainCode code) {
|
|
// /* Luca Nardelli & Saverio Murgia
|
|
// Freeman Chain Code:
|
|
// 321 Values indicate the chain code used to identify next pixel location.
|
|
// 4-0 If I join 2 blobs I can't just append the 2nd blob chain codes, since they will still start
|
|
// 567 from the 1st blob start point
|
|
// */
|
|
cv::Point pt = origin;
|
|
switch (code) {
|
|
case 0: pt.x++; break;
|
|
case 1: pt.x++; pt.y--; break;
|
|
case 2: pt.y--; break;
|
|
case 3: pt.x--; pt.y--; break;
|
|
case 4: pt.x--; break;
|
|
case 5: pt.x--; pt.y++; break;
|
|
case 6: pt.y++; break;
|
|
case 7: pt.x++; pt.y++; break;
|
|
}
|
|
return pt;
|
|
}
|