|
|
|
@ -0,0 +1,635 @@
|
|
|
|
|
|
|
|
#include "TcsCheck.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CLock
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
CLock(pthread_mutex_t * attr) : m_attr(attr) {
|
|
|
|
|
|
|
|
pthread_mutex_lock(m_attr);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
~CLock() {
|
|
|
|
|
|
|
|
pthread_mutex_unlock(m_attr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
|
|
|
pthread_mutex_t * m_attr;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CTcsCheck::CTcsCheck()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
memset(&m_nInitStart, 0, offsetof(CTcsCheck, m_nInitEnd) - offsetof(CTcsCheck, m_nInitStart) + sizeof(m_nInitEnd));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_cpCfg.nAreaLowFilter = 80;
|
|
|
|
|
|
|
|
m_cpCfg.nBlockSize = 100;
|
|
|
|
|
|
|
|
m_cpCfg.nDiscardTop = 170;
|
|
|
|
|
|
|
|
m_cpCfg.nDiscardBottom = 170;
|
|
|
|
|
|
|
|
m_cpCfg.nDiscardLeft = 180;
|
|
|
|
|
|
|
|
m_cpCfg.nDiscardRight = 460;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_cpCfg.fZoomRatio = 0.25;
|
|
|
|
|
|
|
|
m_cpCfg.nFilterLow = 15;
|
|
|
|
|
|
|
|
m_cpCfg.nFilterHigh = 15;
|
|
|
|
|
|
|
|
m_cpCfg.nAreaFilter = 10;
|
|
|
|
|
|
|
|
m_cpCfg.nCountFilter = 50;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
CTcsCheck::~CTcsCheck()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTcsCheck::SetCheckDir(std::string dirIn,std::string dirOut)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_strDirIn = dirIn;
|
|
|
|
|
|
|
|
m_strDirOut = dirOut;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTcsCheck::SetChecConfig(CHECK_PARAM* cp)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
memcpy(&m_cpCfg,cp,sizeof(CHECK_PARAM));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CTcsCheck::GetFileName(const std::string& path) const
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::size_t pos = path.find_last_of("/\\");
|
|
|
|
|
|
|
|
std::string name = (pos == std::string::npos) ? path : path.substr(pos + 1);
|
|
|
|
|
|
|
|
std::size_t dot = name.find_last_of('.');
|
|
|
|
|
|
|
|
if (dot == std::string::npos) {
|
|
|
|
|
|
|
|
return name;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return name.substr(0, dot);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 递归创建多级目录
|
|
|
|
|
|
|
|
bool CTcsCheck::CreateDirectories(std::string path, mode_t mode )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::string fullPath = path;
|
|
|
|
|
|
|
|
size_t pos = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while ((pos = fullPath.find_first_of("/", pos + 1)) != std::string::npos) {
|
|
|
|
|
|
|
|
std::string subPath = fullPath.substr(0, pos);
|
|
|
|
|
|
|
|
if (subPath.empty()) continue;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (mkdir(subPath.c_str(), mode) != 0 && errno != EEXIST) {
|
|
|
|
|
|
|
|
std::cerr << "创建子目录失败: " << subPath << " - " << strerror(errno) << std::endl;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 创建最后一级目录
|
|
|
|
|
|
|
|
if (mkdir(path.c_str(), mode) != 0 && errno != EEXIST) {
|
|
|
|
|
|
|
|
std::cerr << "创建最终目录失败: " << path << " - " << strerror(errno) << std::endl;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "多级目录创建成功: " << path << std::endl;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTcsCheck::LoadImages()
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (access(m_strDirIn.c_str(),F_OK) == 0)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::glob(m_strDirIn, m_fileList);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::cout << "Dir " << m_strDirIn << " is NOT existed!" << std::endl;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
std::cout << "Create OUT directory : " << m_strDirOut << std::endl;
|
|
|
|
|
|
|
|
CreateDirectories(m_strDirOut);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::Rect CTcsCheck::GetBoundingRect(cv::Mat matBinary)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Mat labels;
|
|
|
|
|
|
|
|
cv::Mat stats;
|
|
|
|
|
|
|
|
cv::Mat centroids;
|
|
|
|
|
|
|
|
const int nLabels = cv::connectedComponentsWithStats(matBinary, labels, stats, centroids, 8, CV_32S);
|
|
|
|
|
|
|
|
if (nLabels <= 1) {
|
|
|
|
|
|
|
|
return cv::Rect(0,0,0,0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int maxArea = 0;
|
|
|
|
|
|
|
|
int maxLabel = -1;
|
|
|
|
|
|
|
|
for (int label = 1; label < nLabels; ++label) {
|
|
|
|
|
|
|
|
const int area = stats.at<int>(label, cv::CC_STAT_AREA);
|
|
|
|
|
|
|
|
if (area > maxArea) {
|
|
|
|
|
|
|
|
maxArea = area;
|
|
|
|
|
|
|
|
maxLabel = label;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (maxLabel < 0) {
|
|
|
|
|
|
|
|
return cv::Rect(0,0,0,0);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int x = stats.at<int>(maxLabel, cv::CC_STAT_LEFT);
|
|
|
|
|
|
|
|
const int y = stats.at<int>(maxLabel, cv::CC_STAT_TOP);
|
|
|
|
|
|
|
|
const int w = stats.at<int>(maxLabel, cv::CC_STAT_WIDTH);
|
|
|
|
|
|
|
|
const int h = stats.at<int>(maxLabel, cv::CC_STAT_HEIGHT);
|
|
|
|
|
|
|
|
return cv::Rect(x, y, w, h);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::Rect CTcsCheck::GetCropArea(cv::Rect rtValid)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 在原图坐标系中对 ROI 四边分别内收对应的 discard 宽度。
|
|
|
|
|
|
|
|
// 若尺寸不足以同时内收,则保持原 ROI(不做"部分边"裁剪)。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int outW = rtValid.width - m_cpCfg.nDiscardLeft - m_cpCfg.nDiscardRight;
|
|
|
|
|
|
|
|
const int outH = rtValid.height - m_cpCfg.nDiscardTop - m_cpCfg.nDiscardBottom;
|
|
|
|
|
|
|
|
if (outW <= 0 || outH <= 0) {
|
|
|
|
|
|
|
|
return rtValid;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Rect innerRect(rtValid.x + m_cpCfg.nDiscardLeft, rtValid.y + m_cpCfg.nDiscardTop, outW, outH);
|
|
|
|
|
|
|
|
const cv::Rect imageRect(0, 0, m_sizeImage.width, m_sizeImage.height);
|
|
|
|
|
|
|
|
innerRect = innerRect & imageRect;
|
|
|
|
|
|
|
|
if (innerRect.width <= 0 || innerRect.height <= 0) {
|
|
|
|
|
|
|
|
return rtValid;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return innerRect;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::Mat CTcsCheck::AdaptiveBinary(cv::Mat matBlur)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 按 localBlockSize_ 分块,使用每个块的局部均值做动态阈值。
|
|
|
|
|
|
|
|
// 当前规则:落在 [avg-lower_, avg+upper_] 内置 0,超出置 255。
|
|
|
|
|
|
|
|
cv::Mat result = cv::Mat::zeros(matBlur.size(), CV_8UC1);
|
|
|
|
|
|
|
|
for (int y = 0; y < matBlur.rows; y += m_cpCfg.nBlockSize) {
|
|
|
|
|
|
|
|
for (int x = 0; x < matBlur.cols; x += m_cpCfg.nBlockSize) {
|
|
|
|
|
|
|
|
const int blockW = std::min(m_cpCfg.nBlockSize, matBlur.cols - x);
|
|
|
|
|
|
|
|
const int blockH = std::min(m_cpCfg.nBlockSize, matBlur.rows - y);
|
|
|
|
|
|
|
|
cv::Rect blockRect(x, y, blockW, blockH);
|
|
|
|
|
|
|
|
cv::Mat block = matBlur(blockRect);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const double avg = cv::mean(block)[0];
|
|
|
|
|
|
|
|
const double minGrey = std::max(0.0, avg - m_cpCfg.nFilterLow);
|
|
|
|
|
|
|
|
const double maxGrey = std::min(255.0, avg + m_cpCfg.nFilterHigh);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat blockBin;
|
|
|
|
|
|
|
|
cv::inRange(block, minGrey, maxGrey, blockBin);
|
|
|
|
|
|
|
|
cv::bitwise_not(blockBin, blockBin);
|
|
|
|
|
|
|
|
blockBin.copyTo(result(blockRect));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// ClassifyBlobs — 纯分类函数
|
|
|
|
|
|
|
|
// 对残点二值图做连通域分析 + 缺陷分类,结果写入 m_vecDefectInfo
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
void CTcsCheck::ClassifyBlobs(const cv::Mat& blurCrop, const cv::Mat& imgBlob)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_vecDefectInfo.clear();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat labels;
|
|
|
|
|
|
|
|
cv::Mat stats;
|
|
|
|
|
|
|
|
cv::Mat centroids;
|
|
|
|
|
|
|
|
const int nLabels = cv::connectedComponentsWithStats(imgBlob, labels, stats, centroids, 8, CV_32S);
|
|
|
|
|
|
|
|
if (nLabels <= 1) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct BlobItem { int label; int area; };
|
|
|
|
|
|
|
|
std::vector<BlobItem> blobs;
|
|
|
|
|
|
|
|
blobs.reserve(std::max(0, nLabels - 1));
|
|
|
|
|
|
|
|
for (int label = 1; label < nLabels; ++label) {
|
|
|
|
|
|
|
|
const int area = stats.at<int>(label, cv::CC_STAT_AREA);
|
|
|
|
|
|
|
|
if (area > m_cpCfg.nAreaFilter) {
|
|
|
|
|
|
|
|
blobs.push_back({ label, area });
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blobs.empty()) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::sort(blobs.begin(), blobs.end(), [](const BlobItem& a, const BlobItem& b) {
|
|
|
|
|
|
|
|
return a.area > b.area;
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 大块缺陷的面积阈值
|
|
|
|
|
|
|
|
const int largeAreaThreshold = std::max(1, m_cpCfg.nBlockSize * m_cpCfg.nBlockSize / 4);
|
|
|
|
|
|
|
|
const int topK = std::min(m_cpCfg.nCountFilter, static_cast<int>(blobs.size()));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < topK; ++i) {
|
|
|
|
|
|
|
|
const int label = blobs[i].label;
|
|
|
|
|
|
|
|
const int area = blobs[i].area;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// bounding rect
|
|
|
|
|
|
|
|
const int x = stats.at<int>(label, cv::CC_STAT_LEFT);
|
|
|
|
|
|
|
|
const int y = stats.at<int>(label, cv::CC_STAT_TOP);
|
|
|
|
|
|
|
|
const int w = stats.at<int>(label, cv::CC_STAT_WIDTH);
|
|
|
|
|
|
|
|
const int h = stats.at<int>(label, cv::CC_STAT_HEIGHT);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// blob 区域均值
|
|
|
|
|
|
|
|
cv::Mat blobMask = (labels == label);
|
|
|
|
|
|
|
|
double blobMean = cv::mean(blurCrop, blobMask)[0];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 所属局部块均值
|
|
|
|
|
|
|
|
const double cx = centroids.at<double>(label, 0);
|
|
|
|
|
|
|
|
const double cy = centroids.at<double>(label, 1);
|
|
|
|
|
|
|
|
int blockX = std::max(0, std::min(static_cast<int>(cx) / m_cpCfg.nBlockSize, (blurCrop.cols - 1) / m_cpCfg.nBlockSize));
|
|
|
|
|
|
|
|
int blockY = std::max(0, std::min(static_cast<int>(cy) / m_cpCfg.nBlockSize, (blurCrop.rows - 1) / m_cpCfg.nBlockSize));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int roiX = blockX * m_cpCfg.nBlockSize;
|
|
|
|
|
|
|
|
const int roiY = blockY * m_cpCfg.nBlockSize;
|
|
|
|
|
|
|
|
const int roiW = std::min(m_cpCfg.nBlockSize, blurCrop.cols - roiX);
|
|
|
|
|
|
|
|
const int roiH = std::min(m_cpCfg.nBlockSize, blurCrop.rows - roiY);
|
|
|
|
|
|
|
|
const double localMean = cv::mean(blurCrop(cv::Rect(roiX, roiY, roiW, roiH)))[0];
|
|
|
|
|
|
|
|
const double greyDiff = blobMean - localMean;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// 缺陷分类决策树
|
|
|
|
|
|
|
|
// 长宽比 >= 3 → SCRATCH (划伤)
|
|
|
|
|
|
|
|
// 紧凑 + 面积小 → POINT (硬质颗粒)
|
|
|
|
|
|
|
|
// 紧凑 + 大面积+暗 → DIRTY (脏污)
|
|
|
|
|
|
|
|
// 紧凑 + 大面积+亮 → FADING (淡斑)
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
float aspectRatio = static_cast<float>(std::max(w, h)) / std::max(1, std::min(w, h));
|
|
|
|
|
|
|
|
int defectType = DEFECT_TYPE_OK;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (aspectRatio >= 3.0f) {
|
|
|
|
|
|
|
|
defectType = DEFECT_TYPE_SCRATCH;
|
|
|
|
|
|
|
|
} else if (area < largeAreaThreshold) {
|
|
|
|
|
|
|
|
defectType = DEFECT_TYPE_POINT;
|
|
|
|
|
|
|
|
} else if (greyDiff < 0) {
|
|
|
|
|
|
|
|
defectType = DEFECT_TYPE_DIRTY;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
defectType = DEFECT_TYPE_FADING_SPOTS;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEFECT_INFO info;
|
|
|
|
|
|
|
|
info.nDefectType = defectType;
|
|
|
|
|
|
|
|
info.nDefectArea = area;
|
|
|
|
|
|
|
|
info.nDefectX = x;
|
|
|
|
|
|
|
|
info.nDefectY = y;
|
|
|
|
|
|
|
|
info.nDefectWidth = w;
|
|
|
|
|
|
|
|
info.nDefectHeight = h;
|
|
|
|
|
|
|
|
info.dGreyDiff = greyDiff;
|
|
|
|
|
|
|
|
info.strDefectCode = DEFECT_TYPE_CODE[defectType];
|
|
|
|
|
|
|
|
info.strDefectDesc = DEFECT_TYPE_DESC[defectType];
|
|
|
|
|
|
|
|
m_vecDefectInfo.push_back(info);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
// DrawBlobInfoImage — 纯绘制函数
|
|
|
|
|
|
|
|
// 基于 m_vecDefectInfo 在 crop 图上绘制缺陷标注
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
|
|
|
cv::Mat CTcsCheck::DrawBlobInfoImage(const cv::Mat& imgCrop, const cv::Mat& imgBlob)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Mat cropColor;
|
|
|
|
|
|
|
|
cv::cvtColor(imgCrop, cropColor, cv::COLOR_GRAY2BGR);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const auto& info : m_vecDefectInfo) {
|
|
|
|
|
|
|
|
const cv::Rect blobRect(info.nDefectX, info.nDefectY, info.nDefectWidth, info.nDefectHeight);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 根据缺陷类型使用不同颜色绘制
|
|
|
|
|
|
|
|
cv::Scalar drawColor;
|
|
|
|
|
|
|
|
switch (info.nDefectType) {
|
|
|
|
|
|
|
|
case DEFECT_TYPE_POINT: drawColor = cv::Scalar(0, 255, 0); break; // 绿色
|
|
|
|
|
|
|
|
case DEFECT_TYPE_SCRATCH: drawColor = cv::Scalar(0, 0, 255); break; // 红色
|
|
|
|
|
|
|
|
case DEFECT_TYPE_DIRTY: drawColor = cv::Scalar(255, 0, 0); break; // 蓝色
|
|
|
|
|
|
|
|
case DEFECT_TYPE_FADING_SPOTS: drawColor = cv::Scalar(255, 255, 0); break; // 青色
|
|
|
|
|
|
|
|
default: drawColor = cv::Scalar(0, 255, 0); break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 在 blob 区域内找轮廓
|
|
|
|
|
|
|
|
cv::Mat roiBlob = imgBlob(blobRect & cv::Rect(0, 0, imgBlob.cols, imgBlob.rows));
|
|
|
|
|
|
|
|
std::vector<std::vector<cv::Point>> contours;
|
|
|
|
|
|
|
|
cv::findContours(roiBlob.clone(), contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
|
|
|
|
|
|
// 偏移轮廓坐标到原图位置
|
|
|
|
|
|
|
|
for (auto& cnt : contours) {
|
|
|
|
|
|
|
|
for (auto& pt : cnt) {
|
|
|
|
|
|
|
|
pt.x += info.nDefectX;
|
|
|
|
|
|
|
|
pt.y += info.nDefectY;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::drawContours(cropColor, contours, -1, drawColor, 2);
|
|
|
|
|
|
|
|
cv::rectangle(cropColor, blobRect, cv::Scalar(0, 0, 255), 1);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 文本标注
|
|
|
|
|
|
|
|
std::ostringstream oss;
|
|
|
|
|
|
|
|
oss << info.strDefectCode << " A:" << info.nDefectArea
|
|
|
|
|
|
|
|
<< " dG:" << std::fixed << std::setprecision(1) << info.dGreyDiff;
|
|
|
|
|
|
|
|
int textX = std::max(0, info.nDefectX);
|
|
|
|
|
|
|
|
int textY = std::max(15, info.nDefectY - 3);
|
|
|
|
|
|
|
|
cv::putText(cropColor, oss.str(), cv::Point(textX, textY),
|
|
|
|
|
|
|
|
cv::FONT_HERSHEY_SIMPLEX, 0.45, drawColor, 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return cropColor;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTcsCheck::ProcessImages(bool bDrawResult)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
LoadImages();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "TOTAL file count = " << m_fileList.size() << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < m_fileList.size(); i ++ )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
m_matLoad = cv::imread(m_fileList[i],cv::IMREAD_GRAYSCALE);
|
|
|
|
|
|
|
|
m_strCurFile = GetFileName(m_fileList[i]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m_sizeImage = m_matLoad.size();
|
|
|
|
|
|
|
|
Process(bDrawResult);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CTcsCheck::DetectWithAdaptiveBinary(bool bDraw )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Mat matBinary,matCrop,matResized,matBlur,matDraw;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::threshold(m_matLoad,matBinary,m_cpCfg.nAreaLowFilter,255,cv::THRESH_BINARY);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Obtain Key Region Range
|
|
|
|
|
|
|
|
cv::Rect rtValid = GetBoundingRect(matBinary);
|
|
|
|
|
|
|
|
if (rtValid == cv::Rect(0,0,0,0))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::cout << "NO product" << std::endl;
|
|
|
|
|
|
|
|
return ;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// crop image
|
|
|
|
|
|
|
|
cv::Rect rtCrop = GetCropArea(rtValid);
|
|
|
|
|
|
|
|
matCrop = m_matLoad(rtCrop).clone();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// image zoom
|
|
|
|
|
|
|
|
const int outW = std::max(1, static_cast<int>(matBinary.cols * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
const int outH = std::max(1, static_cast<int>(matBinary.rows * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
cv::resize(matCrop, matResized, cv::Size(outW, outH), 0, 0, cv::INTER_AREA);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// image blur
|
|
|
|
|
|
|
|
cv::GaussianBlur(matResized, matBlur, cv::Size(5, 5), 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 二值化
|
|
|
|
|
|
|
|
m_matBlob = AdaptiveBinary(matBlur);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 分类:对残点二值图做连通域分析+缺陷分类
|
|
|
|
|
|
|
|
ClassifyBlobs(matBlur, m_matBlob);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tEnd = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
double elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(tEnd - tStart).count();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "耗时(ms): " << elapsedMs << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bDraw)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
matDraw = DrawBlobInfoImage(matResized, m_matBlob);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string strOutFile,strDebug;
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Binary.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile,matBinary);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" +m_strCurFile + "_Crop.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile,matResized);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" +m_strCurFile + "_Blob.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile,m_matBlob);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" +m_strCurFile + "_Draw.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile,matDraw);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void CTcsCheck::DetectWithDoH(bool bDraw,double dSigma,int nThreshold )
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
cv::Mat matBinary,matCrop,matResized,matBlur,matDraw;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::threshold(m_matLoad,matBinary,m_cpCfg.nAreaLowFilter,255,cv::THRESH_BINARY);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Obtain Key Region Range
|
|
|
|
|
|
|
|
cv::Rect rtValid = GetBoundingRect(matBinary);
|
|
|
|
|
|
|
|
if (rtValid == cv::Rect(0,0,0,0))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::cout << "NO product" << std::endl;
|
|
|
|
|
|
|
|
return ;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// crop image
|
|
|
|
|
|
|
|
cv::Rect rtCrop = GetCropArea(rtValid);
|
|
|
|
|
|
|
|
matCrop = m_matLoad(rtCrop).clone();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// image zoom
|
|
|
|
|
|
|
|
const int outW = std::max(1, static_cast<int>(matBinary.cols * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
const int outH = std::max(1, static_cast<int>(matBinary.rows * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
cv::resize(matCrop, matResized, cv::Size(outW, outH), 0, 0, cv::INTER_AREA);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::Mat matFloat,matDoh,matNorm;
|
|
|
|
|
|
|
|
// 转换为浮点型以进行精确的导数计算
|
|
|
|
|
|
|
|
matResized.convertTo(matFloat, CV_64F);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 高斯平滑(可选,用于去噪和定义尺度)
|
|
|
|
|
|
|
|
double sigma = dSigma; // 根据斑点大小调整
|
|
|
|
|
|
|
|
cv::Mat smoothed;
|
|
|
|
|
|
|
|
cv::GaussianBlur(matFloat, matBlur, cv::Size(0, 0), sigma);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 计算二阶导数 Ixx, Iyy, Ixy
|
|
|
|
|
|
|
|
cv::Mat Ixx, Iyy, Ixy;
|
|
|
|
|
|
|
|
cv::Sobel(matBlur, Ixx, CV_64F, 2, 0, 3); // 对 x 求二阶导
|
|
|
|
|
|
|
|
cv::Sobel(matBlur, Iyy, CV_64F, 0, 2, 3); // 对 y 求二阶导
|
|
|
|
|
|
|
|
cv::Sobel(matBlur, Ixy, CV_64F, 1, 1, 3); // 先对 x 求导,再对 y 求导
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 计算 DoH 响应:det(H) = Ixx * Iyy - Ixy^2
|
|
|
|
|
|
|
|
cv::multiply(Ixx, Iyy, matDoh);
|
|
|
|
|
|
|
|
cv::subtract(matDoh, Ixy.mul(Ixy), matDoh);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 后处理:寻找局部极大值作为斑点
|
|
|
|
|
|
|
|
cv::normalize(matDoh, matNorm, 0, 255, cv::NORM_MINMAX, CV_8U);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tEnd = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
double elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(tEnd - tStart).count();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "耗时(ms): " << elapsedMs << std::endl;
|
|
|
|
|
|
|
|
// 6. 绘制并显示结果
|
|
|
|
|
|
|
|
if ( bDraw)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 寻找局部极大值(简化示例,实际应用需更严谨的邻域比较)
|
|
|
|
|
|
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
|
|
|
|
|
|
int threshold_value = nThreshold; // 需要根据图像调整的阈值
|
|
|
|
|
|
|
|
for (int i = 1; i < matDoh.rows - 1; ++i) {
|
|
|
|
|
|
|
|
for (int j = 1; j < matDoh.cols - 1; ++j) {
|
|
|
|
|
|
|
|
double val = matDoh.at<double>(i, j);
|
|
|
|
|
|
|
|
if (val > threshold_value)
|
|
|
|
|
|
|
|
//&& val > matDoh.at<double>(i - 1, j) && val > matDoh.at<double>(i + 1, j) &&
|
|
|
|
|
|
|
|
//val > matDoh.at<double>(i, j - 1) && val > matDoh.at<double>(i, j + 1))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 此处可更精确地计算斑点的尺度和响应强度
|
|
|
|
|
|
|
|
keypoints.push_back(cv::KeyPoint(j, i, 2 * sigma));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
cv::cvtColor(matNorm, matDraw, cv::COLOR_GRAY2BGR);
|
|
|
|
|
|
|
|
cv::drawKeypoints(matDraw, keypoints, matDraw, cv::Scalar(0, 0, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string strOutFile;
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Resize.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile,matResized);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Norm.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile,matNorm);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Draw.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile,matDraw);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CTcsCheck::DetectWithLoG(bool bDraw, double dSigma, int nThreshold)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// ===================================================================
|
|
|
|
|
|
|
|
// LoG (Laplacian of Gaussian) 斑点检测
|
|
|
|
|
|
|
|
// 原理: LoG = trace(H) = Ixx + Iyy,是 Hessian 矩阵的迹
|
|
|
|
|
|
|
|
// 相比 DoH (det(H)=Ixx*Iyy-Ixy²):
|
|
|
|
|
|
|
|
// - 速度: 只需1次 Laplacian,DoH 需要3次 Sobel + 乘减运算 (~3x 加速)
|
|
|
|
|
|
|
|
// - 精度: LoG 是旋转不变的各向同性斑点检测器,对圆形缺陷响应最优
|
|
|
|
|
|
|
|
// ===================================================================
|
|
|
|
|
|
|
|
cv::Mat matBinary, matCrop, matResized, matBlur, matDraw;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 全局阈值 -> 定位产品区域
|
|
|
|
|
|
|
|
cv::threshold(m_matLoad, matBinary, m_cpCfg.nAreaLowFilter, 255, cv::THRESH_BINARY);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 获取有效区域外接矩形
|
|
|
|
|
|
|
|
cv::Rect rtValid = GetBoundingRect(matBinary);
|
|
|
|
|
|
|
|
if (rtValid == cv::Rect(0, 0, 0, 0))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::cout << "NO product" << std::endl;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 裁剪边缘
|
|
|
|
|
|
|
|
cv::Rect rtCrop = GetCropArea(rtValid);
|
|
|
|
|
|
|
|
matCrop = m_matLoad(rtCrop).clone();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 缩放
|
|
|
|
|
|
|
|
const int outW = std::max(1, static_cast<int>(matBinary.cols * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
const int outH = std::max(1, static_cast<int>(matBinary.rows * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
cv::resize(matCrop, matResized, cv::Size(outW, outH), 0, 0, cv::INTER_AREA);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 转浮点型 + 高斯平滑(尺度选择)
|
|
|
|
|
|
|
|
cv::Mat matFloat;
|
|
|
|
|
|
|
|
matResized.convertTo(matFloat, CV_64F);
|
|
|
|
|
|
|
|
cv::GaussianBlur(matFloat, matBlur, cv::Size(0, 0), dSigma);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 核心: Laplacian 响应(等价于 Ixx + Iyy)
|
|
|
|
|
|
|
|
// 相比 DoH 省去了 Ixy 计算和乘减步骤
|
|
|
|
|
|
|
|
cv::Mat matLoG;
|
|
|
|
|
|
|
|
cv::Laplacian(matBlur, matLoG, CV_64F, 3);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 取绝对值(同时检测亮斑和暗斑)
|
|
|
|
|
|
|
|
matLoG = cv::abs(matLoG);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 8. 归一化到 [0,255]
|
|
|
|
|
|
|
|
cv::Mat matNorm;
|
|
|
|
|
|
|
|
cv::normalize(matLoG, matNorm, 0, 255, cv::NORM_MINMAX, CV_8U);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 保存斑点图供外部使用
|
|
|
|
|
|
|
|
m_matBlob = matNorm.clone();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tEnd = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
double elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(tEnd - tStart).count();
|
|
|
|
|
|
|
|
std::cout << "LoG 耗时(ms): " << elapsedMs << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 9. 可视化
|
|
|
|
|
|
|
|
if (bDraw)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// 非极大值抑制 + 阈值筛选关键点
|
|
|
|
|
|
|
|
std::vector<cv::KeyPoint> keypoints;
|
|
|
|
|
|
|
|
for (int i = 1; i < matLoG.rows - 1; ++i)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
for (int j = 1; j < matLoG.cols - 1; ++j)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
double val = matLoG.at<double>(i, j);
|
|
|
|
|
|
|
|
// 局部极大值检测(3×3邻域)
|
|
|
|
|
|
|
|
if (val > nThreshold &&
|
|
|
|
|
|
|
|
val >= matLoG.at<double>(i - 1, j) &&
|
|
|
|
|
|
|
|
val >= matLoG.at<double>(i + 1, j) &&
|
|
|
|
|
|
|
|
val >= matLoG.at<double>(i, j - 1) &&
|
|
|
|
|
|
|
|
val >= matLoG.at<double>(i, j + 1))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
keypoints.push_back(cv::KeyPoint(j, i, 2.0f * static_cast<float>(dSigma)));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::cvtColor(matNorm, matDraw, cv::COLOR_GRAY2BGR);
|
|
|
|
|
|
|
|
cv::drawKeypoints(matDraw, keypoints, matDraw, cv::Scalar(0, 0, 255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string strOutFile;
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Resize.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile, matResized);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Norm.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile, matNorm);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Draw.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile, matDraw);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CTcsCheck::DetectWithAdaptiveBinaryOptimized(bool bDraw)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// ===================================================================
|
|
|
|
|
|
|
|
// DetectWithAdaptiveBinary 优化版
|
|
|
|
|
|
|
|
// 与原始版保持相同的 OpenCV SIMD 加速路径, 避免 naive 像素循环
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// 当下真正有效的优化方向 (非本次实现):
|
|
|
|
|
|
|
|
// - cv::UMat: 启用 OpenCL GPU 加速, 代码改动 2 行 (Mat → UMat)
|
|
|
|
|
|
|
|
// - 多线程并行: 用 cv::parallel_for_ 处理分块
|
|
|
|
|
|
|
|
// - 缩小 nBlockSize 减少块数 (精度/速度 trade-off)
|
|
|
|
|
|
|
|
// ===================================================================
|
|
|
|
|
|
|
|
cv::Mat matBinary, matCrop, matResized, matBlur, matDraw;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tStart = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cv::threshold(m_matLoad, matBinary, m_cpCfg.nAreaLowFilter, 255, cv::THRESH_BINARY);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Obtain Key Region Range
|
|
|
|
|
|
|
|
cv::Rect rtValid = GetBoundingRect(matBinary);
|
|
|
|
|
|
|
|
if (rtValid == cv::Rect(0, 0, 0, 0))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
std::cout << "NO product" << std::endl;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// crop image
|
|
|
|
|
|
|
|
cv::Rect rtCrop = GetCropArea(rtValid);
|
|
|
|
|
|
|
|
matCrop = m_matLoad(rtCrop).clone();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// image zoom
|
|
|
|
|
|
|
|
const int outW = std::max(1, static_cast<int>(matBinary.cols * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
const int outH = std::max(1, static_cast<int>(matBinary.rows * m_cpCfg.fZoomRatio));
|
|
|
|
|
|
|
|
cv::resize(matCrop, matResized, cv::Size(outW, outH), 0, 0, cv::INTER_AREA);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// image blur
|
|
|
|
|
|
|
|
cv::GaussianBlur(matResized, matBlur, cv::Size(5, 5), 0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 二值化 — 使用原始 AdaptiveBinary (OpenCV SIMD 内部加速)
|
|
|
|
|
|
|
|
m_matBlob = AdaptiveBinary(matBlur);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 分类:对残点二值图做连通域分析+缺陷分类
|
|
|
|
|
|
|
|
ClassifyBlobs(matBlur, m_matBlob);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto tEnd = std::chrono::high_resolution_clock::now();
|
|
|
|
|
|
|
|
double elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(tEnd - tStart).count();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::cout << "AdaptiveBinaryOpt 耗时(ms): " << elapsedMs << std::endl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bDraw)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
matDraw = DrawBlobInfoImage(matResized, m_matBlob);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string strOutFile;
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Binary.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile, matBinary);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Crop.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile, matResized);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Blob.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile, m_matBlob);
|
|
|
|
|
|
|
|
strOutFile = m_strDirOut + "/" + m_strCurFile + "_Draw.png";
|
|
|
|
|
|
|
|
cv::imwrite(strOutFile, matDraw);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void CTcsCheck::Process(bool bDraw)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
//默认使用优化版 AdaptiveBinary:积分图加速 + findNonZero 快速定位
|
|
|
|
|
|
|
|
//DetectWithAdaptiveBinaryOptimized(bDraw);
|
|
|
|
|
|
|
|
// 备选方法:
|
|
|
|
|
|
|
|
DetectWithAdaptiveBinary(bDraw);
|
|
|
|
|
|
|
|
// DetectWithLoG(bDraw, 2.0, 48);
|
|
|
|
|
|
|
|
// DetectWithDoH(bDraw, 2.0, 48);
|
|
|
|
|
|
|
|
}
|