添加opencv验证工具
This commit is contained in:
parent
a326008bf5
commit
93627c8c23
30
examples/contour/Resource.h
Normal file
30
examples/contour/Resource.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
//{{NO_DEPENDENCIES}}
|
||||||
|
// Microsoft Visual C++ 生成的包含文件。
|
||||||
|
// 使用者 contour.rc
|
||||||
|
|
||||||
|
#define IDS_APP_TITLE 103
|
||||||
|
|
||||||
|
#define IDR_MAINFRAME 128
|
||||||
|
#define IDD_CONTOUR_DIALOG 102
|
||||||
|
#define IDD_ABOUTBOX 103
|
||||||
|
#define IDM_ABOUT 104
|
||||||
|
#define IDM_EXIT 105
|
||||||
|
#define IDI_CONTOUR 107
|
||||||
|
#define IDI_SMALL 108
|
||||||
|
#define IDC_CONTOUR 109
|
||||||
|
#define IDC_MYICON 2
|
||||||
|
#ifndef IDC_STATIC
|
||||||
|
#define IDC_STATIC -1
|
||||||
|
#endif
|
||||||
|
// 新对象的下一组默认值
|
||||||
|
//
|
||||||
|
#ifdef APSTUDIO_INVOKED
|
||||||
|
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||||
|
|
||||||
|
#define _APS_NO_MFC 130
|
||||||
|
#define _APS_NEXT_RESOURCE_VALUE 129
|
||||||
|
#define _APS_NEXT_COMMAND_VALUE 32771
|
||||||
|
#define _APS_NEXT_CONTROL_VALUE 1000
|
||||||
|
#define _APS_NEXT_SYMED_VALUE 110
|
||||||
|
#endif
|
||||||
|
#endif
|
301
examples/contour/basic_form.cpp
Normal file
301
examples/contour/basic_form.cpp
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
#include "basic_form.h"
|
||||||
|
|
||||||
|
#include <atlstr.h>
|
||||||
|
#include <afxdlgs.h>
|
||||||
|
#include <afxwin.h>
|
||||||
|
#include <io.h>
|
||||||
|
#include <iosfwd>
|
||||||
|
#include <iostream>
|
||||||
|
#include <ios>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
|
#include "opencv2/opencv.hpp"
|
||||||
|
#include "blob/blob.h"
|
||||||
|
#include "blob/BlobDetector.h"
|
||||||
|
#include "blob/BlobGroup.h"
|
||||||
|
#include "blob/BlobContour.h"
|
||||||
|
#include "i_contours_extractor.hpp"
|
||||||
|
#include "contours_extractor_factory.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const std::wstring BasicForm::kClassName = L"Basic";
|
||||||
|
|
||||||
|
BasicForm::BasicForm()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicForm::~BasicForm()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring BasicForm::GetSkinFolder()
|
||||||
|
{
|
||||||
|
return L"basic";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring BasicForm::GetSkinFile()
|
||||||
|
{
|
||||||
|
return L"contour.xml";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring BasicForm::GetWindowClassName() const
|
||||||
|
{
|
||||||
|
return kClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LRESULT BasicForm::OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
||||||
|
{
|
||||||
|
return WindowImplBase::OnNcHitTest(uMsg, wParam, lParam, bHandled);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string to_byte_string(const std::wstring& input)
|
||||||
|
{
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
|
||||||
|
return converter.to_bytes(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int bz2_read( struct bspatch_stream* stream, void* buffer, int length)
|
||||||
|
{
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<char>* ReadFile(std::string path) {
|
||||||
|
std::vector<char>* ret = nullptr;
|
||||||
|
std::ifstream file(path.c_str(), std::ios::in | std::ios::binary);
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
// <20><><EFBFBD>ն<EFBFBD><D5B6><EFBFBD><EFBFBD>Ƹ<EFBFBD>ʽ<EFBFBD><CABD>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD>
|
||||||
|
file.seekg(0, std::ios::end);
|
||||||
|
long long fileSize = file.tellg();
|
||||||
|
printf("size of firm: %lld\n", fileSize);
|
||||||
|
|
||||||
|
// <20><><EFBFBD><EFBFBD>дλ<D0B4><CEBB><EFBFBD>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>ͷ<EFBFBD><CDB7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ棬<DAB4><E6A3AC><EFBFBD>̼<EFBFBD><CCBC><EFBFBD><EFBFBD>ݴ<EFBFBD><DDB4><EFBFBD>buffer
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
char* buffer = new char[fileSize];
|
||||||
|
file.read(buffer, sizeof(char) * fileSize);
|
||||||
|
|
||||||
|
ret = new std::vector<char>(buffer, buffer + sizeof(char) * fileSize);
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << "Failed to open file." << std::endl;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
double smin_contour = 200.0;
|
||||||
|
double soffset_controu = 11;
|
||||||
|
double sepsilon = 2;
|
||||||
|
double ssmooth_step = 0.001;
|
||||||
|
double sangel_threshold = 20.0;
|
||||||
|
|
||||||
|
void BasicForm::InitWindow() {
|
||||||
|
|
||||||
|
ui::Button* btn_open1 = dynamic_cast<ui::Button*>(FindControl(L"btn_do_open1"));
|
||||||
|
ui::Button* btn_set_config = dynamic_cast<ui::Button*>(FindControl(L"btn_set_config"));
|
||||||
|
|
||||||
|
ui::RichEdit* min_contour = dynamic_cast<ui::RichEdit*>(FindControl(L"min_contour"));
|
||||||
|
ui::RichEdit* offset_controu = dynamic_cast<ui::RichEdit*>(FindControl(L"offset_controu"));
|
||||||
|
ui::RichEdit* epsilon = dynamic_cast<ui::RichEdit*>(FindControl(L"epsilon"));
|
||||||
|
ui::RichEdit* angel_threshold = dynamic_cast<ui::RichEdit*>(FindControl(L"angel_threshold"));
|
||||||
|
ui::RichEdit* smooth_step = dynamic_cast<ui::RichEdit*>(FindControl(L"smooth_step"));
|
||||||
|
ui::RichEdit* file1_dir = dynamic_cast<ui::RichEdit*>(FindControl(L"file1_dir"));
|
||||||
|
|
||||||
|
ui::Control* img1 = dynamic_cast<ui::Control*>(FindControl(L"img1"));
|
||||||
|
ui::Control* img2 = dynamic_cast<ui::Control*>(FindControl(L"img2"));
|
||||||
|
ui::Label* cnt = dynamic_cast<ui::Label*>(FindControl(L"concnt"));
|
||||||
|
|
||||||
|
min_contour->SetText(L"200.0"); // 最小轮廓阀值
|
||||||
|
offset_controu->SetText(L"11");
|
||||||
|
epsilon->SetText(L"2");
|
||||||
|
smooth_step->SetText(L"0.001");
|
||||||
|
angel_threshold->SetText(L"20.0");
|
||||||
|
|
||||||
|
btn_set_config->AttachClick([this, min_contour, offset_controu,
|
||||||
|
epsilon, smooth_step, angel_threshold,
|
||||||
|
file1_dir, img1, img2, cnt](ui::EventArgs*) {
|
||||||
|
size_t len = wcstombs(nullptr, min_contour->GetText().c_str(), 0) + 1;
|
||||||
|
char* buffer = new char[len];
|
||||||
|
wcstombs(buffer, min_contour->GetText().c_str(), len);
|
||||||
|
std::string str(buffer);
|
||||||
|
smin_contour = atof(str.c_str());
|
||||||
|
|
||||||
|
len = wcstombs(nullptr, offset_controu->GetText().c_str(), 0) + 1;
|
||||||
|
buffer = new char[len];
|
||||||
|
wcstombs(buffer, offset_controu->GetText().c_str(), len);
|
||||||
|
std::string str1(buffer);
|
||||||
|
soffset_controu = atof(str1.c_str());
|
||||||
|
|
||||||
|
len = wcstombs(nullptr, epsilon->GetText().c_str(), 0) + 1;
|
||||||
|
buffer = new char[len];
|
||||||
|
wcstombs(buffer, epsilon->GetText().c_str(), len);
|
||||||
|
std::string str2(buffer);
|
||||||
|
sepsilon = atof(str2.c_str());
|
||||||
|
|
||||||
|
|
||||||
|
len = wcstombs(nullptr, smooth_step->GetText().c_str(), 0) + 1;
|
||||||
|
buffer = new char[len];
|
||||||
|
wcstombs(buffer, smooth_step->GetText().c_str(), len);
|
||||||
|
std::string str3(buffer);
|
||||||
|
ssmooth_step = atof(str3.c_str());
|
||||||
|
|
||||||
|
len = wcstombs(nullptr, angel_threshold->GetText().c_str(), 0) + 1;
|
||||||
|
buffer = new char[len];
|
||||||
|
wcstombs(buffer, angel_threshold->GetText().c_str(), len);
|
||||||
|
std::string str4(buffer);
|
||||||
|
sangel_threshold = atof(str4.c_str());
|
||||||
|
|
||||||
|
std::cout<<smin_contour<<std::endl;
|
||||||
|
std::cout<<soffset_controu<<std::endl;
|
||||||
|
std::cout<<sepsilon<<std::endl;
|
||||||
|
std::cout<<ssmooth_step<<std::endl;
|
||||||
|
std::cout<<sangel_threshold<<std::endl;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
img1->SetBkImage(file1_dir->GetText());
|
||||||
|
|
||||||
|
len = wcstombs(nullptr, file1_dir->GetText().c_str(), 0) + 1;
|
||||||
|
char* buffer1 = new char[len];
|
||||||
|
wcstombs(buffer1, file1_dir->GetText().c_str(), len);
|
||||||
|
std::string str5(buffer1);
|
||||||
|
|
||||||
|
cv::Mat color = cv::imread(str5.c_str(), cv::ImreadModes::IMREAD_UNCHANGED);
|
||||||
|
|
||||||
|
if (color.channels() < 3) {
|
||||||
|
std::cout << "" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<cv::Mat> channels;
|
||||||
|
cv::split(color, channels);
|
||||||
|
|
||||||
|
cv::Mat alpha = channels[3];
|
||||||
|
|
||||||
|
auto extractor = cvpr::ContoursExtractorFactory::create();
|
||||||
|
|
||||||
|
extractor->init();
|
||||||
|
|
||||||
|
extractor->setContourOffset(soffset_controu);
|
||||||
|
|
||||||
|
extractor->setApproxPolyEpsilon(sepsilon);
|
||||||
|
|
||||||
|
extractor->setSmoothMethod(cvpr::SMOOTH_METHOD::BSPLINE);
|
||||||
|
|
||||||
|
extractor->setDefectThreshold(smin_contour);
|
||||||
|
|
||||||
|
extractor->setSmoothStep(ssmooth_step);
|
||||||
|
|
||||||
|
std::vector<std::vector<cv::Point>> contours;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
extractor->extract(alpha, contours);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
cv::Mat img = color.clone();
|
||||||
|
//cv::namedWindow("camera", 0);//CV_WINDOW_NORMAL就是0
|
||||||
|
img = img.clone();
|
||||||
|
cv::Mat empty_mask = cv::Mat::ones(img.rows, img.cols, CV_8UC3) * 255;
|
||||||
|
cv::drawContours(img, contours, -1, cv::Scalar(0, 0, 255, 255), 3);
|
||||||
|
|
||||||
|
cv::imwrite("d://res2222.png", img);
|
||||||
|
img2->SetBkImage(L"d://res2222.png");
|
||||||
|
wchar_t t[200];
|
||||||
|
wsprintf(t, L"count %d", contours[0].size());
|
||||||
|
cnt->SetText(std::wstring(t));
|
||||||
|
extractor->destroy();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
btn_open1->AttachClick([this, file1_dir, img1, img2, cnt](ui::EventArgs*) {
|
||||||
|
CString strFilter;
|
||||||
|
CString m_strTmpFile;
|
||||||
|
CStdioFile cfLogFile;
|
||||||
|
|
||||||
|
strFilter = "dat file (*.*)|*.*";
|
||||||
|
CFileDialog TmpDlg(true, 0, 0, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFilter.GetBuffer(),
|
||||||
|
CWnd::FromHandle(this->m_hWnd));
|
||||||
|
|
||||||
|
if (TmpDlg.DoModal() == IDOK)
|
||||||
|
{
|
||||||
|
m_strTmpFile = TmpDlg.GetPathName();
|
||||||
|
|
||||||
|
file1_dir->SetText(std::wstring(m_strTmpFile));
|
||||||
|
}
|
||||||
|
img1->SetBkImage(file1_dir->GetText());
|
||||||
|
|
||||||
|
size_t len = wcstombs(nullptr, file1_dir->GetText().c_str(), 0) + 1;
|
||||||
|
char* buffer = new char[len];
|
||||||
|
wcstombs(buffer, file1_dir->GetText().c_str(), len);
|
||||||
|
std::string str2(buffer);
|
||||||
|
|
||||||
|
cv::Mat color = cv::imread(str2.c_str(), cv::ImreadModes::IMREAD_UNCHANGED);
|
||||||
|
|
||||||
|
if (color.channels() < 3) {
|
||||||
|
std::cout << "" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<cv::Mat> channels;
|
||||||
|
cv::split(color, channels);
|
||||||
|
|
||||||
|
cv::Mat alpha = channels[3];
|
||||||
|
|
||||||
|
auto extractor = cvpr::ContoursExtractorFactory::create();
|
||||||
|
|
||||||
|
extractor->init();
|
||||||
|
|
||||||
|
extractor->setContourOffset(soffset_controu);
|
||||||
|
|
||||||
|
extractor->setApproxPolyEpsilon(sepsilon);
|
||||||
|
|
||||||
|
extractor->setSmoothMethod(cvpr::SMOOTH_METHOD::BSPLINE);
|
||||||
|
|
||||||
|
extractor->setDefectThreshold(smin_contour);
|
||||||
|
|
||||||
|
extractor->setSmoothStep(ssmooth_step);
|
||||||
|
|
||||||
|
std::vector<std::vector<cv::Point>> contours;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
extractor->extract(alpha, contours);
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::cout << e.what()<<std::endl;
|
||||||
|
}
|
||||||
|
cv::Mat img = color.clone();
|
||||||
|
//cv::namedWindow("camera", 0);//CV_WINDOW_NORMAL就是0
|
||||||
|
img = img.clone();
|
||||||
|
cv::Mat empty_mask = cv::Mat::ones(img.rows, img.cols, CV_8UC3) * 255;
|
||||||
|
cv::drawContours(img, contours, -1, cv::Scalar(0, 0, 255,255), 3);
|
||||||
|
|
||||||
|
cv::imwrite("d://res2222.png", img);
|
||||||
|
img2->SetBkImage(L"d://res2222.png");
|
||||||
|
wchar_t t[200];
|
||||||
|
|
||||||
|
wsprintf(t, L"cnt: %d", contours[0].size());
|
||||||
|
cnt->SetText(L"shit");
|
||||||
|
extractor->destroy();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
LRESULT BasicForm::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
|
||||||
|
{
|
||||||
|
PostQuitMessage(0L);
|
||||||
|
return __super::OnClose(uMsg, wParam, lParam, bHandled);
|
||||||
|
}
|
53
examples/contour/basic_form.h
Normal file
53
examples/contour/basic_form.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <afxwin.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "targetver.h"
|
||||||
|
|
||||||
|
// C runtime header
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// base header
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
// duilib
|
||||||
|
#include "duilib/UIlib.h"
|
||||||
|
|
||||||
|
class BasicForm : public ui::WindowImplBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BasicForm();
|
||||||
|
~BasicForm();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 一下三个接口是必须要覆写的接口,父类会调用这三个接口来构建窗口
|
||||||
|
* GetSkinFolder 接口设置你要绘制的窗口皮肤资源路径
|
||||||
|
* GetSkinFile 接口设置你要绘制的窗口的 xml 描述文件
|
||||||
|
* GetWindowClassName 接口设置窗口唯一的类名称
|
||||||
|
*/
|
||||||
|
virtual std::wstring GetSkinFolder() override;
|
||||||
|
virtual std::wstring GetSkinFile() override;
|
||||||
|
virtual std::wstring GetWindowClassName() const override;
|
||||||
|
virtual LRESULT OnNcHitTest(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到 WM_CREATE 消息时该函数会被调用,通常做一些控件初始化的操作
|
||||||
|
*/
|
||||||
|
virtual void InitWindow() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到 WM_CLOSE 消息时该函数会被调用
|
||||||
|
*/
|
||||||
|
virtual LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
|
||||||
|
|
||||||
|
static const std::wstring kClassName;
|
||||||
|
|
||||||
|
ui::RichEdit* mLabel1;
|
||||||
|
ui::RichEdit* mLabel2;
|
||||||
|
};
|
275
examples/contour/blob/BlobContour.cpp
Normal file
275
examples/contour/blob/BlobContour.cpp
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
#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;
|
||||||
|
}
|
122
examples/contour/blob/BlobContour.h
Normal file
122
examples/contour/blob/BlobContour.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#ifndef BLOBCONTOUR_H_INCLUDED
|
||||||
|
#define BLOBCONTOUR_H_INCLUDED
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include "opencv2/imgproc/imgproc_c.h"
|
||||||
|
#include <opencv2/opencv.hpp>
|
||||||
|
#include <opencv2/core/core.hpp>
|
||||||
|
|
||||||
|
//! Forward declaration in order to enable the "parent" field
|
||||||
|
class Blob;
|
||||||
|
|
||||||
|
//! Type of chain codes
|
||||||
|
typedef unsigned char ChainCode;
|
||||||
|
|
||||||
|
//! Type of list of chain codes
|
||||||
|
typedef std::vector<ChainCode> ChainCodeList;
|
||||||
|
|
||||||
|
//! In order to emulate CvSeq objects and to comply with opencv 2.0 interface
|
||||||
|
typedef std::vector<ChainCodeList> ChainCodeContours;
|
||||||
|
|
||||||
|
//! Type of list of points
|
||||||
|
typedef std::vector<cv::Point> PointList;
|
||||||
|
|
||||||
|
typedef std::vector<PointList> Contours;
|
||||||
|
|
||||||
|
|
||||||
|
//! Max order of calculated moments
|
||||||
|
#define MAX_MOMENTS_ORDER 3
|
||||||
|
|
||||||
|
//! Blob contour class (in crack code)
|
||||||
|
class BlobContour
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Constructors
|
||||||
|
BlobContour();
|
||||||
|
|
||||||
|
//! Size is used to empirically reserve internal vectors for contour points.
|
||||||
|
//! This can be a help for very small images, where the vector would be too large.
|
||||||
|
BlobContour(const cv::Point& startPoint, const cv::Size& imageRes = cv::Size(-1, -1));
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
BlobContour(BlobContour* source);
|
||||||
|
|
||||||
|
BlobContour(const BlobContour& source);
|
||||||
|
|
||||||
|
~BlobContour();
|
||||||
|
|
||||||
|
//! Assigment operator
|
||||||
|
BlobContour& operator=(const BlobContour& source);
|
||||||
|
|
||||||
|
//! Add point to end of contour, according to chain code.
|
||||||
|
void addChainCode(ChainCode code);
|
||||||
|
|
||||||
|
//! Return freeman chain coded contour
|
||||||
|
ChainCodeList& getChainCodeList() {
|
||||||
|
return m_contour[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEmpty() {
|
||||||
|
return m_contour.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Returns first contour
|
||||||
|
const PointList& getContourPoints();
|
||||||
|
|
||||||
|
//! Returns all contours (compatible with drawContours structure)
|
||||||
|
Contours& getContours();
|
||||||
|
|
||||||
|
void shiftBlobContour(int x, int y);
|
||||||
|
|
||||||
|
const cv::Point& getStartPoint() const {
|
||||||
|
return m_startPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! Clears chain code contour
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
//! Computes area from contour
|
||||||
|
double getArea();
|
||||||
|
|
||||||
|
//! Computes perimeter from contour
|
||||||
|
double getPerimeter();
|
||||||
|
|
||||||
|
//! Get contour moment (p,q up to MAX_CALCULATED_MOMENTS)
|
||||||
|
double getMoment(int p, int q);
|
||||||
|
|
||||||
|
//! Crack code list
|
||||||
|
ChainCodeContours m_contour;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Blob;
|
||||||
|
friend class CompLabeler;
|
||||||
|
|
||||||
|
//! Starting point of the contour
|
||||||
|
cv::Point m_startPoint;
|
||||||
|
|
||||||
|
//! All points from the contour
|
||||||
|
Contours m_contourPoints;
|
||||||
|
|
||||||
|
//! Computed area from contour
|
||||||
|
double m_area;
|
||||||
|
|
||||||
|
//! Computed perimeter from contour
|
||||||
|
double m_perimeter;
|
||||||
|
|
||||||
|
//! Computed moments from contour
|
||||||
|
CvMoments m_moments;
|
||||||
|
|
||||||
|
static const PointList EMPTY_LIST;
|
||||||
|
|
||||||
|
//! This value is actually used mainly in the detection part, for the labels.
|
||||||
|
Blob* m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
ChainCode points2ChainCode(const cv::Point& p1, const cv::Point& p2);
|
||||||
|
|
||||||
|
cv::Point chainCode2Point(const cv::Point& origin, ChainCode code);
|
||||||
|
|
||||||
|
#endif //!BLOBCONTOUR_H_INCLUDED
|
||||||
|
|
||||||
|
|
171
examples/contour/blob/BlobDetector.cpp
Normal file
171
examples/contour/blob/BlobDetector.cpp
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
#include "BlobDetector.h"
|
||||||
|
|
||||||
|
BlobDetector::BlobDetector()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: Blob
|
||||||
|
- FUNCTIONALITY: Constructor from an image. Fills an object with all the blobs in
|
||||||
|
the image
|
||||||
|
- PARAMETERS:
|
||||||
|
- source: image to extract the blobs from
|
||||||
|
- mask: optional mask to apply. The blobs will be extracted where the mask is not 0.
|
||||||
|
- numThreads: number of labelling threads.
|
||||||
|
- RESULT:
|
||||||
|
- object with all the blobs in the image.
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Oct-2013. Luca Nardelli and Saverio Murgia. Changed to comply with reimplemented labelling algorithm
|
||||||
|
*/
|
||||||
|
BlobDetector::BlobDetector(IplImage* source, IplImage* mask, int numThreads)
|
||||||
|
{
|
||||||
|
if (mask != NULL) {
|
||||||
|
cv::Mat temp = cv::Mat::zeros(cv::Size(source->width, source->height), CV_8UC1);
|
||||||
|
cv::cvarrToMat(source).copyTo(temp, cv::cvarrToMat(mask));
|
||||||
|
m_compLabeler.set(numThreads, temp);
|
||||||
|
m_compLabeler.doLabeling(m_blobGroup.m_blobs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_compLabeler.set(numThreads, cv::cvarrToMat(source));
|
||||||
|
m_compLabeler.doLabeling(m_blobGroup.m_blobs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobDetector
|
||||||
|
- FUNCTIONALITY: Copy constructor
|
||||||
|
- PARAMETERS:
|
||||||
|
- source: object to copy
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobDetector::BlobDetector(const BlobDetector& source)
|
||||||
|
{
|
||||||
|
// create the new from the past as a parameter
|
||||||
|
//m_blobs = BlobVector(source.getNumBlobs());
|
||||||
|
m_blobGroup.m_blobs.reserve(source.m_blobGroup.getNumBlobs());
|
||||||
|
// copy the blobs from the origin to the current one
|
||||||
|
BlobVector::const_iterator pBlobsSrc = source.m_blobGroup.m_blobs.begin();
|
||||||
|
//BlobVector::iterator pBlobsDst = m_blobs.begin();
|
||||||
|
while (pBlobsSrc != source.m_blobGroup.m_blobs.end()) {
|
||||||
|
// can't call the operator = since BlobVector is a
|
||||||
|
// Blob vector *. So create a new blob from the
|
||||||
|
// original blob
|
||||||
|
m_blobGroup.m_blobs.push_back(new Blob(**pBlobsSrc));
|
||||||
|
pBlobsSrc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlobDetector& BlobDetector::operator=(const BlobDetector& source)
|
||||||
|
{
|
||||||
|
if (this != &source) {
|
||||||
|
m_blobGroup.m_blobs.reserve(source.m_blobGroup.getNumBlobs());
|
||||||
|
BlobVector::const_iterator pBlobsSrc = source.m_blobGroup.m_blobs.begin();
|
||||||
|
while (pBlobsSrc != source.m_blobGroup.m_blobs.end()) {
|
||||||
|
m_blobGroup.m_blobs.push_back(new Blob(**pBlobsSrc));
|
||||||
|
pBlobsSrc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlobDetector::BlobDetector(BlobDetector&& source) noexcept
|
||||||
|
{
|
||||||
|
std::exchange(m_blobGroup, std::move(source.m_blobGroup));
|
||||||
|
}
|
||||||
|
|
||||||
|
BlobDetector& BlobDetector::operator=(BlobDetector&& source) noexcept
|
||||||
|
{
|
||||||
|
if (this != &source) {
|
||||||
|
std::exchange(m_blobGroup, std::move(source.m_blobGroup));
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: ~BlobDetector
|
||||||
|
- FUNCTIONALITY: Destructor
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobDetector::~BlobDetector()
|
||||||
|
{
|
||||||
|
m_blobGroup.clearBlobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobDetector
|
||||||
|
- FUNCTIONALITY: Constructor from an image. Fills an object with all the blobs in
|
||||||
|
the image, OPENCV 2 interface
|
||||||
|
- PARAMETERS:
|
||||||
|
- source: Mat to extract the blobs from, CV_8UC1
|
||||||
|
- mask: optional mask to apply. The blobs will be extracted where the mask is
|
||||||
|
not 0. All the neighbouring blobs where the mask is 0 will be external blobs
|
||||||
|
- numThreads: number of labelling threads.
|
||||||
|
- RESULT:
|
||||||
|
- object with all the blobs in the image.
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Saverio Murgia & Luca Nardelli
|
||||||
|
- CREATION DATE: 06-04-2013.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobDetector::BlobDetector(cv::Mat& source, const cv::Mat& mask, int numThreads) {
|
||||||
|
if (mask.data) {
|
||||||
|
cv::Mat temp = cv::Mat::zeros(source.size(), source.type());
|
||||||
|
source.copyTo(temp, mask);
|
||||||
|
m_compLabeler.set(numThreads, temp);
|
||||||
|
m_compLabeler.doLabeling(m_blobGroup.m_blobs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_compLabeler.set(numThreads, source);
|
||||||
|
m_compLabeler.doLabeling(m_blobGroup.m_blobs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: detect
|
||||||
|
- FUNCTIONALITY: detects blob in the image
|
||||||
|
- PARAMETERS:
|
||||||
|
- source: Mat to extract the blobs from, CV_8UC1
|
||||||
|
- mask: optional mask to apply. The blobs will be extracted where the mask is
|
||||||
|
not 0. All the neighbouring blobs where the mask is 0 will be external blobs
|
||||||
|
- numThreads: number of labelling threads.
|
||||||
|
- RESULT:
|
||||||
|
- the object will contain the detected blobs.
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Saverio Murgia & Luca Nardelli
|
||||||
|
- CREATION DATE: 10-04-2014.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
const BlobGroup& BlobDetector::detect(cv::Mat& source, const cv::Mat& mask /*= cv::Mat()*/, int numThreads/*=1*/)
|
||||||
|
{
|
||||||
|
m_blobGroup.clearBlobs();
|
||||||
|
if (mask.data) {
|
||||||
|
cv::Mat temp = cv::Mat::zeros(source.size(), source.type());
|
||||||
|
source.copyTo(temp, mask);
|
||||||
|
m_compLabeler.set(numThreads, temp);
|
||||||
|
m_compLabeler.doLabeling(m_blobGroup.m_blobs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_compLabeler.set(numThreads, source);
|
||||||
|
m_compLabeler.doLabeling(m_blobGroup.m_blobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_blobGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BlobGroup& BlobDetector::getBlobGroup()
|
||||||
|
{
|
||||||
|
return m_blobGroup;
|
||||||
|
}
|
||||||
|
|
45
examples/contour/blob/BlobDetector.h
Normal file
45
examples/contour/blob/BlobDetector.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef BLOBDETECTOR_H
|
||||||
|
#define BLOBDETECTOR_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#include "opencv2/core/core_c.h"
|
||||||
|
#include <opencv2/opencv.hpp>
|
||||||
|
#include "blob.h"
|
||||||
|
#include "BlobOperators.h"
|
||||||
|
#include "ComponentLabeling.h"
|
||||||
|
#include "BlobGroup.h"
|
||||||
|
|
||||||
|
class BlobDetector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlobDetector();
|
||||||
|
|
||||||
|
BlobDetector(IplImage* source, IplImage* mask = NULL, int numThreads = 1);
|
||||||
|
|
||||||
|
BlobDetector(cv::Mat& source, const cv::Mat& mask = cv::Mat(), int numThreads = 1);
|
||||||
|
|
||||||
|
BlobDetector(const BlobDetector& source);
|
||||||
|
|
||||||
|
BlobDetector& operator=(const BlobDetector& source);
|
||||||
|
|
||||||
|
BlobDetector(BlobDetector&& source) noexcept;
|
||||||
|
|
||||||
|
BlobDetector& operator=(BlobDetector&& source) noexcept;
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
virtual ~BlobDetector();
|
||||||
|
|
||||||
|
//! Function to detect blobs in a new image
|
||||||
|
const BlobGroup& detect(cv::Mat& source, const cv::Mat& mask = cv::Mat(), int numThreads = 1);
|
||||||
|
|
||||||
|
const BlobGroup& getBlobGroup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CompLabelerGroup m_compLabeler;
|
||||||
|
|
||||||
|
BlobGroup m_blobGroup;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // BLOBDETECTOR_H
|
663
examples/contour/blob/BlobGroup.cpp
Normal file
663
examples/contour/blob/BlobGroup.cpp
Normal file
@ -0,0 +1,663 @@
|
|||||||
|
/************************************************************************
|
||||||
|
BlobGroup.cpp
|
||||||
|
|
||||||
|
FUNCIONALITAT: Implementation de la classe BlobGroup
|
||||||
|
AUTOR: Inspecta S.L.
|
||||||
|
MODIFICACIONS (Modificacition, Autor, Data):
|
||||||
|
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "BlobGroup.h"
|
||||||
|
//! Show errors functions: only works for windows releases
|
||||||
|
#ifdef _SHOW_ERRORS
|
||||||
|
#include <afx.h> //suport per a CStrings
|
||||||
|
#include <afxwin.h> //suport per a AfxMessageBox
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
Constructors / Destructors
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGroup
|
||||||
|
- FUNCTIONALITY: Standard constructor
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- creates an empty set of blobs
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobGroup::BlobGroup()
|
||||||
|
{
|
||||||
|
m_blobs = BlobVector();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGroup
|
||||||
|
- FUNCTIONALITY: Copy constructor
|
||||||
|
- PARAMETERS:
|
||||||
|
- source: object to copy
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobGroup::BlobGroup(const BlobGroup& source)
|
||||||
|
{
|
||||||
|
// create the new from the past as a parameter
|
||||||
|
//m_blobs = BlobVector(source.getNumBlobs());
|
||||||
|
m_blobs.reserve(source.getNumBlobs());
|
||||||
|
// copy the blobs from the origin to the current one
|
||||||
|
BlobVector::const_iterator pBlobsSrc = source.m_blobs.begin();
|
||||||
|
//BlobVector::iterator pBlobsDst = m_blobs.begin();
|
||||||
|
while (pBlobsSrc != source.m_blobs.end()) {
|
||||||
|
// can't call the operator = since BlobVector is a
|
||||||
|
// Blob vector *. So create a new blob from the
|
||||||
|
// original blob
|
||||||
|
m_blobs.push_back(new Blob(**pBlobsSrc));
|
||||||
|
pBlobsSrc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: ~BlobGroup
|
||||||
|
- FUNCTIONALITY: Destructor
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobGroup::~BlobGroup()
|
||||||
|
{
|
||||||
|
clearBlobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
Operadors / Operators
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: Assigment operator
|
||||||
|
- FUNCTIONALITY:
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobGroup& BlobGroup::operator=(const BlobGroup& source)
|
||||||
|
{
|
||||||
|
// if they are already the same, nothing needs to be done
|
||||||
|
if (this != &source) {
|
||||||
|
// release the old set of blobs
|
||||||
|
for (int i = 0; i < getNumBlobs(); ++i) {
|
||||||
|
delete m_blobs[i];
|
||||||
|
}
|
||||||
|
m_blobs.clear();
|
||||||
|
// create the new from the past as a parameter
|
||||||
|
m_blobs = BlobVector(source.getNumBlobs());
|
||||||
|
// copy the blobs from the origin to the current one
|
||||||
|
BlobVector::const_iterator pBlobsSrc = source.m_blobs.begin();
|
||||||
|
BlobVector::iterator pBlobsDst = m_blobs.begin();
|
||||||
|
|
||||||
|
while (pBlobsSrc != source.m_blobs.end()) {
|
||||||
|
// can't call the operator = since BlobVector is a
|
||||||
|
// Blob vector *. So create a new blob from the
|
||||||
|
// original blob
|
||||||
|
*pBlobsDst = new Blob(**pBlobsSrc);
|
||||||
|
pBlobsSrc++;
|
||||||
|
pBlobsDst++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: + operator
|
||||||
|
- FUNCTIONALITY: Joins the blobs in source with the current ones
|
||||||
|
- PARAMETERS:
|
||||||
|
- source: object to copy the blobs
|
||||||
|
- RESULT:
|
||||||
|
- object with the actual blobs and the source blobs
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
BlobGroup BlobGroup::operator+(const BlobGroup& source) const
|
||||||
|
{
|
||||||
|
// create the result from the current blobs
|
||||||
|
BlobGroup result(*this);
|
||||||
|
|
||||||
|
// reserve memory for the new blobs
|
||||||
|
result.m_blobs.resize(result.getNumBlobs() + source.getNumBlobs());
|
||||||
|
|
||||||
|
// declare the iterators to cross the origin and destination blobs
|
||||||
|
BlobVector::const_iterator pBlobsSrc = source.m_blobs.begin();
|
||||||
|
BlobVector::iterator pBlobsDst = result.m_blobs.end();
|
||||||
|
|
||||||
|
// insert the blobs from the origin to the current one
|
||||||
|
while (pBlobsSrc != source.m_blobs.end()) {
|
||||||
|
pBlobsDst--;
|
||||||
|
*pBlobsDst = new Blob(**pBlobsSrc);
|
||||||
|
pBlobsSrc++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
Operacions / Operations
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTIONS: addBlob
|
||||||
|
- FUNCIONALITAT: Afegeix un blob al conjunt
|
||||||
|
- PARAMETERS:
|
||||||
|
- blob: blob a afegir
|
||||||
|
- RESULTAT:
|
||||||
|
- modifica el conjunt de blobs actual
|
||||||
|
- RESTRICCIONS:
|
||||||
|
- AUTOR: Ricard Borràs
|
||||||
|
- DATE OF CREATION: 2006/03/01
|
||||||
|
- MODIFICATION: Data. Autor. Description.
|
||||||
|
*/
|
||||||
|
void BlobGroup::addBlob(Blob* blob)
|
||||||
|
{
|
||||||
|
if (blob != NULL) {
|
||||||
|
Blob* tp = new Blob(blob);
|
||||||
|
m_blobs.push_back(tp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MATRIXCV_ACTIU
|
||||||
|
/**
|
||||||
|
- FUNCTION: getResult
|
||||||
|
- FUNCTIONALITY: Computes the function evaluator on all the blobs of the class
|
||||||
|
and returns a vector with the result
|
||||||
|
- PARAMETERS:
|
||||||
|
- evaluator: function to apply to each blob (any object derived from the
|
||||||
|
OperatorBlob class)
|
||||||
|
- RESULT:
|
||||||
|
- vector with all the results in the same order as the blobs
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
DoubleVector BlobGroup::getResult(BlobOperator* evaluator) const
|
||||||
|
{
|
||||||
|
if (getNumBlobs() <= 0) {
|
||||||
|
return DoubleVector();
|
||||||
|
}
|
||||||
|
|
||||||
|
// define the result
|
||||||
|
DoubleVector result = DoubleVector(getNumBlobs());
|
||||||
|
// and iterators on the blobs and the result
|
||||||
|
DoubleVector::iterator itResult = result.GetIterator();
|
||||||
|
BlobVector::const_iterator itBlobs = m_blobs.begin();
|
||||||
|
|
||||||
|
// evaluate the function on all blobs
|
||||||
|
while (itBlobs != m_blobs.end()) {
|
||||||
|
*itResult = (*evaluator)(**itBlobs);
|
||||||
|
itBlobs++;
|
||||||
|
itResult++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: getResult
|
||||||
|
- FUNCTIONALITY: Computes the function evaluator on all the blobs of the class
|
||||||
|
and returns a vector with the result
|
||||||
|
- PARAMETERS:
|
||||||
|
- evaluator: function to apply to each blob (any object derived from the
|
||||||
|
OperatorBlob class)
|
||||||
|
- RESULT:
|
||||||
|
- vector with all the results in the same order as the blobs
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
DoubleStlVector BlobGroup::getStlResult(BlobOperator* evaluator) const
|
||||||
|
{
|
||||||
|
if (getNumBlobs() <= 0) {
|
||||||
|
return DoubleStlVector();
|
||||||
|
}
|
||||||
|
|
||||||
|
// define the result
|
||||||
|
DoubleStlVector result = DoubleStlVector(getNumBlobs());
|
||||||
|
// and iterators on the blobs and the result
|
||||||
|
DoubleStlVector::iterator itResult = result.begin();
|
||||||
|
BlobVector::const_iterator itBlobs = m_blobs.begin();
|
||||||
|
|
||||||
|
// evaluate the function on all blobs
|
||||||
|
while (itBlobs != m_blobs.end()) {
|
||||||
|
*itResult = (*evaluator)(**itBlobs);
|
||||||
|
itBlobs++;
|
||||||
|
itResult++;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: getNumber
|
||||||
|
- FUNCTIONALITY: Computes the function evaluator on a blob of the class
|
||||||
|
- PARAMETERS:
|
||||||
|
- indexBlob: index of the blob to compute the function
|
||||||
|
- evaluator: function to apply to each blob (any object derived from the
|
||||||
|
OperatorBlob class)
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGroup::getNumber(int indexBlob, BlobOperator* evaluator) const
|
||||||
|
{
|
||||||
|
if (indexBlob < 0 || indexBlob >= getNumBlobs()) {
|
||||||
|
raiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS);
|
||||||
|
}
|
||||||
|
return (*evaluator)(*m_blobs[indexBlob]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: filter (const version)
|
||||||
|
- FUNCTIONALITY: Get some blobs from the class based on conditions on measures
|
||||||
|
of the blobs.
|
||||||
|
- PARAMETERS:
|
||||||
|
- dst: where to store the selected blobs
|
||||||
|
- filterAction: B_INCLUDE: include the blobs which pass the filter in the result
|
||||||
|
B_EXCLUDE: exclude the blobs which pass the filter in the result
|
||||||
|
- evaluator: Object to evaluate the blob
|
||||||
|
- Condition: How to decide if the result returned by evaluator on each blob
|
||||||
|
is included or not. It can be:
|
||||||
|
B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL,
|
||||||
|
B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE
|
||||||
|
- LowLimit: numerical value to evaluate the Condition on evaluator(blob)
|
||||||
|
- HighLimit: numerical value to evaluate the Condition on evaluator(blob).
|
||||||
|
Only useful for B_INSIDE and B_OUTSIDE
|
||||||
|
- RESULT:
|
||||||
|
- It returns on dst the blobs that accomplish (B_INCLUDE) or discards (B_EXCLUDE)
|
||||||
|
the Condition on the result returned by evaluator on each blob
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
/////////////////////////// FILTRAT DE BLOBS ////////////////////////////////////
|
||||||
|
void BlobGroup::filter(BlobGroup& dst, int filterAction, BlobOperator* evaluator, int condition, double lowLimit, double highLimit /*=0*/) const
|
||||||
|
|
||||||
|
{
|
||||||
|
// do the job
|
||||||
|
doFilter(dst, filterAction, evaluator, condition, lowLimit, highLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: filter
|
||||||
|
- FUNCTIONALITY: Get some blobs from the class based on conditions on measures
|
||||||
|
of the blobs.
|
||||||
|
- PARAMETERS:
|
||||||
|
- dst: where to store the selected blobs
|
||||||
|
- filterAction: B_INCLUDE: include the blobs which pass the filter in the result
|
||||||
|
B_EXCLUDE: exclude the blobs which pass the filter in the result
|
||||||
|
- evaluator: Object to evaluate the blob
|
||||||
|
- Condition: How to decide if the result returned by evaluator on each blob
|
||||||
|
is included or not. It can be:
|
||||||
|
B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL,
|
||||||
|
B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE
|
||||||
|
- LowLimit: numerical value to evaluate the Condition on evaluator(blob)
|
||||||
|
- HighLimit: numerical value to evaluate the Condition on evaluator(blob).
|
||||||
|
Only useful for B_INSIDE and B_OUTSIDE
|
||||||
|
- RESULT:
|
||||||
|
- It returns on dst the blobs that accomplish (B_INCLUDE) or discards (B_EXCLUDE)
|
||||||
|
the Condition on the result returned by evaluator on each blob
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
void BlobGroup::filter(BlobGroup& dst, int filterAction, BlobOperator* evaluator, int condition, double lowLimit, double highLimit /*=0*/)
|
||||||
|
|
||||||
|
{
|
||||||
|
int numBlobs = getNumBlobs();
|
||||||
|
|
||||||
|
// do the job
|
||||||
|
doFilter(dst, filterAction, evaluator, condition, lowLimit, highLimit);
|
||||||
|
|
||||||
|
// inline operation: remove previous blobs
|
||||||
|
if (&dst == this) {
|
||||||
|
// delete the first blobs (which are the originals)
|
||||||
|
// since we will have them replicated at the end if they pass the filter
|
||||||
|
BlobVector::iterator itBlobs = m_blobs.begin();
|
||||||
|
for (int i = 0; i < numBlobs; i++) {
|
||||||
|
delete *itBlobs;
|
||||||
|
itBlobs++;
|
||||||
|
}
|
||||||
|
m_blobs.erase(m_blobs.begin(), itBlobs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlobGroup::filter(BlobGroup& dst, FilterAction filterAction, BlobOperator* evaluator, FilterCondition condition, double lowLimit, double highLimit /*= 0 */)
|
||||||
|
{
|
||||||
|
filter(dst, (int)filterAction, evaluator, (int)condition, lowLimit, highLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//! Does the filter method job
|
||||||
|
void BlobGroup::doFilter(BlobGroup& dst, int filterAction, BlobOperator* evaluator, int condition, double lowLimit, double highLimit/* = 0*/) const
|
||||||
|
{
|
||||||
|
int i, numBlobs;
|
||||||
|
bool evaluationResult;
|
||||||
|
DoubleStlVector evaluationBlobs;
|
||||||
|
DoubleStlVector::iterator itEvaluationBlobs;
|
||||||
|
|
||||||
|
if (getNumBlobs() <= 0) { return; }
|
||||||
|
if (!evaluator) { return; }
|
||||||
|
// evaluate the blobs with the relevant function
|
||||||
|
evaluationBlobs = getStlResult(evaluator);
|
||||||
|
itEvaluationBlobs = evaluationBlobs.begin();
|
||||||
|
numBlobs = getNumBlobs();
|
||||||
|
switch(condition)
|
||||||
|
{
|
||||||
|
case B_EQUAL:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult= *itEvaluationBlobs == lowLimit;
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case B_NOT_EQUAL:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult = *itEvaluationBlobs != lowLimit;
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case B_GREATER:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult = *itEvaluationBlobs > lowLimit;
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case B_LESS:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult = *itEvaluationBlobs < lowLimit;
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case B_GREATER_OR_EQUAL:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult = *itEvaluationBlobs>= lowLimit;
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case B_LESS_OR_EQUAL:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult = *itEvaluationBlobs <= lowLimit;
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case B_INSIDE:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult = (*itEvaluationBlobs >= lowLimit) && (*itEvaluationBlobs <= highLimit);
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case B_OUTSIDE:
|
||||||
|
for (i = 0; i < numBlobs; ++i, ++itEvaluationBlobs) {
|
||||||
|
evaluationResult = (*itEvaluationBlobs < lowLimit) || (*itEvaluationBlobs > highLimit);
|
||||||
|
if ((evaluationResult && filterAction == B_INCLUDE) ||
|
||||||
|
(!evaluationResult && filterAction == B_EXCLUDE)) {
|
||||||
|
dst.m_blobs.push_back(new Blob(getBlob(i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: getBlob
|
||||||
|
- FUNCTIONALITY: Gets the n-th blob (without ordering the blobs)
|
||||||
|
- PARAMETERS:
|
||||||
|
- index: index in the blob array
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
Blob BlobGroup::getBlob(int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= getNumBlobs()) {
|
||||||
|
raiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *m_blobs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Blob *BlobGroup::getBlob(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= getNumBlobs()) {
|
||||||
|
raiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS);
|
||||||
|
}
|
||||||
|
return m_blobs[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Blob BlobGroup::getBlobByID(LabelID id) const{
|
||||||
|
for (int i = 0; i < getNumBlobs(); ++i) {
|
||||||
|
if (getBlob(i).getID() == id) {
|
||||||
|
return m_blobs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raiseError(EXCEPTION_EXECUTE_FAULT);
|
||||||
|
return Blob();
|
||||||
|
}
|
||||||
|
|
||||||
|
Blob *BlobGroup::getBlobByID(LabelID id) {
|
||||||
|
for (int i = 0; i < getNumBlobs(); ++i) {
|
||||||
|
if (getBlob(i)->getID() == id) {
|
||||||
|
return m_blobs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raiseError(EXCEPTION_EXECUTE_FAULT);
|
||||||
|
return (new Blob());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: getNthBlob
|
||||||
|
- FUNCTIONALITY: Gets the n-th blob ordering first the blobs with some criteria
|
||||||
|
- PARAMETERS:
|
||||||
|
- criteria: criteria to order the blob array
|
||||||
|
- nBlob: index of the returned blob in the ordered blob array
|
||||||
|
- dst: where to store the result
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
void BlobGroup::getNthBlob(BlobOperator* criteria, int nBlob, Blob& dst) const
|
||||||
|
{
|
||||||
|
// verify that we are not accessing out the blobs vector
|
||||||
|
if(nBlob < 0 || nBlob >= getNumBlobs()) {
|
||||||
|
//raiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS);
|
||||||
|
dst = Blob();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DoubleStlVector evaluationBlobs, evaluationBlobsOrdered;
|
||||||
|
double value;
|
||||||
|
|
||||||
|
// evaluate the blobs with the relevant function
|
||||||
|
evaluationBlobs = getStlResult(criteria);
|
||||||
|
|
||||||
|
evaluationBlobsOrdered = DoubleStlVector(getNumBlobs());
|
||||||
|
|
||||||
|
// get the nBlob first results (in descending order)
|
||||||
|
std::partial_sort_copy(evaluationBlobs.begin(),
|
||||||
|
evaluationBlobs.end(),
|
||||||
|
evaluationBlobsOrdered.begin(),
|
||||||
|
evaluationBlobsOrdered.end(),
|
||||||
|
std::greater<double>());
|
||||||
|
|
||||||
|
value = evaluationBlobsOrdered[nBlob];
|
||||||
|
|
||||||
|
// look for the first blob that has the value n-ssim
|
||||||
|
DoubleStlVector::const_iterator itEvaluation = evaluationBlobs.begin();
|
||||||
|
|
||||||
|
bool foundBlob = false;
|
||||||
|
int indexBlob = 0;
|
||||||
|
while (itEvaluation != evaluationBlobs.end() && !foundBlob) {
|
||||||
|
if (*itEvaluation == value) {
|
||||||
|
foundBlob = true;
|
||||||
|
dst = Blob(getBlob(indexBlob));
|
||||||
|
}
|
||||||
|
itEvaluation++;
|
||||||
|
indexBlob++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: clearBlobs
|
||||||
|
- FUNCTIONALITY: Clears all the blobs from the object and releases all its memory
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
void BlobGroup::clearBlobs()
|
||||||
|
{
|
||||||
|
BlobVector::iterator itBlobs = m_blobs.begin();
|
||||||
|
while (itBlobs != m_blobs.end()) {
|
||||||
|
delete *itBlobs;
|
||||||
|
itBlobs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_blobs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: raiseError
|
||||||
|
- FUNCTIONALITY: Error handling function
|
||||||
|
- PARAMETERS:
|
||||||
|
- errorCode: reason of the error
|
||||||
|
- RESULT:
|
||||||
|
- in _SHOW_ERRORS version, shows a message box with the error. In release is silent.
|
||||||
|
In both cases throws an exception with the error.
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
void BlobGroup::raiseError(int errorCode) const
|
||||||
|
{
|
||||||
|
//! Do we need to show errors?
|
||||||
|
#ifdef _SHOW_ERRORS
|
||||||
|
CString msg, format = "Error en BlobGroup: %s";
|
||||||
|
|
||||||
|
switch (errorCode)
|
||||||
|
{
|
||||||
|
case EXCEPTION_BLOB_OUT_OF_BOUNDS:
|
||||||
|
msg.Format(format, "Intentant accedir a un blob no existent");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
msg.Format(format, "Codi d'error desconegut");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
AfxMessageBox(msg);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
throw errorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
Auxiliars / Auxiliary functions
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: printBlobs
|
||||||
|
- FUNCTIONALITY: Prints some blob features in an ASCII file
|
||||||
|
- PARAMETERS:
|
||||||
|
- fileName: full path + filename to generate
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
void BlobGroup::printBlobs(char* fileName) const
|
||||||
|
{
|
||||||
|
DoubleStlVector area, /*perimeter,*/ exterior, compactness, length,
|
||||||
|
externalPerimeter, perimeterConvex, perimeter;
|
||||||
|
int i;
|
||||||
|
FILE* fp;
|
||||||
|
|
||||||
|
area = getStlResult(BlobGetArea());
|
||||||
|
perimeter = getStlResult(BlobGetPerimeter());
|
||||||
|
exterior = getStlResult(BlobGetExterior());
|
||||||
|
compactness = getStlResult(BlobGetCompactness());
|
||||||
|
length = getStlResult(BlobGetLength());
|
||||||
|
externalPerimeter = getStlResult(BlobGetExternalPerimeter());
|
||||||
|
perimeterConvex = getStlResult(BlobGetHullPerimeter());
|
||||||
|
|
||||||
|
fp = fopen(fileName, "w");
|
||||||
|
|
||||||
|
for (i = 0; i < getNumBlobs(); ++i) {
|
||||||
|
fprintf(fp, "blob %d ->\t a=%7.0f\t p=%8.2f (%8.2f external)\t pconvex=%8.2f\t ext=%.0f\t c=%3.2f\t l=%8.2f\n",
|
||||||
|
i, area[i], perimeter[i], externalPerimeter[i], perimeterConvex[i], exterior[i], compactness[i], length[i]);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Blob* BlobGroup::getBlobNearestTo(const cv::Point& pt)
|
||||||
|
{
|
||||||
|
float minD = FLT_MAX, d = 0;
|
||||||
|
int numBlobs = m_blobs.size();
|
||||||
|
int idxNearest = -1;
|
||||||
|
for (int i = 0; i < numBlobs; ++i) {
|
||||||
|
cv::Point diff = m_blobs[i]->getCenter() - pt;
|
||||||
|
d = diff.x * diff.x + diff.y * diff.y;
|
||||||
|
if (minD > d) {
|
||||||
|
idxNearest = i;
|
||||||
|
minD = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idxNearest != -1) {
|
||||||
|
return m_blobs[idxNearest];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
116
examples/contour/blob/BlobGroup.h
Normal file
116
examples/contour/blob/BlobGroup.h
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/************************************************************************
|
||||||
|
BlobGroup.h
|
||||||
|
|
||||||
|
FUNCTIONALITY: Definition of the BlobGroup class
|
||||||
|
AUTHOR: Inspecta S.L.
|
||||||
|
MODIFICATIONS (Modification, Author, Date):
|
||||||
|
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#if !defined(_CLASSE_BLOBRESULT_INCLUDED)
|
||||||
|
#define _CLASSE_BLOBRESULT_INCLUDED
|
||||||
|
|
||||||
|
#if _MSC_VER > 1000
|
||||||
|
#pragma once
|
||||||
|
#endif // _MSC_VER > 1000
|
||||||
|
|
||||||
|
#include "BlobLibraryConfiguration.h"
|
||||||
|
#include "ComponentLabeling.h"
|
||||||
|
#include "defines.h"
|
||||||
|
#include <math.h>
|
||||||
|
#include "opencv2/core/core_c.h"
|
||||||
|
#include <opencv2/opencv.hpp>
|
||||||
|
#include <deque>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cfloat>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
#include "blob.h"
|
||||||
|
#include "BlobOperators.h"
|
||||||
|
#include "ComponentLabeling.h"
|
||||||
|
|
||||||
|
class BlobGroup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Constructor, opencv 1.0 and 2.0 interfaces.
|
||||||
|
BlobGroup();
|
||||||
|
|
||||||
|
BlobGroup(const BlobGroup& source);
|
||||||
|
|
||||||
|
//! Destructor
|
||||||
|
virtual ~BlobGroup();
|
||||||
|
|
||||||
|
//! Assigment operator
|
||||||
|
BlobGroup& operator=(const BlobGroup& source);
|
||||||
|
|
||||||
|
//! Addition operator to concatenate two sets of blobs
|
||||||
|
BlobGroup operator+(const BlobGroup& source) const;
|
||||||
|
|
||||||
|
//! Adds a blob to the set of blobs
|
||||||
|
void addBlob(Blob* blob);
|
||||||
|
|
||||||
|
#ifdef MATRIXCV_ACTIU
|
||||||
|
//! Computes some property on all the blobs of the class
|
||||||
|
DoubleVector getResult(BlobOperator* evaluator) const;
|
||||||
|
#endif
|
||||||
|
//! Computes some property on all the blobs of the class
|
||||||
|
DoubleStlVector getStlResult(BlobOperator* evaluator) const;
|
||||||
|
|
||||||
|
//! Computes some property on one blob of the class
|
||||||
|
double getNumber(int indexblob, BlobOperator* evaluator) const;
|
||||||
|
|
||||||
|
//! Filters the blobs of the class using some property
|
||||||
|
void filter(BlobGroup& dst, int filterAction, BlobOperator* evaluator, int condition, double lowLimit, double highLimit = 0);
|
||||||
|
|
||||||
|
void filter(BlobGroup& dst, int filterAction, BlobOperator* evaluator, int condition, double lowLimit, double highLimit = 0) const;
|
||||||
|
|
||||||
|
void filter(BlobGroup& dst, FilterAction filterAction, BlobOperator* evaluator, FilterCondition condition, double lowLimit, double highLimit = 0);
|
||||||
|
|
||||||
|
//! Sorts the blobs of the class acording to some criteria and returns the n-th blob
|
||||||
|
void getNthBlob(BlobOperator* criteria, int nBlob, Blob& dst) const;
|
||||||
|
|
||||||
|
//! Gets the n-th blob of the class (without sorting)
|
||||||
|
Blob getBlob(int index) const;
|
||||||
|
|
||||||
|
Blob* getBlob(int index);
|
||||||
|
|
||||||
|
Blob getBlobByID(LabelID id) const;
|
||||||
|
|
||||||
|
Blob* getBlobByID(LabelID id);
|
||||||
|
|
||||||
|
//! Clears all the blobs of the class
|
||||||
|
void clearBlobs();
|
||||||
|
|
||||||
|
//! Prints some features of all the blobs in a file
|
||||||
|
void printBlobs(char* fileName) const;
|
||||||
|
|
||||||
|
//! Returns blob with center nearest to point pt
|
||||||
|
Blob* getBlobNearestTo(const cv::Point& pt);
|
||||||
|
|
||||||
|
//! Gets the total number of blobs
|
||||||
|
int getNumBlobs() const {
|
||||||
|
return (m_blobs.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
const BlobVector& getBlobVector() { return m_blobs; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Function to manage the errors
|
||||||
|
void raiseError(int errorCode) const;
|
||||||
|
|
||||||
|
//! Does the filter method job
|
||||||
|
void doFilter(BlobGroup& dst, int filterAction, BlobOperator* evaluator, int condition, double lowLimit, double highLimit = 0) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//! Vector with all the blobs
|
||||||
|
BlobVector m_blobs;
|
||||||
|
|
||||||
|
friend class BlobDetector;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !defined(_CLASSE_BLOBRESULT_INCLUDED)
|
28
examples/contour/blob/BlobLibraryConfiguration.h
Normal file
28
examples/contour/blob/BlobLibraryConfiguration.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/************************************************************************
|
||||||
|
BlobLibraryConfiguration.h
|
||||||
|
|
||||||
|
FUNCTIONALITY: Global configuration of the library
|
||||||
|
AUTHOR: Inspecta S.L.
|
||||||
|
MODIFICATIONS (Modification, Author, Date):
|
||||||
|
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
//! Indicates whether the MatrixCV is to be used or not
|
||||||
|
//! Use/Not use the MatrixCV class
|
||||||
|
//#define MATRIXCV_ACTIU
|
||||||
|
|
||||||
|
//! Uses/not use the blob object factory
|
||||||
|
//#define BLOB_OBJECT_FACTORY
|
||||||
|
|
||||||
|
//! Show/not show blob access errors
|
||||||
|
//#define _SHOW_ERRORS
|
||||||
|
|
||||||
|
#ifndef EXCEPTION_READ_FAULT
|
||||||
|
#define EXCEPTION_READ_FAULT 0 // Access violation was caused by a read
|
||||||
|
#endif
|
||||||
|
#ifndef EXCEPTION_WRITE_FAULT
|
||||||
|
#define EXCEPTION_WRITE_FAULT 1 // Access violation was caused by a read
|
||||||
|
#endif
|
||||||
|
#ifndef EXCEPTION_EXECUTE_FAULT
|
||||||
|
#define EXCEPTION_EXECUTE_FAULT 8 // Access violation was caused by a read
|
||||||
|
#endif
|
456
examples/contour/blob/BlobOperators.cpp
Normal file
456
examples/contour/blob/BlobOperators.cpp
Normal file
@ -0,0 +1,456 @@
|
|||||||
|
#include <limits.h>
|
||||||
|
#include "BlobOperators.h"
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
Implementation of the helper classes to perform operations on blobs
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: moment
|
||||||
|
- FUNCTIONALITY: Calculates the pq moment of the blob
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- returns the pq moment or 0 if the moment it is not implemented
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- Currently, implemented moments up to 3
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 20-07-2004.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetMoment::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.moment(m_p, m_q);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetHullPerimeter
|
||||||
|
- FUNCTIONALITY: Calculates the convex hull perimeter of the blob
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- returns the convex hull perimeter of the blob or the perimeter if the
|
||||||
|
blob edges could not be retrieved
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetHullPerimeter::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
Contours convexHull;
|
||||||
|
blob.getConvexHull(convexHull);
|
||||||
|
double perimeter;
|
||||||
|
|
||||||
|
if (convexHull.size() != 0) {
|
||||||
|
perimeter = fabs(arcLength(convexHull[0], true));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return perimeter;
|
||||||
|
}
|
||||||
|
|
||||||
|
double BlobGetHullArea::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
Contours convexHull;
|
||||||
|
blob.getConvexHull(convexHull);
|
||||||
|
double area;
|
||||||
|
|
||||||
|
if (convexHull.size() == 0) {
|
||||||
|
area = fabs(contourArea(convexHull[0], true));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetMinXatMinY
|
||||||
|
- FUNCTIONALITY: Calculates the minimum X on the minimum Y
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetMinXatMinY::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double result = static_cast<double>(LONG_MAX);
|
||||||
|
|
||||||
|
//CvSeqReader reader;
|
||||||
|
//CvPoint actualPoint;
|
||||||
|
PointList externalContour;
|
||||||
|
|
||||||
|
externalContour = blob.getExternalContour()->getContourPoints();
|
||||||
|
if (externalContour.size()==0) return result;
|
||||||
|
PointList::iterator it = externalContour.begin(), en = externalContour.end();
|
||||||
|
for (; it != en; ++it) {
|
||||||
|
cv::Point &actualPoint = *it;
|
||||||
|
|
||||||
|
if ((actualPoint.y == blob.minY()) && (actualPoint.x < result)) {
|
||||||
|
result = actualPoint.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetMinXatMinY
|
||||||
|
- FUNCTIONALITY: Calculates the minimum Y on the maximum X
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetMinYatMaxX::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double result = static_cast<double>(LONG_MAX);
|
||||||
|
|
||||||
|
//CvSeqReader reader;
|
||||||
|
//CvPoint actualPoint;
|
||||||
|
PointList externalContour;
|
||||||
|
|
||||||
|
externalContour = blob.getExternalContour()->getContourPoints();
|
||||||
|
if (externalContour.size() == 0) { return result; }
|
||||||
|
|
||||||
|
PointList::iterator it = externalContour.begin(), en = externalContour.end();
|
||||||
|
for (; it != en; ++it) {
|
||||||
|
cv::Point actualPoint = *it;
|
||||||
|
|
||||||
|
if ((actualPoint.x == blob.maxX()) && (actualPoint.y < result)) {
|
||||||
|
result = actualPoint.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetMaxXatMaxY
|
||||||
|
- FUNCTIONALITY: Calculates the maximum X on the maximum Y
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetMaxXatMaxY::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double result = LONG_MIN;
|
||||||
|
|
||||||
|
//CvSeqReader reader;
|
||||||
|
//CvPoint actualPoint;
|
||||||
|
PointList externalContour;
|
||||||
|
|
||||||
|
externalContour = blob.getExternalContour()->getContourPoints();
|
||||||
|
if (externalContour.size() == 0) { return result; }
|
||||||
|
|
||||||
|
PointList::iterator it = externalContour.begin(), en = externalContour.end();
|
||||||
|
for (; it != en; ++it) {
|
||||||
|
cv::Point &actualPoint = *it;
|
||||||
|
|
||||||
|
if ((actualPoint.y == blob.maxY()) && (actualPoint.x > result)) {
|
||||||
|
result = actualPoint.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetMaxYatMinX
|
||||||
|
- FUNCTIONALITY: Calculates the maximum Y on the minimum X
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetMaxYatMinX::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double result = LONG_MIN;
|
||||||
|
|
||||||
|
//CvSeqReader reader;
|
||||||
|
//CvPoint actualPoint;
|
||||||
|
PointList externalContour;
|
||||||
|
|
||||||
|
externalContour = blob.getExternalContour()->getContourPoints();
|
||||||
|
if (externalContour.size() == 0) { return result; }
|
||||||
|
|
||||||
|
|
||||||
|
PointList::iterator it = externalContour.begin(), en = externalContour.end();
|
||||||
|
for (; it != en; ++it) {
|
||||||
|
cv::Point &actualPoint = *it;
|
||||||
|
|
||||||
|
if ((actualPoint.x == blob.minX()) && (actualPoint.y > result)) {
|
||||||
|
result = actualPoint.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetElongation
|
||||||
|
- FUNCTIONALITY: Calculates the elongation of the blob (length/breadth)
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS: - See below to see how the lenght and the breadth are aproximated
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetElongation::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double widthC, lengthC, width, length;
|
||||||
|
|
||||||
|
double tmp;
|
||||||
|
|
||||||
|
tmp = blob.perimeter()*blob.perimeter() - 16 * blob.area();
|
||||||
|
|
||||||
|
if (tmp > 0.0) {
|
||||||
|
widthC = (double) (blob.perimeter() + sqrt(tmp)) / 4;
|
||||||
|
// error intrínsec en els càlculs de l'àrea i el perímetre
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widthC = (double) (blob.perimeter()) / 4;
|
||||||
|
}
|
||||||
|
if (widthC <= 0.0) { return 0; }
|
||||||
|
lengthC = (double) blob.area() / widthC;
|
||||||
|
|
||||||
|
length = MAX(lengthC, widthC);
|
||||||
|
width = MIN(lengthC, widthC);
|
||||||
|
|
||||||
|
return (double) length/width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetCompactness
|
||||||
|
- FUNCTIONALITY: Calculates the compactness of the blob
|
||||||
|
(maximum for circle shaped blobs, minimum for the rest)
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetCompactness::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
if (blob.area() != 0.0) {
|
||||||
|
return (double) pow(blob.perimeter(), 2)/(4 * CV_PI * blob.area());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetRoughness
|
||||||
|
- FUNCTIONALITY: Calculates the roughness of the blob
|
||||||
|
(ratio between perimeter and convex hull perimeter)
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetRoughness::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
BlobGetHullPerimeter getHullPerimeter = BlobGetHullPerimeter();
|
||||||
|
|
||||||
|
double hullPerimeter = getHullPerimeter(blob);
|
||||||
|
|
||||||
|
if (hullPerimeter != 0.0) {
|
||||||
|
return blob.perimeter() / hullPerimeter;//HullPerimeter();
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetLength
|
||||||
|
- FUNCTIONALITY: Calculates the lenght of the blob (the biggest axis of the blob)
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- The lenght is an aproximation to the real lenght
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetLength::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double widthC, lengthC;
|
||||||
|
double tmp;
|
||||||
|
|
||||||
|
tmp = blob.perimeter() * blob.perimeter() - 16 * blob.area();
|
||||||
|
|
||||||
|
if (tmp > 0.0) {
|
||||||
|
widthC = (double) (blob.perimeter() + sqrt(tmp)) / 4;
|
||||||
|
// error intrínsec en els càlculs de l'àrea i el perímetre
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widthC = (double) (blob.perimeter()) / 4;
|
||||||
|
}
|
||||||
|
if (widthC <= 0.0) { return 0; }
|
||||||
|
lengthC = (double) blob.area() / widthC;
|
||||||
|
|
||||||
|
return MAX(lengthC , widthC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetBreadth
|
||||||
|
- FUNCTIONALITY: Calculates the breadth of the blob (the smallest axis of the blob)
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- The breadth is an aproximation to the real breadth
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetBreadth::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double widthC, lengthC;
|
||||||
|
double tmp;
|
||||||
|
|
||||||
|
tmp = blob.perimeter() * blob.perimeter() - 16 * blob.area();
|
||||||
|
|
||||||
|
if (tmp > 0.0) {
|
||||||
|
widthC = (double) (blob.perimeter() + sqrt(tmp)) / 4;
|
||||||
|
// error intrínsec en els càlculs de l'àrea i el perímetre
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widthC = (double) (blob.perimeter()) / 4;
|
||||||
|
}
|
||||||
|
if (widthC <= 0.0) { return 0; }
|
||||||
|
lengthC = (double) blob.area() / widthC;
|
||||||
|
|
||||||
|
return MIN(lengthC , widthC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetDistanceFromPoint
|
||||||
|
- FUNCTIONALITY: Calculates the euclidean distance between the blob center and
|
||||||
|
the point specified in the constructor
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Ricard Borràs
|
||||||
|
- CREATION DATE: 25-05-2005.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetDistanceFromPoint::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
BlobGetXCenter getXCenter;
|
||||||
|
BlobGetYCenter getYCenter;
|
||||||
|
|
||||||
|
double deltaX = m_x - getXCenter(blob);
|
||||||
|
double deltaY = m_y - getYCenter(blob);
|
||||||
|
|
||||||
|
return sqrt((deltaX * deltaX) + (deltaY * deltaY));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: BlobGetXYInside
|
||||||
|
- FUNCTIONALITY: Calculates whether a point is inside the
|
||||||
|
rectangular bounding box of a blob
|
||||||
|
- PARAMETERS:
|
||||||
|
- RESULT:
|
||||||
|
- returns 1 if it is inside; o if not
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- AUTHOR: Francesc Pinyol Margalef
|
||||||
|
- CREATION DATE: 16-01-2006.
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
double BlobGetXYInside::operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
const PointList &contourPoints = blob.getExternalContour()->getContourPoints();
|
||||||
|
if (contourPoints.size() == 0) {
|
||||||
|
return pointPolygonTest(contourPoints, m_p, false) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#ifdef BLOB_OBJECT_FACTORY
|
||||||
|
|
||||||
|
/**
|
||||||
|
- FUNCTION: RegisterAll Operators
|
||||||
|
- FUNCTIONALITY: Register all operators defined in blob.h
|
||||||
|
- PARAMETERS:
|
||||||
|
- fabricaOperadorsBlob: factory where the operators will be registered
|
||||||
|
- RESULT:
|
||||||
|
- Modifies the manufacturesOperatorsBlob object
|
||||||
|
- RESTRICTIONS:
|
||||||
|
- Only blob.h operators will be registered. If you want to add them, you need to add them with
|
||||||
|
the Register method of the factory.
|
||||||
|
- AUTHOR: trees
|
||||||
|
- DATE OF CREATION: 2006/05/18
|
||||||
|
- MODIFICATION: Date. Author. Description.
|
||||||
|
*/
|
||||||
|
void RegistraTotsOperadors(t_OperadorBlobFactory &fabricaOperadorsBlob)
|
||||||
|
{
|
||||||
|
// blob shape
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetArea().name(), Type2Type<BlobGetArea>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetBreadth().name(), Type2Type<BlobGetBreadth>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetCompactness().name(), Type2Type<BlobGetCompactness>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetElongation().name(), Type2Type<BlobGetElongation>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetExterior().name(), Type2Type<BlobGetExterior>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetLength().name(), Type2Type<BlobGetLength>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetPerimeter().name(), Type2Type<BlobGetPerimeter>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetRoughness().name(), Type2Type<BlobGetRoughness>());
|
||||||
|
|
||||||
|
// blob color
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMean(NULL).name(), Type2Type<BlobGetMean>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetStdDev(NULL).name(), Type2Type<BlobGetStdDev>());
|
||||||
|
|
||||||
|
// external pixels
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetExternalPerimeterRatio().name(), Type2Type<BlobGetExternalPerimeterRatio>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetExternalHullPerimeterRatio().name(), Type2Type<BlobGetExternalHullPerimeterRatio>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetExternalPerimeter().name(), Type2Type<BlobGetExternalPerimeter>());
|
||||||
|
|
||||||
|
|
||||||
|
// hull
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetHullPerimeter().name(), Type2Type<BlobGetHullPerimeter>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetHullArea().name(), Type2Type<BlobGetHullArea>());
|
||||||
|
|
||||||
|
|
||||||
|
// elipse info
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMajorAxisLength().name(), Type2Type<BlobGetMajorAxisLength>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMinorAxisLength().name(), Type2Type<BlobGetMinorAxisLength>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetAxisRatio().name(), Type2Type<BlobGetAxisRatio>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetOrientation().name(), Type2Type<BlobGetOrientation>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetOrientationCos().name(), Type2Type<BlobGetOrientationCos>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetAreaElipseRatio().name(), Type2Type<BlobGetAreaElipseRatio>());
|
||||||
|
|
||||||
|
// min an max
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMaxX().name(), Type2Type<BlobGetMaxX>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMaxY().name(), Type2Type<BlobGetMaxY>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMinX().name(), Type2Type<BlobGetMinX>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMinY().name(), Type2Type<BlobGetMinY>());
|
||||||
|
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMaxXatMaxY().name(), Type2Type<BlobGetMaxXatMaxY>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMaxYatMinX().name(), Type2Type<BlobGetMaxYatMinX>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMinXatMinY().name(), Type2Type<BlobGetMinXatMinY>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMinYatMaxX().name(), Type2Type<BlobGetMinYatMaxX>());
|
||||||
|
|
||||||
|
// coordinate info
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetXYInside().name(), Type2Type<BlobGetXYInside>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetDiffY().name(), Type2Type<BlobGetDiffY>());
|
||||||
|
fabricaOperadorsBlob.Register(CBlobGetDiffX().name(), Type2Type<CBlobGetDiffX>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetXCenter().name(), Type2Type<BlobGetXCenter>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetYCenter().name(), Type2Type<BlobGetYCenter>());
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetDistanceFromPoint().name(), Type2Type<BlobGetDistanceFromPoint>());
|
||||||
|
|
||||||
|
// moments
|
||||||
|
fabricaOperadorsBlob.Register(BlobGetMoment().name(), Type2Type<BlobGetMoment>());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //BLOB_OBJECT_FACTORY
|
731
examples/contour/blob/BlobOperators.h
Normal file
731
examples/contour/blob/BlobOperators.h
Normal file
@ -0,0 +1,731 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
Definition of classes to perform operations on blobs
|
||||||
|
Helper classes to perform operations on blobs
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef BLOB_OPERATORS_H_INCLUDED
|
||||||
|
#define BLOB_OPERATORS_H_INCLUDED
|
||||||
|
|
||||||
|
#include "blob.h"
|
||||||
|
|
||||||
|
//! Degree to radian conversion factor
|
||||||
|
#define DEGREE2RAD (CV_PI / 180.0)
|
||||||
|
|
||||||
|
|
||||||
|
//! Interface to derive all blob operations
|
||||||
|
class OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~OperatorBlob() {};
|
||||||
|
|
||||||
|
//! Apply operator to blob
|
||||||
|
virtual double operator()(Blob& blob) = 0;
|
||||||
|
//! Get operator name
|
||||||
|
virtual const char *name() = 0;
|
||||||
|
|
||||||
|
operator OperatorBlob*()
|
||||||
|
{
|
||||||
|
return (OperatorBlob*)this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef OperatorBlob BlobOperator;
|
||||||
|
|
||||||
|
#ifdef BLOB_OBJECT_FACTORY
|
||||||
|
/**
|
||||||
|
Function to compare two identifiers within the COperadorBlobs factory
|
||||||
|
*/
|
||||||
|
struct functorComparacioIdOperador
|
||||||
|
{
|
||||||
|
bool operator()(const char* s1, const char* s2) const
|
||||||
|
{
|
||||||
|
return strcmp(s1, s2) < 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Definition of Object factory type for OperatorBlob objects
|
||||||
|
typedef ObjectFactory<OperatorBlob, const char *, functorComparacioIdOperador > t_OperadorBlobFactory;
|
||||||
|
|
||||||
|
//! Global function to register all operators defined in blob.h
|
||||||
|
void RegistraTotsOperadors(t_OperadorBlobFactory &fabricaOperadorsBlob);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
//! Class to get ID of a blob
|
||||||
|
class BlobGetID : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.getID();
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetID";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get bool m_toBeDeleted
|
||||||
|
class BlobGetTBDeleted : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.m_toBeDeleted;
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetTBDeleted";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! Class to get the area of a blob
|
||||||
|
class BlobGetArea : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.area();
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetArea";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the perimeter of a blob
|
||||||
|
class BlobGetPerimeter : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.perimeter();
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetPerimeter";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the external flag of a blob
|
||||||
|
class BlobGetExterior : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlobGetExterior()
|
||||||
|
{
|
||||||
|
m_mask = NULL;
|
||||||
|
m_xBorder = true;
|
||||||
|
m_yBorder = true;
|
||||||
|
}
|
||||||
|
BlobGetExterior(IplImage* mask, bool xBorder = true, bool yBorder = true)
|
||||||
|
{
|
||||||
|
m_mask = mask;
|
||||||
|
m_xBorder = xBorder;
|
||||||
|
m_yBorder = yBorder;
|
||||||
|
}
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.exterior(m_mask, m_xBorder, m_yBorder);
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetExterior";
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
IplImage* m_mask;
|
||||||
|
bool m_xBorder, m_yBorder;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the mean grey level of a blob
|
||||||
|
class BlobGetMean : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlobGetMean()
|
||||||
|
{
|
||||||
|
m_image = NULL;
|
||||||
|
}
|
||||||
|
BlobGetMean(IplImage* image)
|
||||||
|
{
|
||||||
|
m_image = image;
|
||||||
|
};
|
||||||
|
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.mean(m_image);
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMean";
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
|
IplImage* m_image;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! Class to get the standard deviation of the grey level values of a blob
|
||||||
|
class BlobGetStdDev : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlobGetStdDev()
|
||||||
|
{
|
||||||
|
m_image = NULL;
|
||||||
|
}
|
||||||
|
BlobGetStdDev(IplImage* image)
|
||||||
|
{
|
||||||
|
m_image = image;
|
||||||
|
};
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.stdDev(m_image);
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetStdDev";
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
|
IplImage* m_image;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the compactness of a blob
|
||||||
|
class BlobGetCompactness : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetCompactness";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the length of a blob
|
||||||
|
class BlobGetLength : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetLength";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the breadth of a blob
|
||||||
|
class BlobGetBreadth : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetBreadth";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the difference in X of the blob
|
||||||
|
class CBlobGetDiffX : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.getBoundingBox().width;
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "CBlobGetDiffX";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the difference in X of the blob
|
||||||
|
class BlobGetDiffY : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.getBoundingBox().height;
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetDiffY";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the P, Q moment of a blob
|
||||||
|
class BlobGetMoment : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Standard constructor (gets the 00 moment)
|
||||||
|
BlobGetMoment()
|
||||||
|
{
|
||||||
|
m_p = m_q = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Constructor: gets the P, Q moment
|
||||||
|
BlobGetMoment(int p, int q)
|
||||||
|
{
|
||||||
|
m_p = p;
|
||||||
|
m_q = q;
|
||||||
|
};
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMoment";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! moment que volem calcular
|
||||||
|
int m_p, m_q;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the convex hull perimeter of a blob
|
||||||
|
class BlobGetHullPerimeter : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetHullPerimeter";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the convex hull area of a blob
|
||||||
|
class BlobGetHullArea : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetHullArea";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the minimum x on the minimum y
|
||||||
|
class BlobGetMinXatMinY : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMinXatMinY";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the minimum y on the maximum x
|
||||||
|
class BlobGetMinYatMaxX : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMinYatMaxX";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the maximum x on the maximum y
|
||||||
|
class BlobGetMaxXatMaxY : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMaxXatMaxY";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the maximum y on the minimum y
|
||||||
|
class BlobGetMaxYatMinX : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMaxYatMinX";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the minimum x
|
||||||
|
class BlobGetMinX : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.minX();
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMinX";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the maximum x
|
||||||
|
class BlobGetMaxX : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.maxX();
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMaxX";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the minimum y
|
||||||
|
class BlobGetMinY : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.minY();
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMinY";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the maximum y
|
||||||
|
class BlobGetMaxY : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.maxY();
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMaxY";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! Class to calculate the elongation of the blob
|
||||||
|
class BlobGetElongation : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetElongation";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the roughness of the blob
|
||||||
|
class BlobGetRoughness : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetRoughness";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the euclidean distance between the center of a blob and a given point
|
||||||
|
class BlobGetDistanceFromPoint : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Standard constructor (distance to point 0,0)
|
||||||
|
BlobGetDistanceFromPoint()
|
||||||
|
{
|
||||||
|
m_x = m_y = 0.0;
|
||||||
|
}
|
||||||
|
//! Constructor (distance to point x,y)
|
||||||
|
BlobGetDistanceFromPoint(const double x, const double y)
|
||||||
|
{
|
||||||
|
m_x = x;
|
||||||
|
m_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetDistanceFromPoint";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// coordinates of the point where we want to calculate the distance
|
||||||
|
double m_x, m_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to get the number of external pixels of a blob
|
||||||
|
class BlobGetExternalPerimeter : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlobGetExternalPerimeter()
|
||||||
|
{
|
||||||
|
m_mask = NULL;
|
||||||
|
m_xBorder = true;
|
||||||
|
m_yBorder = true;
|
||||||
|
}
|
||||||
|
BlobGetExternalPerimeter(IplImage* mask, bool xBorder = true, bool yBorder = true)
|
||||||
|
{
|
||||||
|
m_mask = mask;
|
||||||
|
m_xBorder = xBorder;
|
||||||
|
m_yBorder = yBorder;
|
||||||
|
}
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.externalPerimeter(m_mask, m_xBorder, m_yBorder);
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetExternalPerimeter";
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
IplImage* m_mask;
|
||||||
|
bool m_xBorder, m_yBorder;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the ratio between the perimeter and number of external pixels
|
||||||
|
//! values close to 0 indicate that most of the blob is internal
|
||||||
|
//! values close to 1 indicate that most of the blob is external
|
||||||
|
//! Class to calculate the ratio between the perimeter and the number of external pixels
|
||||||
|
class BlobGetExternalPerimeterRatio : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlobGetExternalPerimeterRatio()
|
||||||
|
{
|
||||||
|
m_mask = NULL;
|
||||||
|
m_xBorder = false;
|
||||||
|
m_yBorder = false;
|
||||||
|
}
|
||||||
|
BlobGetExternalPerimeterRatio(IplImage* mask, bool xBorder = true, bool yBorder = true)
|
||||||
|
{
|
||||||
|
m_mask = mask;
|
||||||
|
m_xBorder = xBorder;
|
||||||
|
m_yBorder = yBorder;
|
||||||
|
}
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
if (blob.perimeter() != 0) {
|
||||||
|
return blob.externalPerimeter(m_mask, m_xBorder, m_yBorder) / blob.perimeter();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return blob.externalPerimeter(m_mask, m_xBorder, m_yBorder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetExternalPerimeterRatio";
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
IplImage* m_mask;
|
||||||
|
bool m_xBorder, m_yBorder;
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the ratio between the convex perimeter and the number of external pixels
|
||||||
|
//! values close to 0 indicate that most of the blob is internal blob
|
||||||
|
//! values close to 1 indicate that most of the blob is external
|
||||||
|
//! Class to calculate the ratio between the perimeter and the number of external pixels
|
||||||
|
class BlobGetExternalHullPerimeterRatio : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BlobGetExternalHullPerimeterRatio()
|
||||||
|
{
|
||||||
|
m_mask = NULL;
|
||||||
|
m_xBorder = false;
|
||||||
|
m_yBorder = false;
|
||||||
|
}
|
||||||
|
BlobGetExternalHullPerimeterRatio(IplImage* mask, bool xBorder = true, bool yBorder = true)
|
||||||
|
{
|
||||||
|
m_mask = mask;
|
||||||
|
m_xBorder = xBorder;
|
||||||
|
m_yBorder = yBorder;
|
||||||
|
}
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
BlobGetHullPerimeter getHullPerimeter;
|
||||||
|
double hullPerimeter;
|
||||||
|
|
||||||
|
if ((hullPerimeter = getHullPerimeter(blob)) != 0) {
|
||||||
|
return blob.externalPerimeter(m_mask, m_xBorder, m_yBorder) / hullPerimeter;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return blob.externalPerimeter(m_mask, m_xBorder, m_yBorder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetExternalHullPerimeterRatio";
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
IplImage* m_mask;
|
||||||
|
bool m_xBorder, m_yBorder;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the center in the X direction
|
||||||
|
class BlobGetXCenter : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.minX() + ((blob.maxX() - blob.minX()) / 2.0);
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetXCenter";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the center in the Y direction
|
||||||
|
class BlobGetYCenter : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
return blob.minY() + ((blob.maxY() - blob.minY()) / 2.0);
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetYCenter";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the length of the major axis of the ellipse that fits the blob edges
|
||||||
|
class BlobGetMajorAxisLength : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
cv::RotatedRect elipse = blob.getEllipse();
|
||||||
|
|
||||||
|
return elipse.size.width;
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMajorAxisLength";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the ratio between the area of the ellipse and that of the spot
|
||||||
|
class BlobGetAreaElipseRatio : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
if (blob.area() == 0.0) { return 0.0; }
|
||||||
|
|
||||||
|
cv::RotatedRect elipse = blob.getEllipse();
|
||||||
|
|
||||||
|
double ratioAreaElipseAreaTaca = ((elipse.size.width/2.0) * (elipse.size.height/2.0) * CV_PI) / blob.area();
|
||||||
|
|
||||||
|
return ratioAreaElipseAreaTaca;
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetAreaElipseRatio";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the length of the minor axis of the ellipse that fits the blob edges
|
||||||
|
class BlobGetMinorAxisLength : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
cv::RotatedRect elipse = blob.getEllipse();
|
||||||
|
|
||||||
|
return elipse.size.height;
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetMinorAxisLength";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the orientation of the ellipse that fits the blob edges in radians
|
||||||
|
class BlobGetOrientation : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
cv::RotatedRect elipse = blob.getEllipse();
|
||||||
|
/*
|
||||||
|
if (elipse.angle > 180.0)
|
||||||
|
return ((elipse.angle - 180.0)* DEGREE2RAD);
|
||||||
|
else
|
||||||
|
return (elipse.angle * DEGREE2RAD);
|
||||||
|
*/
|
||||||
|
return elipse.angle;
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetOrientation";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate the cosinus of the orientation of the ellipse that fits the blob edges
|
||||||
|
class BlobGetOrientationCos : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
BlobGetOrientation getOrientation;
|
||||||
|
return fabs(cos(getOrientation(blob)*DEGREE2RAD));
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetOrientationCos";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//! Class to calculate the ratio between both axes of the ellipse
|
||||||
|
class BlobGetAxisRatio : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double operator()(Blob& blob)
|
||||||
|
{
|
||||||
|
double major,minor;
|
||||||
|
BlobGetMajorAxisLength getMajor;
|
||||||
|
BlobGetMinorAxisLength getMinor;
|
||||||
|
|
||||||
|
major = getMajor(blob);
|
||||||
|
minor = getMinor(blob);
|
||||||
|
|
||||||
|
if (major != 0) {
|
||||||
|
return minor / major;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetAxisRatio";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Class to calculate whether a point is inside a blob
|
||||||
|
class BlobGetXYInside : public OperatorBlob
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
//! Standard constructor
|
||||||
|
BlobGetXYInside()
|
||||||
|
{
|
||||||
|
m_p.x = 0;
|
||||||
|
m_p.y = 0;
|
||||||
|
}
|
||||||
|
//! Constructor: sets the point
|
||||||
|
BlobGetXYInside(cv::Point2f p)
|
||||||
|
{
|
||||||
|
m_p = p;
|
||||||
|
};
|
||||||
|
double operator()(Blob& blob);
|
||||||
|
const char *name()
|
||||||
|
{
|
||||||
|
return "BlobGetXYInside";
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! point to be considered
|
||||||
|
cv::Point2f m_p;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!BLOB_OPERATORS_H_INCLUDED
|
538
examples/contour/blob/ComponentLabeling.cpp
Normal file
538
examples/contour/blob/ComponentLabeling.cpp
Normal file
@ -0,0 +1,538 @@
|
|||||||
|
#include "ComponentLabeling.h"
|
||||||
|
|
||||||
|
CompLabeler::CompLabeler(cv::Mat& binImage, BlobContour** lab, cv::Point start, cv::Point end)
|
||||||
|
: m_binaryImage(binImage)
|
||||||
|
, m_startPoint(start)
|
||||||
|
, m_endPoint(end)
|
||||||
|
{
|
||||||
|
m_parent = NULL;
|
||||||
|
m_labels = lab;
|
||||||
|
m_r = 0;
|
||||||
|
m_c = 0;
|
||||||
|
m_dir = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompLabeler::~CompLabeler()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabeler::reset()
|
||||||
|
{
|
||||||
|
m_blobs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabeler::label()
|
||||||
|
{
|
||||||
|
m_ptrDataBinary = m_binaryImage.data;
|
||||||
|
m_ptrDataLabels = m_labels;
|
||||||
|
m_currentBlob = NULL;
|
||||||
|
|
||||||
|
m_h = m_binaryImage.size().height;
|
||||||
|
m_w = m_binaryImage.size().width;
|
||||||
|
BlobContour* label = NULL;
|
||||||
|
for (m_r = m_startPoint.y; m_r < m_endPoint.y; ++m_r) {
|
||||||
|
//First col
|
||||||
|
m_pos = m_r * m_w;
|
||||||
|
m_c = m_startPoint.x;
|
||||||
|
if (m_ptrDataBinary[m_pos]) {
|
||||||
|
label = m_ptrDataLabels[m_pos];
|
||||||
|
if (label) {
|
||||||
|
m_currentBlob = label->m_parent;
|
||||||
|
}
|
||||||
|
//Else if so to not check for label==NULL
|
||||||
|
else if (m_ptrDataBinary[m_pos] /*&& ptrDataLabels[pos]==NULL*/) {
|
||||||
|
m_currentBlob = new Blob(m_currentLabel, cv::Point(m_c, m_r), cv::Size(m_w, m_h));
|
||||||
|
m_blobs.push_back(m_currentBlob);
|
||||||
|
tracerExt();
|
||||||
|
}
|
||||||
|
if (!m_ptrDataBinary[m_pos + 1] && !m_ptrDataLabels[m_pos + 1]) {
|
||||||
|
tracerInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Other cols
|
||||||
|
for (m_c = m_startPoint.x+1; m_c < m_endPoint.x-1; ++m_c) {
|
||||||
|
m_pos = m_r * m_w + m_c;
|
||||||
|
if (m_ptrDataBinary[m_pos]) {
|
||||||
|
label = m_ptrDataLabels[m_pos];
|
||||||
|
if (label != 0) {
|
||||||
|
m_currentBlob = label->m_parent;
|
||||||
|
}
|
||||||
|
else if (!m_ptrDataBinary[m_pos - 1]) {
|
||||||
|
m_currentBlob = new Blob(m_currentLabel, cv::Point(m_c, m_r), cv::Size(m_w, m_h));
|
||||||
|
m_blobs.push_back(m_currentBlob);
|
||||||
|
tracerExt();
|
||||||
|
}
|
||||||
|
if (!m_ptrDataBinary[m_pos+1] && m_ptrDataLabels[m_pos+1] == 0) {
|
||||||
|
tracerInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_pos = m_r * m_w + m_c;
|
||||||
|
//Last column
|
||||||
|
if (m_ptrDataBinary[m_pos]) {
|
||||||
|
label = m_ptrDataLabels[m_pos];
|
||||||
|
if (label != 0) {
|
||||||
|
m_currentBlob = label->m_parent;
|
||||||
|
}
|
||||||
|
else if (!m_ptrDataBinary[m_pos - 1]) {
|
||||||
|
m_currentBlob = new Blob(m_currentLabel, cv::Point(m_c, m_r), cv::Size(m_w, m_h));
|
||||||
|
m_blobs.push_back(m_currentBlob);
|
||||||
|
tracerExt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void* CompLabeler::threadLabeling(void* o)
|
||||||
|
{
|
||||||
|
CompLabeler* obj = (CompLabeler*)o;
|
||||||
|
obj->label();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabeler::tracerExt()
|
||||||
|
{
|
||||||
|
//Dir:
|
||||||
|
//321
|
||||||
|
//4-0
|
||||||
|
//567
|
||||||
|
//cout << "tracerExt\t" << c<<","<<r<<endl;
|
||||||
|
m_currentContour = &m_currentBlob->m_externalContour;
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
std::cout << "tracerExt\t" << m_c << ", "<< m_r << std::endl;
|
||||||
|
bool debugDraw = true;
|
||||||
|
#endif
|
||||||
|
int sR = m_r, sC = m_c;
|
||||||
|
int startPos = sR * m_w + sC;
|
||||||
|
m_dir = 6;
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 150; //Debug
|
||||||
|
#endif
|
||||||
|
getNextPointCCW();
|
||||||
|
if (m_singlePixBlob) {
|
||||||
|
m_r = sR;
|
||||||
|
m_c = sC;
|
||||||
|
m_pos = m_r * m_w + m_c;
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ChainCodeList* cont = &m_currentBlob->m_externalContour.m_contour[0];
|
||||||
|
while (m_pos != startPos) {
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 150;
|
||||||
|
#endif
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
getNextPointCCW();
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
if (debugDraw) {
|
||||||
|
cv::namedWindow("im", CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
|
||||||
|
cv::imshow("im", m_binaryImage);
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << m_c << ", " << m_r;
|
||||||
|
cv::displayOverlay("im", ss.str());
|
||||||
|
int k = cv::waitKey();
|
||||||
|
if (k == ' ') {
|
||||||
|
debugDraw = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
//For blobs in which the starting point must be crossed many times
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
getNextPointCCW();
|
||||||
|
if (m_ptrDataLabels[m_pos] == 0) {
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 150;
|
||||||
|
#endif
|
||||||
|
while (m_pos != startPos) {
|
||||||
|
//cout << r << "," << c << endl;
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
getNextPointCCW();
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos]= 150;
|
||||||
|
if (debugDraw) {
|
||||||
|
cv::namedWindow("im", CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
|
||||||
|
cv::imshow("im", m_binaryImage);
|
||||||
|
int k = cv::waitKey();
|
||||||
|
if (k == ' ') {
|
||||||
|
debugDraw = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_r = sR;
|
||||||
|
m_c = sC;
|
||||||
|
m_pos = m_r * m_w + m_c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabeler::tracerInt(int startDir /*= 5*/)
|
||||||
|
{
|
||||||
|
//Dir:
|
||||||
|
//321
|
||||||
|
//4-0
|
||||||
|
//567
|
||||||
|
//cout << "tracerInt\t" << c<<","<<r<<endl;
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
std::cout << "tracerInt\t" << m_c << ", " << m_r << std::endl;
|
||||||
|
bool debugDraw=true;
|
||||||
|
#endif
|
||||||
|
int sR = m_r, sC = m_c;
|
||||||
|
int startPos = sR * m_w + sC;
|
||||||
|
m_currentContour = new BlobContour(cv::Point(m_c, m_r), cv::Size(m_w, m_h));
|
||||||
|
m_currentContour->m_parent = m_currentBlob;
|
||||||
|
ChainCodeList* cont = &m_currentContour->m_contour[0];
|
||||||
|
m_dir = startDir;
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 50;
|
||||||
|
#endif
|
||||||
|
getNextPointCW();
|
||||||
|
//uchar firstDir = m_dir;
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 100;
|
||||||
|
#endif
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
while (m_pos != startPos) {
|
||||||
|
// cout << r << "," << c << endl;
|
||||||
|
getNextPointCW();
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 100;
|
||||||
|
if (debugDraw) {
|
||||||
|
cv::namedWindow("im", CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
|
||||||
|
cv::imshow("im", m_binaryImage);
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << m_c << ", " << m_r;
|
||||||
|
cv::displayOverlay("im", ss.str());
|
||||||
|
int k = cv::waitKey();
|
||||||
|
if (k == ' ') {
|
||||||
|
debugDraw = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//For internal contours in which the starting point must be crossed many times, like:
|
||||||
|
// ooooooooooooooo
|
||||||
|
// ooooos o
|
||||||
|
// ooooo o o
|
||||||
|
// ooooo o o
|
||||||
|
// ooooo o
|
||||||
|
// ooooooooooooooo
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
getNextPointCW();
|
||||||
|
//In tracerExt i check for label==0, beacuse there can not be situations in which a pixel belongs to 2 contours.
|
||||||
|
//This can happen with internal contorus, so I have to modify the condition.
|
||||||
|
if (m_ptrDataLabels[m_pos] != m_currentContour) {
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 100;
|
||||||
|
#endif
|
||||||
|
while (m_pos != startPos) {
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
getNextPointCW();
|
||||||
|
#ifdef DEBUG_COMPONENT_LABELLING
|
||||||
|
m_ptrDataBinary[m_pos] = 100;
|
||||||
|
if (debugDraw) {
|
||||||
|
cv::namedWindow("im",CV_WINDOW_NORMAL + CV_GUI_EXPANDED + CV_WINDOW_KEEPRATIO);
|
||||||
|
cv::imshow("im", m_binaryImage);
|
||||||
|
int k = cv::waitKey();
|
||||||
|
if (k == ' ') {
|
||||||
|
debugDraw = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
cont->push_back(m_dir);
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_r = sR;
|
||||||
|
m_c = sC;
|
||||||
|
m_pos = m_r * m_w + m_c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If labeler has m_parent it means that I'm using more than 1 thread.
|
||||||
|
//Then I must check for collisions when adding internal contours to the same blob.
|
||||||
|
if (m_parent) {
|
||||||
|
std::lock_guard<std::mutex> lock(m_parent->m_mutexBlob);
|
||||||
|
//m_parent->acquireMutex();
|
||||||
|
m_currentBlob->m_internalContours.push_back(m_currentContour);
|
||||||
|
//m_parent->releaseMutex();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_currentBlob->m_internalContours.push_back(m_currentContour);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabeler::getNextPointCCW()
|
||||||
|
{
|
||||||
|
//Dir:
|
||||||
|
//321
|
||||||
|
//4-0
|
||||||
|
//567
|
||||||
|
|
||||||
|
m_dir = (m_dir + 6) % 8;
|
||||||
|
int i = 0;
|
||||||
|
for (; i < 8; i++, m_dir = (m_dir + 1) % 8) {
|
||||||
|
m_tempR = m_r + m_freemanR[m_dir];
|
||||||
|
m_tempC = m_c + m_freemanC[m_dir];
|
||||||
|
if (!(m_tempR < 0 || m_tempR >= m_h || m_tempC < 0 || m_tempC >= m_w)) {
|
||||||
|
m_pos = m_tempR * m_w + m_tempC;
|
||||||
|
if (m_ptrDataBinary[m_pos]) {
|
||||||
|
m_r = m_tempR;
|
||||||
|
m_c = m_tempC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_singlePixBlob = i == 8;
|
||||||
|
m_pos = m_r * m_w + m_c;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// I tried to pre-create all the various direction vectors in order to avoid checking too many times for the boundary of the image.
|
||||||
|
// However, there was no gain in performance.
|
||||||
|
//It's useless (in terms of performance) to create the direction vectors according to all cases.
|
||||||
|
8 directions
|
||||||
|
|
||||||
|
cases:
|
||||||
|
0-r=0
|
||||||
|
1-r=0,c=0
|
||||||
|
2-r=0,c=w
|
||||||
|
3-r=h
|
||||||
|
4-r=h,c=0
|
||||||
|
5-r=h,c=w
|
||||||
|
6-c=0
|
||||||
|
7-c=w
|
||||||
|
8-otherwise
|
||||||
|
total:9
|
||||||
|
*/
|
||||||
|
|
||||||
|
//static uchar directions[8][9][8] = {{{0,4,5,6,7,0,0,0},{0,6,7,0,0,0,0,0},{5,6,7,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{0,1,2,6,7,0,0,0},{2,3,4,5,6,0,0,0},{0,1,2,3,4,5,6,7}},{{4,5,6,7,0,0,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{1,2,3,4,0,0,0,0},{1,2,0,0,0,0,0,0},{2,3,4,0,0,0,0,0},{1,2,6,7,0,0,0,0},{2,3,4,5,6,0,0,0},{1,2,3,4,5,6,7,0}},{{4,5,6,7,0,0,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{2,3,4,0,1,0,0,0},{2,0,1,0,0,0,0,0},{2,3,4,0,0,0,0,0},{2,6,7,0,1,0,0,0},{2,3,4,5,6,0,0,0},{2,3,4,5,6,7,0,1}},{{4,5,6,7,0,0,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{3,4,0,1,2,0,0,0},{0,1,2,0,0,0,0,0},{3,4,2,0,0,0,0,0},{6,7,0,1,2,0,0,0},{3,4,5,6,2,0,0,0},{3,4,5,6,7,0,1,2}},{{4,5,6,7,0,4,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{4,0,1,2,3,0,0,0},{0,1,2,0,0,0,0,0},{4,2,3,0,0,0,0,0},{6,7,0,1,2,0,0,0},{4,5,6,2,3,0,0,0},{4,5,6,7,0,1,2,3}},{{5,6,7,0,4,5,0,0},{6,7,0,0,0,0,0,0},{5,6,7,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{6,7,0,1,2,0,0,0},{5,6,2,3,4,0,0,0},{5,6,7,0,1,2,3,4}},{{6,7,0,4,5,6,0,0},{6,7,0,0,0,0,0,0},{6,7,5,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{6,7,0,1,2,0,0,0},{6,2,3,4,5,0,0,0},{6,7,0,1,2,3,4,5}},{{7,0,4,5,6,7,0,0},{7,0,6,0,0,0,0,0},{7,5,6,0,0,0,0,0},{0,1,2,3,4,0,0,0},{0,1,2,0,0,0,0,0},{2,3,4,0,0,0,0,0},{7,0,1,2,6,0,0,0},{2,3,4,5,6,0,0,0},{7,0,1,2,3,4,5,6}}};
|
||||||
|
//static uchar nDirs[8][9] = {{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8},{5,3,3,5,3,3,5,5,8}};
|
||||||
|
//static uchar* dirVec;
|
||||||
|
//int p =-1;
|
||||||
|
//if (r== 0) {
|
||||||
|
// if (c= =0)
|
||||||
|
// p = 1;
|
||||||
|
// else if (c == (w-1))
|
||||||
|
// p = 2;
|
||||||
|
// else
|
||||||
|
// p = 0;
|
||||||
|
//}
|
||||||
|
//else if (r== (h-1)) {
|
||||||
|
// if (c == 0)
|
||||||
|
// p = 4;
|
||||||
|
// else if (c == (w-1))
|
||||||
|
// p = 5;
|
||||||
|
// else
|
||||||
|
// p = 3;
|
||||||
|
//}
|
||||||
|
//else if (c == 0)
|
||||||
|
// p = 6;
|
||||||
|
//else if (c == (w-1))
|
||||||
|
// p = 7;
|
||||||
|
//else
|
||||||
|
// p = 8;
|
||||||
|
//
|
||||||
|
//dirVec = directions[dir][p];
|
||||||
|
//int i = 0;
|
||||||
|
//int d = dir;
|
||||||
|
//for (i; i < nDirs[d][p]; ++i) {
|
||||||
|
// tempR = r + freemanR[dirVec[i]];
|
||||||
|
// tempC = c + freemanC[dirVec[i]];
|
||||||
|
// pos = tempR*w +tempC;
|
||||||
|
// if (ptrDataBinary[pos]) {
|
||||||
|
// r = tempR;
|
||||||
|
// c = tempC;
|
||||||
|
// dir = dirVec[i];
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// ptrDataLabels[pos] = (int)currentBlob;
|
||||||
|
//}
|
||||||
|
//singlePixBlob = i == nDirs[d][p];
|
||||||
|
//pos = r*w+c;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabeler::getNextPointCW()
|
||||||
|
{
|
||||||
|
//Dir:
|
||||||
|
//321
|
||||||
|
//4-0
|
||||||
|
//567
|
||||||
|
m_dir = (m_dir + 2) % 8;
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
m_tempR = m_r + m_freemanR[m_dir];
|
||||||
|
m_tempC = m_c + m_freemanC[m_dir];
|
||||||
|
if (!(m_tempR < 0 || m_tempR >= m_h || m_tempC < 0 || m_tempC >= m_w)) {
|
||||||
|
m_pos = m_tempR * m_w + m_tempC;
|
||||||
|
if (m_ptrDataBinary[m_pos]) {
|
||||||
|
m_r = m_tempR;
|
||||||
|
m_c = m_tempC;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_ptrDataLabels[m_pos] = m_currentContour;
|
||||||
|
}
|
||||||
|
m_dir--;
|
||||||
|
m_dir = m_dir % 8; //To cycle through values 7->0
|
||||||
|
}
|
||||||
|
m_pos = m_r * m_w + m_c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CompLabeler::m_freemanC[8] = {1, 1, 0, -1, -1, -1, 0, 1};
|
||||||
|
|
||||||
|
int CompLabeler::m_freemanR[8] = {0, -1, -1, -1, 0, 1, 1, 1};
|
||||||
|
|
||||||
|
void CompLabelerGroup::doLabeling(BlobVector& blobs)
|
||||||
|
{
|
||||||
|
LabelID label = 0;
|
||||||
|
if (m_numThreads > 1) {
|
||||||
|
//Preliminary step in order to pre-compute all the blobs crossing the border
|
||||||
|
for (int i = 1; i < m_numThreads; i++) {
|
||||||
|
cv::Point offset(m_img.size().width, 1);
|
||||||
|
CompLabeler lbl(m_img, m_labels, m_labelers[i]->m_startPoint, m_labelers[i]->m_startPoint + offset);
|
||||||
|
lbl.m_parent = this;
|
||||||
|
lbl.label();
|
||||||
|
//cout << "Single pass\t" << lbl.blobs.size()<<endl;
|
||||||
|
for (unsigned int i = 0; i < lbl.m_blobs.size(); i++) {
|
||||||
|
lbl.m_blobs[i]->setID(label);
|
||||||
|
label++;
|
||||||
|
blobs.push_back(lbl.m_blobs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<std::thread> ths;
|
||||||
|
for (int i = 0; i < m_numThreads; i++) {
|
||||||
|
//pthread_create(&tIds[i], NULL, CompLabeler::thread_Labeling, labelers[i]);
|
||||||
|
ths.emplace_back(std::thread([label = m_labelers[i]]() {
|
||||||
|
CompLabeler::threadLabeling(label);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < m_numThreads; i++) {
|
||||||
|
//pthread_join(tIds[i], 0);
|
||||||
|
if (ths[i].joinable()) {
|
||||||
|
ths[i].join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_labelers[0]->label();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_numThreads; ++i) {
|
||||||
|
//cout << "MT pass " <<i<<"\t" << labelers[i]->blobs.size()<<endl;
|
||||||
|
for (unsigned int j = 0; j < m_labelers[i]->m_blobs.size(); ++j) {
|
||||||
|
m_labelers[i]->m_blobs[j]->setID(label);
|
||||||
|
label++;
|
||||||
|
blobs.push_back(m_labelers[i]->m_blobs[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompLabelerGroup::CompLabelerGroup()
|
||||||
|
{
|
||||||
|
//m_mutexBlob = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
m_labels = NULL;
|
||||||
|
m_labelers = NULL;
|
||||||
|
//m_tIds = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompLabelerGroup::~CompLabelerGroup()
|
||||||
|
{
|
||||||
|
if (m_labelers) {
|
||||||
|
for (int i = 0; i < m_numThreads; ++i) {
|
||||||
|
delete m_labelers[i];
|
||||||
|
}
|
||||||
|
delete []m_labelers;
|
||||||
|
}
|
||||||
|
//if (m_tIds) {
|
||||||
|
// delete [] m_tIds;
|
||||||
|
//}
|
||||||
|
if (m_labels) {
|
||||||
|
delete [] m_labels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabelerGroup::set(int nThreads, const cv::Mat& binImg)
|
||||||
|
{
|
||||||
|
this->m_numThreads = nThreads;
|
||||||
|
if (binImg.isContinuous()) {
|
||||||
|
this->m_img = binImg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->m_img = binImg.clone();
|
||||||
|
}
|
||||||
|
//this->labels = Mat_<int>::zeros(img.size());
|
||||||
|
if (m_labels) {
|
||||||
|
delete [] m_labels;
|
||||||
|
}
|
||||||
|
int nPts = binImg.size().width * binImg.size().height;
|
||||||
|
m_labels = new BlobContour*[nPts];
|
||||||
|
memset(m_labels, 0, nPts*sizeof(Blob*));
|
||||||
|
|
||||||
|
//if (m_tIds) {
|
||||||
|
// delete [] m_tIds;
|
||||||
|
//}
|
||||||
|
if (m_labelers) {
|
||||||
|
for (int i = 0; i < nThreads; ++i) {
|
||||||
|
delete m_labelers[i];
|
||||||
|
}
|
||||||
|
delete [] m_labelers;
|
||||||
|
}
|
||||||
|
//m_tIds = new pthread_t[nThreads];
|
||||||
|
m_labelers = new CompLabeler*[nThreads];
|
||||||
|
cv::Size sz = binImg.size();
|
||||||
|
//int numPx = sz.width*sz.width;
|
||||||
|
int i = 0;
|
||||||
|
for (; i < nThreads-1; ++i) {
|
||||||
|
int yStart = (int)((float)i / nThreads * sz.height);
|
||||||
|
int yEnd = (int)((float)(i + 1) / nThreads * sz.height);
|
||||||
|
cv::Point st(0, yStart);
|
||||||
|
cv::Point en(sz.width, yEnd);
|
||||||
|
m_labelers[i] = new CompLabeler(m_img, m_labels, st, en);
|
||||||
|
m_labelers[i]->m_parent = this;
|
||||||
|
}
|
||||||
|
cv::Point st(0, (int)((float)i / nThreads * sz.height));
|
||||||
|
cv::Point en(sz.width, sz.height);
|
||||||
|
//st = Point(0,187);
|
||||||
|
m_labelers[i] = new CompLabeler(m_img, m_labels, st, en);
|
||||||
|
m_labelers[i]->m_parent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabelerGroup::reset()
|
||||||
|
{
|
||||||
|
if (m_labels) {
|
||||||
|
delete [] m_labels;
|
||||||
|
m_labels = NULL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < m_numThreads; ++i) {
|
||||||
|
m_labelers[i]->reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabelerGroup::acquireMutex()
|
||||||
|
{
|
||||||
|
//pthread_mutex_lock(&m_mutexBlob);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompLabelerGroup::releaseMutex()
|
||||||
|
{
|
||||||
|
//pthread_mutex_unlock(&m_mutexBlob);
|
||||||
|
}
|
||||||
|
|
120
examples/contour/blob/ComponentLabeling.h
Normal file
120
examples/contour/blob/ComponentLabeling.h
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#if !defined(_COMPONENT_LABELING_H_INCLUDED)
|
||||||
|
#define _COMPONENT_LABELING_H_INCLUDED
|
||||||
|
|
||||||
|
//#define DEBUG_COMPONENT_LABELLING
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
#include "BlobContour.h"
|
||||||
|
#include "defines.h"
|
||||||
|
#include "blob.h"
|
||||||
|
#include "opencv2/opencv.hpp"
|
||||||
|
//#include <pthread.h>
|
||||||
|
|
||||||
|
//! forward decl. for "parent" field of CompLabeler.
|
||||||
|
class CompLabelerGroup;
|
||||||
|
|
||||||
|
//! implementation of F.Chang algorithm (Luca Nardelli)
|
||||||
|
class CompLabeler {
|
||||||
|
public:
|
||||||
|
//! Double pointer so to pass the array of blob pointers
|
||||||
|
CompLabeler(cv::Mat& binImage, BlobContour** lab, cv::Point start = cv::Point(-1, -1), cv::Point end = cv::Point(-1, -1));
|
||||||
|
~CompLabeler();
|
||||||
|
|
||||||
|
//! Do labeling in region defined by startpoint and endpoint, populating blobs
|
||||||
|
void label();
|
||||||
|
|
||||||
|
//! Resets internal buffers
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
//! External contours tracer
|
||||||
|
void tracerExt();
|
||||||
|
|
||||||
|
//! Internal contours tracer
|
||||||
|
void tracerInt(int startDir = 5);
|
||||||
|
|
||||||
|
//! Counter clockwise
|
||||||
|
void getNextPointCCW();
|
||||||
|
|
||||||
|
//! Clockwise
|
||||||
|
void getNextPointCW();
|
||||||
|
|
||||||
|
//! Thread function
|
||||||
|
static void* threadLabeling(void* o);
|
||||||
|
|
||||||
|
public:
|
||||||
|
BlobVector m_blobs;
|
||||||
|
|
||||||
|
cv::Mat m_binaryImage;
|
||||||
|
|
||||||
|
cv::Point m_startPoint, m_endPoint;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class CompLabelerGroup;
|
||||||
|
|
||||||
|
//! CompLabelerGroup parent, in order to get access to mutexes.
|
||||||
|
CompLabelerGroup* m_parent;
|
||||||
|
|
||||||
|
BlobContour** m_labels;
|
||||||
|
|
||||||
|
//! currentLabel
|
||||||
|
int m_currentLabel;
|
||||||
|
|
||||||
|
int m_r, m_c, m_pos;
|
||||||
|
|
||||||
|
//! Width& Height of image
|
||||||
|
int m_w, m_h;
|
||||||
|
|
||||||
|
uchar m_dir;
|
||||||
|
|
||||||
|
static int m_freemanR[8], m_freemanC[8];
|
||||||
|
|
||||||
|
bool m_singlePixBlob;
|
||||||
|
|
||||||
|
uchar* m_ptrDataBinary;
|
||||||
|
|
||||||
|
BlobContour** m_ptrDataLabels;
|
||||||
|
|
||||||
|
int m_tempR, m_tempC;
|
||||||
|
|
||||||
|
Blob* m_currentBlob;
|
||||||
|
|
||||||
|
BlobContour* m_currentContour;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CompLabelerGroup {
|
||||||
|
public:
|
||||||
|
CompLabelerGroup();
|
||||||
|
|
||||||
|
~CompLabelerGroup();
|
||||||
|
|
||||||
|
void doLabeling(BlobVector& blobs);
|
||||||
|
|
||||||
|
void set(int numThreads, const cv::Mat& img);
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void acquireMutex();
|
||||||
|
|
||||||
|
void releaseMutex();
|
||||||
|
|
||||||
|
public:
|
||||||
|
cv::Mat m_img;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class CompLabeler;
|
||||||
|
|
||||||
|
CompLabeler** m_labelers;
|
||||||
|
|
||||||
|
int m_numThreads;
|
||||||
|
|
||||||
|
std::mutex m_mutexBlob;
|
||||||
|
|
||||||
|
//pthread_t* m_tIds;
|
||||||
|
//pthread_mutex_t m_mutexBlob;
|
||||||
|
|
||||||
|
BlobContour** m_labels;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //!_COMPONENT_LABELING_H_INCLUDED
|
1044
examples/contour/blob/blob.cpp
Normal file
1044
examples/contour/blob/blob.cpp
Normal file
File diff suppressed because it is too large
Load Diff
268
examples/contour/blob/blob.h
Normal file
268
examples/contour/blob/blob.h
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
/************************************************************************
|
||||||
|
Blob.h
|
||||||
|
|
||||||
|
FUNCTIONALITY: Definition of the Blob class and some helper classes to perform
|
||||||
|
some calculations on it
|
||||||
|
AUTHOR: Inspecta S.L.
|
||||||
|
MODIFICATIONS (Modification, Author, Date):
|
||||||
|
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
//! Disable warnings referred to 255 character truncation for the std:map
|
||||||
|
//#pragma warning(disable : 4786)
|
||||||
|
|
||||||
|
#ifndef CBLOB_INSPECTA_INCLUDED
|
||||||
|
#define CBLOB_INSPECTA_INCLUDED
|
||||||
|
|
||||||
|
class Blob;
|
||||||
|
#include "opencv2/core/core_c.h"
|
||||||
|
#include "opencv2/opencv.hpp"
|
||||||
|
#include "BlobLibraryConfiguration.h"
|
||||||
|
#include "BlobContour.h"
|
||||||
|
#include <deque>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef BLOB_OBJECT_FACTORY
|
||||||
|
//! Object factory pattern implementation
|
||||||
|
#include "..\inspecta\DesignPatterns\ObjectFactory.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Type of labelled images
|
||||||
|
typedef unsigned int LabelID;
|
||||||
|
typedef std::list<Blob*> BlobList;
|
||||||
|
typedef std::list<BlobContour*> BlobContourList;
|
||||||
|
|
||||||
|
enum AreaMode {GREEN, PIXELWISE};
|
||||||
|
|
||||||
|
//! Blob class
|
||||||
|
class Blob
|
||||||
|
{
|
||||||
|
friend class CompLabeler;
|
||||||
|
public:
|
||||||
|
Blob();
|
||||||
|
Blob(LabelID id, const cv::Point& startPoint, const cv::Size& originalImageSize);
|
||||||
|
~Blob();
|
||||||
|
|
||||||
|
//! Copy constructor
|
||||||
|
Blob(const Blob& src);
|
||||||
|
Blob(const Blob* src);
|
||||||
|
|
||||||
|
//! Assigment operator
|
||||||
|
Blob& operator=(const Blob& src);
|
||||||
|
|
||||||
|
//! Adds a new internal contour to the blob
|
||||||
|
void addInternalContour(const BlobContour& newContour);
|
||||||
|
|
||||||
|
//! Retrieves contour in Freeman's chain code
|
||||||
|
BlobContour* getExternalContour() {
|
||||||
|
return &m_externalContour;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlobContourList& getInternalContours() {
|
||||||
|
return m_internalContours;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Bool to permit deletion with filter function
|
||||||
|
double m_toBeDeleted;
|
||||||
|
|
||||||
|
//! Get label ID
|
||||||
|
LabelID getID() {
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setID(LabelID newID) {
|
||||||
|
m_id = newID;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! > 0 for external blobs, 0 if not
|
||||||
|
int exterior(IplImage* mask, bool xBorder = true, bool yBorder = true);
|
||||||
|
|
||||||
|
//! opencv2 Interface
|
||||||
|
int exterior(const cv::Mat& mask, bool xBorder = true, bool yBorder = true);
|
||||||
|
|
||||||
|
//! Computes the area of the blob.
|
||||||
|
//! areaCompMode defines which way to compute the areas:
|
||||||
|
//! - Using green's formula (not exact result, probably faster)
|
||||||
|
//! - Counting the pixels (probably slower)
|
||||||
|
double area(AreaMode areaCompMode = GREEN);
|
||||||
|
|
||||||
|
//! Compute blob's perimeter
|
||||||
|
double perimeter();
|
||||||
|
|
||||||
|
//! Compute blob's moment (p,q up to MAX_CALCULATED_MOMENTS)
|
||||||
|
//! if intContours = false, internal contours do not count for moment computation (i.e. the blob is considered without holes).
|
||||||
|
double moment(int p, int q, bool intContours = true);
|
||||||
|
|
||||||
|
//! Compute external perimeter
|
||||||
|
double externalPerimeter(IplImage* mask, bool xBorder = true, bool yBorder = true);
|
||||||
|
|
||||||
|
//! opencv2 interface
|
||||||
|
double externalPerimeter(const cv::Mat& mask, bool xBorder = true, bool yBorder = true);
|
||||||
|
|
||||||
|
//! Get mean grey color
|
||||||
|
//(Warning: use meanStdDev for simultaneous computation of mean and std. dev, and for RGB images).
|
||||||
|
double mean(IplImage *image);
|
||||||
|
|
||||||
|
//! opencv2 interface
|
||||||
|
//(Warning: use meanStdDev for simultaneous computation of mean and std. dev, and for RGB images).
|
||||||
|
double mean(const cv::Mat& image);
|
||||||
|
|
||||||
|
//! Get standard deviation grey color
|
||||||
|
//(Warning: use meanStdDev for simultaneous computation of mean and std. dev, and for RGB images).
|
||||||
|
double stdDev(IplImage *image);
|
||||||
|
|
||||||
|
//! opencv2 interface
|
||||||
|
//(Warning: use meanStdDev for simultaneous computation of mean and std. dev, and for RGB images).
|
||||||
|
double stdDev(const cv::Mat& image);
|
||||||
|
|
||||||
|
//! Computes mean and standard deviation of image, which can be in any opencv format
|
||||||
|
//! Since mean and standard deviation are computed with the same function call, this results quicker than
|
||||||
|
//! calling separately mean and standard deviation.
|
||||||
|
void meanStdDev(const cv::Mat& image, cv::Scalar& mean, cv::Scalar& stddev);
|
||||||
|
|
||||||
|
//void meanStdDev(Mat image, double *mean, double *stddev);
|
||||||
|
|
||||||
|
//! Shows if the blob has associated information
|
||||||
|
bool isEmpty();
|
||||||
|
|
||||||
|
//! Calculates the convex hull of the blob
|
||||||
|
void getConvexHull(Contours& hull);
|
||||||
|
|
||||||
|
//! Paints the blob in an image
|
||||||
|
//! intContours - determines wheter to draw the holes of the blob (true) or not (false)
|
||||||
|
//! srcImage - image from where to copy the holes contents. If unassigned and intContours is true, the internal pixels will be set to black.
|
||||||
|
void fillBlob(IplImage *image, cv::Scalar color, int offsetX = 0, int offsetY = 0, bool intContours = false, const IplImage* srcImage = NULL);
|
||||||
|
|
||||||
|
void fillBlob(const cv::Mat& image, cv::Scalar color, int offsetX = 0, int offsetY = 0, bool intContours = false, const cv::Mat& srcImage = cv::Mat());
|
||||||
|
|
||||||
|
//! Joins a blob to current one
|
||||||
|
//! NOTE: All the data is copied, a new blob is created and joined to the caller one.
|
||||||
|
void joinBlob(Blob* blob);
|
||||||
|
|
||||||
|
//! Get bounding box
|
||||||
|
cv::Rect getBoundingBox();
|
||||||
|
|
||||||
|
//! Get bounding ellipse
|
||||||
|
cv::RotatedRect getEllipse();
|
||||||
|
|
||||||
|
//! Minimun X
|
||||||
|
double minX() {
|
||||||
|
return getBoundingBox().x;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Minimun Y
|
||||||
|
double minY() {
|
||||||
|
return getBoundingBox().y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Maximun X
|
||||||
|
double maxX() {
|
||||||
|
return getBoundingBox().x + getBoundingBox().width;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Maximun Y
|
||||||
|
double maxY() {
|
||||||
|
return getBoundingBox().y + getBoundingBox().height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Computes extremes for the contour (i.e. 4 points, respectively with max X, max Y, min X, minY)
|
||||||
|
In case of perfect rectangular shapes, the code will return the vertexes in counterclockwise order.
|
||||||
|
TODO: Extend function to joined blobs
|
||||||
|
*/
|
||||||
|
void getExtremes(cv::Point& xmax, cv::Point& xmin, cv::Point& ymax, cv::Point& ymin);
|
||||||
|
|
||||||
|
|
||||||
|
//! Shifts the blob by (x,y)
|
||||||
|
void shiftBlob(int x, int y);
|
||||||
|
|
||||||
|
//! Returns the number of overlapping pixels between the caller blob and blob.
|
||||||
|
//! A preliminary check is performed with respect to the bounding boxes in order to avoid unnecessary computations
|
||||||
|
int overlappingPixels(Blob* blob);
|
||||||
|
|
||||||
|
//! Computes the density of the blob, i.e. the ratio (blob Area) / (ConvexHullArea)
|
||||||
|
//! areaCalculationMode defines which way to compute the areas:
|
||||||
|
//! - Using green's formula (not exact result, probably faster)
|
||||||
|
//! - Counting the pixels
|
||||||
|
double density(AreaMode areaCalculationMode);
|
||||||
|
|
||||||
|
//! Returns blob center in pixels (integers).
|
||||||
|
cv::Point getCenter();
|
||||||
|
|
||||||
|
//! Return blob centroid
|
||||||
|
cv::Point2f getCentroid(bool useInternalContours);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Border: 0 = top, 1 = right, 2 = bottom, 3 = left
|
||||||
|
*/
|
||||||
|
/*Contours getPointsTouchingBorder(int border);*/
|
||||||
|
|
||||||
|
//! For joined blobs, return the number of sub-blobs.
|
||||||
|
int getNumJoinedBlobs();
|
||||||
|
|
||||||
|
cv::Size originalImageSize() const { return m_originalImageSize; }
|
||||||
|
|
||||||
|
void originalImageSize(int width, int height) { m_originalImageSize.width = width; m_originalImageSize.height = height; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void requestDeletion(Blob* blob);
|
||||||
|
|
||||||
|
//! Deallocates all contours
|
||||||
|
void clearContours();
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! Just for multithread joining routine;
|
||||||
|
bool m_startPassed;
|
||||||
|
|
||||||
|
bool m_isJoined;
|
||||||
|
|
||||||
|
BlobList m_joinedBlobs;
|
||||||
|
|
||||||
|
Blob* m_deleteRequestOwnerBlob;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Blob contours
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//! External contour of the blob (crack codes)
|
||||||
|
BlobContour m_externalContour;
|
||||||
|
|
||||||
|
//! Internal contours (crack codes)
|
||||||
|
BlobContourList m_internalContours;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// Blob features
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
//! Label number
|
||||||
|
LabelID m_id;
|
||||||
|
|
||||||
|
//! Area
|
||||||
|
double m_area;
|
||||||
|
|
||||||
|
//! Perimeter
|
||||||
|
double m_perimeter;
|
||||||
|
|
||||||
|
//! external perimeter from blob
|
||||||
|
double m_externalPerimeter;
|
||||||
|
|
||||||
|
//! mean gray color
|
||||||
|
double m_meanGray;
|
||||||
|
|
||||||
|
//! Standard deviation from gray color blob distribution
|
||||||
|
double m_stdDevGray;
|
||||||
|
|
||||||
|
//! Bounding box
|
||||||
|
cv::Rect m_boundingBox;
|
||||||
|
|
||||||
|
//! Bounding ellipse
|
||||||
|
cv::RotatedRect m_ellipse;
|
||||||
|
|
||||||
|
//! Sizes from image where blob is extracted
|
||||||
|
cv::Size m_originalImageSize;
|
||||||
|
|
||||||
|
friend class BlobGroup;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //CBLOB_INSPECTA_INCLUDED
|
52
examples/contour/blob/defines.h
Normal file
52
examples/contour/blob/defines.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifdef MATRIXCV_ACTIU
|
||||||
|
#include "matrixCV.h"
|
||||||
|
#else
|
||||||
|
#include "vector"
|
||||||
|
//! Vector de doubles
|
||||||
|
typedef std::vector<double> DoubleStlVector;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
Filtres / Filters
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
//! Actions performed by a filter (include or exclude blobs)
|
||||||
|
#define B_INCLUDE 1L
|
||||||
|
#define B_EXCLUDE 2L
|
||||||
|
|
||||||
|
enum FilterAction {FLT_INCLUDE = 1, FLT_EXCLUDE};
|
||||||
|
|
||||||
|
//! Conditions to apply the filters
|
||||||
|
#define B_EQUAL 3L
|
||||||
|
#define B_NOT_EQUAL 4L
|
||||||
|
#define B_GREATER 5L
|
||||||
|
#define B_LESS 6L
|
||||||
|
#define B_GREATER_OR_EQUAL 7L
|
||||||
|
#define B_LESS_OR_EQUAL 8L
|
||||||
|
#define B_INSIDE 9L
|
||||||
|
#define B_OUTSIDE 10L
|
||||||
|
|
||||||
|
enum FilterCondition {FLT_EQUAL = 3, FLT_NOTEQUAL, FLT_GREATER, FLT_LESS, FLT_GREATEROREQUAL, FLT_LESSOREQUAL, FLT_INSIDE, FLT_OUTSIDE};
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
Excepcions / Exceptions
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
//! Exceptions thrown by functions:
|
||||||
|
#define EXCEPTION_BLOB_OUT_OF_BOUNDS 1000
|
||||||
|
#define EXCEPCIO_CALCUL_BLOBS 1001
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that contains a set of blobs and allows properties to be extracted or filter them according to certain criteria.
|
||||||
|
* Class to calculate the blobs of an image and calculate some properties on them. Also, the class provides functions to filter the blobs using some criteria.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Blob;
|
||||||
|
|
||||||
|
//! vector of blob pointers typedef
|
||||||
|
typedef std::vector<Blob*> BlobVector;
|
144
examples/contour/bspline.cpp
Normal file
144
examples/contour/bspline.cpp
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
#include "bspline.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
BSpline::BSpline() : m_isValid(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BSpline::BSpline(const std::vector<std::vector<double>>& points, int degree)
|
||||||
|
{
|
||||||
|
createImpl(points, degree);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSpline::create(const std::vector<std::vector<double>>& points, int degree)
|
||||||
|
{
|
||||||
|
createImpl(points, degree);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BSpline::createImpl(const std::vector<std::vector<double>>& points, int degree)
|
||||||
|
{
|
||||||
|
m_isValid = true;
|
||||||
|
|
||||||
|
m_knots.clear();
|
||||||
|
|
||||||
|
m_weights.clear();
|
||||||
|
|
||||||
|
m_v.clear();
|
||||||
|
|
||||||
|
m_degree = degree;
|
||||||
|
|
||||||
|
if (points.empty()) {
|
||||||
|
m_isValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int n = points.size();
|
||||||
|
|
||||||
|
if (m_degree < 1) {
|
||||||
|
m_isValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_degree > (n - 1)) {
|
||||||
|
m_isValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int step = m_degree + 1;
|
||||||
|
|
||||||
|
int count = (m_degree + 1 + points.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
if (i < step) {
|
||||||
|
m_knots.emplace_back(0);
|
||||||
|
}
|
||||||
|
else if (i >= count - step) {
|
||||||
|
m_knots.emplace_back(count - 2 * step + 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_knots.emplace_back(i - step + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_dim = points[0].size();
|
||||||
|
|
||||||
|
if (m_weights.empty()) {
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
m_weights.emplace_back(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_knots.empty()) {
|
||||||
|
for (int i = 0; i < n + m_degree + 1; i++) {
|
||||||
|
m_knots.emplace_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (m_knots.size() != (n + m_degree + 1)) {
|
||||||
|
m_isValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_domain = { m_degree, m_knots.size() - 1 - m_degree };
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
m_v.emplace_back(std::vector<double>());
|
||||||
|
for (int j = 0; j < m_dim; j++) {
|
||||||
|
m_v[i].emplace_back(points[i][j] * m_weights[i]);
|
||||||
|
}
|
||||||
|
m_v[i].emplace_back(m_weights[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BSpline::isValid()
|
||||||
|
{
|
||||||
|
return m_isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> BSpline::eval(double t)
|
||||||
|
{
|
||||||
|
if (!isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<double> result;
|
||||||
|
|
||||||
|
double low = m_knots[m_domain.first];
|
||||||
|
|
||||||
|
double high = m_knots[m_domain.second];
|
||||||
|
|
||||||
|
t = t * (high - low) + low;
|
||||||
|
|
||||||
|
if (t < low || t > high) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int s = 0;
|
||||||
|
|
||||||
|
for (s = m_domain.first; s < m_domain.second; s++) {
|
||||||
|
if (t >= m_knots[s] && t <= m_knots[s + 1]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<double>> v = m_v;
|
||||||
|
|
||||||
|
double alpha = 0.0;
|
||||||
|
|
||||||
|
for (int l = 1; l <= m_degree + 1; l++) {
|
||||||
|
for (int i = s; i > s - m_degree - 1 + l; i--) {
|
||||||
|
alpha = (t - m_knots[i]) / (m_knots[i + m_degree + 1 - l] - m_knots[i]);
|
||||||
|
for (int j = 0; j < m_dim + 1; j++) {
|
||||||
|
v[i][j] = (1 - alpha) * v[i - 1][j] + alpha * v[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_dim; i++) {
|
||||||
|
result.emplace_back(v[s][i] / v[s][m_dim]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
39
examples/contour/bspline.hpp
Normal file
39
examples/contour/bspline.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "i_bspline.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
class BSpline : public IBSpline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BSpline();
|
||||||
|
|
||||||
|
BSpline(const std::vector<std::vector<double>>& points, int degree);
|
||||||
|
|
||||||
|
void create(const std::vector<std::vector<double>>& points, int degree) override;
|
||||||
|
|
||||||
|
bool isValid() override;
|
||||||
|
|
||||||
|
std::vector<double> eval(double t) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createImpl(const std::vector<std::vector<double>>& points, int degree);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_isValid;
|
||||||
|
|
||||||
|
std::pair<int, int> m_domain;
|
||||||
|
|
||||||
|
std::vector<double> m_knots;
|
||||||
|
|
||||||
|
std::vector<double> m_weights;
|
||||||
|
|
||||||
|
std::vector<std::vector<double>> m_v;
|
||||||
|
|
||||||
|
int m_dim;
|
||||||
|
|
||||||
|
int m_degree;
|
||||||
|
};
|
||||||
|
}
|
75
examples/contour/contour.cpp
Normal file
75
examples/contour/contour.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// basic.cpp : 定义应用程序的入口点。
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "basic_form.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
enum ThreadId
|
||||||
|
{
|
||||||
|
kThreadUI
|
||||||
|
};
|
||||||
|
|
||||||
|
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
|
||||||
|
_In_opt_ HINSTANCE hPrevInstance,
|
||||||
|
_In_ LPWSTR lpCmdLine,
|
||||||
|
_In_ int nCmdShow)
|
||||||
|
{
|
||||||
|
AllocConsole();
|
||||||
|
freopen("CONOUT$", "w", stdout);
|
||||||
|
|
||||||
|
|
||||||
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
||||||
|
UNREFERENCED_PARAMETER(lpCmdLine);
|
||||||
|
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
|
||||||
|
{
|
||||||
|
// TODO: change error code to suit your needs
|
||||||
|
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// 创建主线程
|
||||||
|
MainThread thread;
|
||||||
|
|
||||||
|
// 执行主线程循环
|
||||||
|
thread.RunOnCurrentThreadWithLoop(nbase::MessageLoop::kUIMessageLoop);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainThread::Init()
|
||||||
|
{
|
||||||
|
nbase::ThreadManager::RegisterThread(kThreadUI);
|
||||||
|
|
||||||
|
// 获取资源路径,初始化全局参数
|
||||||
|
std::wstring theme_dir = nbase::win32::GetCurrentModuleDirectory();
|
||||||
|
#ifdef _DEBUG
|
||||||
|
// Debug 模式下使用本地文件夹作为资源
|
||||||
|
// 默认皮肤使用 resources\\themes\\default
|
||||||
|
// 默认语言使用 resources\\lang\\zh_CN
|
||||||
|
// 如需修改请指定 Startup 最后两个参数
|
||||||
|
ui::GlobalManager::Startup(theme_dir + L"resources\\", ui::CreateControlCallback(), true);
|
||||||
|
std::wcout<< theme_dir + L"resources\\";
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Release 模式下使用资源中的压缩包作为资源
|
||||||
|
// 资源被导入到资源列表分类为 THEME,资源名称为 IDR_THEME
|
||||||
|
// 如果资源使用的是本地的 zip 文件而非资源中的 zip 压缩包
|
||||||
|
// 可以使用 OpenResZip 另一个重载函数打开本地的资源压缩包
|
||||||
|
//ui::GlobalManager::OpenResZip(MAKEINTRESOURCE(IDR_THEME), L"THEME", "");
|
||||||
|
ui::GlobalManager::OpenResZip(L"resources.zip", "");
|
||||||
|
ui::GlobalManager::Startup(L"resources\\", ui::CreateControlCallback(), false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 创建一个默认带有阴影的居中窗口
|
||||||
|
BasicForm* window = new BasicForm();
|
||||||
|
window->Create(NULL, BasicForm::kClassName.c_str(), WS_OVERLAPPEDWINDOW, 0);
|
||||||
|
window->CenterWindow();
|
||||||
|
window->ShowWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainThread::Cleanup()
|
||||||
|
{
|
||||||
|
ui::GlobalManager::Shutdown();
|
||||||
|
SetThreadWasQuitProperly(true);
|
||||||
|
nbase::ThreadManager::UnregisterThread();
|
||||||
|
}
|
3
examples/contour/contour.h
Normal file
3
examples/contour/contour.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "resource.h"
|
BIN
examples/contour/contour.ico
Normal file
BIN
examples/contour/contour.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
examples/contour/contour.rc
Normal file
BIN
examples/contour/contour.rc
Normal file
Binary file not shown.
179
examples/contour/contour.vcxproj
Normal file
179
examples/contour/contour.vcxproj
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<VCProjectVersion>17.0</VCProjectVersion>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<ProjectGuid>{a01dd5d1-1d47-4956-a472-fd84b1af7036}</ProjectGuid>
|
||||||
|
<RootNamespace>contour</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v143</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<IncludePath>G:\project\c++\nim_duilib\examples\contour;G:\project\c++\nim_duilib;C:\Users\caiyu\anaconda3\pkgs\opencv-4.6.0-py311h5d08a89_5\Library\include;G:\project\c++\nim_duilib\examples\contour\blob;$(IncludePath)</IncludePath>
|
||||||
|
<ReferencePath>G:\project\c++\nim_duilib\examples\contour;D:\project\cpp\opencv-4.10.0\build\3rdparty\lib\Debug;$(ReferencePath)</ReferencePath>
|
||||||
|
<LibraryPath>G:\project\c++\nim_duilib\examples\contour;D:\project\cpp\opencv-4.10.0\build\3rdparty\lib\Debug;$(LibraryPath)</LibraryPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||||
|
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||||
|
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<AdditionalDependencies>opencv_world4100d.lib;libjpeg-turbod.lib;zlibd.lib;libpngd.lib;libtiffd.lib;libwebpd.lib;ippiwd.lib;IlmImfd.lib;ittnotifyd.lib;libopenjp2d.lib;libprotobufd.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<ConformanceMode>true</ConformanceMode>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Windows</SubSystem>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="contour.h" />
|
||||||
|
<ClInclude Include="framework.h" />
|
||||||
|
<ClInclude Include="Resource.h" />
|
||||||
|
<ClInclude Include="targetver.h" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="basic_form.cpp" />
|
||||||
|
<ClCompile Include="blob\blob.cpp" />
|
||||||
|
<ClCompile Include="blob\BlobContour.cpp" />
|
||||||
|
<ClCompile Include="blob\BlobDetector.cpp" />
|
||||||
|
<ClCompile Include="blob\BlobGroup.cpp" />
|
||||||
|
<ClCompile Include="blob\BlobOperators.cpp" />
|
||||||
|
<ClCompile Include="blob\ComponentLabeling.cpp" />
|
||||||
|
<ClCompile Include="bspline.cpp" />
|
||||||
|
<ClCompile Include="contour.cpp" />
|
||||||
|
<ClCompile Include="contours_extractor.cpp" />
|
||||||
|
<ClCompile Include="contours_extractor_factory.cpp" />
|
||||||
|
<ClCompile Include="contours_extractor_impl.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="contour.rc" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="contour.ico" />
|
||||||
|
<Image Include="small.ico" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\base\base.vcxproj">
|
||||||
|
<Project>{8d9a6595-717a-41c8-b468-0011a72be3d1}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\duilib\duilib.vcxproj">
|
||||||
|
<Project>{e106acd7-4e53-4aee-942b-d0dd426db34e}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
<ProjectReference Include="..\..\ui_components\ui_components.vcxproj">
|
||||||
|
<Project>{0149ba6e-3c0a-426d-aa0a-0b9ec7742f19}</Project>
|
||||||
|
</ProjectReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
82
examples/contour/contour.vcxproj.filters
Normal file
82
examples/contour/contour.vcxproj.filters
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="源文件">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="头文件">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="资源文件">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="framework.h">
|
||||||
|
<Filter>头文件</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="targetver.h">
|
||||||
|
<Filter>头文件</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="Resource.h">
|
||||||
|
<Filter>头文件</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="contour.h">
|
||||||
|
<Filter>头文件</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="contour.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="basic_form.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="bspline.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="contours_extractor.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="contours_extractor_factory.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="contours_extractor_impl.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="blob\blob.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="blob\BlobContour.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="blob\BlobDetector.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="blob\BlobGroup.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="blob\BlobOperators.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="blob\ComponentLabeling.cpp">
|
||||||
|
<Filter>源文件</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ResourceCompile Include="contour.rc">
|
||||||
|
<Filter>资源文件</Filter>
|
||||||
|
</ResourceCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Image Include="small.ico">
|
||||||
|
<Filter>资源文件</Filter>
|
||||||
|
</Image>
|
||||||
|
<Image Include="contour.ico">
|
||||||
|
<Filter>资源文件</Filter>
|
||||||
|
</Image>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
48
examples/contour/contour.xml
Normal file
48
examples/contour/contour.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Window size="1280,900" caption="0,0,0,35">
|
||||||
|
<VBox bkcolor="bk_wnd_darkcolor">
|
||||||
|
<HBox width="stretch" height="35" bkcolor="bk_wnd_lightcolor">
|
||||||
|
<Control />
|
||||||
|
<Label name="tooltip" text="轮廓线获取" margin="4,6,0,0"/>
|
||||||
|
<Control />
|
||||||
|
|
||||||
|
<Button class="btn_wnd_min" name="minbtn" margin="4,6,0,0"/>
|
||||||
|
<Box width="21" margin="4,6,0,0">
|
||||||
|
<Button class="btn_wnd_max" name="maxbtn"/>
|
||||||
|
<Button class="btn_wnd_restore" name="restorebtn" visible="false"/>
|
||||||
|
</Box>
|
||||||
|
<Button class="btn_wnd_close" name="closebtn" margin="4,6,8,0"/>
|
||||||
|
</HBox>
|
||||||
|
<VBox width="stretch" height="35" margin="10,10,0,0">
|
||||||
|
<HBox>
|
||||||
|
<Label text="文件1" margin="0,10,0,0" height="40" width="80" font="arial_18"/>
|
||||||
|
<RichEdit class="simple input" width="600" name="file1_dir" height="30" margin="5,5,5,5" padding="6,6,6" font="arial_18"/>
|
||||||
|
<Button class="btn_global_blue_80x30" name="btn_do_open1" width="80" text="打开" bkcolor="lightcolor" margin="0,3,5,3" font="arial_18" />
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
|
<HBox margin="10,10,10,10" width="stretch" height="35">
|
||||||
|
<Label text="滑块示例" margin="0,0,10,0" height="100" width="80" font="arial_18"/>
|
||||||
|
<Slider class="slider_green" value="70" margin="10"/>
|
||||||
|
</HBox>
|
||||||
|
<VBox width="stretch" height="35" margin="10,10,0,0">
|
||||||
|
<HBox>
|
||||||
|
<Label text="最小轮廓阈值" margin="0,10,0,0" height="40" width="80" font="arial_18"/>
|
||||||
|
<RichEdit class="simple input" width="50" name="min_contour" height="30" margin="5,5,5,5" padding="6,6,6" font="arial_18"/>
|
||||||
|
<Label text="轮廓的偏置距离" margin="0,10,0,0" height="40" width="80" font="arial_18"/>
|
||||||
|
<RichEdit class="simple input" width="50" name="offset_controu" height="30" margin="5,5,5,5" padding="6,6,6" font="arial_18"/>
|
||||||
|
|
||||||
|
<Label text="多边形逼近精度" margin="0,10,0,0" height="40" width="80" font="arial_18"/>
|
||||||
|
<RichEdit class="simple input" width="50" name="epsilon" height="30" margin="5,5,5,5" padding="6,6,6" font="arial_18"/>
|
||||||
|
|
||||||
|
<Label text="缺陷处的开口阈值" margin="0,10,0,0" height="40" width="80" font="arial_18"/>
|
||||||
|
<RichEdit class="simple input" width="50" name="angel_threshold" height="30" margin="5,5,5,5" padding="6,6,6" font="arial_18"/>
|
||||||
|
<Label text="平滑步长" margin="0,10,0,0" height="40" width="80" font="arial_18"/>
|
||||||
|
<RichEdit class="simple input" width="50" name="smooth_step" height="30" margin="5,5,5,5" padding="6,6,6" font="arial_18"/>
|
||||||
|
|
||||||
|
<Button class="btn_global_blue_80x30" name="btn_set_config" width="80" text="打开" bkcolor="lightcolor" margin="0,3,5,3" font="arial_18" />
|
||||||
|
|
||||||
|
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
|
</VBox>
|
||||||
|
</Window>
|
96
examples/contour/contours_extractor.cpp
Normal file
96
examples/contour/contours_extractor.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#include "contours_extractor.hpp"
|
||||||
|
#include "contours_extractor_impl.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
ContoursExtractor::ContoursExtractor()
|
||||||
|
: m_impl(std::make_shared<ContoursExtractorImpl>())
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::init()
|
||||||
|
{
|
||||||
|
m_impl->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::destroy()
|
||||||
|
{
|
||||||
|
m_impl->destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::setAreaThreshold(double threshold)
|
||||||
|
{
|
||||||
|
m_impl->setAngelThreshold(threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractor::areaThreshold()
|
||||||
|
{
|
||||||
|
return m_impl->angelThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::setContourOffset(int32_t offset)
|
||||||
|
{
|
||||||
|
m_impl->setContourOffset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ContoursExtractor::contourOffset()
|
||||||
|
{
|
||||||
|
return m_impl->contourOffset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::setApproxPolyEpsilon(double epsilon)
|
||||||
|
{
|
||||||
|
m_impl->setApproxPolyEpsilon(epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractor::approxPolyEpsilon()
|
||||||
|
{
|
||||||
|
return m_impl->approxPolyEpsilon();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::setSmoothMethod(SMOOTH_METHOD method)
|
||||||
|
{
|
||||||
|
m_impl->setSmoothMethod(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
SMOOTH_METHOD ContoursExtractor::smoothMethod()
|
||||||
|
{
|
||||||
|
return m_impl->smoothMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::setAngelThreshold(double threshold)
|
||||||
|
{
|
||||||
|
m_impl->setAngelThreshold(threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractor::angelThreshold()
|
||||||
|
{
|
||||||
|
return m_impl->angelThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::setDefectThreshold(double threshold)
|
||||||
|
{
|
||||||
|
m_impl->setDefectThreshold(threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractor::defectThreshold()
|
||||||
|
{
|
||||||
|
return m_impl->defectThreshold();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::setSmoothStep(double step)
|
||||||
|
{
|
||||||
|
m_impl->setSmoothStep(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractor::smoothStep()
|
||||||
|
{
|
||||||
|
return m_impl->smoothStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractor::extract(const cv::Mat& alpha, std::vector<std::vector<cv::Point>>& outputContours)
|
||||||
|
{
|
||||||
|
m_impl->extract(alpha, outputContours);
|
||||||
|
}
|
||||||
|
}
|
50
examples/contour/contours_extractor.hpp
Normal file
50
examples/contour/contours_extractor.hpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "i_contours_extractor.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
class ContoursExtractor : public IContoursExtractor {
|
||||||
|
public:
|
||||||
|
ContoursExtractor();
|
||||||
|
|
||||||
|
~ContoursExtractor() = default;
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
|
||||||
|
void destroy() override;
|
||||||
|
|
||||||
|
void setAreaThreshold(double threshold) override;
|
||||||
|
|
||||||
|
double areaThreshold() override;
|
||||||
|
|
||||||
|
void setContourOffset(int32_t offset) override;
|
||||||
|
|
||||||
|
int32_t contourOffset() override;
|
||||||
|
|
||||||
|
void setApproxPolyEpsilon(double epsilon) override;
|
||||||
|
|
||||||
|
double approxPolyEpsilon() override;
|
||||||
|
|
||||||
|
void setSmoothMethod(SMOOTH_METHOD method) override;
|
||||||
|
|
||||||
|
SMOOTH_METHOD smoothMethod() override;
|
||||||
|
|
||||||
|
void setAngelThreshold(double threshold) override;
|
||||||
|
|
||||||
|
double angelThreshold() override;
|
||||||
|
|
||||||
|
void setDefectThreshold(double threshold) override;
|
||||||
|
|
||||||
|
double defectThreshold() override;
|
||||||
|
|
||||||
|
void setSmoothStep(double step) override;
|
||||||
|
|
||||||
|
double smoothStep() override;
|
||||||
|
|
||||||
|
void extract(const cv::Mat& alpha, std::vector<std::vector<cv::Point>>& outputContours) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<IContoursExtractor> m_impl;
|
||||||
|
};
|
||||||
|
}
|
10
examples/contour/contours_extractor_factory.cpp
Normal file
10
examples/contour/contours_extractor_factory.cpp
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "contours_extractor_factory.hpp"
|
||||||
|
#include "contours_extractor.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
std::shared_ptr<IContoursExtractor> ContoursExtractorFactory::create()
|
||||||
|
{
|
||||||
|
return std::make_shared<ContoursExtractor>();
|
||||||
|
}
|
||||||
|
}
|
12
examples/contour/contours_extractor_factory.hpp
Normal file
12
examples/contour/contours_extractor_factory.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "i_contours_extractor.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
class __declspec(dllexport) ContoursExtractorFactory {
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<IContoursExtractor> create();
|
||||||
|
};
|
||||||
|
}
|
434
examples/contour/contours_extractor_impl.cpp
Normal file
434
examples/contour/contours_extractor_impl.cpp
Normal file
@ -0,0 +1,434 @@
|
|||||||
|
#include "contours_extractor_impl.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include "opencv2/opencv.hpp"
|
||||||
|
#include "opencv2/imgproc/imgproc_c.h"
|
||||||
|
#include "blob/blob.h"
|
||||||
|
#include "blob/BlobDetector.h"
|
||||||
|
#include "blob/BlobGroup.h"
|
||||||
|
#include "blob/BlobContour.h"
|
||||||
|
#include "opencv2/imgproc.hpp"
|
||||||
|
#include "bspline.hpp"
|
||||||
|
|
||||||
|
#define MIN_VALUE 1e-8
|
||||||
|
|
||||||
|
#define IS_DOUBLE_ZERO(d) (abs(d) < MIN_VALUE)
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
ContoursExtractorImpl::ContoursExtractorImpl()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::init()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::destroy()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::setAreaThreshold(double threshold)
|
||||||
|
{
|
||||||
|
m_areaThreshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::areaThreshold()
|
||||||
|
{
|
||||||
|
return m_areaThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::setContourOffset(int32_t offset)
|
||||||
|
{
|
||||||
|
m_contourOffset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ContoursExtractorImpl::contourOffset()
|
||||||
|
{
|
||||||
|
return m_contourOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::setApproxPolyEpsilon(double epsilon)
|
||||||
|
{
|
||||||
|
m_approxPolyEpsilon = epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::approxPolyEpsilon()
|
||||||
|
{
|
||||||
|
return m_approxPolyEpsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::setSmoothMethod(SMOOTH_METHOD method)
|
||||||
|
{
|
||||||
|
m_smoothMethod = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
SMOOTH_METHOD ContoursExtractorImpl::smoothMethod()
|
||||||
|
{
|
||||||
|
return m_smoothMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::setAngelThreshold(double threshold)
|
||||||
|
{
|
||||||
|
m_angelThreshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::angelThreshold()
|
||||||
|
{
|
||||||
|
return m_angelThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::setDefectThreshold(double threshold)
|
||||||
|
{
|
||||||
|
m_defectThreshold = threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::defectThreshold()
|
||||||
|
{
|
||||||
|
return m_defectThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::setSmoothStep(double step)
|
||||||
|
{
|
||||||
|
m_smoothStep = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::smoothStep()
|
||||||
|
{
|
||||||
|
return m_smoothStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::extract(const cv::Mat& alpha, std::vector<std::vector<cv::Point>>& outputContours)
|
||||||
|
{
|
||||||
|
assert(!alpha.empty());
|
||||||
|
if (alpha.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Mat edges;
|
||||||
|
cv::threshold(alpha, edges, 20, 255, cv::THRESH_BINARY);
|
||||||
|
|
||||||
|
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(3, 3));
|
||||||
|
cv::erode(edges, edges, kernel);
|
||||||
|
|
||||||
|
if (m_contourOffset == 0) {
|
||||||
|
cv::findContours(edges, outputContours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG_CONTOURS_EXTRACTOR
|
||||||
|
cv::imshow("edges", edges);
|
||||||
|
cv::waitKey(0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BlobDetector detector(edges);
|
||||||
|
|
||||||
|
BlobGroup group = detector.getBlobGroup();
|
||||||
|
group.filter(group, FilterAction::FLT_EXCLUDE, BlobGetArea(), FilterCondition::FLT_LESS, m_areaThreshold);
|
||||||
|
|
||||||
|
cv::Point center(edges.cols / 2, edges.rows / 2);
|
||||||
|
Blob* centerBlob = group.getBlobNearestTo(center);
|
||||||
|
|
||||||
|
cv::Point pt = centerBlob->getCentroid(true);
|
||||||
|
for (int i = 0; i < group.getNumBlobs(); i++) {
|
||||||
|
auto* blob = group.getBlob(i);
|
||||||
|
if (blob != centerBlob) {
|
||||||
|
auto center = blob->getCenter();
|
||||||
|
cv::line(edges, center, pt, cv::Scalar(255, 255, 255), m_contourOffset, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<cv::Point>> contours;
|
||||||
|
cv::findContours(edges, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
||||||
|
|
||||||
|
cv::Mat tmp = cv::Mat::zeros(edges.rows, edges.cols, CV_8UC3);
|
||||||
|
|
||||||
|
#if DEBUG_CONTOURS_EXTRACTOR
|
||||||
|
cv::drawContours(tmp, contours, 0, cv::Scalar(0, 0, 255), 1);
|
||||||
|
#endif
|
||||||
|
for (int i = 0; i < contours.size(); ++i) {
|
||||||
|
for (int j = 0; j < contours[i].size(); ++j) {
|
||||||
|
cv::circle(tmp, contours[i][j], m_contourOffset, cv::Scalar(255, 255, 255), -1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG_CONTOURS_EXTRACTOR
|
||||||
|
cv::imshow("tmp", tmp);
|
||||||
|
cv::waitKey(0);
|
||||||
|
#endif
|
||||||
|
cv::Mat bin;
|
||||||
|
cv::cvtColor(tmp, bin, CV_BGR2GRAY);
|
||||||
|
|
||||||
|
std::vector<std::vector<cv::Point>> output;
|
||||||
|
cv::findContours(bin, output, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
|
||||||
|
|
||||||
|
#if DEBUG_CONTOURS_EXTRACTOR
|
||||||
|
cv::drawContours(tmp, output, 0, cv::Scalar(0, 0, 255), 1);
|
||||||
|
cv::imshow("tmp", tmp);
|
||||||
|
cv::waitKey(0);
|
||||||
|
#endif
|
||||||
|
if (m_smoothMethod == SMOOTH_METHOD::BSPLINE) {
|
||||||
|
std::vector<cv::Point> approxCurve;
|
||||||
|
cv::approxPolyDP(output[0], approxCurve, m_approxPolyEpsilon, false);
|
||||||
|
|
||||||
|
//output[0].emplace_back(output[0][0]);
|
||||||
|
|
||||||
|
#if DEBUG_CONTOURS_EXTRACTOR
|
||||||
|
cv::Mat color = cv::imread("C:/Users/ouxia/source/repos/Contour/data/origin-03.png", cv::ImreadModes::IMREAD_UNCHANGED);
|
||||||
|
|
||||||
|
std::vector<std::vector<cv::Point >> cs;
|
||||||
|
cs.emplace_back(approxCurve);
|
||||||
|
cv::drawContours(color, cs, 0, cv::Scalar(255, 255, 255), 1);
|
||||||
|
|
||||||
|
std::vector<cv::Point> hullPoints;
|
||||||
|
cv::convexHull(approxCurve, hullPoints, true, true);
|
||||||
|
|
||||||
|
std::vector<std::vector<cv::Point >> hullContours;
|
||||||
|
hullContours.emplace_back(hullPoints);
|
||||||
|
|
||||||
|
cv::drawContours(color, hullContours, 0, cv::Scalar(0, 255, 0), 1);
|
||||||
|
#endif
|
||||||
|
//std::vector<std::vector<int>> indices;
|
||||||
|
//getSubCurves(approxCurve, 150.0, indices);
|
||||||
|
|
||||||
|
//for (const auto& indice : indices) {
|
||||||
|
// if (indice.size() < 3) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// std::vector<std::vector<double>> psArr;
|
||||||
|
// for (int32_t idx = 0; idx < indice.size(); ++idx) {
|
||||||
|
// std::vector<double> pt;
|
||||||
|
// pt.emplace_back(approxCurve[indice[idx]].x);
|
||||||
|
// pt.emplace_back(approxCurve[indice[idx]].y);
|
||||||
|
// psArr.emplace_back(pt);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// BSpline spline(psArr, 2);
|
||||||
|
|
||||||
|
// for (double t = 0.0; t < 1.0; t += 0.05) {
|
||||||
|
// std::vector<double> point = spline.eval(t);
|
||||||
|
// if (point.size() > 1) {
|
||||||
|
// cv::Point pt;
|
||||||
|
// pt.x = point[0];
|
||||||
|
// pt.y = point[1];
|
||||||
|
|
||||||
|
// cv::circle(color, pt, 1, cv::Scalar(0, 0, 255), -1);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
auto curve = approxCurve;
|
||||||
|
|
||||||
|
if (!IS_DOUBLE_ZERO(m_defectThreshold)) {
|
||||||
|
std::vector<int32_t> hull;
|
||||||
|
cv::convexHull(approxCurve, hull, true, false);
|
||||||
|
|
||||||
|
std::vector<cv::Vec4i> defects;
|
||||||
|
cv::convexityDefects(approxCurve, hull, defects);
|
||||||
|
|
||||||
|
for (const auto& defect : defects) {
|
||||||
|
cv::Point startPoint = approxCurve[defect[0]];
|
||||||
|
|
||||||
|
cv::Point endPoint = approxCurve[defect[1]];
|
||||||
|
|
||||||
|
#if DEBUG_CONTOURS_EXTRACTOR
|
||||||
|
cv::Point farthestPoint = cs[0][defect[2]];
|
||||||
|
float depth = defect[3] / 256.0;
|
||||||
|
|
||||||
|
cv::circle(color, startPoint, 5, cv::Scalar(255, 0, 0), -1);
|
||||||
|
cv::circle(color, endPoint, 5, cv::Scalar(255, 127, 0), -1);
|
||||||
|
cv::circle(color, farthestPoint, 5, cv::Scalar(0, 255, 255), -1);
|
||||||
|
|
||||||
|
std::string depthText = "Depth: " + std::to_string(depth);
|
||||||
|
cv::putText(color, depthText, farthestPoint + cv::Point(0, -10), cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(255, 255, 255));
|
||||||
|
|
||||||
|
cv::imshow("Convexity Defects", color);
|
||||||
|
#endif
|
||||||
|
int start = -1;
|
||||||
|
int end = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < curve.size(); ++i) {
|
||||||
|
if (startPoint == curve[i]) {
|
||||||
|
start = i;
|
||||||
|
}
|
||||||
|
if (endPoint == curve[i]) {
|
||||||
|
end = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start != -1 && end != -1 && start != end) {
|
||||||
|
if (euclideanDistance(startPoint, endPoint) < m_defectThreshold) {
|
||||||
|
cv::Point connected = (startPoint + endPoint) / 2;
|
||||||
|
std::vector<cv::Point> sub{ connected };
|
||||||
|
if (start > end) {
|
||||||
|
std::swap(start, end);
|
||||||
|
}
|
||||||
|
replaceSubsequence(curve, start, end, sub);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curve.emplace_back(curve[0]);
|
||||||
|
|
||||||
|
std::vector<std::vector<double>> psArr;
|
||||||
|
for (int i = 0; i < curve.size(); ++i) {
|
||||||
|
std::vector<double> pt;
|
||||||
|
pt.emplace_back(curve[i].x);
|
||||||
|
pt.emplace_back(curve[i].y);
|
||||||
|
psArr.emplace_back(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
BSpline spline(psArr, 2);
|
||||||
|
|
||||||
|
if (spline.isValid()) {
|
||||||
|
std::vector<double> weights;
|
||||||
|
std::vector<cv::Point> points;
|
||||||
|
for (double t = 0.0; t < 1.0; t += m_smoothStep) {
|
||||||
|
std::vector<double> point = spline.eval(t);
|
||||||
|
if (point.size() > 1) {
|
||||||
|
cv::Point pt;
|
||||||
|
pt.x = point[0];
|
||||||
|
pt.y = point[1];
|
||||||
|
points.emplace_back(pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputContours.emplace_back(points);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outputContours = output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
outputContours = output;
|
||||||
|
}
|
||||||
|
#if DEBUG_CONTOURS_EXTRACTOR
|
||||||
|
cv::drawContours(alpha, outputContours, 0, cv::Scalar(255, 255, 255), 1);
|
||||||
|
cv::imshow("alpha", alpha);
|
||||||
|
cv::waitKey(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::calculateAngle(const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3)
|
||||||
|
{
|
||||||
|
cv::Point vec1 = pt1 - pt2;
|
||||||
|
cv::Point vec2 = pt3 - pt2;
|
||||||
|
|
||||||
|
double dotProduct = vec1.x * vec2.x + vec1.y * vec2.y;
|
||||||
|
double magnitude1 = std::sqrt(vec1.x * vec1.x + vec1.y * vec1.y);
|
||||||
|
double magnitude2 = std::sqrt(vec2.x * vec2.x + vec2.y * vec2.y);
|
||||||
|
|
||||||
|
double cosTheta = dotProduct / (magnitude1 * magnitude2);
|
||||||
|
cosTheta = std::clamp<double>(cosTheta, -1.0, 1.0);
|
||||||
|
|
||||||
|
return std::acos(cosTheta) * 180.0 / CV_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::euclideanDistance(const cv::Point& p1, const cv::Point& p2)
|
||||||
|
{
|
||||||
|
return cv::norm(p1 - p2);
|
||||||
|
}
|
||||||
|
|
||||||
|
double ContoursExtractorImpl::curveDistance(const std::vector<cv::Point>& contour, int startIdx, int endIdx)
|
||||||
|
{
|
||||||
|
double curveDistance = 0.0;
|
||||||
|
int n = contour.size();
|
||||||
|
|
||||||
|
for (int i = startIdx; i != endIdx; i = (i + 1) % n) {
|
||||||
|
curveDistance += euclideanDistance(contour[i], contour[(i + 1) % n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return curveDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::getSubCurves(const std::vector<cv::Point>& contour, double thresholdAngle, std::vector<std::vector<int>>& indices)
|
||||||
|
{
|
||||||
|
std::set<int> currentGroup;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < contour.size(); ++i) {
|
||||||
|
const cv::Point& pt1 = contour[(i + contour.size() - 1) % contour.size()];
|
||||||
|
const cv::Point& pt2 = contour[i];
|
||||||
|
const cv::Point& pt3 = contour[(i + 1) % contour.size()];
|
||||||
|
|
||||||
|
double angle = calculateAngle(pt1, pt2, pt3);
|
||||||
|
|
||||||
|
if (angle < thresholdAngle) {
|
||||||
|
currentGroup.insert((i + contour.size() - 1) % contour.size());
|
||||||
|
currentGroup.insert(i);
|
||||||
|
currentGroup.insert((i + 1) % contour.size());
|
||||||
|
}
|
||||||
|
else if (!currentGroup.empty()) {
|
||||||
|
std::vector<int> vec;
|
||||||
|
for (auto it = currentGroup.begin(); it != currentGroup.end(); ++it) {
|
||||||
|
vec.emplace_back(*it);
|
||||||
|
}
|
||||||
|
indices.push_back(vec);
|
||||||
|
currentGroup.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentGroup.empty()) {
|
||||||
|
std::vector<int> vec;
|
||||||
|
for (auto it = currentGroup.begin(); it != currentGroup.end(); ++it) {
|
||||||
|
vec.emplace_back(*it);
|
||||||
|
}
|
||||||
|
indices.push_back(vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::getOptimalPoints(const std::vector<cv::Point>& contour, int& bestIdx1, int& bestIdx2, double& bestCurveDist, double& bestLineDist)
|
||||||
|
{
|
||||||
|
bestCurveDist = 0.0;
|
||||||
|
bestLineDist = std::numeric_limits<double>::max();
|
||||||
|
int n = contour.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
for (int j = i + 1; j < n; ++j) {
|
||||||
|
double curveDist = curveDistance(contour, i, j);
|
||||||
|
|
||||||
|
double lineDist = euclideanDistance(contour[i], contour[j]);
|
||||||
|
|
||||||
|
if (curveDist > bestCurveDist || (curveDist == bestCurveDist && lineDist < bestLineDist)) {
|
||||||
|
bestCurveDist = curveDist;
|
||||||
|
bestLineDist = lineDist;
|
||||||
|
bestIdx1 = i;
|
||||||
|
bestIdx2 = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ContoursExtractorImpl::replaceSubsequence(std::vector<cv::Point>& contour, int startIdx, int endIdx, const std::vector<cv::Point>& newSubsequence)
|
||||||
|
{
|
||||||
|
int n = contour.size();
|
||||||
|
|
||||||
|
if (startIdx < 0 || endIdx < 0 || startIdx >= n || endIdx >= n) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startIdx > endIdx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
contour.erase(contour.begin() + startIdx, contour.begin() + endIdx + 1);
|
||||||
|
|
||||||
|
contour.insert(contour.begin() + startIdx, newSubsequence.begin(), newSubsequence.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
cv::Point ContoursExtractorImpl::getPointOnLine(const cv::Point& pt1, const cv::Point& pt2, int32_t x)
|
||||||
|
{
|
||||||
|
double dx = pt2.x - pt1.x;
|
||||||
|
double dy = pt2.y - pt1.y;
|
||||||
|
|
||||||
|
cv::Point pt;
|
||||||
|
pt.x = pt1.x + x * dx;
|
||||||
|
pt.y = pt1.y + x * dy;
|
||||||
|
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
}
|
77
examples/contour/contours_extractor_impl.hpp
Normal file
77
examples/contour/contours_extractor_impl.hpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "i_contours_extractor.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
class ContoursExtractorImpl : public IContoursExtractor {
|
||||||
|
public:
|
||||||
|
ContoursExtractorImpl();
|
||||||
|
|
||||||
|
~ContoursExtractorImpl() = default;
|
||||||
|
|
||||||
|
void init() override;
|
||||||
|
|
||||||
|
void destroy() override;
|
||||||
|
|
||||||
|
void setAreaThreshold(double threshold) override;
|
||||||
|
|
||||||
|
double areaThreshold() override;
|
||||||
|
|
||||||
|
void setContourOffset(int32_t offset) override;
|
||||||
|
|
||||||
|
int32_t contourOffset() override;
|
||||||
|
|
||||||
|
void setApproxPolyEpsilon(double epsilon) override;
|
||||||
|
|
||||||
|
double approxPolyEpsilon() override;
|
||||||
|
|
||||||
|
void setSmoothMethod(SMOOTH_METHOD method) override;
|
||||||
|
|
||||||
|
SMOOTH_METHOD smoothMethod() override;
|
||||||
|
|
||||||
|
void setAngelThreshold(double threshold) override;
|
||||||
|
|
||||||
|
double angelThreshold() override;
|
||||||
|
|
||||||
|
void setDefectThreshold(double threshold) override;
|
||||||
|
|
||||||
|
double defectThreshold() override;
|
||||||
|
|
||||||
|
void setSmoothStep(double step) override;
|
||||||
|
|
||||||
|
double smoothStep() override;
|
||||||
|
|
||||||
|
void extract(const cv::Mat& alpha, std::vector<std::vector<cv::Point>>& outputContours) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
double calculateAngle(const cv::Point& pt1, const cv::Point& pt2, const cv::Point& pt3);
|
||||||
|
|
||||||
|
double euclideanDistance(const cv::Point& p1, const cv::Point& p2);
|
||||||
|
|
||||||
|
double curveDistance(const std::vector<cv::Point>& contour, int startIdx, int endIdx);
|
||||||
|
|
||||||
|
void getSubCurves(const std::vector<cv::Point>& contour, double thresholdAngle, std::vector<std::vector<int>>& indices);
|
||||||
|
|
||||||
|
void getOptimalPoints(const std::vector<cv::Point>& contour, int& bestIdx1, int& bestIdx2, double& bestCurveDist, double& bestLineDist);
|
||||||
|
|
||||||
|
void replaceSubsequence(std::vector<cv::Point>& contour, int startIdx, int endIdx, const std::vector<cv::Point>& newSubsequence);
|
||||||
|
|
||||||
|
cv::Point getPointOnLine(const cv::Point& pt1, const cv::Point& pt2, int32_t x);
|
||||||
|
|
||||||
|
private:
|
||||||
|
double m_areaThreshold = 500.0;
|
||||||
|
|
||||||
|
int32_t m_contourOffset = 12;
|
||||||
|
|
||||||
|
double m_approxPolyEpsilon = 5;
|
||||||
|
|
||||||
|
SMOOTH_METHOD m_smoothMethod = SMOOTH_METHOD::BSPLINE;
|
||||||
|
|
||||||
|
double m_angelThreshold = 20.0;
|
||||||
|
|
||||||
|
double m_defectThreshold = 0.0;
|
||||||
|
|
||||||
|
double m_smoothStep = 0.001;
|
||||||
|
};
|
||||||
|
}
|
15
examples/contour/framework.h
Normal file
15
examples/contour/framework.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// header.h: 标准系统包含文件的包含文件,
|
||||||
|
// 或特定于项目的包含文件
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "targetver.h"
|
||||||
|
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||||
|
// Windows 头文件
|
||||||
|
#include <windows.h>
|
||||||
|
// C 运行时头文件
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <tchar.h>
|
17
examples/contour/i_bspline.hpp
Normal file
17
examples/contour/i_bspline.hpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
class IBSpline {
|
||||||
|
public:
|
||||||
|
virtual ~IBSpline() = default;
|
||||||
|
|
||||||
|
virtual void create(const std::vector<std::vector<double>>& points, int degree) = 0;
|
||||||
|
|
||||||
|
virtual bool isValid() = 0;
|
||||||
|
|
||||||
|
virtual std::vector<double> eval(double t) = 0;
|
||||||
|
};
|
||||||
|
}
|
79
examples/contour/i_contours_extractor.hpp
Normal file
79
examples/contour/i_contours_extractor.hpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "opencv2/opencv.hpp"
|
||||||
|
|
||||||
|
namespace cvpr
|
||||||
|
{
|
||||||
|
enum class SMOOTH_METHOD {
|
||||||
|
NONE = 0,
|
||||||
|
BSPLINE
|
||||||
|
//DEANGEL
|
||||||
|
};
|
||||||
|
|
||||||
|
class __declspec(dllexport) IContoursExtractor {
|
||||||
|
public:
|
||||||
|
virtual ~IContoursExtractor() = default;
|
||||||
|
|
||||||
|
virtual void init() = 0;
|
||||||
|
|
||||||
|
virtual void destroy() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* threshold, 轮廓检测结果中小于该阈值的轮廓将被剔除
|
||||||
|
*/
|
||||||
|
virtual void setAreaThreshold(double threshold = 500.0) = 0;
|
||||||
|
|
||||||
|
virtual double areaThreshold() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* contourOffset, 轮廓的偏置距离(单位:像素),当contourOffset取值为0时,该函数自动忽略setSmoothMethod()的method参数,不做平滑直接返回原始轮廓
|
||||||
|
*/
|
||||||
|
virtual void setContourOffset(int32_t offset = 12) = 0;
|
||||||
|
|
||||||
|
virtual int32_t contourOffset() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* epsilon, 原始轮廓偏置后的轮廓线的多边形逼近精度
|
||||||
|
*/
|
||||||
|
virtual void setApproxPolyEpsilon(double epsilon = 5) = 0;
|
||||||
|
|
||||||
|
virtual double approxPolyEpsilon() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* method, 原始轮廓偏置后的轮廓线的平滑方法,默认采用b样条曲线
|
||||||
|
*/
|
||||||
|
virtual void setSmoothMethod(SMOOTH_METHOD method = SMOOTH_METHOD::BSPLINE) = 0;
|
||||||
|
|
||||||
|
virtual SMOOTH_METHOD smoothMethod() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* threshold, 采用多边形逼近之后,如果折线的夹角小于该阈值,则采用b样条曲线进行平滑。该参数仅在SMOOTH_METHOD::DEANGEL模式下有效
|
||||||
|
*/
|
||||||
|
virtual void setAngelThreshold(double threshold = 20.0) = 0;
|
||||||
|
|
||||||
|
virtual double angelThreshold() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* threshold, 采用多边形逼近之后,如果缺陷处的开口小于该阈值,则封闭该缺陷
|
||||||
|
*/
|
||||||
|
virtual void setDefectThreshold(double threshold = 0.0) = 0;
|
||||||
|
|
||||||
|
virtual double defectThreshold() = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* smoothStep, 原始轮廓偏置后的轮廓线的平滑步长,取值范围(0.0,1.0)
|
||||||
|
*/
|
||||||
|
virtual void setSmoothStep(double step = 0.001) = 0;
|
||||||
|
|
||||||
|
virtual double smoothStep() = 0;
|
||||||
|
|
||||||
|
/* 描述: 提取轮廓或轮廓偏置线
|
||||||
|
* 参数:
|
||||||
|
* alpha, 图像alpha通道
|
||||||
|
* outputContours, 输出轮廓集合
|
||||||
|
*/
|
||||||
|
virtual void extract(const cv::Mat& alpha, std::vector<std::vector<cv::Point>>& outputContours) = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
48
examples/contour/main.h
Normal file
48
examples/contour/main.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include <afxwin.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "targetver.h"
|
||||||
|
|
||||||
|
// C runtime header
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// base header
|
||||||
|
#include "base/base.h"
|
||||||
|
|
||||||
|
// duilib
|
||||||
|
#include "duilib/UIlib.h"
|
||||||
|
|
||||||
|
#include "resource.h"
|
||||||
|
|
||||||
|
/** @class MainThread
|
||||||
|
* @brief 主线程(UI线程)类,继承 nbase::FrameworkThread
|
||||||
|
* @copyright (c) 2015, NetEase Inc. All rights reserved
|
||||||
|
* @author towik
|
||||||
|
* @date 2015/1/1
|
||||||
|
*/
|
||||||
|
class MainThread : public nbase::FrameworkThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MainThread() : nbase::FrameworkThread("MainThread") {}
|
||||||
|
virtual ~MainThread() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* 虚函数,初始化主线程
|
||||||
|
* @return void 无返回值
|
||||||
|
*/
|
||||||
|
virtual void Init() override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 虚函数,主线程退出时,做一些清理工作
|
||||||
|
* @return void 无返回值
|
||||||
|
*/
|
||||||
|
virtual void Cleanup() override;
|
||||||
|
};
|
BIN
examples/contour/opencv_core4100d.lib
Normal file
BIN
examples/contour/opencv_core4100d.lib
Normal file
Binary file not shown.
BIN
examples/contour/opencv_dnn4100d.lib
Normal file
BIN
examples/contour/opencv_dnn4100d.lib
Normal file
Binary file not shown.
BIN
examples/contour/opencv_world4100d.lib
Normal file
BIN
examples/contour/opencv_world4100d.lib
Normal file
Binary file not shown.
BIN
examples/contour/res.png
Normal file
BIN
examples/contour/res.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
BIN
examples/contour/res2222.png
Normal file
BIN
examples/contour/res2222.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 MiB |
BIN
examples/contour/small.ico
Normal file
BIN
examples/contour/small.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
6
examples/contour/targetver.h
Normal file
6
examples/contour/targetver.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// // 包含 SDKDDKVer.h 可定义可用的最高版本的 Windows 平台。
|
||||||
|
// 如果希望为之前的 Windows 平台构建应用程序,在包含 SDKDDKVer.h 之前请先包含 WinSDKVer.h 并
|
||||||
|
// 将 _WIN32_WINNT 宏设置为想要支持的平台。
|
||||||
|
#include <SDKDDKVer.h>
|
@ -35,6 +35,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmodbus", "libmodbus\libm
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bsdiff", "bsdiff\bsdiff.vcxproj", "{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}"
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bsdiff", "bsdiff\bsdiff.vcxproj", "{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "contour", "contour\contour.vcxproj", "{A01DD5D1-1D47-4956-A472-FD84B1AF7036}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Win32 = Debug|Win32
|
Debug|Win32 = Debug|Win32
|
||||||
@ -153,6 +155,14 @@ Global
|
|||||||
{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}.Release|Win32.Build.0 = Release|Win32
|
{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}.Release|Win32.Build.0 = Release|Win32
|
||||||
{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}.Release|x64.ActiveCfg = Release|x64
|
{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}.Release|x64.ActiveCfg = Release|x64
|
||||||
{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}.Release|x64.Build.0 = Release|x64
|
{F9A0DD24-FD5A-4CCF-8945-3688EF808BFE}.Release|x64.Build.0 = Release|x64
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{A01DD5D1-1D47-4956-A472-FD84B1AF7036}.Release|x64.Build.0 = Release|x64
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
Loading…
Reference in New Issue
Block a user