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.
smokeboxidentification/src/CategoryMatcher.cpp

397 lines
12 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));
initILMatchParam(QCoreApplication::applicationDirPath() + MATCHER_CONFIG);
setILMatch(m_matchParam);
qDebug() << "init CategoryMatcher successed";
}
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, const cv::Mat& inputMat, cv::Mat& rltMat)
{
if (inputMat.empty())
{
qWarning() << "inputMat is empty";
return false;
}
ILImagePtr ilImgGray(__uuidof(LImage));
cv::Mat imgMatGray;
ensureGrayImg(inputMat, imgMatGray);
qDebug() << "ensureGrayImg OK!";
ilImgGray->SetImageData(imgMatGray.cols, imgMatGray.rows, (void*)imgMatGray.data, imgMatGray.step, 0);
qDebug() << "LPV SetImageData OK!";
std::pair<int, QVector<ILMatchResultPtr>> templateLevelAndvecMatchRltPtr;
bool bRlt = smokeMatch(typeNo, ilImgGray, templateLevelAndvecMatchRltPtr);
drawImage(imgMatGray, rltMat, templateLevelAndvecMatchRltPtr);
return bRlt;
}
bool CategoryMatcher::smokeMatch(const QString& typeNo, const ILImagePtr ilImgPtr,
std::pair<int, QVector<ILMatchResultPtr>>& templateLevelAndvecMatchRltPtr)
{
TemplateObject templateObj;
QString templateImgFolderPath = TYPE_TEMPLATE_PATH;
QString folderPath = QString("%1\\%2").arg(templateImgFolderPath).arg(typeNo);
if (getTemplateImgFileInfo(folderPath, templateObj))
{
qDebug() << "Get TemplateImg OK!";
QVector<double> vecMatchSroreMax;
QVector<ILMatchResultPtr> vecMatchRltsMax;
QList<int> keys = templateObj.uniqueKeys();
int templateLevel = keys.length();
qDebug() << "templateLevel = " << templateLevel;
templateLevelAndvecMatchRltPtr.first = templateLevel;
// 遍历不同模板级别
for each (int level in keys)
{
QVector<double> vecMatchSrore;
QVector<ILMatchResultPtr> vecMatchRlts;
QMap<QString, int> strScoreMap = templateObj.value(level);
for (QMap<QString, int>::iterator it = strScoreMap.begin(); it != strScoreMap.end(); ++it)
{
//需要验证没用1类模板但有2类模板的图
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));
ILImageArithmPtr imgArithmF(__uuidof(LImageArithm));
imgArithmF->Flip(ilTemplatePtr, LPVFlipType::LPVFlipH, tempImage);
imgArithmF->Flip(tempImage, LPVFlipType::LPVFlipV, tempImage);
m_ilmatchF->Learn(tempImage, nullptr);
ILMatchResultsPtr matchResults, matchResultsF;
if (level == 1) // 大模板
{
ILImageArithmPtr imgArithm(__uuidof(LImageArithm));
imgArithm->Resize(ilImgPtr, 0.5, 0.5, LPVInterNearest, img);
}
LPVErrorCode err = m_ilmatch->Match((level == 1 ? img : ilImgPtr), nullptr, &matchResults);
LPVErrorCode errF = m_ilmatchF->Match((level == 1 ? img : ilImgPtr), nullptr, &matchResultsF);
if (err < LPVErrorCode::LPVNoError || errF < LPVErrorCode::LPVNoError) {
//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())
{
break;
}
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));
}
}
if (vecMatchSroreMax.isEmpty() || (vecMatchSroreMax.length() != templateLevel))
{
QString outStr = QString("Level: %1 match failed!")
.arg(vecMatchSroreMax.length() + 1);
qDebug() << outStr;
templateLevelAndvecMatchRltPtr.second = vecMatchRltsMax;
return false;
}
else
{
templateLevelAndvecMatchRltPtr.second = vecMatchRltsMax;
}
}
else
{
QString outStr = QString("%1 find templateImage failed!").arg(folderPath);
qDebug() << outStr;
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("_");
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(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;
}
}