修改图像匹配算法

master
zhou.mengjia 2 years ago
parent ff767a0a89
commit 53454c7e96

@ -4,3 +4,7 @@ Identify the types and quantities of smoke boxes
这是基于南京烟草版本,把扫码器和传感器等功能做一个是否加载的选项: 这是基于南京烟草版本,把扫码器和传感器等功能做一个是否加载的选项:
1、2D模板匹配算法修改

@ -1,11 +1,10 @@
{ {
"MatchParam": { "MatchParam": {
"MaxCount": 10, "MaxCount": 10,
"ScaleTolerance": 25, "ScaleTolerance": 50,
"AngleTolerance": 180, "AngleTolerance": 10,
"GrayValueWeight": 40,
"StrictScore": true, "StrictScore": true,
"ScoreThresh": 50, "ScoreThresh": 70,
"UseTotalThresh": true "UseCache": true
} }
} }

Binary file not shown.

@ -4,7 +4,8 @@
CategoryMatcher::CategoryMatcher(QObject *parent) CategoryMatcher::CategoryMatcher(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
m_ilmatchPtr = ILMatchPtr(__uuidof(LMatch)); m_ilmatch = ILMatchPtr(__uuidof(LMatch));
m_ilmatchF = ILMatchPtr(__uuidof(LMatch));
initILMatchParam(QCoreApplication::applicationDirPath() + MATCHER_CONFIG); initILMatchParam(QCoreApplication::applicationDirPath() + MATCHER_CONFIG);
setILMatch(m_matchParam); setILMatch(m_matchParam);
qDebug() << "init CategoryMatcher successed"; qDebug() << "init CategoryMatcher successed";
@ -47,23 +48,28 @@ void CategoryMatcher::initILMatchParam(const QString& path)
m_matchParam.ScaleTolerance = MatchParamObj.value("ScaleTolerance").toInt(); m_matchParam.ScaleTolerance = MatchParamObj.value("ScaleTolerance").toInt();
m_matchParam.AngleTolerance = MatchParamObj.value("AngleTolerance").toInt(); m_matchParam.AngleTolerance = MatchParamObj.value("AngleTolerance").toInt();
m_matchParam.AcceptScore = MatchParamObj.value("ScoreThresh").toInt(); m_matchParam.AcceptScore = MatchParamObj.value("ScoreThresh").toInt();
m_matchParam.GrayValueWeight = MatchParamObj.value("GrayValueWeight").toInt();
m_matchParam.StrictScore = MatchParamObj.value("StrictScore").toBool(); m_matchParam.StrictScore = MatchParamObj.value("StrictScore").toBool();
m_bUseTotalThresh = MatchParamObj.value("UseTotalThresh").toBool(); m_matchParam.UseCache = MatchParamObj.value("UseCache").toBool();
} }
} }
void CategoryMatcher::setILMatch(const MatchParam& matchParam) void CategoryMatcher::setILMatch(const MatchParam& matchParam)
{ {
m_ilmatchPtr->MaxCount = m_matchParam.MaxCount; m_ilmatch->MaxCount = m_matchParam.MaxCount;
m_ilmatchPtr->ScaleTolerance = m_matchParam.ScaleTolerance; m_ilmatch->ScaleTolerance = m_matchParam.ScaleTolerance;
m_ilmatchPtr->AngleTolerance = m_matchParam.AngleTolerance; m_ilmatch->AngleTolerance = m_matchParam.AngleTolerance;
m_ilmatchPtr->GrayValueWeight = m_matchParam.GrayValueWeight; m_ilmatch->StrictScore = m_matchParam.StrictScore;
m_ilmatchPtr->StrictScore = m_matchParam.StrictScore; m_ilmatch->UseCache = m_matchParam.UseCache;
if (m_bUseTotalThresh) m_ilmatch->AcceptScore = m_matchParam.AcceptScore;
{
m_ilmatchPtr->AcceptScore = m_matchParam.AcceptScore;
} m_ilmatchF->MaxCount = m_matchParam.MaxCount;
m_ilmatchF->ScaleTolerance = m_matchParam.ScaleTolerance;
m_ilmatchF->AngleTolerance = m_matchParam.AngleTolerance;
m_ilmatchF->StrictScore = m_matchParam.StrictScore;
m_ilmatchF->UseCache = m_matchParam.UseCache;
m_ilmatchF->AcceptScore = m_matchParam.AcceptScore;
} }
bool CategoryMatcher::smokeMatch(const QString& typeNo, const cv::Mat& inputMat, cv::Mat& rltMat) bool CategoryMatcher::smokeMatch(const QString& typeNo, const cv::Mat& inputMat, cv::Mat& rltMat)
@ -88,37 +94,31 @@ bool CategoryMatcher::smokeMatch(const QString& typeNo, const cv::Mat& inputMat,
bool CategoryMatcher::smokeMatch(const QString& typeNo, const ILImagePtr ilImgPtr, bool CategoryMatcher::smokeMatch(const QString& typeNo, const ILImagePtr ilImgPtr,
std::pair<int, QVector<ILMatchResultPtr>>& templateLevelAndvecMatchRltPtr) std::pair<int, QVector<ILMatchResultPtr>>& templateLevelAndvecMatchRltPtr)
{ {
QVector<ILMatchResultPtr> vecMatchRltPtrMax;
TemplateObject templateObj; TemplateObject templateObj;
QString templateImgFolderPath = TYPE_TEMPLATE_PATH; QString templateImgFolderPath = TYPE_TEMPLATE_PATH;
QString folderPath = QString("%1\\%2").arg(templateImgFolderPath).arg(typeNo); QString folderPath = QString("%1\\%2").arg(templateImgFolderPath).arg(typeNo);
if (getTemplateImgFileInfo(folderPath, templateObj)) if (getTemplateImgFileInfo(folderPath, templateObj))
{ {
qDebug() << "Get TemplateImg OK!"; qDebug() << "Get TemplateImg OK!";
QVector<double> vecMatchSroreMax; QVector<double> vecMatchSroreMax;
ILMatchResultsPtr matchResultsPtr; QVector<ILMatchResultPtr> vecMatchRltsMax;
QList<int> keys = templateObj.uniqueKeys(); QList<int> keys = templateObj.uniqueKeys();
int templateLevel = keys.length(); int templateLevel = keys.length();
qDebug() << "templateLevel = " << templateLevel; qDebug() << "templateLevel = " << templateLevel;
templateLevelAndvecMatchRltPtr.first = templateLevel; templateLevelAndvecMatchRltPtr.first = templateLevel;
// 遍历不同模板级别
for each (int level in keys) for each (int level in keys)
{ {
QVector<double> vecMatchSrore; QVector<double> vecMatchSrore;
QVector<ILMatchResultPtr> vecMatchRltPtr; QVector<ILMatchResultPtr> vecMatchRlts;
QMap<QString, int> strScoreMap = templateObj.value(level); QMap<QString, int> strScoreMap = templateObj.value(level);
for (QMap<QString, int>::iterator it = strScoreMap.begin(); it != strScoreMap.end(); ++it) for (QMap<QString, int>::iterator it = strScoreMap.begin(); it != strScoreMap.end(); ++it)
{ {
//需要验证没用1类模板但有2类模板的图
QString templateImgPath = it.key(); QString templateImgPath = it.key();
int scoreThresh;
if (!m_bUseTotalThresh)
{
scoreThresh = it.value();
m_ilmatchPtr->AcceptScore = scoreThresh;
}
else
{
scoreThresh = m_matchParam.AcceptScore;
}
ILImagePtr ilTemplatePtr(__uuidof(LImage)); ILImagePtr ilTemplatePtr(__uuidof(LImage));
ilTemplatePtr->Load(templateImgPath.toStdString().c_str()); ilTemplatePtr->Load(templateImgPath.toStdString().c_str());
if (ilTemplatePtr->Void()) if (ilTemplatePtr->Void())
@ -128,56 +128,63 @@ bool CategoryMatcher::smokeMatch(const QString& typeNo, const ILImagePtr ilImgPt
continue; continue;
} }
bool isReduced = false; m_ilmatch->Learn(ilTemplatePtr, nullptr);
ILImagePtr img(__uuidof(LImage)); ILImagePtr img(__uuidof(LImage));
ILImagePtr tempImage(__uuidof(LImage));
ILImageArithmPtr imgArithmF(__uuidof(LImageArithm));
imgArithmF->Flip(ilTemplatePtr, LPVFlipType::LPVFlipH, tempImage);
imgArithmF->Flip(tempImage, LPVFlipType::LPVFlipV, tempImage);
m_ilmatchF->Learn(tempImage, nullptr);
if ((ilTemplatePtr->Height > 100 && ilTemplatePtr->Width > 100)) ILMatchResultsPtr matchResults, matchResultsF;
{
isReduced = true;
ILImageArithmPtr imgArithm1(__uuidof(LImageArithm));
ILImageArithmPtr imgArithm2(__uuidof(LImageArithm));
imgArithm1->Resize(ilTemplatePtr, 0.5, 0.5, LPVInterNearest, ilTemplatePtr);
imgArithm2->Resize(ilImgPtr, 0.5, 0.5, LPVInterNearest, img);
}
m_ilmatchPtr->DetailLevel = 0.5; if (level == 1) // 大模板
m_ilmatchPtr->Learn(ilTemplatePtr, nullptr);
// 如果模板的特征点特别多,匹配时间会相当长,需要降低细节
if (m_ilmatchPtr->GetPatFeature()->Count()>2500)
{ {
m_ilmatchPtr->DetailLevel = 0.05; ILImageArithmPtr imgArithm(__uuidof(LImageArithm));
m_ilmatchPtr->Learn(ilTemplatePtr, nullptr); imgArithm->Resize(ilImgPtr, 0.5, 0.5, LPVInterNearest, img);
} }
LPVErrorCode err = m_ilmatch->Match((level == 1 ? img : ilImgPtr), nullptr, &matchResults);
LPVErrorCode err = m_ilmatchPtr->Match((isReduced ? img : ilImgPtr), nullptr, &matchResultsPtr); LPVErrorCode errF = m_ilmatchF->Match((level == 1 ? img : ilImgPtr), nullptr, &matchResultsF);
if (err != LPVErrorCode::LPVNoError) { if (err < LPVErrorCode::LPVNoError || errF < LPVErrorCode::LPVNoError) {
continue; // match failed //continue; // match failed
} }
int objCount = matchResultsPtr->Count();
int objCount = matchResults->Count() + matchResultsF->Count();
if (objCount > 0) if (objCount > 0)
{ {
double maxScore = -1; double valueMaxScore = -1;
ILMatchResultPtr matchRltPtr; ILMatchResultPtr matchRltPtr;
int indexMaxScore = 0; int indexMaxScore = 0;
for (int i = 0; i < objCount; i++) QVector<double> matchSrore;
// 模板方向的匹配结果——旋转180度匹配结果组成一个完整的匹配结果值
for (int i = 0; i < matchResults->Count(); i++)
{ {
matchRltPtr = matchResultsPtr->Item(i); matchSrore.push_back(matchResults->Item(i)->Score);
double score = matchRltPtr->GetScore(); }
if (score < scoreThresh) for (int i = 0; i < matchResultsF->Count(); i++)
{ {
continue; matchSrore.push_back(matchResultsF->Item(i)->Score);
} }
if (maxScore < score)
//选择评分最高的匹配结果
auto maxPos = std::max_element(matchSrore.begin(), matchSrore.end());
valueMaxScore = *maxPos;
indexMaxScore = maxPos - matchSrore.begin();
vecMatchSrore.push_back(valueMaxScore);
if (indexMaxScore < matchResults->Count())
{ {
maxScore = score; vecMatchRlts.push_back(matchResults->Item(indexMaxScore));
indexMaxScore = i;
} }
else
{
vecMatchRlts.push_back(matchResultsF->Item(indexMaxScore - matchResults->Count()));
} }
if (maxScore < 0)
continue;
vecMatchSrore.push_back(maxScore);
vecMatchRltPtr.push_back(matchResultsPtr->Item(indexMaxScore));
} }
} }
if (vecMatchSrore.isEmpty()) if (vecMatchSrore.isEmpty())
{ {
@ -185,10 +192,11 @@ bool CategoryMatcher::smokeMatch(const QString& typeNo, const ILImagePtr ilImgPt
} }
else else
{ {
// 选择该模板等级下评分最高的匹配结果
auto maxMatchScorePos = std::max_element(vecMatchSrore.begin(), vecMatchSrore.end()); auto maxMatchScorePos = std::max_element(vecMatchSrore.begin(), vecMatchSrore.end());
auto maxIndex = maxMatchScorePos - vecMatchSrore.begin(); auto maxIndex = maxMatchScorePos - vecMatchSrore.begin();
vecMatchSroreMax.push_back(*maxMatchScorePos); vecMatchSroreMax.push_back(*maxMatchScorePos);
vecMatchRltPtrMax.push_back(vecMatchRltPtr.at(maxIndex)); vecMatchRltsMax.push_back(vecMatchRlts.at(maxIndex));
} }
} }
if (vecMatchSroreMax.isEmpty() || (vecMatchSroreMax.length() != templateLevel)) if (vecMatchSroreMax.isEmpty() || (vecMatchSroreMax.length() != templateLevel))
@ -196,12 +204,12 @@ bool CategoryMatcher::smokeMatch(const QString& typeNo, const ILImagePtr ilImgPt
QString outStr = QString("Level: %1 match failed!") QString outStr = QString("Level: %1 match failed!")
.arg(vecMatchSroreMax.length() + 1); .arg(vecMatchSroreMax.length() + 1);
qDebug() << outStr; qDebug() << outStr;
templateLevelAndvecMatchRltPtr.second = vecMatchRltPtrMax; templateLevelAndvecMatchRltPtr.second = vecMatchRltsMax;
return false; return false;
} }
else else
{ {
templateLevelAndvecMatchRltPtr.second = vecMatchRltPtrMax; templateLevelAndvecMatchRltPtr.second = vecMatchRltsMax;
} }
} }
else else

@ -32,16 +32,16 @@ struct MatchParam
int MaxCount; //匹配最大数量 int MaxCount; //匹配最大数量
int ScaleTolerance; //缩放比例范围 int ScaleTolerance; //缩放比例范围
int AngleTolerance; //角度范围 int AngleTolerance; //角度范围
int GrayValueWeight; //»Ò¶ÈÆ¥ÅäÈ¨ÖØ
int AcceptScore; //匹配分数阈值 int AcceptScore; //匹配分数阈值
bool StrictScore; //启用严格评分 bool StrictScore; //启用严格评分
bool UseCache; //启用缓存
MatchParam() MatchParam()
{ {
MaxCount = 10; MaxCount = 10;
ScaleTolerance = 20; ScaleTolerance = 20;
AngleTolerance = 180; AngleTolerance = 180;
GrayValueWeight = 40;
StrictScore = true; StrictScore = true;
UseCache = true;
} }
}; };
@ -64,9 +64,9 @@ private:
std::pair<int, QVector<ILMatchResultPtr>> templateLevelAndvecMatchRltPtr); std::pair<int, QVector<ILMatchResultPtr>> templateLevelAndvecMatchRltPtr);
MatchParam m_matchParam; MatchParam m_matchParam;
ILMatchPtr m_ilmatchPtr; ILMatchPtr m_ilmatch;
ILMatchPtr m_ilmatchF;
QMutex m_mutex; QMutex m_mutex;
bool m_bUseTotalThresh{ false };
}; };
bool ensureGrayImg(const cv::Mat& img, cv::Mat& gray); bool ensureGrayImg(const cv::Mat& img, cv::Mat& gray);

@ -50,13 +50,6 @@ SmokeBoxIdentification::SmokeBoxIdentification(QWidget *parent)
connect(m_pNetControl, &NetControl::sgReceiveData, this, &SmokeBoxIdentification::onDecodeMsg, Qt::QueuedConnection); connect(m_pNetControl, &NetControl::sgReceiveData, this, &SmokeBoxIdentification::onDecodeMsg, Qt::QueuedConnection);
connect(this, &SmokeBoxIdentification::sgSendMsg, m_pNetControl, &NetControl::onSendMsg, Qt::QueuedConnection); connect(this, &SmokeBoxIdentification::sgSendMsg, m_pNetControl, &NetControl::onSendMsg, Qt::QueuedConnection);
if (m_moduleParam.Sensor != 0)
{
ADModule::instance()->loadParameters(filePath);
filePath = applicationDirPath + COM_CONFIG;
lpSerialStation::instance()->loadParameters(filePath);
lpSerialStation::instance()->openComs();
}
if (m_moduleParam.CodeScan != 0) if (m_moduleParam.CodeScan != 0)
{ {
@ -65,16 +58,30 @@ SmokeBoxIdentification::SmokeBoxIdentification(QWidget *parent)
CodeScanStation::instance()->openComs(); CodeScanStation::instance()->openComs();
} }
m_pCategoryMatcher = new CategoryMatcher(); if (m_moduleParam.Sensor != 0)
connect(CodeScanStation::instance(), &CodeScanStation::sgNewCodeScanData, lpSerialStation::instance() {
, &lpSerialStation::onNewCodeScanResults); ADModule::instance()->loadParameters(filePath);
connect(this, &SmokeBoxIdentification::sgCategoryMatchFinished, this, &SmokeBoxIdentification::onNumStatistic); filePath = applicationDirPath + COM_CONFIG;
lpSerialStation::instance()->loadParameters(filePath);
lpSerialStation::instance()->openComs();
connect(lpSerialStation::instance(), &lpSerialStation::sgMonitorSensorTriggerStart, this connect(lpSerialStation::instance(), &lpSerialStation::sgMonitorSensorTriggerStart, this
, &SmokeBoxIdentification::onMonitorSensorTriggerStart); , &SmokeBoxIdentification::onMonitorSensorTriggerStart);
connect(lpSerialStation::instance(), &lpSerialStation::sgMonitorSensorTriggerStop, this connect(lpSerialStation::instance(), &lpSerialStation::sgMonitorSensorTriggerStop, this
, &SmokeBoxIdentification::onMonitorSensorTriggerStop); , &SmokeBoxIdentification::onMonitorSensorTriggerStop);
connect(lpSerialStation::instance(), &lpSerialStation::sgNewEmptyCheckResults, this connect(lpSerialStation::instance(), &lpSerialStation::sgNewEmptyCheckResults, this
, &SmokeBoxIdentification::onSendEmptyCheckResults); , &SmokeBoxIdentification::onSendEmptyCheckResults);
}
m_pCategoryMatcher = new CategoryMatcher();
connect(CodeScanStation::instance(), &CodeScanStation::sgNewCodeScanData, lpSerialStation::instance()
, &lpSerialStation::onNewCodeScanResults);
connect(this, &SmokeBoxIdentification::sgCategoryMatchFinished, this, &SmokeBoxIdentification::onNumStatistic);
connect(this, &SmokeBoxIdentification::sgControlSideLight, lpSerialStation::instance() connect(this, &SmokeBoxIdentification::sgControlSideLight, lpSerialStation::instance()
, &lpSerialStation::onControlSideLight); , &lpSerialStation::onControlSideLight);
connect(this, &SmokeBoxIdentification::sgStartEmptyPlaceCheck, lpSerialStation::instance() connect(this, &SmokeBoxIdentification::sgStartEmptyPlaceCheck, lpSerialStation::instance()

@ -0,0 +1,38 @@
Debug | 2024-01-08 14:07:21 周一 | Decode smokeInfo from "D:/Code/Logistics/smokeboxidentification/tpvs17/../runner17/Release\\user\\smokeInfo.xlsx" successed
Debug | 2024-01-08 14:07:21 周一 | "D:/Code/Logistics/smokeboxidentification/tpvs17/../runner17/Release\\templateNum" has not folder
Warning | 2024-01-08 14:07:22 周一 | Do not found any device!
Warning | 2024-01-08 14:07:22 周一 | SerialNumber: "00J50359922" init failed
Warning | 2024-01-08 14:07:22 周一 | Do not found any device!
Warning | 2024-01-08 14:07:22 周一 | SerialNumber: "00J50359927" init failed
Warning | 2024-01-08 14:07:22 周一 | Do not found any device!
Warning | 2024-01-08 14:07:22 周一 | SerialNumber: "00J50359938" init failed
Warning | 2024-01-08 14:07:22 周一 | Do not found any device!
Warning | 2024-01-08 14:07:22 周一 | SerialNumber: "00J50359923" init failed
Warning | 2024-01-08 14:07:22 周一 | QObject::moveToThread: Cannot move objects with a parent
Warning | 2024-01-08 14:07:22 周一 | Do not found any device!
Warning | 2024-01-08 14:07:22 周一 | SerialNumber: "00J50359918" init failed
Warning | 2024-01-08 14:07:22 周一 | Do not found any device!
Warning | 2024-01-08 14:07:22 周一 | SerialNumber: "00J50359945" init failed
Debug | 2024-01-08 14:07:22 周一 | m_vecPCamera size: 0
Debug | 2024-01-08 14:07:30 周一 | init CategoryMatcher successed
Debug | 2024-01-08 14:07:33 周一 | delete netControl
Debug | 2024-01-08 14:07:33 周一 | delete netControl finished
Debug | 2024-01-08 20:26:54 周一 | Decode smokeInfo from "D:/Code/Logistics/smokeboxidentification/tpvs17/../runner17/Release\\user\\smokeInfo.xlsx" successed
Debug | 2024-01-08 20:26:54 周一 | "D:/Code/Logistics/smokeboxidentification/tpvs17/../runner17/Release\\templateNum" has not folder
Warning | 2024-01-08 20:26:55 周一 | Do not found any device!
Warning | 2024-01-08 20:26:55 周一 | SerialNumber: "00J50359922" init failed
Warning | 2024-01-08 20:26:55 周一 | Do not found any device!
Warning | 2024-01-08 20:26:55 周一 | SerialNumber: "00J50359927" init failed
Warning | 2024-01-08 20:26:55 周一 | Do not found any device!
Warning | 2024-01-08 20:26:55 周一 | SerialNumber: "00J50359938" init failed
Warning | 2024-01-08 20:26:55 周一 | Do not found any device!
Warning | 2024-01-08 20:26:55 周一 | SerialNumber: "00J50359923" init failed
Warning | 2024-01-08 20:26:55 周一 | QObject::moveToThread: Cannot move objects with a parent
Warning | 2024-01-08 20:26:55 周一 | Do not found any device!
Warning | 2024-01-08 20:26:55 周一 | SerialNumber: "00J50359918" init failed
Warning | 2024-01-08 20:26:55 周一 | Do not found any device!
Warning | 2024-01-08 20:26:55 周一 | SerialNumber: "00J50359945" init failed
Debug | 2024-01-08 20:26:55 周一 | m_vecPCamera size: 0
Debug | 2024-01-08 20:26:57 周一 | init CategoryMatcher successed
Debug | 2024-01-08 20:26:59 周一 | delete netControl
Debug | 2024-01-08 20:26:59 周一 | delete netControl finished
Loading…
Cancel
Save