自己写的demo记个笔记用
替换掉图片路径和保存路径svm训练的模型路径就可以跑
效果我觉的不行,目前也不知到如何优化、希望有大佬可以给点建议
流程
处理超像素
选择超像素
提取HOG、颜色直方图、LBP直方图特征
训练
预测
cpp
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/ximgproc.hpp>
#include <opencv2/ml.hpp>
using namespace cv;
using namespace std;
using namespace cv::ml;
Mat g_LabelSlic;
Mat g_MaskSlic;
int g_NumSuperPixels;
//目标图像和标签
multimap<int, Mat>g_mapImgSuperPixelsOfTarget;
multimap<int, Mat>g_mapImgSuperPixelsOfNonTarget;
struct MouseCallbackData {
Mat img; // 原始图像
Mat imgClone; // 克隆图像
};
Mat GetSuperPixelsByLabel(const Mat& img, int superpixelID)
{
int minX = img.cols, minY = img.rows, maxX = 0, maxY = 0;
for (int y = 0; y < img.rows; y++) {
for (int x = 0; x < img.cols; x++) {
if (g_LabelSlic.at<int>(y, x) == superpixelID) {
// 更新边界框坐标
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
}
}
}
// 确保边界框有效
if (minX > maxX || minY > maxY) {
cout << "未找到有效的超像素!" << endl;
return Mat();
}
Rect superPixelBoundingBox(minX, minY, maxX - minX + 1, maxY - minY + 1);
Mat croppedRegion = img(superPixelBoundingBox).clone();
for (int y = 0; y < croppedRegion.rows; y++) {
for (int x = 0; x < croppedRegion.cols; x++) {
int origX = x + minX;
int origY = y + minY;
if (g_LabelSlic.at<int>(origY, origX) != superpixelID) {
croppedRegion.at<Vec3b>(y, x) = Vec3b(0, 0, 0);
}
}
}
return croppedRegion;
}
vector<float> GetHOGDescriptor(Mat img)
{
if (img.empty()) {
std::cerr << "输入图像为空!" << std::endl;
return {};
}
resize(img, img, Size(64, 64));
Mat imgGray;
cvtColor(img, imgGray, COLOR_BGR2GRAY);
HOGDescriptor hog(
Size(32, 32), // 图像窗口大小
Size(8, 8), // 块大小
Size(4, 4), // 块步长
Size(4, 4), // cell 大小
9 // 梯度方向 bins 数
);
vector<float> descriptors;
hog.compute(imgGray, descriptors);
return descriptors;
}
// 提取 LBP 特征及其直方图
void ExtractLBPFeatures(Mat img, Mat& lbp, Mat& lbpHist)
{
resize(img, img, Size(64, 64));
// 转换为灰度图
Mat gray;
if (img.channels() == 3) {
cvtColor(img, gray, COLOR_BGR2GRAY);
}
else {
gray = img.clone();
}
// 初始化 LBP 特征矩阵
lbp = Mat(gray.size(), CV_8UC1, Scalar(0));
for (int y = 1; y < gray.rows - 1; y++) {
for (int x = 1; x < gray.cols - 1; x++) {
uchar center = gray.at<uchar>(y, x);
uchar code = 0;
code |= (gray.at<uchar>(y - 1, x - 1) > center) << 7; // 128
code |= (gray.at<uchar>(y - 1, x) > center) << 6; // 64
code |= (gray.at<uchar>(y - 1, x + 1) > center) << 5; // 32
code |= (gray.at<uchar>(y, x + 1) > center) << 4; // 16
code |= (gray.at<uchar>(y + 1, x + 1) > center) << 3; // 8
code |= (gray.at<uchar>(y + 1, x) > center) << 2; // 4
code |= (gray.at<uchar>(y + 1, x - 1) > center) << 1; // 2
code |= (gray.at<uchar>(y, x - 1) > center); // 1
lbp.at<uchar>(y, x) = code; // 将计算的 LBP 值存储
}
}
// 计算 LBP 直方图
const int histSize = 256; // LBP 特征值的范围
const float range[] = { 0, 256 };
const float* histRange = { range };
calcHist(&lbp, 1, 0, Mat(), lbpHist, 1, &histSize, &histRange);
normalize(lbpHist, lbpHist);
}
Mat ExtractHSVHistogram(Mat img)
{
resize(img, img, Size(64, 64));
Mat hsv_img;
cvtColor(img, hsv_img, cv::COLOR_BGR2HSV);
vector<cv::Mat> hsv_planes;
split(hsv_img, hsv_planes); // 分割 H, S, V 通道
int histSize = 256; // 直方图分为 256 个区间
float h_range[] = { 0, 180 }; // H 通道范围是 0-180
float s_v_range[] = { 0, 256 }; // S 和 V 通道范围是 0-256
const float* h_histRange = { h_range };
const float* sv_histRange = { s_v_range };
Mat h_hist, s_hist, v_hist;
calcHist(&hsv_planes[0], 1, 0, cv::Mat(), h_hist, 1, &histSize, &h_histRange, true, false); // H 通道直方图
calcHist(&hsv_planes[1], 1, 0, cv::Mat(), s_hist, 1, &histSize, &sv_histRange, true, false); // S 通道直方图
calcHist(&hsv_planes[2], 1, 0, cv::Mat(), v_hist, 1, &histSize, &sv_histRange, true, false); // V 通道直方图
// 合并 H, S, V 直方图
Mat hist;
hconcat(h_hist, s_hist, hist);
hconcat(hist, v_hist, hist);
Mat normalizedHist;
normalize(hist, normalizedHist);
return normalizedHist; // 返回归一化后的直方图
}
void OnMouse(int event, int x, int y, int flags, void* param)
{
MouseCallbackData* data = static_cast<MouseCallbackData*>(param);
Mat& img = data->img;
Mat& imgClone = data->imgClone;
int mouseButtonClicked = 0;
if (event == EVENT_LBUTTONDOWN) {
mouseButtonClicked = 1;
if (x >= 0 && x < img.cols && y >= 0 && y < img.rows) {
int superpixelID = g_LabelSlic.at<int>(y, x);
cout << "点击目标超像素ID: " << superpixelID << endl;
g_mapImgSuperPixelsOfTarget.insert({ mouseButtonClicked, GetSuperPixelsByLabel(img, superpixelID) });
circle(imgClone, Point(x, y), 3, Scalar(0, 255, 0), -1);
imshow("Imageview", imgClone);
}
}
else if (event == EVENT_RBUTTONDOWN) {
mouseButtonClicked = 2;
if (x >= 0 && x < img.cols && y >= 0 && y < img.rows) {
int superpixelID = g_LabelSlic.at<int>(y, x);
cout << "点击非目标超像素ID: " << superpixelID << endl;
g_mapImgSuperPixelsOfNonTarget.insert({ mouseButtonClicked, GetSuperPixelsByLabel(img, superpixelID) });
circle(imgClone, Point(x, y), 3, Scalar(0, 0, 255), -1);
imshow("Imageview", imgClone);
}
}
}
void SvmClassifier(
multimap<int, vector<float>> HOGDescriptorOFTarget,
multimap<int, Mat> lbpHistOFTarget,
multimap<int, Mat> hsvHistOFTarget,
multimap<int, vector<float>> HOGDescriptorOFNonTarget,
multimap<int, Mat> lbpHistOFNonTarget,
multimap<int, Mat> hsvHistOFNonTarget)
{
cout << "star svm model train ..." << endl;
Mat featureList;
Mat labels;
for (const auto& pair : HOGDescriptorOFTarget) {
Mat hogMat(pair.second, CV_32F);
normalize(hogMat, hogMat, 0, 1, NORM_MINMAX);
hogMat = hogMat.reshape(1, 1);
Mat lbpHist = lbpHistOFTarget.find(pair.first)->second;
lbpHist.convertTo(lbpHist, CV_32F);
lbpHist = lbpHist.reshape(1, 1); // 展平
Mat hsvHist = hsvHistOFTarget.find(pair.first)->second;
hsvHist.convertTo(hsvHist, CV_32F);
hsvHist = hsvHist.reshape(1, 1); // 展平
Mat combinedFeature;
hconcat(hogMat, lbpHist, combinedFeature);
hconcat(combinedFeature, hsvHist, combinedFeature);
featureList.push_back(combinedFeature);
labels.push_back(1);
}
for (const auto& pair : HOGDescriptorOFNonTarget) {
Mat hogMat(pair.second, CV_32F);
normalize(hogMat, hogMat, 0, 1, NORM_MINMAX);
hogMat = hogMat.reshape(1, 1);
Mat lbpHist = lbpHistOFNonTarget.find(pair.first)->second;
lbpHist.convertTo(lbpHist, CV_32F);
lbpHist = lbpHist.reshape(1, 1);
Mat hsvHist = hsvHistOFNonTarget.find(pair.first)->second;
hsvHist.convertTo(hsvHist, CV_32F);
hsvHist = hsvHist.reshape(1, 1);
Mat combinedFeature;
hconcat(hogMat, lbpHist, combinedFeature);
hconcat(combinedFeature, hsvHist, combinedFeature);
featureList.push_back(combinedFeature);
labels.push_back(0);
}
Mat trainingData;
vconcat(featureList, trainingData);
//Mat labelsMat;// = Mat(labels).reshape(1, 1);
Mat labelsMat = labels;
trainingData.convertTo(trainingData, CV_32F);
labelsMat.convertTo(labelsMat, CV_32S);
Ptr<SVM> svm = SVM::create();
svm->setKernel(SVM::RBF);
svm->setType(SVM::C_SVC);
svm->trainAuto(trainingData,ROW_SAMPLE,labelsMat,10);
/*svm->setC(1.5);
svm->setGamma(0.5);
svm->setTermCriteria(cv::TermCriteria(cv::TermCriteria::MAX_ITER, 200, 1e-6));
svm->train(trainingData, ROW_SAMPLE, labelsMat);*/
svm->save("C:/Users/svs/Desktop/svm_model2.xml"); // 保存训练好的模型
}
void DrawSuperpixelRegion(Mat& img, int targetLabel) {
for (int y = 0; y < img.rows; y++) {
for (int x = 0; x < img.cols; x++) {
if (g_LabelSlic.at<int>(y, x) == targetLabel) {
img.at<Vec3b>(y, x) = Vec3b(0, 255, 0);
}
}
}
}
int main() {
Mat img = imread("C:/Users/svs/Desktop/test.jpeg");
if (img.empty()) {
cerr << "无法读取图片!" << endl;
return -1;
}
//图片进行超像素分割
int region_size = 50;
float ruler = 20.0;
int num_iterations = 100;
Ptr<ximgproc::SuperpixelSLIC> slic = ximgproc::createSuperpixelSLIC(img, ximgproc::SLICO,region_size, ruler);
slic->iterate(num_iterations);
slic->getLabels(g_LabelSlic);
slic->getLabelContourMask(g_MaskSlic);
g_NumSuperPixels = slic->getNumberOfSuperpixels();
Mat imgWithContours;
img.copyTo(imgWithContours, ~g_MaskSlic);
imshow("Superpixel Contours", imgWithContours);
cout << "请点击选择一个超像素区域...\n";
namedWindow("Imageview", WINDOW_AUTOSIZE);
imshow("Image", img);
Mat imgClone = img.clone();
imshow("Imageview", imgClone);
MouseCallbackData data;
data.img = img;
data.imgClone = imgClone;
setMouseCallback("Imageview", OnMouse, &data);
// 等待用户按 'q' 退出
while (true) {
const char key = waitKey(0);
if (key == 'q') {
break;
}
}
/*提取获取图片的特征*/
multimap<int, vector<float> >HOGDescriptorOFTarget;
multimap<int, Mat >lbpHistOFTarget;
multimap<int, Mat >hsvHistOFTarget;
multimap<int, vector<float> >HOGDescriptorOFNonTarget;
multimap<int, Mat>lbpHistOFNonTarget;
multimap<int, Mat>hisHistOFNonTarget;
for (const auto& pair : g_mapImgSuperPixelsOfTarget)
{
vector<float> vectorHOGDescriptor = GetHOGDescriptor(pair.second);
HOGDescriptorOFTarget.insert({ pair.first, vectorHOGDescriptor});
Mat lbpImg, lbpHist;
ExtractLBPFeatures(pair.second, lbpImg, lbpHist);
lbpHistOFTarget.insert({ pair.first, lbpHist });
Mat hsvHist;
hsvHist = ExtractHSVHistogram(pair.second);
hsvHistOFTarget.insert({ pair.first,hsvHist });
/*imshow("true", pair.second);
waitKey(0);*/
}
for (const auto& pair : g_mapImgSuperPixelsOfNonTarget)
{
vector<float> vectorHOGDescriptor = GetHOGDescriptor(pair.second);
HOGDescriptorOFNonTarget.insert({ pair.first,vectorHOGDescriptor });
Mat lbpImg, lbpHist;
ExtractLBPFeatures(pair.second, lbpImg, lbpHist);
lbpHistOFNonTarget.insert({ pair.first, lbpHist });
Mat hsvHist;
hsvHist = ExtractHSVHistogram(pair.second);
hisHistOFNonTarget.insert({ pair.first,hsvHist});
/* imshow("false", pair.second);
waitKey(0);*/
}
SvmClassifier(HOGDescriptorOFTarget, lbpHistOFTarget, hsvHistOFTarget,
HOGDescriptorOFNonTarget,lbpHistOFNonTarget, hisHistOFNonTarget);
Ptr<SVM> svm = SVM::load("C:/Users/svs/Desktop/svm_model2.xml");
if (svm.empty()) {
std::cerr << "模型加载失败!\n";
return -1;
}
//进行预测
cout << "star predict ...\n";
multimap<int, Mat> testSuperPixelsImg;
vector<float> testHOGDescriptor;
Mat testLbpHist;
Mat testHsvHist;
// 遍历每个超像素块
for (int superpixelID = 0; superpixelID < g_NumSuperPixels; ++superpixelID) {
Mat superpixelRegion = GetSuperPixelsByLabel(img, superpixelID);
testSuperPixelsImg.insert({ superpixelID, superpixelRegion });
}
for (const auto& pairs : testSuperPixelsImg) {
/*imshow("test", pairs.second);
waitKey(0);*/
testHOGDescriptor = GetHOGDescriptor(pairs.second);
Mat lbpImg;
ExtractLBPFeatures(pairs.second, lbpImg,testLbpHist);
testHsvHist = ExtractHSVHistogram(pairs.second);
if (testHOGDescriptor.empty())
{
cerr << "HOG特征为空,超像素ID: \n";
}
if (testLbpHist.empty())
{
cerr << "LBP特征为空,超像素ID: \n";
}
if(testHsvHist.empty())
{
cerr << "Hsv直方图为空,超像素ID:\n";
}
Mat testHogMat(testHOGDescriptor, CV_32F);
normalize(testHogMat, testHogMat, 0, 1, NORM_MINMAX);
testHogMat = testHogMat.reshape(1, 1);
testLbpHist.convertTo(testLbpHist, CV_32F);
testLbpHist = testLbpHist.reshape(1, 1);
testHsvHist.convertTo(testHsvHist, CV_32F);
testHsvHist = testHsvHist.reshape(1, 1);
Mat combinedFeature;
hconcat(testHogMat, testLbpHist, combinedFeature);
hconcat(combinedFeature, testHsvHist, combinedFeature);
// 进行预测
float response;
response = svm->predict(combinedFeature);
if (response == 1)
{
}
else
{
DrawSuperpixelRegion(img, pairs.first);
}
}
imshow("Image with Green Superpixel", img);
cout << "predict successful\n";
waitKey(0);
destroyAllWindows();
return 0;
}