You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

483 lines
15 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#pragma execution_character_set("utf-8")
#include "CategoryMatcher.h"
CategoryMatcher::CategoryMatcher(QObject *parent)
: QObject(parent)
{
m_ilmatch = ILMatchPtr(__uuidof(LMatch));
m_ilmatchF = ILMatchPtr(__uuidof(LMatch));
m_display = ILDisplayPtr(__uuidof(LDisplay));
m_text = ILTextPtr(__uuidof(LText));
m_textSys= ILSystemPtr(__uuidof(LSystem));
initILMatchParam(QCoreApplication::applicationDirPath() + MATCHER_CONFIG);
setILMatch(m_matchParam);
qDebug() << "init CategoryMatcher successed";
m_text->Alignment = LPVAlignment::LPVAlignLeft; // 设置文本绘制时的对齐方式
m_textSys->SetFontColor(m_text, m_textSys->GetColorRef(0, 255, 0)); // 设置文本的颜色
m_textSys->SetFontHeight(m_text, 80); // 设置字体高度
m_textSys->SetFontFamily(m_text, "Microsoft YaHei"); // 设置字体类型
m_text->PosX = 100;
m_text->PosY = 100; // 设置文本绘制时的位置
}
CategoryMatcher::~CategoryMatcher()
{
}
void CategoryMatcher::initILMatchParam(const QString& path)
{
QString fileMyself = path;
QFile file(fileMyself);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
qWarning() << "打开文件失败!";
return;
}
QByteArray arr = file.readAll();
file.close();
if (arr.isEmpty())
{
qWarning() << "内容为空";
return;
}
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(arr, &err);
if (doc.isEmpty())
{
qWarning() << err.errorString(); //打印失败信息
return;
}
QJsonObject jsMyself = doc.object();
QJsonObject MatchParamObj = jsMyself.value("MatchParam").toObject();
if (!MatchParamObj.isEmpty()) {
m_matchParam.MaxCount = MatchParamObj.value("MaxCount").toInt();
m_matchParam.ScaleTolerance = MatchParamObj.value("ScaleTolerance").toInt();
m_matchParam.AngleTolerance = MatchParamObj.value("AngleTolerance").toInt();
m_matchParam.AcceptScore = MatchParamObj.value("ScoreThresh").toInt();
m_matchParam.StrictScore = MatchParamObj.value("StrictScore").toBool();
m_matchParam.UseCache = MatchParamObj.value("UseCache").toBool();
}
}
void CategoryMatcher::setILMatch(const MatchParam& matchParam)
{
m_ilmatch->MaxCount = m_matchParam.MaxCount;
m_ilmatch->ScaleTolerance = m_matchParam.ScaleTolerance;
m_ilmatch->AngleTolerance = m_matchParam.AngleTolerance;
m_ilmatch->StrictScore = m_matchParam.StrictScore;
m_ilmatch->UseCache = m_matchParam.UseCache;
m_ilmatch->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, ILImagePtr image, int id)
{
QMap<int, ILMatchResultPtr> resultsPair;
bool bRlt = smokeMatch(typeNo, image, resultsPair,id);
drawImage(image, resultsPair);
return bRlt;
}
bool CategoryMatcher::smokeMatch(const QString& typeNo, const ILImagePtr ilImgPtr,
QMap<int, ILMatchResultPtr>& resultsMap, int id)
{
TemplateObject templateObj;
QString templateImgFolderPath = TYPE_TEMPLATE_PATH;
//可能存在问题:之前的模板设计。后缀带了别的数据。需验证
// 模板文件格式:烟箱编码/烟箱编码-模板编码
// 模板编码:模板类型+模板编号
// 模板类型1为大模板模板和匹配图均放缩50%2为小模板
// 例如1类模板的第1个模板11
// 中华硬310102的2类模板的第1个模板310102-21
QString folderPath = QString("%1\\%2").arg(templateImgFolderPath).arg(typeNo);
QString str;
if (getTemplateImgFileInfo(folderPath, templateObj))
{
str = QString("[%1]:Get TemplateImg OK!").arg(id);
qDebug() <<str;
QVector<double> vecMatchSroreMax;
QVector<ILMatchResultPtr> vecMatchRltsMax;
QList<int> keys = templateObj.uniqueKeys();
int templateLevelCount = keys.length();
str = QString("[%1]:templateLevelCount =%2").arg(id).arg(templateLevelCount);
qDebug() << str;
// 遍历不同模板级别
for each (int level in keys)
{
QVector<double> vecMatchSrore;
QVector<ILMatchResultPtr> vecMatchRlts;
resultsMap.insert(level, nullptr);
QMap<QString, int> strScoreMap = templateObj.value(level);
for (QMap<QString, int>::iterator it = strScoreMap.begin(); it != strScoreMap.end(); ++it)
{
QString templateImgPath = it.key();
ILImagePtr ilTemplatePtr(__uuidof(LImage));
ilTemplatePtr->Load(templateImgPath.toStdString().c_str());
if (ilTemplatePtr->Void())
{
QString outStr = templateImgPath + " is empty!";
qDebug() << outStr;
continue;
}
m_ilmatch->Learn(ilTemplatePtr, nullptr);
ILImagePtr img(__uuidof(LImage));
ILImagePtr tempImage(__uuidof(LImage));
//由于如果角度开到±180部分匹配时间过长故而用两次模板匹配分别匹配模板和模板旋转180度情况
ILImageArithmPtr imgArithmF(__uuidof(LImageArithm));
imgArithmF->Flip(ilTemplatePtr, LPVFlipType::LPVFlipH, tempImage);
imgArithmF->Flip(tempImage, LPVFlipType::LPVFlipV, tempImage);
m_ilmatchF->Learn(tempImage, nullptr);
ILMatchResultsPtr matchResults, matchResultsF;
ILRotRectRegionPtr rectRoi(__uuidof(LRotRectRegion));
if (level == 1) // 大模板
{
ILImageArithmPtr imgArithm(__uuidof(LImageArithm));
imgArithm->Resize(ilImgPtr, 0.5, 0.5, LPVInterNearest, img);
rectRoi->SetPlacement(800, 500, 500, 1250, 600);
}
else {
rectRoi->SetPlacement(1600, 1000, 2500, 1200, 0);
}
qInfo() << "setrectRoi end";
LPVErrorCode err = m_ilmatch->Match((level == 1 ? img : ilImgPtr), rectRoi, &matchResults);
LPVErrorCode errF = m_ilmatchF->Match((level == 1 ? img : ilImgPtr), rectRoi, &matchResultsF);
if (err < LPVErrorCode::LPVNoError || errF < LPVErrorCode::LPVNoError) {
str = QString("[%1]:Match Failed;templateImgPath = %2").arg(id).arg(templateImgPath);
qDebug() << str;
continue; // match failed
}
int objCount = matchResults->Count() + matchResultsF->Count();
if (objCount > 0)
{
double valueMaxScore = -1;
ILMatchResultPtr matchRltPtr;
int indexMaxScore = 0;
QVector<double> matchSrore;
// 模板方向的匹配结果——旋转180度匹配结果组成一个完整的匹配结果值
for (int i = 0; i < matchResults->Count(); i++)
{
matchSrore.push_back(matchResults->Item(i)->Score);
}
for (int i = 0; i < matchResultsF->Count(); i++)
{
matchSrore.push_back(matchResultsF->Item(i)->Score);
}
//选择评分最高的匹配结果
auto maxPos = std::max_element(matchSrore.begin(), matchSrore.end());
valueMaxScore = *maxPos;
indexMaxScore = maxPos - matchSrore.begin();
vecMatchSrore.push_back(valueMaxScore);
if (indexMaxScore < matchResults->Count())
{
vecMatchRlts.push_back(matchResults->Item(indexMaxScore));
}
else
{
vecMatchRlts.push_back(matchResultsF->Item(indexMaxScore - matchResults->Count()));
}
}
}
if (vecMatchSrore.isEmpty())
{
str = QString("[%1]:Level: %1 match failed!").arg(id).arg(level);
qDebug() << str;
return false;
}
else
{
// 选择该模板等级下评分最高的匹配结果
auto maxMatchScorePos = std::max_element(vecMatchSrore.begin(), vecMatchSrore.end());
auto maxIndex = maxMatchScorePos - vecMatchSrore.begin();
vecMatchSroreMax.push_back(*maxMatchScorePos);
//vecMatchRltsMax.push_back(vecMatchRlts.at(maxIndex));
resultsMap[level]=vecMatchRlts.at(maxIndex);
}
}
}
else
{
str = QString("[%1]:Find templateImage failed! Folder = %2").arg(id).arg(folderPath);
qDebug() << str;
return false;
}
return true;
}
bool CategoryMatcher::getTemplateImgFileInfo(const QString& folderPath, TemplateObject& templateObject)
{
templateObject.clear();
QDir fileDir(folderPath);
if (!fileDir.exists())
{
qWarning() << folderPath << " is not exist";
return false;
}
for each(QFileInfo imgFileInfo in fileDir.entryInfoList(QDir::Filter::Files | QDir::NoDotAndDotDot))
{
QString fileName = imgFileInfo.fileName();
if (fileName.endsWith(".png") || fileName.endsWith(".PNG")
|| fileName.endsWith(".BMP") || fileName.endsWith(".bmp")
|| fileName.endsWith(".jpeg") || fileName.endsWith(".JPEG")
|| fileName.endsWith(".jpg") || fileName.endsWith(".JPG"))
{
QStringList strList = imgFileInfo.baseName().split("-");
QString templateImgPath = imgFileInfo.filePath(); //相对路径,避免路径存在中文时读取失败
int level = strList.last().mid(0, 1).toInt();
templateObject[level].insert(templateImgPath, 70);
////之前的模板格式例如templateImg/51520312/template_0101_65
//if (strList.first() == "template" && strList.length() == 3)
//{
// int scoreThresh = strList.last().toInt();
// QString templateImgPath = imgFileInfo.filePath(); //相对路径,避免路径存在中文时读取失败
// std::pair<QString, int> imgScorePair(templateImgPath, scoreThresh);
// QString serialNum = strList[1];
// if (serialNum.length() == 4)
// {
// int level = serialNum.mid(0, 2).toInt();
// templateObject[level].insert(templateImgPath, scoreThresh);
// }
//}
}
}
if (templateObject.isEmpty())
{
return false;
}
return true;
}
void CategoryMatcher::drawImage(ILImagePtr image, QMap<int, ILMatchResultPtr> resultsMap)
{
if (image->Void())
{
return;
}
std::stringstream textStream("");
//在Display上绘制图像+结果
m_display->SetImage(image);
m_display->RemoveAllObjects();
QString tittleStr,rltStr;
bool bRlt = false;
for (auto iter = resultsMap.begin(); iter != resultsMap.end(); iter++)
{
ILMatchResultPtr matchRlt = iter.value();
if (matchRlt == nullptr)
{
continue;
}
bRlt = true;
double score = matchRlt->GetScore();
rltStr.append(QString("\n Level %1 score: %2").arg(iter.key()).arg(score));
if (iter.key() == 1)
{
ILRotRectRegionPtr m_rect(__uuidof(LRotRectRegion));
m_rect->SetPlacement(matchRlt->GetCenter()->X * 2, matchRlt->GetCenter()->Y * 2
, matchRlt->GetRect()->Width * 2, matchRlt->GetRect()->Height * 2, matchRlt->GetRect()->Angle);
m_textSys->SetPenWidth(m_rect, 3);
m_textSys->SetPenColor(m_rect, m_textSys->GetColorRef(0, 255, 0));
m_display->AddObject(m_rect, 0);
}
else
{
m_textSys->SetPenWidth(matchRlt, 3);
m_textSys->SetPenColor(matchRlt, m_textSys->GetColorRef(0, 255, 0));
m_display->AddObject(matchRlt, LPVPatDrawFlags::LPVPatDrawBoundingRect);
}
}
ILRotRectRegionPtr m_rect(__uuidof(LRotRectRegion));
m_rect->SetPlacement(1600, 1000, 2500, 1200, 0);
m_textSys->SetPenWidth(m_rect, 3);
m_textSys->SetPenColor(m_rect, m_textSys->GetColorRef(0, 255, 0));
m_display->AddObject(m_rect, 0);
if (bRlt)
{
m_textSys->SetFontColor(m_text, m_textSys->GetColorRef(0, 255, 0));
tittleStr = QString("Match successful! Total level Count: %1").arg(resultsMap.keys().length());
}
else
{
m_textSys->SetFontColor(m_text, m_textSys->GetColorRef(255, 0, 0));
tittleStr = QString("Match failed! Total level Count: %1").arg(resultsMap.keys().length());
}
textStream << tittleStr.toStdString();
textStream << rltStr.toStdString();
m_text->Text = textStream.str().c_str();
m_display->AddObject(m_text,0);
}
void CategoryMatcher::saveResultsImage(const QString& filePath)
{
std::wstring str = filePath.toStdWString();
m_display->SaveSceneToFile(str.c_str(), 1, 1);
}
void CategoryMatcher::drawImage(const cv::Mat& imgMatSrc, cv::Mat& rltCvImg,
std::pair<int, QVector<ILMatchResultPtr>> templateLevelAndvecMatchRltPtr)
{
if (imgMatSrc.empty()) {
return;
}
ensureColorBGRImg(imgMatSrc, rltCvImg);
int level = 1;
int templateLevel = templateLevelAndvecMatchRltPtr.first;
QVector<ILMatchResultPtr> vecMatchRltPtr = templateLevelAndvecMatchRltPtr.second;
QString rltStr;
cv::Scalar textColor(0, 255, 0);
if (!vecMatchRltPtr.isEmpty() && (vecMatchRltPtr.length() == templateLevel))
{
rltStr = QString("Match successful! Total level: %1").arg(templateLevel);
}
else
{
textColor = cv::Scalar(0, 0, 255);
rltStr = QString("Match failed! Total level: %1").arg(templateLevel);
}
int fontFace = cv::FONT_HERSHEY_DUPLEX;
double fontScale = 2.5;
int textThickness = 2;
int baseLine = 0;
int startX = 50;
int startY = 100;
cv::Size textSize = cv::getTextSize(rltStr.toStdString(), fontFace, fontScale, textThickness, &baseLine);
cv::putText(rltCvImg, rltStr.toStdString(), cv::Point(startX, startY),
fontFace, fontScale, textColor, textThickness);
rltStr.clear();
for each(ILMatchResultPtr matchRltPtr in vecMatchRltPtr)
{
double score = matchRltPtr->GetScore();
rltStr.append(QString(" Level %1 score: %2").arg(level++).arg(score));
ILRectPtr ilRectPtr = matchRltPtr->GetRect();
ILPointsPtr ilPointsPtr = ilRectPtr->GetPoints();
cv::Point2f vertices[4];
int count = ilPointsPtr->Count();
if (count != 4)
{
continue;
}
for (int i = 0; i < count; ++i)
{
ILPointPtr ilPointPtr = ilPointsPtr->Item(i);
vertices[i] = cv::Point2f(ilPointPtr->GetX(), ilPointPtr->GetY());
}
for (int i = 0; i < 4; ++i)
{
cv::line(rltCvImg, vertices[i], vertices[(i + 1) % 4], cv::Scalar(255, 0, 0), 8);
}
}
if (!rltStr.isEmpty())
{
cv::putText(rltCvImg, rltStr.toStdString(),
cv::Point(startX, startY + textSize.height + 20),
fontFace, fontScale, textColor, textThickness);
}
}
bool ensureGrayImg(const cv::Mat& img, cv::Mat& gray)
{
if (img.channels() == 3)
{
if (img.depth() == CV_8U) {
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
}
else {
cv::Mat channels[3];
split(img, channels);
gray = channels[2] * 0.299 + channels[1] * 0.587 + channels[0] * 0.114;
}
return true;
}
else if (img.channels() == 4)
{
if (img.depth() == CV_8U) {
cvtColor(img, gray, cv::COLOR_BGRA2GRAY);
}
else {
cv::Mat channels[4];
cv::split(img, channels);
gray = channels[2] * 0.299 + channels[1] * 0.587 + channels[0] * 0.114;
}
return true;
}
else if (img.channels() == 1)
{
gray = img;
return false;
}
else
{
_ASSERTE(false && "unsupported image conversion.");
return false;
}
}
bool ensureColorBGRImg(const cv::Mat& img, cv::Mat& color)
{
if (img.channels() == 3) {
color = img;
return false;
}
else if (img.channels() == 4) {
if (img.depth() == CV_8U) {
cvtColor(img, color, cv::COLOR_BGRA2BGR);
}
else {
static int from4_to3[] = { 0,0,1,1,2,2 };
if (color.cols != img.cols || color.rows != img.rows || color.type() != CV_MAKE_TYPE(img.depth(), 3))
color = cv::Mat(img.rows, img.cols, CV_MAKE_TYPE(img.depth(), 3));
mixChannels(img, color, from4_to3, 3);
}
return true;
}
else if (img.channels() == 1) {
if (img.depth() == CV_8U) {
cvtColor(img, color, cv::COLOR_GRAY2BGR);
}
else {
static int from1_to3[] = { 0,0,0,1,0,2 };
if (color.cols != img.cols || color.rows != img.rows || color.type() != CV_MAKE_TYPE(img.depth(), 3))
color = cv::Mat(img.rows, img.cols, CV_MAKE_TYPE(img.depth(), 3));
mixChannels(img, color, from1_to3, 3);
}
return true;
}
else {
_ASSERTE(false && "unsupported image conversion.");
return false;
}
}