#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; }