[OpenCV3.4.3] KNN使用教程
阿新 • • 發佈:2018-12-13
// 參考資料:(建議先閱讀此處)
// https://blog.csdn.net/chaipp0607/article/details/77966888
// https://stackoverflow.com/questions/28035484/opencv-3-knn-implementation
#include <iostream>
#include <string>
#include <opencv2/ml.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
void Init( )
{
std::cout << "Init..." << std::endl;
// 我們使用的訓練樣本是OpenCV自帶的一張圖,在\opencv\sources\samples\data目錄下
// 我把這張圖拷貝到我的桌面上了,大家可以自行修改路徑
auto Img = cv::imread(R"(C:\Users\Crablet\Desktop\digits.png)");
decltype(Img) Gray;
cv::cvtColor(Img, Gray, CV_BGR2GRAY);
constexpr auto b = 20;
const auto m = Gray.rows / b, n = Gray.cols / b;
auto FileName = 0, FileNum = 0;
for (int i = 0; i < m; ++i)
{
if (i % 5 == 0 && i != 0)
{
++FileName;
FileNum = 0;
}
const auto OffsetRow = i * b;
for (int j = 0; j < n; ++j)
{
const auto OffsetCol = j * b;
const auto Address
= R"(C:\Users\Crablet\Desktop\Out\)"
+ std::to_string(FileName)
+ std::to_string(FileNum++)
+ R"(.jpg)";
decltype(Gray) Tmp;
Gray(cv::Range(OffsetRow, OffsetRow + b), cv::Range(OffsetCol, OffsetCol + b))
.copyTo(Tmp);
cv::imwrite(Address, Tmp);
}
}
std::cout << "Init done.\n" << std::endl;
}
void TrainAndPredict(int k)
{
std::cout << "Training..." << std::endl;
cv::Mat TrainData, TrainLabel;
for (int i = 0; i < 10; ++i)
{
for (int j = 0; j < 400; ++j)
{
const auto Address
= R"(C:\Users\Crablet\Desktop\Out\)"
+ std::to_string(i)
+ std::to_string(j)
+ R"(.jpg)";
const auto Src = cv::imread(Address).reshape(1, 1);
TrainData.push_back(Src);
TrainLabel.push_back(i);
}
}
TrainData.convertTo(TrainData, CV_32F);
// 這是新版OpenCV的介面,網上很多資料都舊了,所以我就寫一篇文章介紹一下新版介面的用法
// 建議先閱讀“參考資料”中的內容
auto KNN = cv::ml::KNearest::create();
KNN->setDefaultK(k);
KNN->setIsClassifier(true);
KNN->setAlgorithmType(cv::ml::KNearest::BRUTE_FORCE);
KNN->train(TrainData, cv::ml::ROW_SAMPLE, TrainLabel);
std::cout << "Predicting..." << std::endl;
auto TestNum = 0, TrueNum = 0;
for (int i = 0; i < 10; ++i)
{
for (int j = 400; j < 500; ++j)
{
++TestNum;
const auto Address
= R"(C:\Users\Crablet\Desktop\Out\)"
+ std::to_string(i)
+ std::to_string(j)
+ R"(.jpg)";
auto TestData = cv::imread(Address).reshape(1, 1);
TestData.convertTo(TestData, CV_32F);
// 這個Dummy只是為了佔個位,我們並不需要它的內容,因為我們只想讀取KNN->findNearest的返回值而已
// 注意:“auto Response = KNN->findNearest(TestData, KNN->getDefaultK(), cv::Mat());”這樣寫是不行的
// 所以我們才需要這個Dummy
cv::Mat Dummy;
auto Response = KNN->findNearest(TestData, KNN->getDefaultK(), Dummy);
if (static_cast<int>(Response) == i)
{
++TrueNum;
}
}
}
std::cout << "K: " << k << std::endl;
std::cout << "TrueNum: " << TrueNum << std::endl;
std::cout << "TestNum: " << TestNum << std::endl;
std::cout << "Result: " << 1.0 * TrueNum / TestNum << std::endl;
std::cout << std::endl;
}
int main()
{
Init();
for (int i = 1; i <= 10; ++i)
{
TrainAndPredict(i);
}
return 0;
}
效果圖: